From 672e7acbc4b85e7743ad271ec2256b75055cd3cc Mon Sep 17 00:00:00 2001 From: Chaphasilor Date: Sat, 9 Nov 2024 16:12:43 +0100 Subject: [PATCH] Automatically download playlist metadata on first launch (#905) * automatically add playlist metadata download item during migration * download playlist metadata only after logging in - also reworded the download item name for it * don't require viewId for downloads, move queue restore into postLaunchHook * provide download location for playlist metadata download * fix playlist metadata status check * reword playlist metadata setting * review findings * remove check for existing playlist metadata --- lib/l10n/app_en.arb | 6 ++--- lib/main.dart | 2 +- lib/models/finamp_models.dart | 13 +++++++---- lib/models/finamp_models.g.dart | 20 ++++++++++------- lib/screens/music_screen.dart | 28 +++++++++++++++++++----- lib/services/downloads_service.dart | 25 ++++++++++++++++++++- lib/services/finamp_settings_helper.dart | 9 +++++++- 7 files changed, 80 insertions(+), 23 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 8130a13b..ebd8b1c7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1369,7 +1369,7 @@ }, "description": "Tooltip for downloadbutton on incidental downloads, which show a lock icon. It says one of the requiring downloads." }, - "finampCollectionNames": "{itemType, select, favorites{Favorites} allPlaylists{All Playlists} fiveLatestAlbums{5 Latest Albums} allPlaylistsMetadata{Info for All Playlists} other{{itemType}} }", + "finampCollectionNames": "{itemType, select, favorites{Favorites} allPlaylists{All Playlists} fiveLatestAlbums{5 Latest Albums} allPlaylistsMetadata{Playlist Metadata} other{{itemType}} }", "@finampCollectionNames": { "placeholders": { "itemType": { @@ -1601,8 +1601,8 @@ }, "trackOfflineFavorites": "Sync all favorite statuses", "trackOfflineFavoritesSubtitle": "This allows showing more up-to-date favorite statuses while offline. Does not download any additional files.", - "allPlaylistsInfoSetting": "Show all playlists offline", - "allPlaylistsInfoSettingSubtitle": "Sync metadata for all playlists to show partially downloaded playlists offline", + "allPlaylistsInfoSetting": "Download Playlist Metadata", + "allPlaylistsInfoSettingSubtitle": "Sync metadata for all playlists to improve your playlist experience", "downloadFavoritesSetting": "Download all favorites", "downloadAllPlaylistsSetting": "Download all playlists", "fiveLatestAlbumsSetting": "Download 5 latest albums", diff --git a/lib/main.dart b/lib/main.dart index acdbe933..38aefc95 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -150,7 +150,7 @@ Future _setupDownloadsHelper() async { final downloadsService = GetIt.instance(); if (!FinampSettingsHelper - .finampSettings.hasCompleteddownloadsServiceMigration) { + .finampSettings.hasCompletedDownloadsServiceMigration) { await downloadsService.migrateFromHive(); FinampSettingsHelper.setHasCompleteddownloadsServiceMigration(true); } diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index 2d78ebbc..f5068b6f 100644 --- a/lib/models/finamp_models.dart +++ b/lib/models/finamp_models.dart @@ -113,6 +113,7 @@ const _showStopButtonOnMediaNotificationDefault = false; const _showSeekControlsOnMediaNotificationDefault = true; const _keepScreenOnOption = KeepScreenOnOption.whileLyrics; const _keepScreenOnWhilePluggedIn = true; +const _hasDownloadedPlaylistInfoDefault = false; const _defaultTranscodingSegmentContainer = FinampSegmentContainer.fragmentedMp4; const _featureChipsConfigurationDefault = @@ -171,7 +172,7 @@ class FinampSettings { this.autoloadLastQueueOnStartup = _autoLoadLastQueueOnStartup, this.hasCompletedBlurhashImageMigration = true, this.hasCompletedBlurhashImageMigrationIdFix = true, - this.hasCompleteddownloadsServiceMigration = true, + this.hasCompletedDownloadsServiceMigration = true, this.requireWifiForDownloads = false, this.onlyShowFullyDownloaded = false, this.showDownloadsWithUnknownLibrary = true, @@ -211,6 +212,7 @@ class FinampSettings { _showSeekControlsOnMediaNotificationDefault, this.keepScreenOnOption = _keepScreenOnOption, this.keepScreenOnWhilePluggedIn = _keepScreenOnWhilePluggedIn, + this.hasDownloadedPlaylistInfo = _hasDownloadedPlaylistInfoDefault, this.transcodingSegmentContainer = _defaultTranscodingSegmentContainer, this.featureChipsConfiguration = _featureChipsConfigurationDefault}); @@ -326,7 +328,7 @@ class FinampSettings { VolumeNormalizationMode volumeNormalizationMode; @HiveField(34, defaultValue: false) - bool hasCompleteddownloadsServiceMigration; + bool hasCompletedDownloadsServiceMigration; @HiveField(35, defaultValue: false) bool requireWifiForDownloads; @@ -449,10 +451,13 @@ class FinampSettings { @HiveField(73, defaultValue: _keepScreenOnWhilePluggedIn) bool keepScreenOnWhilePluggedIn; - @HiveField(74, defaultValue: _defaultTranscodingSegmentContainer) + @HiveField(74, defaultValue: _hasDownloadedPlaylistInfoDefault) + bool hasDownloadedPlaylistInfo; + + @HiveField(75, defaultValue: _defaultTranscodingSegmentContainer) FinampSegmentContainer transcodingSegmentContainer; - @HiveField(75, defaultValue: _featureChipsConfigurationDefault) + @HiveField(76, defaultValue: _featureChipsConfigurationDefault) FinampFeatureChipsConfiguration featureChipsConfiguration; static Future create() async { diff --git a/lib/models/finamp_models.g.dart b/lib/models/finamp_models.g.dart index 5d98677c..15d28fa1 100644 --- a/lib/models/finamp_models.g.dart +++ b/lib/models/finamp_models.g.dart @@ -130,7 +130,7 @@ class FinampSettingsAdapter extends TypeAdapter { fields[23] == null ? false : fields[23] as bool, hasCompletedBlurhashImageMigrationIdFix: fields[24] == null ? false : fields[24] as bool, - hasCompleteddownloadsServiceMigration: + hasCompletedDownloadsServiceMigration: fields[34] == null ? false : fields[34] as bool, requireWifiForDownloads: fields[35] == null ? false : fields[35] as bool, onlyShowFullyDownloaded: fields[36] == null ? false : fields[36] as bool, @@ -185,10 +185,12 @@ class FinampSettingsAdapter extends TypeAdapter { : fields[72] as KeepScreenOnOption, keepScreenOnWhilePluggedIn: fields[73] == null ? true : fields[73] as bool, - transcodingSegmentContainer: fields[74] == null + hasDownloadedPlaylistInfo: + fields[74] == null ? false : fields[74] as bool, + transcodingSegmentContainer: fields[75] == null ? FinampSegmentContainer.fragmentedMp4 - : fields[74] as FinampSegmentContainer, - featureChipsConfiguration: fields[75] == null + : fields[75] as FinampSegmentContainer, + featureChipsConfiguration: fields[76] == null ? const FinampFeatureChipsConfiguration(enabled: true, features: [ FinampFeatureChipType.playCount, FinampFeatureChipType.additionalPeople, @@ -200,7 +202,7 @@ class FinampSettingsAdapter extends TypeAdapter { FinampFeatureChipType.size, FinampFeatureChipType.normalizationGain ]) - : fields[75] as FinampFeatureChipsConfiguration, + : fields[76] as FinampFeatureChipsConfiguration, ) ..disableGesture = fields[19] == null ? false : fields[19] as bool ..showFastScroller = fields[25] == null ? true : fields[25] as bool @@ -210,7 +212,7 @@ class FinampSettingsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, FinampSettings obj) { writer - ..writeByte(74) + ..writeByte(75) ..writeByte(0) ..write(obj.isOffline) ..writeByte(1) @@ -276,7 +278,7 @@ class FinampSettingsAdapter extends TypeAdapter { ..writeByte(33) ..write(obj.volumeNormalizationMode) ..writeByte(34) - ..write(obj.hasCompleteddownloadsServiceMigration) + ..write(obj.hasCompletedDownloadsServiceMigration) ..writeByte(35) ..write(obj.requireWifiForDownloads) ..writeByte(36) @@ -356,8 +358,10 @@ class FinampSettingsAdapter extends TypeAdapter { ..writeByte(73) ..write(obj.keepScreenOnWhilePluggedIn) ..writeByte(74) - ..write(obj.transcodingSegmentContainer) + ..write(obj.hasDownloadedPlaylistInfo) ..writeByte(75) + ..write(obj.transcodingSegmentContainer) + ..writeByte(76) ..write(obj.featureChipsConfiguration); } diff --git a/lib/screens/music_screen.dart b/lib/screens/music_screen.dart index b3478d3d..5ca5f1e4 100644 --- a/lib/screens/music_screen.dart +++ b/lib/screens/music_screen.dart @@ -10,6 +10,7 @@ import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; import 'package:logging/logging.dart'; +import 'package:finamp/services/downloads_service.dart'; import '../components/MusicScreen/music_screen_drawer.dart'; import '../components/MusicScreen/music_screen_tab_view.dart'; import '../components/MusicScreen/sort_by_menu_button.dart'; @@ -22,6 +23,27 @@ import '../services/finamp_settings_helper.dart'; import '../services/finamp_user_helper.dart'; import '../services/jellyfin_api_helper.dart'; +final _musicScreenLogger = Logger("MusicScreen"); + +void postLaunchHook(WidgetRef ref) async { + final downloadsService = GetIt.instance(); + final queueService = GetIt.instance(); + + // make sure playlist info is downloaded for users upgrading from older versions and new installations AFTER logging in and selecting their libraries/views + if (!FinampSettingsHelper.finampSettings.hasDownloadedPlaylistInfo) { + await downloadsService.addDefaultPlaylistInfoDownload().catchError((e) { + // log error without snackbar, we don't want users to be greeted with errors on first launch + _musicScreenLogger.severe("Failed to download playlist metadata: $e"); + }); + FinampSettingsHelper.setHasDownloadedPlaylistInfo(true); + } + + // Restore queue + unawaited(queueService + .performInitialQueueLoad() + .catchError((x) => GlobalSnackbar.error(x))); +} + class MusicScreen extends ConsumerStatefulWidget { const MusicScreen({super.key}); @@ -37,7 +59,6 @@ class _MusicScreenState extends ConsumerState bool _showShuffleFab = false; TextEditingController textEditingController = TextEditingController(); String? searchQuery; - final _musicScreenLogger = Logger("MusicScreen"); final Map refreshMap = {}; TabController? _tabController; @@ -45,7 +66,6 @@ class _MusicScreenState extends ConsumerState final _audioServiceHelper = GetIt.instance(); final _finampUserHelper = GetIt.instance(); final _jellyfinApiHelper = GetIt.instance(); - final _queueService = GetIt.instance(); void _stopSearching() { setState(() { @@ -93,6 +113,7 @@ class _MusicScreenState extends ConsumerState @override void initState() { super.initState(); + postLaunchHook(ref); } @override @@ -179,9 +200,6 @@ class _MusicScreenState extends ConsumerState @override Widget build(BuildContext context) { - _queueService - .performInitialQueueLoad() - .catchError((x) => GlobalSnackbar.error(x)); if (_tabController == null) { _buildTabController(); } diff --git a/lib/services/downloads_service.dart b/lib/services/downloads_service.dart index 693bd158..876e1772 100644 --- a/lib/services/downloads_service.dart +++ b/lib/services/downloads_service.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:background_downloader/background_downloader.dart'; import 'package:collection/collection.dart'; import 'package:finamp/components/global_snackbar.dart'; +import 'package:finamp/services/finamp_user_helper.dart'; import 'package:finamp/services/jellyfin_api_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -434,8 +435,8 @@ class DownloadsService { /// it to the anchor as required and then syncing. Future addDownload({ required DownloadStub stub, - required String viewId, required DownloadProfile transcodeProfile, + String? viewId, }) async { // Comment https://github.com/jmshrv/finamp/issues/134#issuecomment-1563441355 // suggests this does not make a request and always returns failure @@ -1225,6 +1226,28 @@ class DownloadsService { } } + Future addDefaultPlaylistInfoDownload() async { + + String? downloadLocation = + FinampSettingsHelper.finampSettings.defaultDownloadLocation; + if (!FinampSettingsHelper.finampSettings.downloadLocationsMap + .containsKey(downloadLocation)) { + downloadLocation = null; + } + downloadLocation ??= FinampSettingsHelper.finampSettings.internalSongDir.id; + + // Automatically download playlist metadata (to enhance the playlist actions dialog and offline mode) + await addDownload( + stub: DownloadStub.fromFinampCollection( + FinampCollection(type: FinampCollectionType.allPlaylistsMetadata)), + transcodeProfile: + DownloadProfile( + transcodeCodec: FinampTranscodingCodec.original, + downloadLocationId: downloadLocation, + ), + ); + } + /// Get all user-downloaded items. Used to show items on downloads screen. List getUserDownloaded() => getVisibleChildren(_anchor); diff --git a/lib/services/finamp_settings_helper.dart b/lib/services/finamp_settings_helper.dart index 4d1d6e44..e860d4f6 100644 --- a/lib/services/finamp_settings_helper.dart +++ b/lib/services/finamp_settings_helper.dart @@ -290,7 +290,7 @@ class FinampSettingsHelper { static void setHasCompleteddownloadsServiceMigration( bool hasCompleteddownloadsServiceMigration) { FinampSettings finampSettingsTemp = finampSettings; - finampSettingsTemp.hasCompleteddownloadsServiceMigration = + finampSettingsTemp.hasCompletedDownloadsServiceMigration = hasCompleteddownloadsServiceMigration; Hive.box("FinampSettings") .put("FinampSettings", finampSettingsTemp); @@ -305,6 +305,13 @@ class FinampSettingsHelper { .put("FinampSettings", finampSettingsTemp); } + static void setHasDownloadedPlaylistInfo(bool hasDownloadedPlaylistInfo) { + FinampSettings finampSettingsTemp = finampSettings; + finampSettingsTemp.hasDownloadedPlaylistInfo = hasDownloadedPlaylistInfo; + Hive.box("FinampSettings") + .put("FinampSettings", finampSettingsTemp); + } + static void setTabOrder(List newTabOrder) { FinampSettings finampSettingsTemp = finampSettings; finampSettingsTemp.tabOrder = newTabOrder;