mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: artist images are not loading up
This commit is contained in:
parent
d3edf07ac9
commit
5ff1fc9f82
@ -25,7 +25,13 @@ class TopArtists extends HookConsumerWidget {
|
|||||||
ref.watch(historyTopTracksProvider(historyDuration).notifier);
|
ref.watch(historyTopTracksProvider(historyDuration).notifier);
|
||||||
|
|
||||||
final artistsData = useMemoized(
|
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(
|
return Skeletonizer.sliver(
|
||||||
enabled: topTracks.isLoading && !topTracks.isLoadingNextPage,
|
enabled: topTracks.isLoading && !topTracks.isLoadingNextPage,
|
||||||
|
@ -121,7 +121,16 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: AutoComplete(
|
child: AutoComplete(
|
||||||
suggestions: suggestions,
|
suggestions: suggestions.length <= 2
|
||||||
|
? [
|
||||||
|
...suggestions,
|
||||||
|
"Twenty One Pilots",
|
||||||
|
"Linkin Park",
|
||||||
|
"d4vd"
|
||||||
|
]
|
||||||
|
: suggestions,
|
||||||
|
completer: (suggestion) => suggestion,
|
||||||
|
mode: AutoCompleteMode.replaceAll,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/local_track.dart';
|
import 'package:spotube/models/local_track.dart';
|
||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/provider/audio_player/state.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/skip_segments/skip_segments.dart';
|
||||||
import 'package:spotube/provider/scrobbler/scrobbler.dart';
|
import 'package:spotube/provider/scrobbler/scrobbler.dart';
|
||||||
import 'package:spotube/provider/server/sourced_track.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/provider/user_preferences/user_preferences_provider.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
import 'package:spotube/services/audio_services/audio_services.dart';
|
import 'package:spotube/services/audio_services/audio_services.dart';
|
||||||
@ -80,7 +82,7 @@ class AudioPlayerStreamListeners {
|
|||||||
|
|
||||||
StreamSubscription subscribeToScrobbleChanged() {
|
StreamSubscription subscribeToScrobbleChanged() {
|
||||||
String? lastScrobbled;
|
String? lastScrobbled;
|
||||||
return audioPlayer.positionStream.listen((position) {
|
return audioPlayer.positionStream.listen((position) async {
|
||||||
try {
|
try {
|
||||||
final uid = audioPlayerState.activeTrack is LocalTrack
|
final uid = audioPlayerState.activeTrack is LocalTrack
|
||||||
? (audioPlayerState.activeTrack as LocalTrack).path
|
? (audioPlayerState.activeTrack as LocalTrack).path
|
||||||
@ -93,8 +95,23 @@ class AudioPlayerStreamListeners {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scrobbler.scrobble(audioPlayerState.activeTrack!);
|
scrobbler.scrobble(audioPlayerState.activeTrack!);
|
||||||
history.addTrack(audioPlayerState.activeTrack!);
|
|
||||||
lastScrobbled = uid;
|
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) {
|
} catch (e, stack) {
|
||||||
AppLogger.reportError(e, stack);
|
AppLogger.reportError(e, stack);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,11 @@ class PlaybackHistoryActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addTracks(List<Track> tracks) async {
|
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([
|
await _batchInsertHistoryEntries([
|
||||||
for (final track in tracks)
|
for (final track in tracks)
|
||||||
HistoryTableCompanion.insert(
|
HistoryTableCompanion.insert(
|
||||||
@ -50,6 +55,11 @@ class PlaybackHistoryActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addTrack(Track track) async {
|
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(
|
await _db.into(_db.historyTable).insert(
|
||||||
HistoryTableCompanion.insert(
|
HistoryTableCompanion.insert(
|
||||||
type: HistoryEntryType.track,
|
type: HistoryEntryType.track,
|
||||||
|
@ -28,7 +28,15 @@ class HistoryTopTracksState extends PaginatedState<PlaybackHistoryTrack> {
|
|||||||
return groupBy(artists, (artist) => artist.id!)
|
return groupBy(artists, (artist) => artist.id!)
|
||||||
.entries
|
.entries
|
||||||
.map((entry) {
|
.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))
|
.sorted((a, b) => b.count.compareTo(a.count))
|
||||||
.toList();
|
.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
|
@override
|
||||||
fetch(arg, offset, limit) async {
|
fetch(arg, offset, limit) async {
|
||||||
final tracksQuery = createTracksQuery()..limit(limit, offset: offset);
|
final tracksQuery = createTracksQuery()..limit(limit, offset: offset);
|
||||||
|
|
||||||
final items = getTracksWithCount(await tracksQuery.get());
|
final entries = await tracksQuery.get();
|
||||||
|
|
||||||
|
final items = getTracksWithCount(entries);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
items: items,
|
items: items,
|
||||||
@ -123,13 +178,26 @@ class HistoryTopTracksNotifier extends FamilyPaginatedAsyncNotifier<
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<PlaybackHistoryTrack> getTracksWithCount(List<HistoryTableData> tracks) {
|
List<PlaybackHistoryTrack> getTracksWithCount(List<HistoryTableData> tracks) {
|
||||||
|
fixImageNotLoadingForArtistIssue(tracks);
|
||||||
|
|
||||||
return groupBy(
|
return groupBy(
|
||||||
tracks,
|
tracks,
|
||||||
(track) => track.track!.id!,
|
(track) => track.track!.id!,
|
||||||
)
|
)
|
||||||
.entries
|
.entries
|
||||||
.map((entry) {
|
.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))
|
.sorted((a, b) => b.count.compareTo(a.count))
|
||||||
.toList();
|
.toList();
|
||||||
|
20
pubspec.lock
20
pubspec.lock
@ -642,6 +642,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
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:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1919,6 +1927,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
quiver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quiver
|
||||||
|
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
recase:
|
recase:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -2012,10 +2028,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shadcn_flutter
|
name: shadcn_flutter
|
||||||
sha256: "2b6faf9a93628469c29a534e653295e26781f2799efe5dc971b91e91062ebf52"
|
sha256: "8635e8e0cd2e0fba0a3093a53fbe3bdd37d90d038c4c66d761728d7cfcf23ce3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.32"
|
version: "0.0.34"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -102,7 +102,7 @@ dependencies:
|
|||||||
ref: dart-3-support
|
ref: dart-3-support
|
||||||
url: https://github.com/KRTirtho/scrobblenaut.git
|
url: https://github.com/KRTirtho/scrobblenaut.git
|
||||||
scroll_to_index: ^3.0.1
|
scroll_to_index: ^3.0.1
|
||||||
shadcn_flutter: ^0.0.32
|
shadcn_flutter: ^0.0.34
|
||||||
shared_preferences: ^2.2.3
|
shared_preferences: ^2.2.3
|
||||||
shelf: ^1.4.1
|
shelf: ^1.4.1
|
||||||
shelf_router: ^1.1.4
|
shelf_router: ^1.1.4
|
||||||
|
Loading…
Reference in New Issue
Block a user