mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-07-25 13:30:55 +02:00
feat: enrich composer and track totals metadata
This commit is contained in:
@@ -16,12 +16,14 @@ class Track {
|
||||
final int duration;
|
||||
final int? trackNumber;
|
||||
final int? discNumber;
|
||||
final int? totalDiscs;
|
||||
final String? releaseDate;
|
||||
final String? deezerId;
|
||||
final ServiceAvailability? availability;
|
||||
final String? source;
|
||||
final String? albumType;
|
||||
final int? totalTracks;
|
||||
final String? composer;
|
||||
final String? itemType;
|
||||
|
||||
const Track({
|
||||
@@ -37,12 +39,14 @@ class Track {
|
||||
required this.duration,
|
||||
this.trackNumber,
|
||||
this.discNumber,
|
||||
this.totalDiscs,
|
||||
this.releaseDate,
|
||||
this.deezerId,
|
||||
this.availability,
|
||||
this.source,
|
||||
this.albumType,
|
||||
this.totalTracks,
|
||||
this.composer,
|
||||
this.itemType,
|
||||
});
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ Track _$TrackFromJson(Map<String, dynamic> json) => Track(
|
||||
duration: (json['duration'] as num).toInt(),
|
||||
trackNumber: (json['trackNumber'] as num?)?.toInt(),
|
||||
discNumber: (json['discNumber'] as num?)?.toInt(),
|
||||
totalDiscs: (json['totalDiscs'] as num?)?.toInt(),
|
||||
releaseDate: json['releaseDate'] as String?,
|
||||
deezerId: json['deezerId'] as String?,
|
||||
availability: json['availability'] == null
|
||||
@@ -29,6 +30,7 @@ Track _$TrackFromJson(Map<String, dynamic> json) => Track(
|
||||
source: json['source'] as String?,
|
||||
albumType: json['albumType'] as String?,
|
||||
totalTracks: (json['totalTracks'] as num?)?.toInt(),
|
||||
composer: json['composer'] as String?,
|
||||
itemType: json['itemType'] as String?,
|
||||
);
|
||||
|
||||
@@ -45,12 +47,14 @@ Map<String, dynamic> _$TrackToJson(Track instance) => <String, dynamic>{
|
||||
'duration': instance.duration,
|
||||
'trackNumber': instance.trackNumber,
|
||||
'discNumber': instance.discNumber,
|
||||
'totalDiscs': instance.totalDiscs,
|
||||
'releaseDate': instance.releaseDate,
|
||||
'deezerId': instance.deezerId,
|
||||
'availability': instance.availability,
|
||||
'source': instance.source,
|
||||
'albumType': instance.albumType,
|
||||
'totalTracks': instance.totalTracks,
|
||||
'composer': instance.composer,
|
||||
'itemType': instance.itemType,
|
||||
};
|
||||
|
||||
|
||||
@@ -3251,11 +3251,13 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
isrc: backendIsrc ?? baseTrack.isrc,
|
||||
trackNumber: backendTrackNum ?? baseTrack.trackNumber,
|
||||
discNumber: backendDiscNum ?? baseTrack.discNumber,
|
||||
totalDiscs: baseTrack.totalDiscs,
|
||||
releaseDate: backendYear ?? baseTrack.releaseDate,
|
||||
deezerId: baseTrack.deezerId,
|
||||
availability: baseTrack.availability,
|
||||
albumType: baseTrack.albumType,
|
||||
totalTracks: baseTrack.totalTracks,
|
||||
composer: baseTrack.composer,
|
||||
source: baseTrack.source,
|
||||
);
|
||||
}
|
||||
@@ -3329,17 +3331,25 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
'ARTIST': track.artistName,
|
||||
'ALBUM': track.albumName,
|
||||
};
|
||||
String formatIndexTag(int number, int? total) {
|
||||
if (total != null && total > 0) {
|
||||
return '$number/$total';
|
||||
}
|
||||
return number.toString();
|
||||
}
|
||||
|
||||
final albumArtist = _resolveAlbumArtistForMetadata(track, settings);
|
||||
metadata['ALBUMARTIST'] = albumArtist;
|
||||
|
||||
if (track.trackNumber != null && track.trackNumber! > 0) {
|
||||
metadata['TRACKNUMBER'] = track.trackNumber.toString();
|
||||
if (isFlac || isMp3) metadata['TRACK'] = track.trackNumber.toString();
|
||||
final trackTag = formatIndexTag(track.trackNumber!, track.totalTracks);
|
||||
metadata['TRACKNUMBER'] = trackTag;
|
||||
if (isFlac || isMp3) metadata['TRACK'] = trackTag;
|
||||
}
|
||||
if (track.discNumber != null && track.discNumber! > 0) {
|
||||
metadata['DISCNUMBER'] = track.discNumber.toString();
|
||||
if (isFlac || isMp3) metadata['DISC'] = track.discNumber.toString();
|
||||
final discTag = formatIndexTag(track.discNumber!, track.totalDiscs);
|
||||
metadata['DISCNUMBER'] = discTag;
|
||||
if (isFlac || isMp3) metadata['DISC'] = discTag;
|
||||
}
|
||||
if (track.releaseDate != null) {
|
||||
metadata['DATE'] = track.releaseDate!;
|
||||
@@ -3353,6 +3363,9 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
if (copyright != null && copyright.isNotEmpty) {
|
||||
metadata['COPYRIGHT'] = copyright;
|
||||
}
|
||||
if (track.composer != null && track.composer!.isNotEmpty) {
|
||||
metadata['COMPOSER'] = track.composer!;
|
||||
}
|
||||
|
||||
// ── Lyrics ──────────────────────────────────────────────────────
|
||||
final lyricsMode = settings.lyricsMode;
|
||||
@@ -3875,7 +3888,11 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
(trackToDownload.isrc == null ||
|
||||
trackToDownload.isrc!.isEmpty ||
|
||||
trackToDownload.trackNumber == null ||
|
||||
trackToDownload.trackNumber == 0);
|
||||
trackToDownload.trackNumber == 0 ||
|
||||
trackToDownload.totalTracks == null ||
|
||||
trackToDownload.totalTracks == 0 ||
|
||||
(trackToDownload.composer == null ||
|
||||
trackToDownload.composer!.isEmpty));
|
||||
|
||||
if (needsEnrichment) {
|
||||
try {
|
||||
@@ -3923,6 +3940,8 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
isrc: (data['isrc'] as String?) ?? trackToDownload.isrc,
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs:
|
||||
data['total_discs'] as int? ?? trackToDownload.totalDiscs,
|
||||
releaseDate: data['release_date'] as String?,
|
||||
deezerId: rawId,
|
||||
availability: trackToDownload.availability,
|
||||
@@ -3931,6 +3950,8 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
trackToDownload.albumType,
|
||||
totalTracks:
|
||||
data['total_tracks'] as int? ?? trackToDownload.totalTracks,
|
||||
composer:
|
||||
data['composer']?.toString() ?? trackToDownload.composer,
|
||||
source: trackToDownload.source,
|
||||
);
|
||||
_log.d(
|
||||
@@ -4101,7 +4122,12 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
trackData['release_date'] as String?,
|
||||
);
|
||||
final provTrackNum = trackData['track_number'] as int?;
|
||||
final provTotalTracks = trackData['total_tracks'] as int?;
|
||||
final provDiscNum = trackData['disc_number'] as int?;
|
||||
final provTotalDiscs = trackData['total_discs'] as int?;
|
||||
final provComposer = normalizeOptionalString(
|
||||
trackData['composer'] as String?,
|
||||
);
|
||||
|
||||
trackToDownload = Track(
|
||||
id: trackToDownload.id,
|
||||
@@ -4124,11 +4150,21 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
trackToDownload.discNumber! > 0)
|
||||
? trackToDownload.discNumber
|
||||
: provDiscNum,
|
||||
totalDiscs:
|
||||
(trackToDownload.totalDiscs != null &&
|
||||
trackToDownload.totalDiscs! > 0)
|
||||
? trackToDownload.totalDiscs
|
||||
: provTotalDiscs,
|
||||
releaseDate: trackToDownload.releaseDate ?? provReleaseDate,
|
||||
deezerId: trackToDownload.deezerId,
|
||||
availability: trackToDownload.availability,
|
||||
albumType: trackToDownload.albumType,
|
||||
totalTracks: trackToDownload.totalTracks,
|
||||
totalTracks:
|
||||
(trackToDownload.totalTracks != null &&
|
||||
trackToDownload.totalTracks! > 0)
|
||||
? trackToDownload.totalTracks
|
||||
: provTotalTracks,
|
||||
composer: trackToDownload.composer ?? provComposer,
|
||||
source: trackToDownload.source,
|
||||
);
|
||||
|
||||
@@ -4198,7 +4234,12 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
trackData['isrc'] as String?,
|
||||
);
|
||||
final deezerTrackNum = trackData['track_number'] as int?;
|
||||
final deezerTotalTracks = trackData['total_tracks'] as int?;
|
||||
final deezerDiscNum = trackData['disc_number'] as int?;
|
||||
final deezerTotalDiscs = trackData['total_discs'] as int?;
|
||||
final deezerComposer = normalizeOptionalString(
|
||||
trackData['composer'] as String?,
|
||||
);
|
||||
|
||||
final needsEnrich =
|
||||
(trackToDownload.releaseDate == null &&
|
||||
@@ -4210,10 +4251,21 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
trackToDownload.trackNumber! <= 0) &&
|
||||
deezerTrackNum != null &&
|
||||
deezerTrackNum > 0) ||
|
||||
((trackToDownload.totalTracks == null ||
|
||||
trackToDownload.totalTracks! <= 0) &&
|
||||
deezerTotalTracks != null &&
|
||||
deezerTotalTracks > 0) ||
|
||||
((trackToDownload.discNumber == null ||
|
||||
trackToDownload.discNumber! <= 0) &&
|
||||
deezerDiscNum != null &&
|
||||
deezerDiscNum > 0);
|
||||
deezerDiscNum > 0) ||
|
||||
((trackToDownload.totalDiscs == null ||
|
||||
trackToDownload.totalDiscs! <= 0) &&
|
||||
deezerTotalDiscs != null &&
|
||||
deezerTotalDiscs > 0) ||
|
||||
((trackToDownload.composer == null ||
|
||||
trackToDownload.composer!.isEmpty) &&
|
||||
deezerComposer != null);
|
||||
|
||||
if (needsEnrich) {
|
||||
trackToDownload = Track(
|
||||
@@ -4239,11 +4291,21 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
trackToDownload.discNumber! > 0)
|
||||
? trackToDownload.discNumber
|
||||
: deezerDiscNum,
|
||||
totalDiscs:
|
||||
(trackToDownload.totalDiscs != null &&
|
||||
trackToDownload.totalDiscs! > 0)
|
||||
? trackToDownload.totalDiscs
|
||||
: deezerTotalDiscs,
|
||||
releaseDate: trackToDownload.releaseDate ?? deezerReleaseDate,
|
||||
deezerId: deezerTrackId,
|
||||
availability: trackToDownload.availability,
|
||||
albumType: trackToDownload.albumType,
|
||||
totalTracks: trackToDownload.totalTracks,
|
||||
totalTracks:
|
||||
(trackToDownload.totalTracks != null &&
|
||||
trackToDownload.totalTracks! > 0)
|
||||
? trackToDownload.totalTracks
|
||||
: deezerTotalTracks,
|
||||
composer: trackToDownload.composer ?? deezerComposer,
|
||||
source: trackToDownload.source,
|
||||
);
|
||||
_log.d(
|
||||
@@ -4390,6 +4452,8 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
metadataEmbeddingEnabled && settings.maxQualityCover,
|
||||
trackNumber: normalizedTrackNumber,
|
||||
discNumber: normalizedDiscNumber,
|
||||
totalTracks: trackToDownload.totalTracks ?? 0,
|
||||
totalDiscs: trackToDownload.totalDiscs ?? 0,
|
||||
releaseDate: trackToDownload.releaseDate ?? '',
|
||||
itemId: item.id,
|
||||
durationMs: trackToDownload.duration,
|
||||
@@ -4397,6 +4461,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
|
||||
genre: genre ?? '',
|
||||
label: label ?? '',
|
||||
copyright: copyright ?? '',
|
||||
composer: trackToDownload.composer ?? '',
|
||||
qobuzId: payloadQobuzId,
|
||||
tidalId: payloadTidalId,
|
||||
deezerId: deezerTrackId ?? '',
|
||||
|
||||
@@ -906,9 +906,11 @@ class TrackNotifier extends Notifier<TrackState> {
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date'] as String?,
|
||||
albumType: data['album_type'] as String?,
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
composer: data['composer']?.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -939,10 +941,12 @@ class TrackNotifier extends Notifier<TrackState> {
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date']?.toString(),
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
source: effectiveSource,
|
||||
albumType: data['album_type']?.toString(),
|
||||
composer: data['composer']?.toString(),
|
||||
itemType: itemType,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -299,9 +299,11 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
duration: ((data['duration_ms'] as int? ?? 0) / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date'] as String?,
|
||||
albumType: data['album_type'] as String?,
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
composer: data['composer']?.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -410,9 +410,11 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date']?.toString(),
|
||||
albumType: data['album_type']?.toString() ?? album?.albumType,
|
||||
totalTracks: data['total_tracks'] as int? ?? album?.totalTracks,
|
||||
composer: data['composer']?.toString(),
|
||||
source: data['provider_id']?.toString() ?? widget.extensionId,
|
||||
);
|
||||
}
|
||||
@@ -1129,9 +1131,11 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
trackNumber:
|
||||
data['track_position'] as int? ?? data['track_number'] as int?,
|
||||
discNumber: data['disk_number'] as int? ?? data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: album.releaseDate,
|
||||
albumType: album.albumType,
|
||||
totalTracks: album.totalTracks,
|
||||
composer: data['composer']?.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1840,6 +1840,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
duration: item.durationMs ~/ 1000,
|
||||
trackNumber: null,
|
||||
discNumber: null,
|
||||
totalDiscs: null,
|
||||
isrc: null,
|
||||
releaseDate: item.releaseDate,
|
||||
coverUrl: item.coverUrl,
|
||||
@@ -4403,7 +4404,10 @@ class _ExtensionAlbumScreenState extends ConsumerState<ExtensionAlbumScreen> {
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date']?.toString(),
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
composer: data['composer']?.toString(),
|
||||
source: widget.extensionId,
|
||||
);
|
||||
}
|
||||
@@ -4562,7 +4566,10 @@ class _ExtensionPlaylistScreenState
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date']?.toString(),
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
composer: data['composer']?.toString(),
|
||||
source: widget.extensionId,
|
||||
);
|
||||
}
|
||||
@@ -4739,7 +4746,10 @@ class _ExtensionArtistScreenState extends ConsumerState<ExtensionArtistScreen> {
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date']?.toString(),
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
composer: data['composer']?.toString(),
|
||||
source: (data['provider_id'] ?? widget.extensionId).toString(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,7 +164,10 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date']?.toString(),
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
composer: data['composer']?.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -327,8 +327,23 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
|
||||
// Resolve label/copyright from file when the model doesn't carry them
|
||||
// (e.g. local library items, or download history items without these fields).
|
||||
final resolvedTotalTracks = _readPositiveInt(metadata['total_tracks']);
|
||||
final resolvedTotalDiscs = _readPositiveInt(metadata['total_discs']);
|
||||
final resolvedComposer = metadata['composer']?.toString();
|
||||
final resolvedLabel = metadata['label']?.toString();
|
||||
final resolvedCopyright = metadata['copyright']?.toString();
|
||||
final needsTotalTracks =
|
||||
resolvedTotalTracks != null &&
|
||||
resolvedTotalTracks > 0 &&
|
||||
totalTracks == null;
|
||||
final needsTotalDiscs =
|
||||
resolvedTotalDiscs != null &&
|
||||
resolvedTotalDiscs > 0 &&
|
||||
totalDiscs == null;
|
||||
final needsComposer =
|
||||
resolvedComposer != null &&
|
||||
resolvedComposer.isNotEmpty &&
|
||||
(composer == null || composer!.isEmpty);
|
||||
final needsLabel =
|
||||
resolvedLabel != null &&
|
||||
resolvedLabel.isNotEmpty &&
|
||||
@@ -348,6 +363,9 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
resolvedSampleRate != null ||
|
||||
needsAlbum ||
|
||||
needsDuration ||
|
||||
needsTotalTracks ||
|
||||
needsTotalDiscs ||
|
||||
needsComposer ||
|
||||
needsLabel ||
|
||||
needsCopyright ||
|
||||
isPlaceholderQualityLabel(_quality)) &&
|
||||
@@ -361,6 +379,9 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
if (resolvedSampleRate != null) 'sample_rate': resolvedSampleRate,
|
||||
if (needsAlbum) 'album': resolvedAlbum,
|
||||
if (needsDuration) 'duration': resolvedDuration,
|
||||
if (needsTotalTracks) 'total_tracks': resolvedTotalTracks,
|
||||
if (needsTotalDiscs) 'total_discs': resolvedTotalDiscs,
|
||||
if (needsComposer) 'composer': resolvedComposer,
|
||||
if (needsLabel) 'label': resolvedLabel,
|
||||
if (needsCopyright) 'copyright': resolvedCopyright,
|
||||
};
|
||||
@@ -482,6 +503,10 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
: _downloadItem!.trackNumber;
|
||||
}
|
||||
|
||||
int? get totalTracks =>
|
||||
_readPositiveInt(_editedMetadata?['total_tracks']) ??
|
||||
(_isLocalItem ? _localLibraryItem!.totalTracks : null);
|
||||
|
||||
int? get discNumber {
|
||||
final edited = _editedMetadata?['disc_number'];
|
||||
if (edited != null) {
|
||||
@@ -493,6 +518,10 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
: _downloadItem!.discNumber;
|
||||
}
|
||||
|
||||
int? get totalDiscs =>
|
||||
_readPositiveInt(_editedMetadata?['total_discs']) ??
|
||||
(_isLocalItem ? _localLibraryItem!.totalDiscs : null);
|
||||
|
||||
String? get releaseDate =>
|
||||
_editedMetadata?['date']?.toString() ??
|
||||
(_isLocalItem
|
||||
@@ -523,6 +552,9 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
String? get copyright =>
|
||||
_editedMetadata?['copyright']?.toString() ??
|
||||
(_isLocalItem ? _localLibraryItem!.copyright : _downloadItem!.copyright);
|
||||
String? get composer =>
|
||||
_editedMetadata?['composer']?.toString() ??
|
||||
(_isLocalItem ? _localLibraryItem!.composer : null);
|
||||
int? get duration =>
|
||||
_readPositiveInt(_editedMetadata?['duration']) ??
|
||||
(_isLocalItem ? _localLibraryItem!.duration : _downloadItem!.duration);
|
||||
@@ -1257,8 +1289,12 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
_MetadataItem(context.l10n.trackAlbum, albumName),
|
||||
if (trackNumber != null && trackNumber! > 0)
|
||||
_MetadataItem(context.l10n.trackTrackNumber, trackNumber.toString()),
|
||||
if (totalTracks != null && totalTracks! > 0)
|
||||
_MetadataItem('Track Total', totalTracks.toString()),
|
||||
if (discNumber != null && discNumber! > 0)
|
||||
_MetadataItem(context.l10n.trackDiscNumber, discNumber.toString()),
|
||||
if (totalDiscs != null && totalDiscs! > 0)
|
||||
_MetadataItem('Disc Total', totalDiscs.toString()),
|
||||
if (duration != null)
|
||||
_MetadataItem(context.l10n.trackDuration, _formatDuration(duration!)),
|
||||
if (audioQualityStr != null)
|
||||
@@ -1271,6 +1307,8 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
_MetadataItem(context.l10n.trackLabel, label!),
|
||||
if (copyright != null && copyright!.isNotEmpty)
|
||||
_MetadataItem(context.l10n.trackCopyright, copyright!),
|
||||
if (composer != null && composer!.isNotEmpty)
|
||||
_MetadataItem('Composer', composer!),
|
||||
if (isrc != null && isrc!.isNotEmpty) _MetadataItem('ISRC', isrc!),
|
||||
];
|
||||
|
||||
@@ -2527,12 +2565,15 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
'album_name': albumName,
|
||||
'album_artist': albumArtist ?? artistName,
|
||||
'track_number': trackNumber ?? 0,
|
||||
'total_tracks': totalTracks ?? 0,
|
||||
'disc_number': discNumber ?? 0,
|
||||
'total_discs': totalDiscs ?? 0,
|
||||
'release_date': releaseDate ?? '',
|
||||
'isrc': isrc ?? '',
|
||||
'genre': genre ?? '',
|
||||
'label': label ?? '',
|
||||
'copyright': copyright ?? '',
|
||||
'composer': composer ?? '',
|
||||
'duration_ms': durationMs,
|
||||
'search_online': true,
|
||||
};
|
||||
@@ -2550,11 +2591,14 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
'album_artist': enriched['album_artist'] ?? albumArtist,
|
||||
'date': enriched['release_date'] ?? releaseDate,
|
||||
'track_number': enriched['track_number'] ?? trackNumber,
|
||||
'total_tracks': enriched['total_tracks'] ?? totalTracks,
|
||||
'disc_number': enriched['disc_number'] ?? discNumber,
|
||||
'total_discs': enriched['total_discs'] ?? totalDiscs,
|
||||
'isrc': enriched['isrc'] ?? isrc,
|
||||
'genre': enriched['genre'] ?? genre,
|
||||
'label': enriched['label'] ?? label,
|
||||
'copyright': enriched['copyright'] ?? copyright,
|
||||
'composer': enriched['composer'] ?? composer,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -2991,19 +3035,29 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
}
|
||||
|
||||
Map<String, String> _buildFallbackMetadata() {
|
||||
String formatIndexTag(int number, int? total) {
|
||||
if (total != null && total > 0) {
|
||||
return '$number/$total';
|
||||
}
|
||||
return number.toString();
|
||||
}
|
||||
|
||||
return {
|
||||
'TITLE': trackName,
|
||||
'ARTIST': artistName,
|
||||
'ALBUM': albumName,
|
||||
if (albumArtist != null && albumArtist!.isNotEmpty)
|
||||
'ALBUMARTIST': albumArtist!,
|
||||
if (trackNumber != null) 'TRACKNUMBER': trackNumber.toString(),
|
||||
if (discNumber != null) 'DISCNUMBER': discNumber.toString(),
|
||||
if (trackNumber != null)
|
||||
'TRACKNUMBER': formatIndexTag(trackNumber!, totalTracks),
|
||||
if (discNumber != null)
|
||||
'DISCNUMBER': formatIndexTag(discNumber!, totalDiscs),
|
||||
if (releaseDate != null && releaseDate!.isNotEmpty) 'DATE': releaseDate!,
|
||||
if (isrc != null && isrc!.isNotEmpty) 'ISRC': isrc!,
|
||||
if (genre != null && genre!.isNotEmpty) 'GENRE': genre!,
|
||||
if (label != null && label!.isNotEmpty) 'LABEL': label!,
|
||||
if (copyright != null && copyright!.isNotEmpty) 'COPYRIGHT': copyright!,
|
||||
if (composer != null && composer!.isNotEmpty) 'COMPOSER': composer!,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3031,12 +3085,26 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
put('UNSYNCEDLYRICS', source['lyrics']);
|
||||
|
||||
final trackNumber = source['track_number'];
|
||||
final totalTracks = source['total_tracks'];
|
||||
if (trackNumber != null && trackNumber.toString() != '0') {
|
||||
put('TRACKNUMBER', trackNumber);
|
||||
final trackTag =
|
||||
totalTracks != null &&
|
||||
totalTracks.toString().isNotEmpty &&
|
||||
totalTracks.toString() != '0'
|
||||
? '${trackNumber.toString()}/${totalTracks.toString()}'
|
||||
: trackNumber;
|
||||
put('TRACKNUMBER', trackTag);
|
||||
}
|
||||
final discNumber = source['disc_number'];
|
||||
final totalDiscs = source['total_discs'];
|
||||
if (discNumber != null && discNumber.toString() != '0') {
|
||||
put('DISCNUMBER', discNumber);
|
||||
final discTag =
|
||||
totalDiscs != null &&
|
||||
totalDiscs.toString().isNotEmpty &&
|
||||
totalDiscs.toString() != '0'
|
||||
? '${discNumber.toString()}/${totalDiscs.toString()}'
|
||||
: discNumber;
|
||||
put('DISCNUMBER', discTag);
|
||||
}
|
||||
|
||||
return mapped;
|
||||
@@ -4023,13 +4091,17 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
'date': val('date', releaseDate),
|
||||
'track_number': (fileMetadata?['track_number'] ?? trackNumber ?? '')
|
||||
.toString(),
|
||||
'total_tracks': (fileMetadata?['total_tracks'] ?? totalTracks ?? '')
|
||||
.toString(),
|
||||
'disc_number': (fileMetadata?['disc_number'] ?? discNumber ?? '')
|
||||
.toString(),
|
||||
'total_discs': (fileMetadata?['total_discs'] ?? totalDiscs ?? '')
|
||||
.toString(),
|
||||
'genre': val('genre', genre),
|
||||
'isrc': val('isrc', isrc),
|
||||
'label': val('label', label),
|
||||
'copyright': val('copyright', copyright),
|
||||
'composer': fileMetadata?['composer']?.toString() ?? '',
|
||||
'composer': val('composer', composer),
|
||||
'comment': fileMetadata?['comment']?.toString() ?? '',
|
||||
};
|
||||
|
||||
@@ -4316,11 +4388,14 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
'album_artist': 'album_artist',
|
||||
'date': 'date',
|
||||
'track_number': 'track_number',
|
||||
'total_tracks': 'total_tracks',
|
||||
'disc_number': 'disc_number',
|
||||
'total_discs': 'total_discs',
|
||||
'genre': 'genre',
|
||||
'isrc': 'isrc',
|
||||
'label': 'label',
|
||||
'copyright': 'copyright',
|
||||
'composer': 'composer',
|
||||
'cover': 'cover',
|
||||
};
|
||||
|
||||
@@ -4330,7 +4405,9 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
late final TextEditingController _albumArtistCtrl;
|
||||
late final TextEditingController _dateCtrl;
|
||||
late final TextEditingController _trackNumCtrl;
|
||||
late final TextEditingController _trackTotalCtrl;
|
||||
late final TextEditingController _discNumCtrl;
|
||||
late final TextEditingController _discTotalCtrl;
|
||||
late final TextEditingController _genreCtrl;
|
||||
late final TextEditingController _isrcCtrl;
|
||||
late final TextEditingController _labelCtrl;
|
||||
@@ -4518,8 +4595,12 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
return l10n.editMetadataFieldDate;
|
||||
case 'track_number':
|
||||
return l10n.editMetadataFieldTrackNum;
|
||||
case 'total_tracks':
|
||||
return 'Track Total';
|
||||
case 'disc_number':
|
||||
return l10n.editMetadataFieldDiscNum;
|
||||
case 'total_discs':
|
||||
return 'Disc Total';
|
||||
case 'genre':
|
||||
return l10n.editMetadataFieldGenre;
|
||||
case 'isrc':
|
||||
@@ -4528,6 +4609,8 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
return l10n.editMetadataFieldLabel;
|
||||
case 'copyright':
|
||||
return l10n.editMetadataFieldCopyright;
|
||||
case 'composer':
|
||||
return 'Composer';
|
||||
case 'cover':
|
||||
return l10n.editMetadataFieldCover;
|
||||
default:
|
||||
@@ -4549,8 +4632,12 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
return _dateCtrl;
|
||||
case 'track_number':
|
||||
return _trackNumCtrl;
|
||||
case 'total_tracks':
|
||||
return _trackTotalCtrl;
|
||||
case 'disc_number':
|
||||
return _discNumCtrl;
|
||||
case 'total_discs':
|
||||
return _discTotalCtrl;
|
||||
case 'genre':
|
||||
return _genreCtrl;
|
||||
case 'isrc':
|
||||
@@ -4559,6 +4646,8 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
return _labelCtrl;
|
||||
case 'copyright':
|
||||
return _copyrightCtrl;
|
||||
case 'composer':
|
||||
return _composerCtrl;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -4724,11 +4813,14 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
put('album_artist', track['album_artist']);
|
||||
put('date', track['release_date']);
|
||||
put('track_number', track['track_number']);
|
||||
put('total_tracks', track['total_tracks']);
|
||||
put('disc_number', track['disc_number']);
|
||||
put('total_discs', track['total_discs']);
|
||||
put('isrc', track['isrc']);
|
||||
put('genre', track['genre']);
|
||||
put('label', track['label']);
|
||||
put('copyright', track['copyright']);
|
||||
put('composer', track['composer']);
|
||||
}
|
||||
|
||||
Future<_ResolvedAutoFillTrack?> _resolveAutoFillTrackFromIdentifiers(
|
||||
@@ -4929,8 +5021,11 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
'album_artist': (selectedBest['album_artist'] ?? '').toString(),
|
||||
'date': (selectedBest['release_date'] ?? '').toString(),
|
||||
'track_number': (selectedBest['track_number'] ?? '').toString(),
|
||||
'total_tracks': (selectedBest['total_tracks'] ?? '').toString(),
|
||||
'disc_number': (selectedBest['disc_number'] ?? '').toString(),
|
||||
'total_discs': (selectedBest['total_discs'] ?? '').toString(),
|
||||
'isrc': (selectedBest['isrc'] ?? '').toString(),
|
||||
'composer': (selectedBest['composer'] ?? '').toString(),
|
||||
};
|
||||
_mergeOnlineTrackData(enriched, selectedBest);
|
||||
|
||||
@@ -4939,7 +5034,8 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
final needsExtended =
|
||||
_autoFillFields.contains('genre') ||
|
||||
_autoFillFields.contains('label') ||
|
||||
_autoFillFields.contains('copyright');
|
||||
_autoFillFields.contains('copyright') ||
|
||||
_autoFillFields.contains('composer');
|
||||
|
||||
final rawSpotifyId = _extractRawSpotifyTrackId(selectedBest);
|
||||
|
||||
@@ -5101,7 +5197,9 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
_albumArtistCtrl = TextEditingController(text: v['album_artist'] ?? '');
|
||||
_dateCtrl = TextEditingController(text: v['date'] ?? '');
|
||||
_trackNumCtrl = TextEditingController(text: v['track_number'] ?? '');
|
||||
_trackTotalCtrl = TextEditingController(text: v['total_tracks'] ?? '');
|
||||
_discNumCtrl = TextEditingController(text: v['disc_number'] ?? '');
|
||||
_discTotalCtrl = TextEditingController(text: v['total_discs'] ?? '');
|
||||
_genreCtrl = TextEditingController(text: v['genre'] ?? '');
|
||||
_isrcCtrl = TextEditingController(text: v['isrc'] ?? '');
|
||||
_labelCtrl = TextEditingController(text: v['label'] ?? '');
|
||||
@@ -5121,7 +5219,9 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
_albumArtistCtrl.dispose();
|
||||
_dateCtrl.dispose();
|
||||
_trackNumCtrl.dispose();
|
||||
_trackTotalCtrl.dispose();
|
||||
_discNumCtrl.dispose();
|
||||
_discTotalCtrl.dispose();
|
||||
_genreCtrl.dispose();
|
||||
_isrcCtrl.dispose();
|
||||
_labelCtrl.dispose();
|
||||
@@ -5141,7 +5241,9 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
'album_artist': _albumArtistCtrl.text,
|
||||
'date': _dateCtrl.text,
|
||||
'track_number': _trackNumCtrl.text,
|
||||
'track_total': _trackTotalCtrl.text,
|
||||
'disc_number': _discNumCtrl.text,
|
||||
'disc_total': _discTotalCtrl.text,
|
||||
'genre': _genreCtrl.text,
|
||||
'isrc': _isrcCtrl.text,
|
||||
'label': _labelCtrl.text,
|
||||
@@ -5193,12 +5295,18 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
'TRACKNUMBER':
|
||||
(metadata['track_number']?.isNotEmpty == true &&
|
||||
metadata['track_number'] != '0')
|
||||
? metadata['track_number']!
|
||||
? (metadata['track_total']?.isNotEmpty == true &&
|
||||
metadata['track_total'] != '0'
|
||||
? '${metadata['track_number']}/${metadata['track_total']}'
|
||||
: metadata['track_number']!)
|
||||
: '',
|
||||
'DISCNUMBER':
|
||||
(metadata['disc_number']?.isNotEmpty == true &&
|
||||
metadata['disc_number'] != '0')
|
||||
? metadata['disc_number']!
|
||||
? (metadata['disc_total']?.isNotEmpty == true &&
|
||||
metadata['disc_total'] != '0'
|
||||
? '${metadata['disc_number']}/${metadata['disc_total']}'
|
||||
: metadata['disc_number']!)
|
||||
: '',
|
||||
'GENRE': metadata['genre'] ?? '',
|
||||
'ISRC': metadata['isrc'] ?? '',
|
||||
@@ -5411,6 +5519,18 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: _field(
|
||||
'Track Total',
|
||||
_trackTotalCtrl,
|
||||
keyboard: TextInputType.number,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _field(
|
||||
'Disc #',
|
||||
@@ -5418,6 +5538,14 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
|
||||
keyboard: TextInputType.number,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: _field(
|
||||
'Disc Total',
|
||||
_discTotalCtrl,
|
||||
keyboard: TextInputType.number,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_field('Genre', _genreCtrl),
|
||||
|
||||
@@ -17,6 +17,7 @@ class DownloadRequestPayload {
|
||||
final int trackNumber;
|
||||
final int discNumber;
|
||||
final int totalTracks;
|
||||
final int totalDiscs;
|
||||
final String releaseDate;
|
||||
final String itemId;
|
||||
final int durationMs;
|
||||
@@ -24,6 +25,7 @@ class DownloadRequestPayload {
|
||||
final String genre;
|
||||
final String label;
|
||||
final String copyright;
|
||||
final String composer;
|
||||
final String tidalId;
|
||||
final String qobuzId;
|
||||
final String deezerId;
|
||||
@@ -56,6 +58,7 @@ class DownloadRequestPayload {
|
||||
this.trackNumber = 0,
|
||||
this.discNumber = 0,
|
||||
this.totalTracks = 1,
|
||||
this.totalDiscs = 0,
|
||||
this.releaseDate = '',
|
||||
this.itemId = '',
|
||||
this.durationMs = 0,
|
||||
@@ -63,6 +66,7 @@ class DownloadRequestPayload {
|
||||
this.genre = '',
|
||||
this.label = '',
|
||||
this.copyright = '',
|
||||
this.composer = '',
|
||||
this.tidalId = '',
|
||||
this.qobuzId = '',
|
||||
this.deezerId = '',
|
||||
@@ -97,6 +101,7 @@ class DownloadRequestPayload {
|
||||
'track_number': trackNumber,
|
||||
'disc_number': discNumber,
|
||||
'total_tracks': totalTracks,
|
||||
'total_discs': totalDiscs,
|
||||
'release_date': releaseDate,
|
||||
'item_id': itemId,
|
||||
'duration_ms': durationMs,
|
||||
@@ -104,6 +109,7 @@ class DownloadRequestPayload {
|
||||
'genre': genre,
|
||||
'label': label,
|
||||
'copyright': copyright,
|
||||
'composer': composer,
|
||||
'tidal_id': tidalId,
|
||||
'qobuz_id': qobuzId,
|
||||
'deezer_id': deezerId,
|
||||
@@ -142,6 +148,7 @@ class DownloadRequestPayload {
|
||||
trackNumber: trackNumber,
|
||||
discNumber: discNumber,
|
||||
totalTracks: totalTracks,
|
||||
totalDiscs: totalDiscs,
|
||||
releaseDate: releaseDate,
|
||||
itemId: itemId,
|
||||
durationMs: durationMs,
|
||||
@@ -149,6 +156,7 @@ class DownloadRequestPayload {
|
||||
genre: genre,
|
||||
label: label,
|
||||
copyright: copyright,
|
||||
composer: composer,
|
||||
tidalId: tidalId,
|
||||
qobuzId: qobuzId,
|
||||
deezerId: deezerId,
|
||||
|
||||
@@ -20,13 +20,16 @@ class LocalLibraryItem {
|
||||
final int? fileModTime;
|
||||
final String? isrc;
|
||||
final int? trackNumber;
|
||||
final int? totalTracks;
|
||||
final int? discNumber;
|
||||
final int? totalDiscs;
|
||||
final int? duration;
|
||||
final String? releaseDate;
|
||||
final int? bitDepth;
|
||||
final int? sampleRate;
|
||||
final int? bitrate; // kbps, for lossy formats (mp3, opus, ogg)
|
||||
final String? genre;
|
||||
final String? composer;
|
||||
final String? label;
|
||||
final String? copyright;
|
||||
final String? format; // flac, mp3, opus, m4a
|
||||
@@ -43,13 +46,16 @@ class LocalLibraryItem {
|
||||
this.fileModTime,
|
||||
this.isrc,
|
||||
this.trackNumber,
|
||||
this.totalTracks,
|
||||
this.discNumber,
|
||||
this.totalDiscs,
|
||||
this.duration,
|
||||
this.releaseDate,
|
||||
this.bitDepth,
|
||||
this.sampleRate,
|
||||
this.bitrate,
|
||||
this.genre,
|
||||
this.composer,
|
||||
this.label,
|
||||
this.copyright,
|
||||
this.format,
|
||||
@@ -67,13 +73,16 @@ class LocalLibraryItem {
|
||||
'fileModTime': fileModTime,
|
||||
'isrc': isrc,
|
||||
'trackNumber': trackNumber,
|
||||
'totalTracks': totalTracks,
|
||||
'discNumber': discNumber,
|
||||
'totalDiscs': totalDiscs,
|
||||
'duration': duration,
|
||||
'releaseDate': releaseDate,
|
||||
'bitDepth': bitDepth,
|
||||
'sampleRate': sampleRate,
|
||||
'bitrate': bitrate,
|
||||
'genre': genre,
|
||||
'composer': composer,
|
||||
'label': label,
|
||||
'copyright': copyright,
|
||||
'format': format,
|
||||
@@ -91,14 +100,17 @@ class LocalLibraryItem {
|
||||
scannedAt: DateTime.parse(json['scannedAt'] as String),
|
||||
fileModTime: (json['fileModTime'] as num?)?.toInt(),
|
||||
isrc: json['isrc'] as String?,
|
||||
trackNumber: json['trackNumber'] as int?,
|
||||
discNumber: json['discNumber'] as int?,
|
||||
duration: json['duration'] as int?,
|
||||
trackNumber: (json['trackNumber'] as num?)?.toInt(),
|
||||
totalTracks: (json['totalTracks'] as num?)?.toInt(),
|
||||
discNumber: (json['discNumber'] as num?)?.toInt(),
|
||||
totalDiscs: (json['totalDiscs'] as num?)?.toInt(),
|
||||
duration: (json['duration'] as num?)?.toInt(),
|
||||
releaseDate: json['releaseDate'] as String?,
|
||||
bitDepth: json['bitDepth'] as int?,
|
||||
sampleRate: json['sampleRate'] as int?,
|
||||
bitDepth: (json['bitDepth'] as num?)?.toInt(),
|
||||
sampleRate: (json['sampleRate'] as num?)?.toInt(),
|
||||
bitrate: (json['bitrate'] as num?)?.toInt(),
|
||||
genre: json['genre'] as String?,
|
||||
composer: json['composer'] as String?,
|
||||
label: json['label'] as String?,
|
||||
copyright: json['copyright'] as String?,
|
||||
format: json['format'] as String?,
|
||||
@@ -130,7 +142,7 @@ class LibraryDatabase {
|
||||
|
||||
return await openDatabase(
|
||||
path,
|
||||
version: 5,
|
||||
version: 6,
|
||||
onConfigure: (db) async {
|
||||
await db.rawQuery('PRAGMA journal_mode = WAL');
|
||||
await db.execute('PRAGMA synchronous = NORMAL');
|
||||
@@ -156,13 +168,16 @@ class LibraryDatabase {
|
||||
file_mod_time INTEGER,
|
||||
isrc TEXT,
|
||||
track_number INTEGER,
|
||||
total_tracks INTEGER,
|
||||
disc_number INTEGER,
|
||||
total_discs INTEGER,
|
||||
duration INTEGER,
|
||||
release_date TEXT,
|
||||
bit_depth INTEGER,
|
||||
sample_rate INTEGER,
|
||||
bitrate INTEGER,
|
||||
genre TEXT,
|
||||
composer TEXT,
|
||||
label TEXT,
|
||||
copyright TEXT,
|
||||
format TEXT
|
||||
@@ -206,6 +221,13 @@ class LibraryDatabase {
|
||||
await db.execute('ALTER TABLE library ADD COLUMN copyright TEXT');
|
||||
_log.i('Added label/copyright columns');
|
||||
}
|
||||
|
||||
if (oldVersion < 6) {
|
||||
await db.execute('ALTER TABLE library ADD COLUMN total_tracks INTEGER');
|
||||
await db.execute('ALTER TABLE library ADD COLUMN total_discs INTEGER');
|
||||
await db.execute('ALTER TABLE library ADD COLUMN composer TEXT');
|
||||
_log.i('Added total_tracks/total_discs/composer columns');
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _jsonToDbRow(Map<String, dynamic> json) {
|
||||
@@ -221,13 +243,16 @@ class LibraryDatabase {
|
||||
'file_mod_time': json['fileModTime'],
|
||||
'isrc': json['isrc'],
|
||||
'track_number': json['trackNumber'],
|
||||
'total_tracks': json['totalTracks'],
|
||||
'disc_number': json['discNumber'],
|
||||
'total_discs': json['totalDiscs'],
|
||||
'duration': json['duration'],
|
||||
'release_date': json['releaseDate'],
|
||||
'bit_depth': json['bitDepth'],
|
||||
'sample_rate': json['sampleRate'],
|
||||
'bitrate': json['bitrate'],
|
||||
'genre': json['genre'],
|
||||
'composer': json['composer'],
|
||||
'label': json['label'],
|
||||
'copyright': json['copyright'],
|
||||
'format': json['format'],
|
||||
@@ -247,13 +272,16 @@ class LibraryDatabase {
|
||||
'fileModTime': row['file_mod_time'],
|
||||
'isrc': row['isrc'],
|
||||
'trackNumber': row['track_number'],
|
||||
'totalTracks': row['total_tracks'],
|
||||
'discNumber': row['disc_number'],
|
||||
'totalDiscs': row['total_discs'],
|
||||
'duration': row['duration'],
|
||||
'releaseDate': row['release_date'],
|
||||
'bitDepth': row['bit_depth'],
|
||||
'sampleRate': row['sample_rate'],
|
||||
'bitrate': row['bitrate'],
|
||||
'genre': row['genre'],
|
||||
'composer': row['composer'],
|
||||
'label': row['label'],
|
||||
'copyright': row['copyright'],
|
||||
'format': row['format'],
|
||||
|
||||
@@ -142,8 +142,10 @@ class LocalTrackRedownloadService {
|
||||
duration: (durationMs / 1000).round(),
|
||||
trackNumber: data['track_number'] as int?,
|
||||
discNumber: data['disc_number'] as int?,
|
||||
totalDiscs: data['total_discs'] as int?,
|
||||
releaseDate: data['release_date']?.toString(),
|
||||
totalTracks: data['total_tracks'] as int?,
|
||||
composer: data['composer']?.toString(),
|
||||
source: data['source']?.toString() ?? data['provider_id']?.toString(),
|
||||
albumType: data['album_type']?.toString(),
|
||||
itemType: itemType,
|
||||
|
||||
@@ -100,12 +100,28 @@ void mergePlatformMetadataForTagEmbed({
|
||||
put('UNSYNCEDLYRICS', source['lyrics']);
|
||||
|
||||
final trackNumber = source['track_number'];
|
||||
final totalTracks = source['total_tracks'];
|
||||
if (trackNumber != null && trackNumber.toString() != '0') {
|
||||
put('TRACKNUMBER', trackNumber);
|
||||
put(
|
||||
'TRACKNUMBER',
|
||||
totalTracks != null &&
|
||||
totalTracks.toString().isNotEmpty &&
|
||||
totalTracks.toString() != '0'
|
||||
? '${trackNumber.toString()}/${totalTracks.toString()}'
|
||||
: trackNumber,
|
||||
);
|
||||
}
|
||||
|
||||
final discNumber = source['disc_number'];
|
||||
final totalDiscs = source['total_discs'];
|
||||
if (discNumber != null && discNumber.toString() != '0') {
|
||||
put('DISCNUMBER', discNumber);
|
||||
put(
|
||||
'DISCNUMBER',
|
||||
totalDiscs != null &&
|
||||
totalDiscs.toString().isNotEmpty &&
|
||||
totalDiscs.toString() != '0'
|
||||
? '${discNumber.toString()}/${totalDiscs.toString()}'
|
||||
: discNumber,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user