Skip to content

Commit

Permalink
chore: extraction improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
MSOB7YY committed Oct 11, 2023
1 parent d2953e2 commit d0b55d7
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 149 deletions.
311 changes: 163 additions & 148 deletions lib/controller/indexer_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Indexer {

final RxBool isIndexing = false.obs;

final currentTrackPathBeingExtracted = ''.obs;
final RxSet<String> allAudioFiles = <String>{}.obs;
final RxInt filteredForSizeDurationTracks = 0.obs;
final RxInt duplicatedTracksLength = 0.obs;
Expand Down Expand Up @@ -242,172 +243,183 @@ class Indexer {
bool tryExtractingFromFilename = true,
bool extractColor = false,
}) async {
// -- returns null early depending on size [byte] or duration [seconds]
final fileStat = await File(trackPath).stat();
if (minSize != 0 && fileStat.size < minSize) {
if (onMinSizeTrigger != null) onMinSizeTrigger();
return null;
}

late TrackExtended finalTrackExtended;
// -- most methods dont throw, except for timeout
try {
const timeoutDuration = Duration(seconds: 10); // 10s for each method, 2 more fallback methods so totalDuration is upto 30s

FAudioModel? trackInfo;
Uint8List? artwork;
// -- returns null early depending on size [byte] or duration [seconds]
FileStat? fileStat;
try {
fileStat = await File(trackPath).stat();
if (minSize != 0 && fileStat.size < minSize) {
if (onMinSizeTrigger != null) onMinSizeTrigger();
return null;
}
} catch (_) {}

final infoAndArtwork = await _faudiotagger.extractMetadata(trackPath: trackPath);
late TrackExtended finalTrackExtended;

trackInfo = infoAndArtwork.$1;
artwork = infoAndArtwork.$2;
FAudioModel? trackInfo;
Uint8List? artwork;

if (trackInfo == null && !tryExtractingFromFilename) {
return null;
}
final infoAndArtwork = await _faudiotagger.extractMetadata(trackPath: trackPath).timeout(timeoutDuration);

final initialTrack = TrackExtended(
title: UnknownTags.TITLE,
originalArtist: UnknownTags.ARTIST,
artistsList: [UnknownTags.ARTIST],
album: UnknownTags.ALBUM,
albumArtist: UnknownTags.ALBUMARTIST,
originalGenre: UnknownTags.GENRE,
genresList: [UnknownTags.GENRE],
composer: UnknownTags.COMPOSER,
originalMood: UnknownTags.MOOD,
moodList: [UnknownTags.MOOD],
trackNo: 0,
duration: 0,
year: 0,
size: fileStat.size,
dateAdded: fileStat.accessed.millisecondsSinceEpoch,
dateModified: fileStat.changed.millisecondsSinceEpoch,
path: trackPath,
comment: '',
bitrate: 0,
sampleRate: 0,
format: '',
channels: '',
discNo: 0,
language: '',
lyrics: '',
);
if (trackInfo != null) {
int durationInSeconds = trackInfo.length ?? 0;
if (durationInSeconds == 0) {
final d = await NamidaFFMPEG.inst.getMediaDuration(trackPath);
durationInSeconds = d?.inSeconds ?? 0;
trackInfo = infoAndArtwork.$1;
artwork = infoAndArtwork.$2;

if (durationInSeconds == 0) {
final ap = AudioPlayer();
final dur = await ap.setFilePath(trackPath);
durationInSeconds = dur?.inSeconds ?? 0;
ap.dispose();
}
}
if (minDur != 0 && durationInSeconds < minDur) {
if (onMinDurTrigger != null) onMinDurTrigger();
if (trackInfo == null && !tryExtractingFromFilename) {
return null;
}

// -- Split Artists
final artists = splitArtist(
title: trackInfo.title,
originalArtist: trackInfo.artist,
config: ArtistsSplitConfig.settings(),
final initialTrack = TrackExtended(
title: UnknownTags.TITLE,
originalArtist: UnknownTags.ARTIST,
artistsList: [UnknownTags.ARTIST],
album: UnknownTags.ALBUM,
albumArtist: UnknownTags.ALBUMARTIST,
originalGenre: UnknownTags.GENRE,
genresList: [UnknownTags.GENRE],
composer: UnknownTags.COMPOSER,
originalMood: UnknownTags.MOOD,
moodList: [UnknownTags.MOOD],
trackNo: 0,
duration: 0,
year: 0,
size: fileStat?.size ?? 0,
dateAdded: fileStat?.accessed.millisecondsSinceEpoch ?? 0,
dateModified: fileStat?.changed.millisecondsSinceEpoch ?? 0,
path: trackPath,
comment: '',
bitrate: 0,
sampleRate: 0,
format: '',
channels: '',
discNo: 0,
language: '',
lyrics: '',
);
if (trackInfo != null) {
int durationInSeconds = trackInfo.length ?? 0;
if (durationInSeconds == 0) {
final d = await NamidaFFMPEG.inst.getMediaDuration(trackPath).timeout(timeoutDuration);
durationInSeconds = d?.inSeconds ?? 0;

if (durationInSeconds == 0) {
final ap = AudioPlayer();
final dur = await ap.setFilePath(trackPath).timeout(timeoutDuration);
durationInSeconds = dur?.inSeconds ?? 0;
ap.dispose();
}
}
if (minDur != 0 && durationInSeconds < minDur) {
if (onMinDurTrigger != null) onMinDurTrigger();
return null;
}

// -- Split Genres
final genres = splitGenre(
trackInfo.genre,
config: GenresSplitConfig.settings(),
);
// -- Split Artists
final artists = splitArtist(
title: trackInfo.title,
originalArtist: trackInfo.artist,
config: ArtistsSplitConfig.settings(),
);

// -- Split Moods (using same genre splitters)
final moods = splitGenre(
trackInfo.mood,
config: GenresSplitConfig.settings(),
);
// -- Split Genres
final genres = splitGenre(
trackInfo.genre,
config: GenresSplitConfig.settings(),
);

String? trimOrNull(String? value) => value == null ? value : value.trimAll();
String? nullifyEmpty(String? value) => value == '' ? null : value;
String? doMagic(String? value) => nullifyEmpty(trimOrNull(value));

finalTrackExtended = initialTrack.copyWith(
title: doMagic(trackInfo.title),
originalArtist: doMagic(trackInfo.artist),
artistsList: artists,
album: doMagic(trackInfo.album),
albumArtist: doMagic(trackInfo.albumArtist),
originalGenre: doMagic(trackInfo.genre),
genresList: genres,
originalMood: doMagic(trackInfo.mood),
moodList: moods,
composer: doMagic(trackInfo.composer),
trackNo: trackInfo.trackNumber.getIntValue(),
duration: durationInSeconds,
year: trackInfo.year.getIntValue(),
comment: trackInfo.comment,
bitrate: trackInfo.bitRate,
sampleRate: trackInfo.sampleRate,
format: trackInfo.format,
channels: trackInfo.channels,
discNo: trackInfo.discNumber.getIntValue(),
language: trackInfo.language,
lyrics: trackInfo.lyrics,
);
// -- Split Moods (using same genre splitters)
final moods = splitGenre(
trackInfo.mood,
config: GenresSplitConfig.settings(),
);

String? trimOrNull(String? value) => value == null ? value : value.trimAll();
String? nullifyEmpty(String? value) => value == '' ? null : value;
String? doMagic(String? value) => nullifyEmpty(trimOrNull(value));

finalTrackExtended = initialTrack.copyWith(
title: doMagic(trackInfo.title),
originalArtist: doMagic(trackInfo.artist),
artistsList: artists,
album: doMagic(trackInfo.album),
albumArtist: doMagic(trackInfo.albumArtist),
originalGenre: doMagic(trackInfo.genre),
genresList: genres,
originalMood: doMagic(trackInfo.mood),
moodList: moods,
composer: doMagic(trackInfo.composer),
trackNo: trackInfo.trackNumber.getIntValue(),
duration: durationInSeconds,
year: trackInfo.year.getIntValue(),
comment: trackInfo.comment,
bitrate: trackInfo.bitRate,
sampleRate: trackInfo.sampleRate,
format: trackInfo.format,
channels: trackInfo.channels,
discNo: trackInfo.discNumber.getIntValue(),
language: trackInfo.language,
lyrics: trackInfo.lyrics,
);

// ----- if the title || artist weren't found in the tag fields
final isTitleEmpty = finalTrackExtended.title == UnknownTags.TITLE;
final isArtistEmpty = finalTrackExtended.originalArtist == UnknownTags.ARTIST;
if (isTitleEmpty || isArtistEmpty) {
final extractedName = getTitleAndArtistFromFilename(trackPath.getFilenameWOExt);
final newTitle = isTitleEmpty ? extractedName.$1 : null;
final newArtists = isArtistEmpty ? [extractedName.$2] : null;
finalTrackExtended = finalTrackExtended.copyWith(
title: newTitle,
originalArtist: newArtists?.first,
artistsList: newArtists,
// ----- if the title || artist weren't found in the tag fields
final isTitleEmpty = finalTrackExtended.title == UnknownTags.TITLE;
final isArtistEmpty = finalTrackExtended.originalArtist == UnknownTags.ARTIST;
if (isTitleEmpty || isArtistEmpty) {
final extractedName = getTitleAndArtistFromFilename(trackPath.getFilenameWOExt);
final newTitle = isTitleEmpty ? extractedName.$1 : null;
final newArtists = isArtistEmpty ? [extractedName.$2] : null;
finalTrackExtended = finalTrackExtended.copyWith(
title: newTitle,
originalArtist: newArtists?.first,
artistsList: newArtists,
);
}
// ------------------------------------------------------------

extractOneArtwork(
trackPath,
bytes: artwork,
forceReExtract: deleteOldArtwork,
extractColor: extractColor,
albumIdendifier: finalTrackExtended.albumIdentifier,
);
} else {
// --- Adding dummy track with info extracted from filename.
final titleAndArtist = getTitleAndArtistFromFilename(trackPath.getFilenameWOExt);
final title = titleAndArtist.$1;
final artist = titleAndArtist.$2;
finalTrackExtended = initialTrack.copyWith(
title: title,
originalArtist: artist,
artistsList: [artist],
);
extractOneArtwork(
trackPath,
forceReExtract: deleteOldArtwork,
extractColor: extractColor,
albumIdendifier: finalTrackExtended.albumIdentifier,
);
}
// ------------------------------------------------------------

extractOneArtwork(
trackPath,
bytes: artwork,
forceReExtract: deleteOldArtwork,
extractColor: extractColor,
albumIdendifier: finalTrackExtended.albumIdentifier,
);
} else {
// --- Adding dummy track with info extracted from filename.
final titleAndArtist = getTitleAndArtistFromFilename(trackPath.getFilenameWOExt);
final title = titleAndArtist.$1;
final artist = titleAndArtist.$2;
finalTrackExtended = initialTrack.copyWith(
title: title,
originalArtist: artist,
artistsList: [artist],
);
extractOneArtwork(
trackPath,
forceReExtract: deleteOldArtwork,
extractColor: extractColor,
albumIdendifier: finalTrackExtended.albumIdentifier,
);
}

final tr = finalTrackExtended.toTrack();
allTracksMappedByPath[tr] = finalTrackExtended;
_currentFileNamesMap[trackPath.getFilename] = true;
if (checkForDuplicates) {
tracksInfoList.addNoDuplicates(tr);
SearchSortController.inst.trackSearchList.addNoDuplicates(tr);
} else {
tracksInfoList.add(tr);
SearchSortController.inst.trackSearchList.add(tr);
}
final tr = finalTrackExtended.toTrack();
allTracksMappedByPath[tr] = finalTrackExtended;
_currentFileNamesMap[trackPath.getFilename] = true;
if (checkForDuplicates) {
tracksInfoList.addNoDuplicates(tr);
SearchSortController.inst.trackSearchList.addNoDuplicates(tr);
} else {
tracksInfoList.add(tr);
SearchSortController.inst.trackSearchList.add(tr);
}

printy("tracksInfoList length: ${tracksInfoList.length}");
return finalTrackExtended;
printy("tracksInfoList length: ${tracksInfoList.length}");
return finalTrackExtended;
} catch (e) {
printy('Error or timeout occured while extracting $trackPath');
return null;
}
}

/// - Extracts artwork from [bytes] or [pathOfAudio] and save to file.
Expand Down Expand Up @@ -608,13 +620,15 @@ class Indexer {
final minSize = settings.indexMinFileSizeInB.value; // bytes
final prevDuplicated = settings.preventDuplicatedTracks.value;

currentTrackPathBeingExtracted.value = '';
if (audioFiles.isNotEmpty) {
// -- Extracting All Metadata
for (final trackPath in audioFiles) {
// breaks the loop if another indexing session has been started
if (_cancelableIndexingCompleter[cancelTokenTime]?.isCompleted == true) break;

printy(trackPath);
currentTrackPathBeingExtracted.value = trackPath;

/// skip duplicated tracks according to filename
if (prevDuplicated) {
Expand All @@ -635,6 +649,7 @@ class Indexer {

printy('Extracted All Metadata');
}
currentTrackPathBeingExtracted.value = '';

/// doing some checks to remove unqualified tracks.
/// removes tracks after changing `duration` or `size`.
Expand Down
16 changes: 15 additions & 1 deletion lib/ui/widgets/settings/indexer_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,28 @@ class IndexerSettings extends StatelessWidget {
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0),
child: Obx(
() => Text(
'${lang.DUPLICATED_TRACKS}: ${Indexer.inst.duplicatedTracksLength.value}\n${lang.TRACKS_EXCLUDED_BY_NOMEDIA}: ${Indexer.inst.tracksExcludedByNoMedia.value}\n${lang.FILTERED_BY_SIZE_AND_DURATION}: ${Indexer.inst.filteredForSizeDurationTracks.value}',
style: context.textTheme.displaySmall,
),
),
),
Obx(
() {
final p = Indexer.inst.currentTrackPathBeingExtracted.value;
return p == ''
? const SizedBox()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 4.0),
child: Text(
Indexer.inst.currentTrackPathBeingExtracted.value,
style: context.textTheme.displaySmall?.copyWith(fontSize: 11.0.multipliedFontScale),
),
);
},
),
Obx(
() => CustomSwitchListTile(
icon: Broken.copy,
Expand Down

0 comments on commit d0b55d7

Please sign in to comment.