Skip to content

Commit

Permalink
core: indexer re-implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Feb 22, 2024
1 parent 6058818 commit 0a17af1
Show file tree
Hide file tree
Showing 15 changed files with 1,389 additions and 678 deletions.
2 changes: 2 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,7 @@ repositories {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'net.jthink:jaudiotagger:3.0.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.3'
}
451 changes: 451 additions & 0 deletions android/app/src/main/kotlin/com/example/namida/FAudioTagger.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class NamidaMainActivity : FlutterActivity() {
private var toast: Toast? = null

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
flutterEngine.plugins.add(FAudioTagger())

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
pipBuilder = PictureInPictureParams.Builder()
Expand Down
234 changes: 234 additions & 0 deletions lib/class/faudiomodel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import 'dart:io';
import 'dart:typed_data';

import 'package:namida/core/extensions.dart';

class FArtwork {
/// if specified directory to save in.
/// or to save a new artwork when writing tags.
File? file;

/// if no directory to save in was specified.
Uint8List? bytes;

int? size;

bool get hasArtwork => file != null || bytes != null;

int? get sizeActual => bytes?.length ?? file?.sizeInBytesSync();

FArtwork({
this.file,
this.bytes,
this.size,
});

factory FArtwork.fromMap(Map<String, dynamic> map) {
final art = map["artwork"];
return FArtwork(
file: art is String ? File(art) : null,
bytes: art is Uint8List ? art : null,
size: map["artworkLength"],
);
}

dynamic toMapValue() => file?.path ?? bytes;

@override
String toString() {
return file?.toString() ?? bytes?.length.toString() ?? 'null';
}
}

class FTags {
/// Used for bulk extractions.
final String path;
final FArtwork artwork;
final String? title;
final String? album;
final String? albumArtist;
final String? artist;
final String? composer;
final String? genre;
final String? trackNumber;
final String? trackTotal;
final String? discNumber;
final String? discTotal;
final String? lyrics;
final String? comment;
final String? year;
final String? language;
final String? lyricist;
final String? djmixer;
final String? mixer;
final String? mood;
final String? rating;
final String? remixer;
final String? tags;
final String? tempo;
final String? country;
final String? recordLabel;

const FTags({
required this.path,
required this.artwork,
this.title,
this.album,
this.albumArtist,
this.artist,
this.composer,
this.genre,
this.trackNumber,
this.trackTotal,
this.discNumber,
this.discTotal,
this.lyrics,
this.comment,
this.year,
this.language,
this.lyricist,
this.djmixer,
this.mixer,
this.mood,
this.rating,
this.remixer,
this.tags,
this.tempo,
this.country,
this.recordLabel,
});

Map<String, dynamic> toMap() {
return <String, dynamic>{
"path": path,
"artwork": artwork.toMapValue(),
"title": title,
"album": album,
"albumArtist": albumArtist,
"artist": artist,
"composer": composer,
"genre": genre,
"comment": comment,
"year": year,
"trackNumber": trackNumber,
"trackTotal": trackTotal,
"discNumber": discNumber,
"discTotal": discTotal,
"lyrics": lyrics,
"lyricist": lyricist,
"djmixer": djmixer,
"mixer": mixer,
"mood": mood,
"rating": rating,
"remixer": remixer,
"tags": tags,
"tempo": tempo,
"country": country,
"recordLabel": recordLabel,
"language": language,
};
}
}

class FAudioModel {
final FTags tags;
final int? length;
final int? bitRate;
final String? channels;
final String? encodingType;
final String? format;
final int? sampleRate;
final bool? isVariableBitRate;
final bool? isLoseless;
final bool hasError;
final Map<String, String> errorsMap;

const FAudioModel({
required this.tags,
this.length,
this.bitRate,
this.channels,
this.encodingType,
this.format,
this.sampleRate,
this.isVariableBitRate,
this.isLoseless,
this.hasError = false,
this.errorsMap = const {},
});

factory FAudioModel.dummy(String? path) {
return FAudioModel(tags: FTags(path: path ?? '', artwork: FArtwork(size: 0)), hasError: true);
}

factory FAudioModel.fromMap(Map<String, dynamic> map) {
return FAudioModel(
tags: FTags(
path: map["path"],
artwork: FArtwork.fromMap(map),
title: map["title"],
album: map["album"],
albumArtist: map["albumArtist"],
artist: map["artist"],
composer: map["composer"],
genre: map["genre"],
trackNumber: map["trackNumber"],
trackTotal: map["trackTotal"],
discNumber: map["discNumber"],
discTotal: map["discTotal"],
lyrics: map["lyrics"],
comment: map["comment"],
year: map["year"],
language: map["language"],
lyricist: map["lyricist"],
djmixer: map["djmixer"],
mixer: map["mixer"],
mood: map["mood"],
rating: map["rating"],
remixer: map["remixer"],
tags: map["tags"],
tempo: map["tempo"],
country: map["country"],
recordLabel: map["recordLabel"],
),
length: map["length"],
bitRate: map["bitRate"],
channels: map["channels"],
encodingType: map["encodingType"],
format: map["format"],
sampleRate: map["sampleRate"],
isVariableBitRate: map["isVariableBitRate"],
isLoseless: map["isLoseless"],
hasError: map["ERROR_FAULTY"] == true,
errorsMap: (map["ERRORS"] as Map?)?.cast() ?? {},
);
}

Map<String, dynamic> _toMapMini() {
final tagsMap = tags.toMap();
tagsMap.addAll(<String, dynamic>{
"length": length,
"bitRate": bitRate,
"channels": channels,
"encodingType": encodingType,
"format": format,
"sampleRate": sampleRate,
"isVariableBitRate": isVariableBitRate,
"isLoseless": isLoseless,
});
return tagsMap;
}

Map<String, dynamic> toMap() {
final map = _toMapMini();
map["artwork"] = tags.artwork.toMapValue();
return map;
}

@override
String toString() {
final map = _toMapMini();
map["artworkDetails"] = tags.artwork.toString();
return map.toString();
}
}
3 changes: 3 additions & 0 deletions lib/class/media_info.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
class MediaInfo {
final String path;
final List<MIStream>? streams;
final MIFormat? format;

const MediaInfo({
required this.path,
this.streams,
this.format,
});

factory MediaInfo.fromMap(Map<dynamic, dynamic> json) {
return MediaInfo(
path: json["PATH"],
streams: (json["streams"] as List?)?.map((e) => MIStream.fromMap(e)).toList(),
format: json["format"] == null ? null : MIFormat.fromMap(json["format"]),
);
Expand Down
4 changes: 2 additions & 2 deletions lib/class/track.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:io';

import 'package:faudiotagger/models/tag.dart';
import 'package:history_manager/history_manager.dart';
import 'package:intl/intl.dart';

import 'package:namida/class/faudiomodel.dart';
import 'package:namida/class/folder.dart';
import 'package:namida/class/split_config.dart';
import 'package:namida/controller/indexer_controller.dart';
Expand Down Expand Up @@ -353,7 +353,7 @@ extension TrackExtUtils on TrackExtended {
}

TrackExtended copyWithTag({
required Tag tag,
required FTags tag,
int? dateModified,
String? path,
}) {
Expand Down
20 changes: 7 additions & 13 deletions lib/controller/edit_delete_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:namida/controller/playlist_controller.dart';
import 'package:namida/controller/queue_controller.dart';
import 'package:namida/controller/selected_tracks_controller.dart';
import 'package:namida/controller/settings_controller.dart';
import 'package:namida/controller/tagger_controller.dart';
import 'package:namida/controller/video_controller.dart';
import 'package:namida/core/constants.dart';
import 'package:namida/core/extensions.dart';
Expand Down Expand Up @@ -56,20 +57,13 @@ class EditDeleteController {
}
final saveDir = await Directory(AppDirs.SAVED_ARTWORKS).create(recursive: true);
final saveDirPath = saveDir.path;
final newPath = "$saveDirPath${Platform.pathSeparator}${track.filenameWOExt}.png";
final imgFiles = await Indexer.inst.extractTracksArtworks(
[track.path],
albumIdendifiers: {track.path: track.albumIdentifier},
final info = await FAudioTaggerController.inst.extractMetadata(
trackPath: track.path,
cacheDirectoryPath: saveDirPath,
);
final imgFile = imgFiles.firstOrNull;
try {
// try copying
await imgFile?.copy(newPath);
return saveDirPath;
} catch (e) {
printy(e, isError: true);
return null;
}
final imgFile = info.tags.artwork.file;
if (imgFile != null) return saveDirPath;
return null;
}

/// returns save directory path if saved successfully
Expand Down
26 changes: 16 additions & 10 deletions lib/controller/ffmpeg_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:get/get_rx/get_rx.dart';

import 'package:namida/class/media_info.dart';
import 'package:namida/class/track.dart';
import 'package:namida/controller/indexer_controller.dart';
import 'package:namida/controller/tagger_controller.dart';
import 'package:namida/controller/thumbnail_manager.dart';
import 'package:namida/core/constants.dart';
import 'package:namida/core/extensions.dart';
Expand All @@ -32,6 +32,7 @@ class NamidaFFMPEG {
if (output != null && output != '') {
try {
final decoded = jsonDecode(output);
decoded["PATH"] = path;
final mi = MediaInfo.fromMap(decoded);
final formatGood = (decoded['format'] as Map?)?.isNotEmpty ?? false;
final tagsGood = (decoded['format']?['tags'] as Map?)?.isNotEmpty ?? false;
Expand All @@ -44,10 +45,12 @@ class NamidaFFMPEG {

final map = information?.getAllProperties();
if (map != null) {
map["PATH"] = path;
final miBackup = MediaInfo.fromMap(map);
final format = miBackup.format;
final tags = information?.getTags() ?? (map['streams'] as List?)?.firstWhereEff((e) => e['tags'].isNotEmpty)?['tags'];
final mi = MediaInfo(
path: path,
streams: miBackup.streams,
format: MIFormat(
bitRate: format?.bitRate ?? information?.getBitrate(),
Expand Down Expand Up @@ -261,15 +264,18 @@ class NamidaFFMPEG {
if (cachedThumbnail == null) {
currentFailed++;
} else {
final file = await Indexer.inst
.extractTracksArtworks(
[filee.path],
forceReExtract: true,
artworkPaths: {filee.path: cachedThumbnail.path},
albumIdendifiers: {filee.path: tr.albumIdentifier},
)
.then((value) => value.first);
final didUpdate = file == null ? false : await editAudioThumbnail(audioPath: filee.path, thumbnailPath: file.path);
final copiedArtwork = await FAudioTaggerController.inst.copyArtworkToCache(
trackPath: filee.path,
trackExtended: tr,
artworkFile: cachedThumbnail,
);

final didUpdate = copiedArtwork == null
? false
: await editAudioThumbnail(
audioPath: filee.path,
thumbnailPath: copiedArtwork.path,
);
if (!didUpdate) currentFailed++;
}

Expand Down
Loading

0 comments on commit 0a17af1

Please sign in to comment.