fix: correct track/disc defaults, forward extension metadata, and fix service ID display

- Default track/disc number to 0 (unknown) instead of 1, letting the
  backend use the service-provided value or skip the field entirely
- Add releaseDate to ExploreItem so explore downloads carry release info
- Pass discNumber and releaseDate from extension album/playlist tracks
- Fix isDeezer detection using service field instead of substring match
- Add _displayServiceTrackId() to properly strip prefixes for all services
This commit is contained in:
zarzet
2026-04-02 15:13:11 +07:00
parent 81e25d7dab
commit 76d50fab3a
5 changed files with 53 additions and 13 deletions
+4 -4
View File
@@ -2811,8 +2811,8 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
'artist': track.artistName,
'album': track.albumName,
'album_artist': resolvedAlbumArtist,
'track_number': track.trackNumber ?? 1,
'disc_number': track.discNumber ?? 1,
'track_number': track.trackNumber ?? 0,
'disc_number': track.discNumber ?? 0,
'isrc': track.isrc ?? '',
'release_date': track.releaseDate ?? '',
'duration_ms': track.duration * 1000,
@@ -4649,12 +4649,12 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
(trackToDownload.trackNumber != null &&
trackToDownload.trackNumber! > 0)
? trackToDownload.trackNumber!
: 1;
: 0;
final normalizedDiscNumber =
(trackToDownload.discNumber != null &&
trackToDownload.discNumber! > 0)
? trackToDownload.discNumber!
: 1;
: 0;
String payloadSpotifyId = trackToDownload.id;
String payloadQobuzId = '';
+4
View File
@@ -19,6 +19,7 @@ class ExploreItem {
final String? providerId;
final String? albumId;
final String? albumName;
final String? releaseDate;
final int durationMs;
const ExploreItem({
@@ -32,6 +33,7 @@ class ExploreItem {
this.providerId,
this.albumId,
this.albumName,
this.releaseDate,
this.durationMs = 0,
});
@@ -47,6 +49,7 @@ class ExploreItem {
providerId: json['provider_id'] as String?,
albumId: json['album_id'] as String?,
albumName: json['album_name'] as String?,
releaseDate: json['release_date']?.toString(),
durationMs: json['duration_ms'] as int? ?? 0,
);
}
@@ -62,6 +65,7 @@ class ExploreItem {
'provider_id': providerId,
'album_id': albumId,
'album_name': albumName,
'release_date': releaseDate,
'duration_ms': durationMs,
};
}
+7 -3
View File
@@ -1845,10 +1845,10 @@ class _HomeTabState extends ConsumerState<HomeTab>
albumName: item.albumName ?? '',
albumId: item.albumId,
duration: item.durationMs ~/ 1000,
trackNumber: 1,
discNumber: 1,
trackNumber: null,
discNumber: null,
isrc: null,
releaseDate: null,
releaseDate: item.releaseDate,
coverUrl: item.coverUrl,
source: item.providerId ?? 'spotify-web',
);
@@ -4272,6 +4272,8 @@ class _ExtensionAlbumScreenState extends ConsumerState<ExtensionAlbumScreen> {
isrc: data['isrc']?.toString(),
duration: (durationMs / 1000).round(),
trackNumber: data['track_number'] as int?,
discNumber: data['disc_number'] as int?,
releaseDate: data['release_date']?.toString(),
source: widget.extensionId,
);
}
@@ -4429,6 +4431,8 @@ class _ExtensionPlaylistScreenState
isrc: data['isrc']?.toString(),
duration: (durationMs / 1000).round(),
trackNumber: data['track_number'] as int?,
discNumber: data['disc_number'] as int?,
releaseDate: data['release_date']?.toString(),
source: widget.extensionId,
);
}
+36 -4
View File
@@ -552,6 +552,36 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
return parsed;
}
String _displayServiceTrackId(String value) {
final raw = value.trim();
if (raw.isEmpty) return raw;
final spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$');
if (raw.startsWith('deezer:')) return raw.substring('deezer:'.length);
if (raw.startsWith('tidal:')) return raw.substring('tidal:'.length);
if (raw.startsWith('qobuz:')) return raw.substring('qobuz:'.length);
if (spotifyTrackIdPattern.hasMatch(raw)) return raw;
if (raw.startsWith('spotify:')) {
final last = raw.split(':').last.trim();
if (spotifyTrackIdPattern.hasMatch(last)) return last;
return raw;
}
final uri = Uri.tryParse(raw);
if (uri != null &&
uri.host.contains('spotify.com') &&
uri.pathSegments.length >= 2 &&
uri.pathSegments.first == 'track') {
final candidate = uri.pathSegments[1].trim();
if (spotifyTrackIdPattern.hasMatch(candidate)) {
return candidate;
}
}
return raw;
}
String? get _displayAudioQuality {
final fileName = _extractFileNameFromPathOrUri(cleanFilePath);
final fileExt = fileName.contains('.')
@@ -1102,8 +1132,9 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
Future<void> _openServiceUrl(BuildContext context) async {
if (_spotifyId == null) return;
final isDeezer = _spotifyId!.contains('deezer');
final rawId = _spotifyId!.replaceAll('deezer:', '');
final isDeezer =
_service.toLowerCase() == 'deezer' || _spotifyId!.startsWith('deezer:');
final rawId = _displayServiceTrackId(_spotifyId!);
final svc = _service.toLowerCase();
String webUrl;
@@ -1192,8 +1223,9 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
];
if (!_isLocalItem && _spotifyId != null && _spotifyId!.isNotEmpty) {
final isDeezer = _spotifyId!.contains('deezer');
final cleanId = _spotifyId!.replaceAll('deezer:', '');
final isDeezer =
_service.toLowerCase() == 'deezer' || _spotifyId!.startsWith('deezer:');
final cleanId = _displayServiceTrackId(_spotifyId!);
String idLabel;
if (isDeezer) {
idLabel = 'Deezer ID';
+2 -2
View File
@@ -53,8 +53,8 @@ class DownloadRequestPayload {
this.artistTagMode = 'joined',
this.embedLyrics = true,
this.embedMaxQualityCover = true,
this.trackNumber = 1,
this.discNumber = 1,
this.trackNumber = 0,
this.discNumber = 0,
this.totalTracks = 1,
this.releaseDate = '',
this.itemId = '',