feat(library): show saved animated artwork on local albums

Reuse downloaded motion artwork for local and downloaded album headers, with static fallbacks and reduced-motion support. Opening an album does not trigger an artwork search or download.
This commit is contained in:
zarzet committed 2026-09-25 01:21:10 +07:00
1 parent 9a62244263
commit cec9d5cbce
4 files changed
+146 -4

No files matched your search

+18 -1
View File
@@ -22,6 +22,7 @@ import 'package:spotiflac_android/providers/download_queue_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/providers/playback_provider.dart';
import 'package:spotiflac_android/providers/music_player_provider.dart';
import 'package:spotiflac_android/providers/player_motion_artwork_provider.dart';
import 'package:spotiflac_android/screens/collapsing_header_scroll_mixin.dart';
import 'package:spotiflac_android/screens/selection_mode_mixin.dart';
import 'package:spotiflac_android/screens/track_metadata_screen.dart';
@@ -36,6 +37,7 @@ import 'package:spotiflac_android/widgets/selection_bottom_bar.dart';
import 'package:spotiflac_android/widgets/disc_separator_chip.dart';
import 'package:spotiflac_android/widgets/album_detail_header.dart';
import 'package:spotiflac_android/widgets/mornye_artist_header.dart';
import 'package:spotiflac_android/widgets/motion_header_banner.dart';
class DownloadedAlbumScreen extends ConsumerStatefulWidget {
final String albumName;
@@ -345,6 +347,18 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen>
final expandedHeight = calculateExpandedHeight(context);
final embeddedCoverPath = _resolveAlbumEmbeddedCoverPath(tracks);
final commonQuality = _getCommonQuality(tracks, qualityLabelMode);
final motion = context.isMornye && !MediaQuery.disableAnimationsOf(context)
? ref
.watch(
playerMotionArtworkProvider((
album: widget.albumName,
artist: tracks.isEmpty
? widget.artistName
: tracks.first.artistName,
)),
)
.value
: null;
final cacheWidth = coverCacheWidthForViewport(context);
final Widget background = embeddedCoverPath != null
@@ -377,9 +391,12 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen>
return AlbumDetailHeader(
title: widget.albumName,
immersive: context.isMornye,
squareArtwork: motion == null,
expandedHeight: expandedHeight,
showTitleInAppBar: showTitleInAppBar,
background: background,
background: motion == null
? background
: MotionHeaderBanner(videoUrl: motion.source, fallback: background),
paletteSource: embeddedCoverPath ?? widget.coverUrl,
blurAndScrimBackground:
embeddedCoverPath != null || widget.coverUrl != null,
+16 -1
View File
@@ -20,6 +20,7 @@ import 'package:spotiflac_android/services/local_track_redownload_service.dart';
import 'package:spotiflac_android/providers/local_library_provider.dart';
import 'package:spotiflac_android/providers/playback_provider.dart';
import 'package:spotiflac_android/providers/music_player_provider.dart';
import 'package:spotiflac_android/providers/player_motion_artwork_provider.dart';
import 'package:spotiflac_android/screens/collapsing_header_scroll_mixin.dart';
import 'package:spotiflac_android/screens/selection_mode_mixin.dart';
import 'package:spotiflac_android/widgets/collection_scaffold.dart';
@@ -31,6 +32,7 @@ import 'package:spotiflac_android/widgets/selection_bottom_bar.dart';
import 'package:spotiflac_android/widgets/disc_separator_chip.dart';
import 'package:spotiflac_android/widgets/album_detail_header.dart';
import 'package:spotiflac_android/widgets/mornye_artist_header.dart';
import 'package:spotiflac_android/widgets/motion_header_banner.dart';
class LocalAlbumScreen extends ConsumerStatefulWidget {
final String albumName;
@@ -216,6 +218,16 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen>
String qualityLabelMode,
) {
final expandedHeight = calculateExpandedHeight(context);
final motion = context.isMornye && !MediaQuery.disableAnimationsOf(context)
? ref
.watch(
playerMotionArtworkProvider((
album: widget.albumName,
artist: _sortedTracksCache.first.artistName,
)),
)
.value
: null;
final cacheWidth = coverCacheWidthForViewport(context);
final Widget background = widget.coverPath != null
@@ -239,9 +251,12 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen>
return AlbumDetailHeader(
title: widget.albumName,
immersive: context.isMornye,
squareArtwork: motion == null,
expandedHeight: expandedHeight,
showTitleInAppBar: showTitleInAppBar,
background: background,
background: motion == null
? background
: MotionHeaderBanner(videoUrl: motion.source, fallback: background),
paletteSource: widget.coverPath,
blurAndScrimBackground: widget.coverPath != null,
coverBuilder: (context, coverSize) => widget.coverPath != null