From a669ad48f073fada40fe13f4058c0754c0a4a9f1 Mon Sep 17 00:00:00 2001 From: MSOB7YY Date: Mon, 19 Feb 2024 02:24:15 +0200 Subject: [PATCH] feat: use local track as a fallback youtube audio cache --- lib/base/audio_handler.dart | 31 +++++++++++++++++++++----- lib/controller/indexer_controller.dart | 18 +++++++++++---- lib/youtube/yt_utils.dart | 9 ++++++-- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/lib/base/audio_handler.dart b/lib/base/audio_handler.dart index e503daac..54a5ac3b 100644 --- a/lib/base/audio_handler.dart +++ b/lib/base/audio_handler.dart @@ -788,6 +788,8 @@ class NamidaAudioVideoHandler extends BasicAudioHandler { disableVideo: isAudioOnlyPlayback, whatToAwait: () async => await playerStoppingSeikoo.future, startPlaying: startPlaying, + possibleAudioFiles: audioCacheMap[item.id] ?? [], + possibleLocalFiles: Indexer.inst.allTracksMappedByYTID[item.id] ?? [], ); if (item != currentVideo) return; @@ -923,6 +925,8 @@ class NamidaAudioVideoHandler extends BasicAudioHandler { disableVideo: isAudioOnlyPlayback, whatToAwait: () async => await playerStoppingSeikoo.future, startPlaying: startPlaying, + possibleAudioFiles: audioCacheMap[item.id] ?? [], + possibleLocalFiles: Indexer.inst.allTracksMappedByYTID[item.id] ?? [], ); if (!okaySetFromCache()) { showSnackError('skipping'); @@ -962,6 +966,8 @@ class NamidaAudioVideoHandler extends BasicAudioHandler { required bool disableVideo, required Future Function() whatToAwait, required bool startPlaying, + required List possibleAudioFiles, + required List possibleLocalFiles, }) async { // ------ Getting Video ------ final allCachedVideos = VideoController.inst.getNVFromID(item.id); @@ -980,12 +986,27 @@ class NamidaAudioVideoHandler extends BasicAudioHandler { final mediaItem = item.toMediaItem(currentVideoInfo.value, currentVideoThumbnail.value, index, currentQueue.length); // ------ Getting Audio ------ - final audioFiles = await _getCachedAudiosForID.thready({ - "dirPath": AppDirs.AUDIOS_CACHE, - "id": item.id, - }); + final audioFiles = possibleAudioFiles.isNotEmpty + ? possibleAudioFiles + : await _getCachedAudiosForID.thready({ + "dirPath": AppDirs.AUDIOS_CACHE, + "id": item.id, + }); final finalAudioFiles = audioFiles..sortByReverseAlt((e) => e.bitrate ?? 0, (e) => e.file.fileSizeSync() ?? 0); - final cachedAudio = finalAudioFiles.firstOrNull; + AudioCacheDetails? cachedAudio = finalAudioFiles.firstOrNull; + + if (cachedAudio == null) { + final localTrack = possibleLocalFiles.firstOrNull; + if (localTrack != null) { + cachedAudio = AudioCacheDetails( + youtubeId: item.id, + bitrate: localTrack.bitrate, + langaugeCode: null, + langaugeName: null, + file: File(localTrack.path), + ); + } + } // ------ Playing ------ if (cachedVideo != null && cachedAudio != null && !disableVideo) { diff --git a/lib/controller/indexer_controller.dart b/lib/controller/indexer_controller.dart index 24f407d8..764070bf 100644 --- a/lib/controller/indexer_controller.dart +++ b/lib/controller/indexer_controller.dart @@ -69,6 +69,7 @@ class Indexer { final trackStatsMap = {}.obs; var allFolderCovers = {}; // {directoryPath, imagePath} + var allTracksMappedByYTID = >{}; /// Used to prevent duplicated track (by filename). final Map _currentFileNamesMap = {}; @@ -667,6 +668,7 @@ class Indexer { for (final trext in tracks) { final tr = trext.toTrack(); allTracksMappedByPath[tr] = trext; + allTracksMappedByYTID.addForce(trext.youtubeID, tr); _currentFileNamesMap[trext.path.getFilename] = true; if (checkForDuplicates) { tracksInfoList.addNoDuplicates(tr); @@ -876,6 +878,7 @@ class Indexer { oldTracks.add(ot); newTracks.add(nt); allTracksMappedByPath[ot] = e.value; + allTracksMappedByYTID.addForce(e.value.youtubeID, ot); _currentFileNamesMap.remove(ot.filename); _currentFileNamesMap[nt.filename] = true; @@ -923,6 +926,7 @@ class Indexer { void _clearLists() { tracksInfoList.clear(); allTracksMappedByPath.clear(); + allTracksMappedByYTID.clear(); SearchSortController.inst.sortMedia(MediaType.track); } @@ -965,6 +969,7 @@ class Indexer { final trs = await _fetchMediaStoreTracks(); tracksInfoList.clear(); allTracksMappedByPath.clear(); + allTracksMappedByYTID.clear(); _currentFileNamesMap.clear(); _addTracksToLists(trs.map((e) => e.$1), false); } else { @@ -1077,8 +1082,9 @@ class Indexer { genresConfig: GenresSplitConfig.settings(), ); final tracksResult = await _readTracksFileCompute.thready(splitconfig); - allTracksMappedByPath.value = tracksResult; - tracksInfoList.addAll(tracksResult.keys); + allTracksMappedByPath.value = tracksResult.$1; + allTracksMappedByYTID = tracksResult.$2; + tracksInfoList.value = tracksResult.$3; printy("All Tracks Length From File: ${tracksInfoList.length}"); } @@ -1100,8 +1106,10 @@ class Indexer { return map; } - static Future> _readTracksFileCompute(_SplitArtistGenreConfig config) async { + static (Map, Map>, List) _readTracksFileCompute(_SplitArtistGenreConfig config) { final map = {}; + final idsMap = >{}; + final allTracks = []; final list = File(config.path).readAsJsonSync() as List?; if (list != null) { for (int i = 0; i <= list.length - 1; i++) { @@ -1114,12 +1122,14 @@ class Indexer { ); final track = trExt.toTrack(); map[track] = trExt; + allTracks.add(track); + idsMap.addForce(trExt.youtubeID, track); } catch (e) { continue; } } } - return map; + return (map, idsMap, allTracks); } static List splitBySeparators(String? string, Iterable separators, String fallback, Iterable blacklist) { diff --git a/lib/youtube/yt_utils.dart b/lib/youtube/yt_utils.dart index be390257..99c1ed94 100644 --- a/lib/youtube/yt_utils.dart +++ b/lib/youtube/yt_utils.dart @@ -9,6 +9,7 @@ import 'package:playlist_manager/module/playlist_id.dart'; import 'package:share_plus/share_plus.dart'; import 'package:namida/class/video.dart'; +import 'package:namida/controller/indexer_controller.dart'; import 'package:namida/controller/ffmpeg_controller.dart'; import 'package:namida/controller/miniplayer_controller.dart'; import 'package:namida/controller/navigator_controller.dart'; @@ -81,7 +82,9 @@ class YTUtils { child: Icon( Broken.video, size: 15.0, - color: iconsColor?.withOpacity(VideoController.inst.getNVFromID(videoId).isNotEmpty ? 0.6 : 0.1), + color: iconsColor?.withOpacity( + VideoController.inst.getNVFromID(videoId).isNotEmpty ? 0.6 : 0.1, + ), ), ), const SizedBox(width: 4.0), @@ -90,7 +93,9 @@ class YTUtils { child: Icon( Broken.audio_square, size: 15.0, - color: iconsColor?.withOpacity(Player.inst.audioCacheMap[videoId] != null ? 0.6 : 0.1), + color: iconsColor?.withOpacity( + Player.inst.audioCacheMap[videoId] != null || Indexer.inst.allTracksMappedByYTID[videoId] != null ? 0.6 : 0.1, + ), ), ), ],