diff --git a/lib/components/AudioServiceSettingsScreen/loadQueueOnStartup_selector.dart b/lib/components/AudioServiceSettingsScreen/loadQueueOnStartup_selector.dart index 173349dac..197afc40e 100644 --- a/lib/components/AudioServiceSettingsScreen/loadQueueOnStartup_selector.dart +++ b/lib/components/AudioServiceSettingsScreen/loadQueueOnStartup_selector.dart @@ -14,12 +14,10 @@ class LoadQueueOnStartupSelector extends StatelessWidget { valueListenable: FinampSettingsHelper.finampSettingsListener, builder: (_, box, __) { return SwitchListTile.adaptive( - title: - Text(AppLocalizations.of(context)!.autoloadLastQueueOnStartup), - subtitle: Text(AppLocalizations.of(context)! - .autoloadLastQueueOnStartupSubtitle), - value: - FinampSettingsHelper.finampSettings.autoloadLastQueueOnStartup, + title: Text(AppLocalizations.of(context)!.autoloadLastQueueOnStartup), + subtitle: Text( + AppLocalizations.of(context)!.autoloadLastQueueOnStartupSubtitle), + value: FinampSettingsHelper.finampSettings.autoloadLastQueueOnStartup, onChanged: (value) => FinampSettingsHelper.setAutoloadLastQueueOnStartup(value), ); diff --git a/lib/components/PlayerScreen/queue_list.dart b/lib/components/PlayerScreen/queue_list.dart index 0f6025d90..d810f7711 100644 --- a/lib/components/PlayerScreen/queue_list.dart +++ b/lib/components/PlayerScreen/queue_list.dart @@ -274,7 +274,8 @@ Future showQueueBottomSheet(BuildContext context) { builder: (context) { return Consumer( builder: (BuildContext context, WidgetRef ref, Widget? child) { - final imageTheme = ref.watch(playerScreenThemeProvider(Theme.of(context).brightness)); + final imageTheme = + ref.watch(playerScreenThemeProvider(Theme.of(context).brightness)); return AnimatedTheme( duration: const Duration(milliseconds: 500), @@ -728,8 +729,9 @@ class _CurrentTrackState extends State { AlbumImage( item: baseItem, borderRadius: BorderRadius.zero, - itemsToPrecache: - _queueService.getNextXTracksInQueue(3,reverse: 1).map((e) { + itemsToPrecache: _queueService + .getNextXTracksInQueue(3, reverse: 1) + .map((e) { final item = e.item.extras?["itemJson"] != null ? jellyfin_models.BaseItemDto.fromJson( e.item.extras!["itemJson"] @@ -1434,13 +1436,17 @@ class PreviousTracksSectionHeader extends SliverPersistentHeaderDelegate { return Icon( TablerIcons.chevron_up, size: 28.0, - color: Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white, + color: Theme.of(context).brightness == Brightness.light + ? Colors.black + : Colors.white, ); } else { return Icon( TablerIcons.chevron_down, size: 28.0, - color: Theme.of(context).brightness == Brightness.light ? Colors.black : Colors.white, + color: Theme.of(context).brightness == Brightness.light + ? Colors.black + : Colors.white, ); } }), diff --git a/lib/components/PlayerScreen/song_info.dart b/lib/components/PlayerScreen/song_info.dart index 607d3c1fe..ead3b7e5b 100644 --- a/lib/components/PlayerScreen/song_info.dart +++ b/lib/components/PlayerScreen/song_info.dart @@ -1,12 +1,8 @@ -import 'dart:math'; - -import 'package:audio_service/audio_service.dart'; import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/queue_service.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:get_it/get_it.dart'; import '../../models/jellyfin_models.dart' as jellyfin_models; @@ -154,7 +150,8 @@ class _PlayerScreenAlbumImage extends StatelessWidget { // Here we get the next 3 queue items so that we // can precache them (so that the image is already loaded // when the next song comes on). - itemsToPrecache: queueService.getNextXTracksInQueue(3,reverse: 1).map((e) { + itemsToPrecache: + queueService.getNextXTracksInQueue(3, reverse: 1).map((e) { final item = e.item.extras?["itemJson"] != null ? jellyfin_models.BaseItemDto.fromJson( e.item.extras!["itemJson"] as Map) diff --git a/lib/components/QueueRestoreScreen/queue_restore_tile.dart b/lib/components/QueueRestoreScreen/queue_restore_tile.dart index 70d7707a0..d3e196590 100644 --- a/lib/components/QueueRestoreScreen/queue_restore_tile.dart +++ b/lib/components/QueueRestoreScreen/queue_restore_tile.dart @@ -28,9 +28,12 @@ class QueueRestoreTile extends StatelessWidget { if (info.currentTrack == null) { track = Future.value(null); } else if (FinampSettingsHelper.finampSettings.isOffline) { - track = Future.value(downloadsHelper.getDownloadedSong(info.currentTrack!)?.song); + track = Future.value( + downloadsHelper.getDownloadedSong(info.currentTrack!)?.song); } else { - track = jellyfinApiHelper.getItemById(info.currentTrack!).then((x) => x, onError: (_) => null); + track = jellyfinApiHelper + .getItemById(info.currentTrack!) + .then((x) => x, onError: (_) => null); } return ListTile( @@ -58,7 +61,7 @@ class QueueRestoreTile extends StatelessWidget { ]) + [ Text(AppLocalizations.of(context)! - .queueRestoreSubtitle2(info.songCount,remainingSongs)) + .queueRestoreSubtitle2(info.songCount, remainingSongs)) ])), trailing: IconButton( icon: const Icon(Icons.arrow_circle_right_outlined), diff --git a/lib/components/album_image.dart b/lib/components/album_image.dart index d60997942..ee4ffc0d7 100644 --- a/lib/components/album_image.dart +++ b/lib/components/album_image.dart @@ -15,6 +15,7 @@ class AlbumImage extends ConsumerWidget { const AlbumImage({ Key? key, this.item, + this.imageListenable, this.itemsToPrecache, this.borderRadius, this.placeholderBuilder, @@ -23,6 +24,8 @@ class AlbumImage extends ConsumerWidget { /// The item to get an image for. final BaseItemDto? item; + final ProviderListenable>? imageListenable; + /// A list of items to precache final List? itemsToPrecache; @@ -36,8 +39,8 @@ class AlbumImage extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final borderRadius = this.borderRadius ?? defaultBorderRadius; - if (item == null || item!.imageId == null) { - + assert(item == null || imageListenable == null); + if ((item == null || item!.imageId == null) && imageListenable == null) { return ClipRRect( borderRadius: borderRadius, child: const AspectRatio( @@ -64,11 +67,13 @@ class AlbumImage extends ConsumerWidget { (constraints.maxHeight * mediaQuery.devicePixelRatio).toInt(); return BareAlbumImage( - item: item!, + item: item, + imageListenable: imageListenable, maxWidth: physicalWidth, maxHeight: physicalHeight, itemsToPrecache: itemsToPrecache, - placeholderBuilder: placeholderBuilder ?? BareAlbumImage.defaultPlaceholderBuilder, + placeholderBuilder: + placeholderBuilder ?? BareAlbumImage.defaultPlaceholderBuilder, ); }), ), @@ -80,7 +85,8 @@ class AlbumImage extends ConsumerWidget { class BareAlbumImage extends ConsumerWidget { const BareAlbumImage({ Key? key, - required this.item, + this.item, + this.imageListenable, this.maxWidth, this.maxHeight, this.errorBuilder = defaultErrorBuilder, @@ -88,7 +94,8 @@ class BareAlbumImage extends ConsumerWidget { this.itemsToPrecache, }) : super(key: key); - final BaseItemDto item; + final BaseItemDto? item; + final ProviderListenable>? imageListenable; final int? maxWidth; final int? maxHeight; final WidgetBuilder placeholderBuilder; @@ -107,7 +114,7 @@ class BareAlbumImage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - + assert(item != null || imageListenable != null); for (final itemToPrecache in itemsToPrecache ?? []) { ref.listen( albumImageProvider(AlbumImageRequest( @@ -121,14 +128,14 @@ class BareAlbumImage extends ConsumerWidget { }); } - AsyncValue image = - ref.watch(albumImageProvider(AlbumImageRequest( - item: item, - maxWidth: maxWidth, - maxHeight: maxHeight, - ))); + AsyncValue image = ref.watch(imageListenable ?? + albumImageProvider(AlbumImageRequest( + item: item!, + maxWidth: maxWidth, + maxHeight: maxHeight, + ))); - if (image.hasValue) { + if (image.hasValue && image.value != null) { return OctoImage( image: image.value!, fit: BoxFit.cover, diff --git a/lib/components/now_playing_bar.dart b/lib/components/now_playing_bar.dart index bd8623817..fb282382b 100644 --- a/lib/components/now_playing_bar.dart +++ b/lib/components/now_playing_bar.dart @@ -468,8 +468,8 @@ class NowPlayingBar extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { // BottomNavBar's default elevation is 8 (https://api.flutter.dev/flutter/material/BottomNavigationBar/elevation.html) final queueService = GetIt.instance(); - var imageTheme = ref - .watch(playerScreenThemeProvider(Theme.of(context).brightness)); + var imageTheme = + ref.watch(playerScreenThemeProvider(Theme.of(context).brightness)); return Hero( tag: "nowplaying", diff --git a/lib/main.dart b/lib/main.dart index c78f8d65b..d4ce9beb2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -321,7 +321,8 @@ class Finamp extends StatelessWidget { PlaybackHistoryScreen.routeName: (context) => const PlaybackHistoryScreen(), LogsScreen.routeName: (context) => const LogsScreen(), - QueueRestoreScreen.routeName: (context) => const QueueRestoreScreen(), + QueueRestoreScreen.routeName: (context) => + const QueueRestoreScreen(), SettingsScreen.routeName: (context) => const SettingsScreen(), TranscodingSettingsScreen.routeName: (context) => diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index c3dac98d2..1c957ded1 100644 --- a/lib/models/finamp_models.dart +++ b/lib/models/finamp_models.dart @@ -821,15 +821,20 @@ class FinampStorableQueueInfo { required this.source, }); - FinampStorableQueueInfo.fromQueueInfo(FinampQueueInfo info, int? seek): - previousTracks=info.previousTracks.map((track) => track.item.extras?["itemJson"]["Id"]).toList(), - currentTrack=info.currentTrack?.item.extras?["itemJson"]["Id"], - currentTrackSeek=seek, - nextUp=info.nextUp.map((track) => track.item.extras?["itemJson"]["Id"]).toList(), - queue=info.queue.map((track) => track.item.extras?["itemJson"]["Id"]).toList(), - creation=DateTime.now().millisecondsSinceEpoch, - source=info.source - ; + FinampStorableQueueInfo.fromQueueInfo(FinampQueueInfo info, int? seek) + : previousTracks = info.previousTracks + .map((track) => track.item.extras?["itemJson"]["Id"]) + .toList(), + currentTrack = info.currentTrack?.item.extras?["itemJson"]["Id"], + currentTrackSeek = seek, + nextUp = info.nextUp + .map((track) => track.item.extras?["itemJson"]["Id"]) + .toList(), + queue = info.queue + .map((track) => track.item.extras?["itemJson"]["Id"]) + .toList(), + creation = DateTime.now().millisecondsSinceEpoch, + source = info.source; @HiveField(0) List previousTracks; @@ -853,12 +858,16 @@ class FinampStorableQueueInfo { @HiveField(6) QueueItemSource? source; - String toString(){ + @override + String toString() { return "previous:$previousTracks current:$currentTrack seek:$currentTrackSeek next:$nextUp queue:$queue"; } - int get songCount{ - return previousTracks.length + ((currentTrack == null)?0:1) + nextUp.length + queue.length; + int get songCount { + return previousTracks.length + + ((currentTrack == null) ? 0 : 1) + + nextUp.length + + queue.length; } } diff --git a/lib/screens/music_screen.dart b/lib/screens/music_screen.dart index 1b59b0a4c..917d6174c 100644 --- a/lib/screens/music_screen.dart +++ b/lib/screens/music_screen.dart @@ -163,7 +163,9 @@ class _MusicScreenState extends State @override Widget build(BuildContext context) { - _queueService.performInitialQueueLoad().catchError((x) => errorSnackbar(x,context)); + _queueService + .performInitialQueueLoad() + .catchError((x) => errorSnackbar(x, context)); if (_tabController == null) { _buildTabController(); } diff --git a/lib/screens/player_screen.dart b/lib/screens/player_screen.dart index 52bbd88d2..9d06e142e 100644 --- a/lib/screens/player_screen.dart +++ b/lib/screens/player_screen.dart @@ -24,7 +24,8 @@ class PlayerScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final imageTheme = ref.watch(playerScreenThemeProvider(Theme.of(context).brightness)); + final imageTheme = + ref.watch(playerScreenThemeProvider(Theme.of(context).brightness)); return AnimatedTheme( duration: const Duration(milliseconds: 500), diff --git a/lib/services/album_image_provider.dart b/lib/services/album_image_provider.dart index 5117659d8..d12c24f64 100644 --- a/lib/services/album_image_provider.dart +++ b/lib/services/album_image_provider.dart @@ -21,8 +21,11 @@ class AlbumImageRequest { final int? maxHeight; @override - bool operator ==(Object other){ - return other is AlbumImageRequest && other.maxHeight == maxHeight && other.maxWidth == maxWidth && other.item.id == item.id; + bool operator ==(Object other) { + return other is AlbumImageRequest && + other.maxHeight == maxHeight && + other.maxWidth == maxWidth && + other.item.id == item.id; } @override @@ -30,9 +33,8 @@ class AlbumImageRequest { } final AutoDisposeFutureProviderFamily - albumImageProvider = - FutureProvider.autoDispose.family( - (ref, request) async { + albumImageProvider = FutureProvider.autoDispose + .family((ref, request) async { if (request.item.imageId == null) { return null; } @@ -67,8 +69,10 @@ final AutoDisposeFutureProviderFamily // If we've got this far, the download image has failed to verify. // We recurse, which will either return a NetworkImage or an error depending // on if the app is offline. - return ref.read(albumImageProvider(AlbumImageRequest( - item: request.item, - maxWidth: request.maxWidth, - maxHeight: request.maxHeight))).value; + return ref + .read(albumImageProvider(AlbumImageRequest( + item: request.item, + maxWidth: request.maxWidth, + maxHeight: request.maxHeight))) + .value; }); diff --git a/lib/services/current_album_image_provider.dart b/lib/services/current_album_image_provider.dart index 38c122dfc..0499380cf 100644 --- a/lib/services/current_album_image_provider.dart +++ b/lib/services/current_album_image_provider.dart @@ -8,8 +8,10 @@ import 'album_image_provider.dart'; /// Provider to handle syncing up the current playing item's image provider. /// Used on the player screen to sync up loading the blurred background. -final currentAlbumImageProvider = FutureProvider.autoDispose((ref) async { - final currentTrack = ref.watch(currentSongProvider.select((data) => data.value?.item)); +final currentAlbumImageProvider = + FutureProvider((ref) async { + final currentTrack = + ref.watch(currentSongProvider.select((data) => data.value?.item)); if (currentTrack != null) { final currentTrackBaseItem = currentTrack.extras?["itemJson"] != null ? BaseItemDto.fromJson( @@ -18,8 +20,8 @@ final currentAlbumImageProvider = FutureProvider.autoDispose((re if (currentTrackBaseItem != null) { final request = AlbumImageRequest( item: currentTrackBaseItem, - maxWidth: 100, - maxHeight: 100, + maxHeight: 300, + maxWidth: 300, ); return ref.read(albumImageProvider(request).future); } diff --git a/lib/services/finamp_settings_helper.dart b/lib/services/finamp_settings_helper.dart index 83a976f69..2bcf654ad 100644 --- a/lib/services/finamp_settings_helper.dart +++ b/lib/services/finamp_settings_helper.dart @@ -108,11 +108,9 @@ class FinampSettingsHelper { .put("FinampSettings", finampSettingsTemp); } - static void setAutoloadLastQueueOnStartup( - bool autoloadLastQueueOnStartup) { + static void setAutoloadLastQueueOnStartup(bool autoloadLastQueueOnStartup) { FinampSettings finampSettingsTemp = finampSettings; - finampSettingsTemp.autoloadLastQueueOnStartup = - autoloadLastQueueOnStartup; + finampSettingsTemp.autoloadLastQueueOnStartup = autoloadLastQueueOnStartup; Hive.box("FinampSettings") .put("FinampSettings", finampSettingsTemp); } diff --git a/lib/services/player_screen_theme_provider.dart b/lib/services/player_screen_theme_provider.dart index b769fa5b2..3a89e9358 100644 --- a/lib/services/player_screen_theme_provider.dart +++ b/lib/services/player_screen_theme_provider.dart @@ -7,63 +7,62 @@ import 'package:palette_generator/palette_generator.dart'; import '../generate_material_color.dart'; import 'current_album_image_provider.dart'; -final AutoDisposeProviderFamily playerScreenThemeProvider = -Provider.family.autoDispose((ref, brightness) { - ColorScheme? scheme = ref.watch(playerScreenThemeNullableProvider(brightness)).value; - if(scheme != null){ - return scheme; - }else{ - final lighter = brightness == Brightness.dark; - Color accent = lighter - ? const Color.fromARGB(255, 50, 50, 50) - : const Color.fromARGB(255, 200, 200, 200); +final AutoDisposeProviderFamily + playerScreenThemeProvider = + Provider.family.autoDispose((ref, brightness) { + ColorScheme? scheme = + ref.watch(playerScreenThemeNullableProvider(brightness)).value; + if (scheme == null) { + Color accent = brightness == Brightness.dark + ? const Color.fromARGB(255, 133, 133, 133) + : const Color.fromARGB(255, 61, 61, 61); - final background = Color.alphaBlend( - lighter - ? Colors.black.withOpacity(0.675) - : Colors.white.withOpacity(0.675), - accent); - - accent = accent.atContrast(4.5, background, lighter); - - return ColorScheme.fromSwatch( - primarySwatch: generateMaterialColor(accent), - accentColor: accent, - brightness: brightness, - ); - } + return ColorScheme.fromSwatch( + primarySwatch: generateMaterialColor(accent), + accentColor: accent, + brightness: brightness, + ); + } else { + return scheme; + } }); -final AutoDisposeFutureProviderFamily playerScreenThemeNullableProvider = - FutureProvider.family.autoDispose((ref, brightness) async { - +final AutoDisposeFutureProviderFamily + playerScreenThemeNullableProvider = FutureProvider.family + .autoDispose((ref, brightness) async { ImageProvider? image = await ref.watch(currentAlbumImageProvider.future); - if (image==null) return null; + if (image == null) { + return null; + } Logger("colorProvider").fine("Re-theming based on image $image"); - final PaletteGenerator? palette = await PaletteGenerator.fromImageProvider( - image, - timeout: const Duration(seconds: 5), - ).then((value) => value, onError: (_)=>null); - if (palette==null) return null; - // Color accent = palette.dominantColor!.color; - Color accent = palette.vibrantColor?.color ?? - palette.dominantColor?.color ?? - const Color.fromARGB(255, 0, 164, 220); + final PaletteGenerator? palette = await PaletteGenerator.fromImageProvider( + image, + timeout: const Duration(seconds: 5), + ).then((value) => value, onError: (_) => null); + if (palette == null) { + return ColorScheme.fromSeed( + seedColor: const Color.fromARGB(255, 0, 164, 220), + brightness: brightness); + } + // Color accent = palette.dominantColor!.color; + Color accent = palette.vibrantColor?.color ?? + palette.dominantColor?.color ?? + const Color.fromARGB(255, 0, 164, 220); - final lighter = brightness == Brightness.dark; + final lighter = brightness == Brightness.dark; - final background = Color.alphaBlend( - lighter - ? Colors.black.withOpacity(0.675) - : Colors.white.withOpacity(0.675), - accent); + final background = Color.alphaBlend( + lighter + ? Colors.black.withOpacity(0.675) + : Colors.white.withOpacity(0.675), + accent); - accent = accent.atContrast(4.5, background, lighter); + accent = accent.atContrast(4.5, background, lighter); - return ColorScheme.fromSwatch( - primarySwatch: generateMaterialColor(accent), - accentColor: accent, - brightness: brightness, - ); + return ColorScheme.fromSwatch( + primarySwatch: generateMaterialColor(accent), + accentColor: accent, + brightness: brightness, + ); }); diff --git a/lib/services/queue_service.dart b/lib/services/queue_service.dart index 9f8d41f27..1525b45fe 100644 --- a/lib/services/queue_service.dart +++ b/lib/services/queue_service.dart @@ -95,16 +95,16 @@ class QueueService { if (previousIndex != _queueAudioSourceIndex) { int adjustedQueueIndex = (playbackOrder == - FinampPlaybackOrder.shuffled && - _queueAudioSource.shuffleIndices.isNotEmpty) + FinampPlaybackOrder.shuffled && + _queueAudioSource.shuffleIndices.isNotEmpty) ? _queueAudioSource.shuffleIndices.indexOf(_queueAudioSourceIndex) : _queueAudioSourceIndex; _queueServiceLogger.finer( "Play queue index changed, new index: $adjustedQueueIndex (actual index: $_queueAudioSourceIndex)"); _queueFromConcatenatingAudioSource(); - }else{ - _saveUpdateImemdiate=true; + } else { + _saveUpdateImemdiate = true; } }); @@ -112,21 +112,22 @@ class QueueService { // Update once per minute in background, and up to once every ten seconds if // pausing/seeking is occuring // We also update on every track switch. - if ( _saveUpdateCycleCount >= 5 || _saveUpdateImemdiate ) { - if ( _savedQueueState == SavedQueueState.pendingSave && ! _audioHandler.paused ) { + if (_saveUpdateCycleCount >= 5 || _saveUpdateImemdiate) { + if (_savedQueueState == SavedQueueState.pendingSave && + !_audioHandler.paused) { _savedQueueState = SavedQueueState.saving; } - if ( _savedQueueState == SavedQueueState.saving ) { - _saveUpdateImemdiate=false; - _saveUpdateCycleCount=0; + if (_savedQueueState == SavedQueueState.saving) { + _saveUpdateImemdiate = false; + _saveUpdateCycleCount = 0; FinampStorableQueueInfo info = FinampStorableQueueInfo.fromQueueInfo( getQueue(), _audioHandler.playbackPosition.inMilliseconds); - if ( info.songCount != 0 ){ + if (info.songCount != 0) { _queuesBox.put("latest", info); _queueServiceLogger.finest("Saved new periodic queue $info"); } } - }else{ + } else { _saveUpdateCycleCount++; } }); @@ -232,14 +233,15 @@ class QueueService { .map((e) => e.item) .toList()); - if ( _savedQueueState == SavedQueueState.saving ){ - FinampStorableQueueInfo info = FinampStorableQueueInfo.fromQueueInfo(getQueue(),null); - if ( info.songCount != 0 ){ - _queuesBox.put("latest",info); + if (_savedQueueState == SavedQueueState.saving) { + FinampStorableQueueInfo info = + FinampStorableQueueInfo.fromQueueInfo(getQueue(), null); + if (info.songCount != 0) { + _queuesBox.put("latest", info); _queueServiceLogger.finest("Saved new rebuilt queue $info"); } - _saveUpdateImemdiate=false; - _saveUpdateCycleCount=0; + _saveUpdateImemdiate = false; + _saveUpdateCycleCount = 0; } // only log queue if there's a change @@ -258,23 +260,25 @@ class QueueService { var info = _queuesBox.get("latest"); if (info != null) { // push latest into queue history - if (info.songCount != 0){ + if (info.songCount != 0) { await _queuesBox.put(info.creation.toString(), info); } - var keys = _queuesBox.values.map((x) => - DateTime.fromMillisecondsSinceEpoch(x.creation)).toList(); + var keys = _queuesBox.values + .map((x) => DateTime.fromMillisecondsSinceEpoch(x.creation)) + .toList(); keys.sort(); _queueServiceLogger.finest("Stored queue dates: $keys"); if (keys.length > _maxSavedQueues) { - var extra = keys.getRange(0, keys.length - _maxSavedQueues).map((e) => - e.millisecondsSinceEpoch.toString()); + var extra = keys + .getRange(0, keys.length - _maxSavedQueues) + .map((e) => e.millisecondsSinceEpoch.toString()); _queueServiceLogger.finest("Deleting stored queues: $extra"); _queuesBox.deleteAll(extra); } - if (FinampSettingsHelper.finampSettings.autoloadLastQueueOnStartup){ + if (FinampSettingsHelper.finampSettings.autoloadLastQueueOnStartup) { await loadSavedQueue(info); - } else{ + } else { _savedQueueState = SavedQueueState.pendingSave; } } @@ -286,7 +290,8 @@ class QueueService { } Future retryQueueLoad() async { - if (_savedQueueState == SavedQueueState.failed && _failedSavedQueue != null) { + if (_savedQueueState == SavedQueueState.failed && + _failedSavedQueue != null) { await loadSavedQueue(_failedSavedQueue!); } } @@ -304,38 +309,45 @@ class QueueService { await stopPlayback(); refreshQueueStream(); - List allIds = info.previousTracks + ( (info.currentTrack == null) ? []:[info.currentTrack!] ) + info.nextUp + info.queue; + List allIds = info.previousTracks + + ((info.currentTrack == null) ? [] : [info.currentTrack!]) + + info.nextUp + + info.queue; List uniqueIds = allIds.toSet().toList(); - Map idMap = {}; + Map idMap = {}; if (FinampSettingsHelper.finampSettings.isOffline) { for (var id in uniqueIds) { - jellyfin_models.BaseItemDto? item = _downloadsHelper.getDownloadedSong(id)?.song; - if(item!=null){ - idMap[id]=item; + jellyfin_models.BaseItemDto? item = + _downloadsHelper.getDownloadedSong(id)?.song; + if (item != null) { + idMap[id] = item; } } } else { for (var slice in uniqueIds.slices(200)) { - List itemList = await _jellyfinApiHelper.getItems(itemIds: slice) ?? []; - for (var d2 in itemList){ + List itemList = + await _jellyfinApiHelper.getItems(itemIds: slice) ?? []; + for (var d2 in itemList) { idMap[d2.id] = d2; } } } Map> items = { - "previous": info.previousTracks.map( (e) => idMap[e] ).whereNotNull().toList(), - "current": [info.currentTrack].map( (e) => idMap[e] ).whereNotNull().toList(), - "next": info.nextUp.map( (e) => idMap[e] ).whereNotNull().toList(), - "queue": info.queue.map( (e) => idMap[e] ).whereNotNull().toList(), + "previous": + info.previousTracks.map((e) => idMap[e]).whereNotNull().toList(), + "current": + [info.currentTrack].map((e) => idMap[e]).whereNotNull().toList(), + "next": info.nextUp.map((e) => idMap[e]).whereNotNull().toList(), + "queue": info.queue.map((e) => idMap[e]).whereNotNull().toList(), }; sumLengths(int sum, Iterable val) => val.length + sum; int loadedSongs = items.values.fold(0, sumLengths); int droppedSongs = info.songCount - loadedSongs; if (_savedQueueState != SavedQueueState.loading) { - finalState=null; + finalState = null; return Future.error("Loading of saved Queue was interrupted."); } @@ -343,27 +355,26 @@ class QueueService { itemList: items["previous"]! + items["current"]! + items["queue"]!, initialIndex: items["previous"]!.length, beginPlaying: false, - source: info.source ?? QueueItemSource( - type: QueueItemSourceType.unknown, - name: const QueueItemSourceName( - type: QueueItemSourceNameType.savedQueue), - id: "savedqueue" - ) - ); + source: info.source ?? + QueueItemSource( + type: QueueItemSourceType.unknown, + name: const QueueItemSourceName( + type: QueueItemSourceNameType.savedQueue), + id: "savedqueue")); Future seekFuture = Future.value(); if ((info.currentTrackSeek ?? 0) > 5000) { - seekFuture = _audioHandler.seek( - Duration(milliseconds: (info.currentTrackSeek ?? 0) - 1000)); + seekFuture = _audioHandler + .seek(Duration(milliseconds: (info.currentTrackSeek ?? 0) - 1000)); } await addToNextUp(items: items["next"]!); await seekFuture; _queueServiceLogger.info("Loaded saved queue."); - if (loadedSongs == 0 && info.songCount > 0 ) { + if (loadedSongs == 0 && info.songCount > 0) { finalState = SavedQueueState.failed; _failedSavedQueue = info; - } else if (droppedSongs > 0 ) { + } else if (droppedSongs > 0) { return Future.error( "$droppedSongs songs in the Now Playing Queue could not be loaded."); } @@ -373,7 +384,7 @@ class QueueService { } refreshQueueStream(); } -} + } Future startPlayback({ required List items, @@ -381,7 +392,7 @@ class QueueService { FinampPlaybackOrder? order, int? startingIndex, }) async { - // _initialQueue = list; // save original PlaybackList for looping/restarting and meta info + // _initialQueue = list; // save original PlaybackList for looping/restarting and meta info if (startingIndex == null) { if (order == FinampPlaybackOrder.shuffled) { @@ -391,7 +402,7 @@ class QueueService { } } - if ( _savedQueueState == SavedQueueState.saving ) { + if (_savedQueueState == SavedQueueState.saving) { var info = _queuesBox.get("latest"); if (info != null && info.songCount != 0) { await _queuesBox.put(info.creation.toString(), info); @@ -410,13 +421,12 @@ class QueueService { /// Replaces the queue with the given list of items. If startAtIndex is specified, Any items below it /// will be ignored. This is used for when the user taps in the middle of an album to start from that point. - Future _replaceWholeQueue({ - required List itemList, - required QueueItemSource source, - required int initialIndex, - FinampPlaybackOrder? order, - bool beginPlaying = true - }) async { + Future _replaceWholeQueue( + {required List itemList, + required QueueItemSource source, + required int initialIndex, + FinampPlaybackOrder? order, + bool beginPlaying = true}) async { try { if (initialIndex > itemList.length) { return Future.error( @@ -489,9 +499,9 @@ class QueueService { // _queueStream.add(getQueue()); _queueFromConcatenatingAudioSource(); - if(beginPlaying){ + if (beginPlaying) { await _audioHandler.play(); - }else{ + } else { await _audioHandler.pause(); } @@ -519,7 +529,7 @@ class QueueService { QueueItemSource? source, }) async { try { - if ( _savedQueueState == SavedQueueState.pendingSave ) { + if (_savedQueueState == SavedQueueState.pendingSave) { _savedQueueState = SavedQueueState.saving; } List queueItems = []; @@ -551,7 +561,7 @@ class QueueService { QueueItemSource? source, }) async { try { - if ( _savedQueueState == SavedQueueState.pendingSave ) { + if (_savedQueueState == SavedQueueState.pendingSave) { _savedQueueState = SavedQueueState.saving; } List queueItems = []; @@ -569,7 +579,7 @@ class QueueService { } for (final queueItem in queueItems.reversed) { - int offset = min(_queueAudioSource.length,1); + int offset = min(_queueAudioSource.length, 1); await _queueAudioSource.insert(_queueAudioSourceIndex + offset, await _queueItemToAudioSource(queueItem)); _queueServiceLogger.fine( @@ -588,7 +598,7 @@ class QueueService { QueueItemSource? source, }) async { try { - if ( _savedQueueState == SavedQueueState.pendingSave ) { + if (_savedQueueState == SavedQueueState.pendingSave) { _savedQueueState = SavedQueueState.saving; } List queueItems = []; @@ -606,7 +616,7 @@ class QueueService { } _queueFromConcatenatingAudioSource(); // update internal queues - int offset = _queueNextUp.length + min(_queueAudioSource.length,1); + int offset = _queueNextUp.length + min(_queueAudioSource.length, 1); for (final queueItem in queueItems) { await _queueAudioSource.insert(_queueAudioSourceIndex + offset, @@ -685,10 +695,12 @@ class QueueService { /// Returns the next [amount] QueueItems from Next Up and the regular queue. /// The length of the returned list may be less than [amount] if there are not enough items in the queue - List getNextXTracksInQueue(int amount, { int reverse = 0 }) { + List getNextXTracksInQueue(int amount, {int reverse = 0}) { List nextTracks = []; if (_queuePreviousTracks.isNotEmpty && reverse > 0) { - nextTracks.addAll(_queuePreviousTracks.sublist( max(0,_queuePreviousTracks.length-reverse), _queuePreviousTracks.length)); + nextTracks.addAll(_queuePreviousTracks.sublist( + max(0, _queuePreviousTracks.length - reverse), + _queuePreviousTracks.length)); } if (_queueNextUp.isNotEmpty) { nextTracks