fix(playlist): ignore container title during album verification #519

This commit is contained in:
zarzet
2026-08-11 17:02:17 +07:00
parent f7439c6793
commit 1d363d5166
5 changed files with 55 additions and 9 deletions
+15 -2
View File
@@ -82,7 +82,11 @@ class Track {
Map<String, dynamic> toJson() => _$TrackToJson(this);
/// Builds a [Track] from a backend/extension search payload map.
factory Track.fromBackendMap(Map<String, dynamic> data, {String? source}) {
factory Track.fromBackendMap(
Map<String, dynamic> data, {
String? source,
String? playlistName,
}) {
final durationMs = extractDurationMs(data);
final itemType = data['item_type']?.toString();
@@ -93,12 +97,21 @@ class Track {
final preferredId = effectiveSource != null && effectiveSource.isNotEmpty
? (nativeId.isNotEmpty ? nativeId : spotifyId)
: (spotifyId.isNotEmpty ? spotifyId : nativeId);
final rawAlbumName = (data['album_name'] ?? data['album'] ?? '').toString();
final normalizedAlbumName = normalizeOptionalString(rawAlbumName);
final normalizedPlaylistName = normalizeOptionalString(playlistName);
final albumName =
normalizedPlaylistName != null &&
normalizedAlbumName?.toLowerCase() ==
normalizedPlaylistName.toLowerCase()
? ''
: rawAlbumName;
return Track(
id: preferredId,
name: (data['name'] ?? '').toString(),
artistName: (data['artists'] ?? data['artist'] ?? '').toString(),
albumName: (data['album_name'] ?? data['album'] ?? '').toString(),
albumName: albumName,
albumArtist: data['album_artist']?.toString(),
artistId: (data['artist_id'] ?? data['artistId'])?.toString(),
albumId: data['album_id']?.toString(),
+4 -2
View File
@@ -224,11 +224,13 @@ class TrackNotifier extends Notifier<TrackState> {
} else if ((type == 'album' || type == 'playlist') &&
result['tracks'] != null) {
final trackList = result['tracks'] as List<dynamic>;
final collectionName = result['name'] as String?;
final tracks = trackList
.map(
(t) => Track.fromBackendMap(
t as Map<String, dynamic>,
source: extensionId,
playlistName: type == 'playlist' ? collectionName : null,
),
)
.toList();
@@ -238,9 +240,9 @@ class TrackNotifier extends Notifier<TrackState> {
albumId:
(result['album'] as Map<String, dynamic>?)?['id'] as String?,
albumName:
result['name'] as String? ??
collectionName ??
(result['album'] as Map<String, dynamic>?)?['name'] as String?,
playlistName: type == 'playlist' ? result['name'] as String? : null,
playlistName: type == 'playlist' ? collectionName : null,
coverUrl: normalizeCoverReference(result['cover_url']?.toString()),
headerVideoUrl: normalizeRemoteHttpUrl(
result['header_video']?.toString(),
+6 -2
View File
@@ -1155,12 +1155,16 @@ class _ExtensionPlaylistScreenState
}
Track _parseTrack(Map<String, dynamic> data) {
final base = Track.fromBackendMap(data, source: widget.extensionId);
final base = Track.fromBackendMap(
data,
source: widget.extensionId,
playlistName: widget.playlistName,
);
return Track(
id: (data['id'] ?? '').toString(),
name: base.name,
artistName: base.artistName,
albumName: (data['album_name'] ?? '').toString(),
albumName: base.albumName,
artistId: base.artistId,
albumId: base.albumId,
coverUrl: _resolveTrackCoverUrl(
+10 -3
View File
@@ -207,18 +207,25 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen>
final playlistInfo = result['playlist_info'] as Map<String, dynamic>?;
final owner = playlistInfo?['owner'] as Map<String, dynamic>?;
final resolvedPlaylistName =
(playlistInfo?['name'] ?? owner?['name'])?.toString() ??
_playlistName;
final trackList = result['track_list'] as List<dynamic>? ?? [];
final tracks = trackList
.map((t) => Track.fromBackendMap(t as Map<String, dynamic>))
.map(
(t) => Track.fromBackendMap(
t as Map<String, dynamic>,
playlistName: resolvedPlaylistName,
),
)
.toList();
final headerVideo = playlistInfo?['header_video']?.toString();
setState(() {
_fetchedTracks = tracks;
_resolvedPlaylistName = (playlistInfo?['name'] ?? owner?['name'])
?.toString();
_resolvedPlaylistName = resolvedPlaylistName;
_resolvedCoverUrl = (playlistInfo?['images'] ?? owner?['images'])
?.toString();
_resolvedHeaderVideoUrl =
+20
View File
@@ -443,6 +443,26 @@ void main() {
expect(track.isExplicit, isTrue);
});
test('does not treat a playlist container name as a track album', () {
final playlistTrack = Track.fromBackendMap({
'id': 'playlist-track-1',
'name': 'Song',
'artists': 'Artist',
'album_name': ' Road Trip ',
'duration_ms': 180000,
}, playlistName: 'road trip');
final releaseTrack = Track.fromBackendMap({
'id': 'playlist-track-2',
'name': 'Song',
'artists': 'Artist',
'album_name': 'Real Album',
'duration_ms': 180000,
}, playlistName: 'Road Trip');
expect(playlistTrack.albumName, isEmpty);
expect(releaseTrack.albumName, 'Real Album');
});
test('exposes collection, source, and quality flags', () {
const album = Track(
id: 'album-1',