fix: artist images are not loading up

This commit is contained in:
Kingkor Roy Tirtho 2025-04-25 16:27:07 +06:00
parent d3edf07ac9
commit 5ff1fc9f82
7 changed files with 136 additions and 10 deletions

View File

@ -25,7 +25,13 @@ class TopArtists extends HookConsumerWidget {
ref.watch(historyTopTracksProvider(historyDuration).notifier);
final artistsData = useMemoized(
() => topTracks.asData?.value.artists ?? [], [topTracks.asData?.value]);
() => topTracks.asData?.value.artists ?? [],
[topTracks.asData?.value],
);
for (final artist in artistsData) {
print("${artist.artist.name} has ${artist.artist.images?.length} images");
}
return Skeletonizer.sliver(
enabled: topTracks.isLoading && !topTracks.isLoadingNextPage,

View File

@ -121,7 +121,16 @@ class SearchPage extends HookConsumerWidget {
}
},
child: AutoComplete(
suggestions: suggestions,
suggestions: suggestions.length <= 2
? [
...suggestions,
"Twenty One Pilots",
"Linkin Park",
"d4vd"
]
: suggestions,
completer: (suggestion) => suggestion,
mode: AutoCompleteMode.replaceAll,
child: TextField(
autofocus: true,
controller: controller,

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:math';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/provider/audio_player/audio_player.dart';
import 'package:spotube/provider/audio_player/state.dart';
@ -10,6 +11,7 @@ import 'package:spotube/provider/history/history.dart';
import 'package:spotube/provider/skip_segments/skip_segments.dart';
import 'package:spotube/provider/scrobbler/scrobbler.dart';
import 'package:spotube/provider/server/sourced_track.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/audio_services/audio_services.dart';
@ -80,7 +82,7 @@ class AudioPlayerStreamListeners {
StreamSubscription subscribeToScrobbleChanged() {
String? lastScrobbled;
return audioPlayer.positionStream.listen((position) {
return audioPlayer.positionStream.listen((position) async {
try {
final uid = audioPlayerState.activeTrack is LocalTrack
? (audioPlayerState.activeTrack as LocalTrack).path
@ -93,8 +95,23 @@ class AudioPlayerStreamListeners {
}
scrobbler.scrobble(audioPlayerState.activeTrack!);
history.addTrack(audioPlayerState.activeTrack!);
lastScrobbled = uid;
/// The [Track] from Playlist.getTracks doesn't contain artist images
/// so we need to fetch them from the API
final activeTrack =
Track.fromJson(audioPlayerState.activeTrack!.toJson());
if (audioPlayerState.activeTrack!.artists
?.any((a) => a.images == null) ??
false) {
activeTrack.artists =
await ref.read(spotifyProvider).api.artists.list([
for (final artist in audioPlayerState.activeTrack!.artists!)
artist.id!,
]).then((value) => value.toList());
}
await history.addTrack(activeTrack);
} catch (e, stack) {
AppLogger.reportError(e, stack);
}

View File

@ -39,6 +39,11 @@ class PlaybackHistoryActions {
}
Future<void> addTracks(List<Track> tracks) async {
assert(
tracks.every((t) => t.artists?.every((a) => a.images != null) ?? false),
'Track artists must have images',
);
await _batchInsertHistoryEntries([
for (final track in tracks)
HistoryTableCompanion.insert(
@ -50,6 +55,11 @@ class PlaybackHistoryActions {
}
Future<void> addTrack(Track track) async {
assert(
track.artists?.every((a) => a.images != null) ?? false,
'Track artists must have images',
);
await _db.into(_db.historyTable).insert(
HistoryTableCompanion.insert(
type: HistoryEntryType.track,

View File

@ -28,7 +28,15 @@ class HistoryTopTracksState extends PaginatedState<PlaybackHistoryTrack> {
return groupBy(artists, (artist) => artist.id!)
.entries
.map((entry) {
return (count: entry.value.length, artist: entry.value.first);
return (
count: entry.value.length,
/// Previously, due to a bug, artist images were not being saved.
/// Now it's fixed, but we need to handle the case where images are null.
/// So we take the first artist with images if available, otherwise the first one.
artist: entry.value.firstWhereOrNull((a) => a.images != null) ??
entry.value.first,
);
})
.sorted((a, b) => b.count.compareTo(a.count))
.toList();
@ -85,11 +93,58 @@ class HistoryTopTracksNotifier extends FamilyPaginatedAsyncNotifier<
);
}
Future<void> fixImageNotLoadingForArtistIssue(
List<HistoryTableData> entries,
) async {
final nonImageArtistTracks =
entries.where((e) => e.track!.artists!.any((a) => a.images == null));
if (nonImageArtistTracks.isEmpty) return;
final artistIds = nonImageArtistTracks
.map((e) => e.track!.artists!.map((a) => a.id!))
.expand((e) => e)
.toSet()
.toList();
if (artistIds.isEmpty) return;
final artists = await ref.read(spotifyProvider).api.artists.list(artistIds);
final imagedArtistTracks = nonImageArtistTracks.map((e) {
final track = e.track!;
final includedArtists = track.artists!
.map((a) => artists.firstWhereOrNull((artist) => artist.id == a.id))
.nonNulls
.toList();
track.artists = includedArtists;
return e.copyWith(data: track.toJson());
});
assert(
imagedArtistTracks
.every((e) => e.track!.artists!.every((a) => a.images != null)),
'Tracks artists should have images',
);
final database = ref.read(databaseProvider);
await database.batch((batch) {
batch.insertAllOnConflictUpdate(
database.historyTable,
imagedArtistTracks,
);
});
}
@override
fetch(arg, offset, limit) async {
final tracksQuery = createTracksQuery()..limit(limit, offset: offset);
final items = getTracksWithCount(await tracksQuery.get());
final entries = await tracksQuery.get();
final items = getTracksWithCount(entries);
return (
items: items,
@ -123,13 +178,26 @@ class HistoryTopTracksNotifier extends FamilyPaginatedAsyncNotifier<
}
List<PlaybackHistoryTrack> getTracksWithCount(List<HistoryTableData> tracks) {
fixImageNotLoadingForArtistIssue(tracks);
return groupBy(
tracks,
(track) => track.track!.id!,
)
.entries
.map((entry) {
return (count: entry.value.length, track: entry.value.first.track!);
return (
count: entry.value.length,
/// Previously, due to a bug, artist images were not being saved.
/// Now it's fixed, but we need to handle the case where images are null.
/// So we take the first artist with images if available, otherwise the first one.
track: entry.value
.firstWhereOrNull(
(t) => t.track!.artists!.every((a) => a.images != null))
?.track! ??
entry.value.first.track!,
);
})
.sorted((a, b) => b.count.compareTo(a.count))
.toList();

View File

@ -642,6 +642,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.7"
expressions:
dependency: transitive
description:
name: expressions
sha256: "308a621b602923dd8a0cf3072793b24850d06453eb49c6b698cbda41a282e904"
url: "https://pub.dev"
source: hosted
version: "0.2.5+2"
fake_async:
dependency: transitive
description:
@ -1919,6 +1927,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
quiver:
dependency: transitive
description:
name: quiver
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
url: "https://pub.dev"
source: hosted
version: "3.2.2"
recase:
dependency: transitive
description:
@ -2012,10 +2028,10 @@ packages:
dependency: "direct main"
description:
name: shadcn_flutter
sha256: "2b6faf9a93628469c29a534e653295e26781f2799efe5dc971b91e91062ebf52"
sha256: "8635e8e0cd2e0fba0a3093a53fbe3bdd37d90d038c4c66d761728d7cfcf23ce3"
url: "https://pub.dev"
source: hosted
version: "0.0.32"
version: "0.0.34"
shared_preferences:
dependency: "direct main"
description:

View File

@ -102,7 +102,7 @@ dependencies:
ref: dart-3-support
url: https://github.com/KRTirtho/scrobblenaut.git
scroll_to_index: ^3.0.1
shadcn_flutter: ^0.0.32
shadcn_flutter: ^0.0.34
shared_preferences: ^2.2.3
shelf: ^1.4.1
shelf_router: ^1.1.4