mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-14 22:05:39 +02:00
fix(metadata): preserve track tags across collection screens
This commit is contained in:
@@ -320,6 +320,13 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen>
|
||||
totalTracksFallback ??
|
||||
_albumTotalTracks,
|
||||
composer: data['composer']?.toString(),
|
||||
genre: data['genre']?.toString(),
|
||||
label: data['label']?.toString(),
|
||||
copyright: data['copyright']?.toString(),
|
||||
comment: data['comment']?.toString(),
|
||||
upc: normalizeOptionalString(
|
||||
(data['upc'] ?? data['barcode'])?.toString(),
|
||||
),
|
||||
audioQuality: data['audio_quality']?.toString(),
|
||||
audioModes: data['audio_modes']?.toString(),
|
||||
previewUrl: data['preview_url']?.toString(),
|
||||
|
||||
@@ -428,6 +428,15 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen>
|
||||
album?.albumType,
|
||||
totalTracks: data['total_tracks'] as int? ?? album?.totalTracks,
|
||||
composer: data['composer']?.toString(),
|
||||
genre: data['genre']?.toString(),
|
||||
label: data['label']?.toString(),
|
||||
copyright: data['copyright']?.toString(),
|
||||
comment: data['comment']?.toString(),
|
||||
upc: normalizeOptionalString(
|
||||
(data['upc'] ?? data['barcode'])?.toString(),
|
||||
),
|
||||
audioQuality: data['audio_quality']?.toString(),
|
||||
audioModes: data['audio_modes']?.toString(),
|
||||
source: data['provider_id']?.toString() ?? widget.extensionId,
|
||||
previewUrl: data['preview_url']?.toString(),
|
||||
explicit: parseExplicitFlag(data['explicit']),
|
||||
|
||||
@@ -1018,10 +1018,8 @@ class _ExtensionAlbumScreenState extends ConsumerState<ExtensionAlbumScreen> {
|
||||
int? totalTracksFallback,
|
||||
}) {
|
||||
final base = Track.fromBackendMap(data, source: widget.extensionId);
|
||||
return Track(
|
||||
return base.copyWith(
|
||||
id: (data['id'] ?? '').toString(),
|
||||
name: base.name,
|
||||
artistName: base.artistName,
|
||||
albumName: (data['album_name'] ?? widget.albumName).toString(),
|
||||
albumArtist: normalizeOptionalString(data['album_artist']?.toString()),
|
||||
artistId: base.artistId ?? _artistId,
|
||||
@@ -1030,20 +1028,9 @@ class _ExtensionAlbumScreenState extends ConsumerState<ExtensionAlbumScreen> {
|
||||
data['cover_url']?.toString(),
|
||||
widget.coverUrl,
|
||||
),
|
||||
isrc: base.isrc,
|
||||
duration: (_legacyTrackDurationMs(data) / 1000).round(),
|
||||
trackNumber: base.trackNumber,
|
||||
discNumber: base.discNumber,
|
||||
totalDiscs: base.totalDiscs,
|
||||
releaseDate: base.releaseDate,
|
||||
albumType: base.albumType ?? albumTypeFallback ?? _albumType,
|
||||
totalTracks: base.totalTracks ?? totalTracksFallback ?? _albumTotalTracks,
|
||||
composer: base.composer,
|
||||
source: base.source,
|
||||
audioQuality: base.audioQuality,
|
||||
audioModes: base.audioModes,
|
||||
previewUrl: base.previewUrl,
|
||||
explicit: base.explicit,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1170,30 +1157,13 @@ class _ExtensionPlaylistScreenState
|
||||
source: widget.extensionId,
|
||||
playlistName: widget.playlistName,
|
||||
);
|
||||
return Track(
|
||||
return base.copyWith(
|
||||
id: (data['id'] ?? '').toString(),
|
||||
name: base.name,
|
||||
artistName: base.artistName,
|
||||
albumName: base.albumName,
|
||||
artistId: base.artistId,
|
||||
albumId: base.albumId,
|
||||
coverUrl: _resolveTrackCoverUrl(
|
||||
data['cover_url']?.toString(),
|
||||
widget.coverUrl,
|
||||
),
|
||||
isrc: base.isrc,
|
||||
duration: (_legacyTrackDurationMs(data) / 1000).round(),
|
||||
trackNumber: base.trackNumber,
|
||||
discNumber: base.discNumber,
|
||||
totalDiscs: base.totalDiscs,
|
||||
releaseDate: base.releaseDate,
|
||||
totalTracks: base.totalTracks,
|
||||
composer: base.composer,
|
||||
source: base.source,
|
||||
audioQuality: base.audioQuality,
|
||||
audioModes: base.audioModes,
|
||||
previewUrl: base.previewUrl,
|
||||
explicit: base.explicit,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1341,26 +1311,11 @@ class _ExtensionArtistScreenState extends ConsumerState<ExtensionArtistScreen>
|
||||
|
||||
Track _parseTrack(Map<String, dynamic> data) {
|
||||
final base = Track.fromBackendMap(data);
|
||||
return Track(
|
||||
return base.copyWith(
|
||||
id: (data['id'] ?? data['spotify_id'] ?? '').toString(),
|
||||
name: base.name,
|
||||
artistName: base.artistName,
|
||||
albumName: base.albumName,
|
||||
albumArtist: base.albumArtist,
|
||||
artistId: base.artistId ?? widget.artistId,
|
||||
albumId: base.albumId,
|
||||
coverUrl: base.coverUrl,
|
||||
isrc: base.isrc,
|
||||
duration: (_legacyTrackDurationMs(data) / 1000).round(),
|
||||
trackNumber: base.trackNumber,
|
||||
discNumber: base.discNumber,
|
||||
totalDiscs: base.totalDiscs,
|
||||
releaseDate: base.releaseDate,
|
||||
totalTracks: base.totalTracks,
|
||||
composer: base.composer,
|
||||
source: (data['provider_id'] ?? widget.extensionId).toString(),
|
||||
previewUrl: base.previewUrl,
|
||||
explicit: base.explicit,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotiflac_android/l10n/app_localizations.dart';
|
||||
import 'package:spotiflac_android/models/track.dart';
|
||||
import 'package:spotiflac_android/screens/album_screen.dart';
|
||||
import 'package:spotiflac_android/widgets/track_list_tile.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('album tracks retain the extended tags supplied in search', (
|
||||
tester,
|
||||
) async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
await tester.binding.setSurfaceSize(const Size(430, 1200));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
const channel = MethodChannel('com.zarz.spotiflac/backend');
|
||||
const payload = <String, dynamic>{
|
||||
'id': 'song-1',
|
||||
'spotify_id': 'song-1',
|
||||
'name': 'Example Song',
|
||||
'artists': 'Example Artist',
|
||||
'album_name': 'Example Album',
|
||||
'duration_ms': 180000,
|
||||
'track_number': 1,
|
||||
'genre': 'Example Genre',
|
||||
'label': 'Example Label',
|
||||
'copyright': 'Example Copyright',
|
||||
'comment': 'Example Comment',
|
||||
'upc': '0123456789012',
|
||||
};
|
||||
var albumRequested = false;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (call) async {
|
||||
if (call.method == 'getProviderMetadata') {
|
||||
expect(call.arguments, {
|
||||
'provider_id': 'example-metadata',
|
||||
'resource_type': 'album',
|
||||
'resource_id': 'metadata-album',
|
||||
});
|
||||
albumRequested = true;
|
||||
return jsonEncode({
|
||||
'track_list': [payload],
|
||||
'album_info': {
|
||||
'name': 'Example Album',
|
||||
'total_tracks': 1,
|
||||
'album_type': 'album',
|
||||
},
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
addTearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
|
||||
final searchTrack = Track.fromBackendMap(payload);
|
||||
expect(searchTrack.genre, 'Example Genre');
|
||||
expect(searchTrack.label, 'Example Label');
|
||||
expect(searchTrack.copyright, 'Example Copyright');
|
||||
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
child: MaterialApp(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const AlbumScreen(
|
||||
albumId: 'metadata-album',
|
||||
albumName: 'Example Album',
|
||||
extensionId: 'example-metadata',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(albumRequested, isTrue);
|
||||
final albumTrack = tester
|
||||
.widget<TrackListTile>(find.byType(TrackListTile).first)
|
||||
.track;
|
||||
expect(albumTrack.id, searchTrack.id);
|
||||
expect(albumTrack.name, searchTrack.name);
|
||||
expect(albumTrack.genre, searchTrack.genre);
|
||||
expect(albumTrack.label, searchTrack.label);
|
||||
expect(albumTrack.copyright, searchTrack.copyright);
|
||||
expect(albumTrack.comment, searchTrack.comment);
|
||||
expect(albumTrack.upc, searchTrack.upc);
|
||||
expect(albumTrack.albumId, 'metadata-album');
|
||||
expect(albumTrack.albumType, 'album');
|
||||
expect(albumTrack.totalTracks, 1);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotiflac_android/l10n/app_localizations.dart';
|
||||
import 'package:spotiflac_android/screens/artist_screen.dart';
|
||||
import 'package:spotiflac_android/screens/home_tab.dart';
|
||||
import 'package:spotiflac_android/screens/playlist_screen.dart';
|
||||
import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
import 'package:spotiflac_android/widgets/track_collection_quick_actions.dart';
|
||||
|
||||
void main() {
|
||||
final screens = <String, Widget>{
|
||||
'artist': const ArtistScreen(
|
||||
artistId: 'direct-artist',
|
||||
artistName: 'Example Artist',
|
||||
extensionId: 'example-metadata',
|
||||
),
|
||||
'extension album': const ExtensionAlbumScreen(
|
||||
extensionId: 'example-metadata',
|
||||
albumId: 'extension-album',
|
||||
albumName: 'Example Album',
|
||||
),
|
||||
'extension playlist': const ExtensionPlaylistScreen(
|
||||
extensionId: 'example-metadata',
|
||||
playlistId: 'extension-playlist',
|
||||
playlistName: 'Example Playlist',
|
||||
),
|
||||
'extension artist': const ExtensionArtistScreen(
|
||||
extensionId: 'example-metadata',
|
||||
artistId: 'extension-artist',
|
||||
artistName: 'Example Artist',
|
||||
),
|
||||
'playlist': const PlaylistScreen(
|
||||
playlistId: 'direct-playlist',
|
||||
playlistName: 'Example Playlist',
|
||||
metadataProviderId: 'example-metadata',
|
||||
tracks: [],
|
||||
),
|
||||
};
|
||||
|
||||
for (final entry in screens.entries) {
|
||||
testWidgets('${entry.key} preserves supplied track metadata', (
|
||||
tester,
|
||||
) async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
await tester.binding.setSurfaceSize(const Size(800, 1600));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
addTearDown(() async {
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
// Flush the debounced platform metadata cache before the next case.
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
const payload = <String, dynamic>{
|
||||
'id': 'example-song',
|
||||
'spotify_id': 'example-song',
|
||||
'provider_id': 'example-metadata',
|
||||
'name': 'Example Song',
|
||||
'artists': 'Example Artist',
|
||||
'album_name': 'Example Album',
|
||||
'album_artist': 'Example Album Artist',
|
||||
'album_type': 'album',
|
||||
'duration_ms': 180000,
|
||||
'track_number': 1,
|
||||
'genre': 'Example Genre',
|
||||
'label': 'Example Label',
|
||||
'copyright': 'Example Copyright',
|
||||
'comment': 'Example Comment',
|
||||
'upc': '0123456789012',
|
||||
'audio_quality': '24-bit/96000Hz',
|
||||
'audio_modes': 'STEREO',
|
||||
};
|
||||
const channel = MethodChannel('com.zarz.spotiflac/backend');
|
||||
var requested = false;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, (call) async {
|
||||
if (call.method == 'getProviderMetadata') {
|
||||
requested = true;
|
||||
return jsonEncode({
|
||||
'track_list': [payload],
|
||||
'top_tracks': [payload],
|
||||
'albums': <Map<String, dynamic>>[],
|
||||
'album_info': {'name': 'Example Album', 'total_tracks': 1},
|
||||
'playlist_info': {'name': 'Example Playlist'},
|
||||
'artist_info': {'name': 'Example Artist'},
|
||||
});
|
||||
}
|
||||
return null;
|
||||
});
|
||||
addTearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
await PlatformBridge.clearTrackCache();
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
child: MaterialApp(
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: entry.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(requested, isTrue);
|
||||
final track = tester
|
||||
.widget<TrackCollectionQuickActions>(
|
||||
find.byType(TrackCollectionQuickActions).first,
|
||||
)
|
||||
.track;
|
||||
expect(track.id, 'example-song');
|
||||
expect(track.duration, 180);
|
||||
expect(track.genre, payload['genre']);
|
||||
expect(track.label, payload['label']);
|
||||
expect(track.copyright, payload['copyright']);
|
||||
expect(track.comment, payload['comment']);
|
||||
expect(track.upc, payload['upc']);
|
||||
expect(track.albumArtist, payload['album_artist']);
|
||||
expect(track.albumType, payload['album_type']);
|
||||
expect(track.audioQuality, payload['audio_quality']);
|
||||
expect(track.audioModes, payload['audio_modes']);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
await PlatformBridge.clearTrackCache();
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user