mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-05-25 09:14:17 +02:00
fix: preserve embedded metadata details
This commit is contained in:
@@ -453,7 +453,7 @@ func apeKeysFromFields(fields map[string]string) map[string]struct{} {
|
||||
"artist": "ARTIST",
|
||||
"album": "ALBUM",
|
||||
"album_artist": "ALBUM ARTIST",
|
||||
"date": "YEAR",
|
||||
"date": "DATE",
|
||||
"genre": "GENRE",
|
||||
"track_number": "TRACK",
|
||||
"disc_number": "DISC",
|
||||
@@ -475,7 +475,7 @@ func apeKeysFromFields(fields map[string]string) map[string]struct{} {
|
||||
}
|
||||
}
|
||||
// Some fields have reader aliases that must also be cleared when the
|
||||
// canonical key is updated (e.g. "Year" writer ↔ DATE/YEAR reader,
|
||||
// canonical key is updated (e.g. DATE writer ↔ DATE/YEAR reader,
|
||||
// DISC ↔ DISCNUMBER, TRACK ↔ TRACKNUMBER, "ALBUM ARTIST" ↔ ALBUMARTIST,
|
||||
// LABEL ↔ PUBLISHER, LYRICS ↔ UNSYNCEDLYRICS).
|
||||
if _, present := fields["date"]; present {
|
||||
|
||||
@@ -31,6 +31,8 @@ type LibraryScanResult struct {
|
||||
SampleRate int `json:"sampleRate,omitempty"`
|
||||
Bitrate int `json:"bitrate,omitempty"` // kbps, for lossy formats (MP3, Opus, Vorbis)
|
||||
Genre string `json:"genre,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Copyright string `json:"copyright,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
MetadataFromFilename bool `json:"metadataFromFilename,omitempty"`
|
||||
}
|
||||
@@ -368,6 +370,8 @@ func scanFLACFile(filePath string, result *LibraryScanResult, displayNameHint st
|
||||
result.DiscNumber = metadata.DiscNumber
|
||||
result.ReleaseDate = metadata.Date
|
||||
result.Genre = metadata.Genre
|
||||
result.Label = metadata.Label
|
||||
result.Copyright = metadata.Copyright
|
||||
|
||||
quality, err := GetAudioQuality(filePath)
|
||||
if err == nil {
|
||||
@@ -403,6 +407,8 @@ func scanM4AFile(filePath string, result *LibraryScanResult, displayNameHint str
|
||||
result.ReleaseDate = metadata.Year
|
||||
}
|
||||
result.Genre = metadata.Genre
|
||||
result.Label = metadata.Label
|
||||
result.Copyright = metadata.Copyright
|
||||
}
|
||||
|
||||
quality, err := GetM4AQuality(filePath)
|
||||
@@ -435,6 +441,8 @@ func scanMP3File(filePath string, result *LibraryScanResult, displayNameHint str
|
||||
result.ReleaseDate = metadata.Year
|
||||
}
|
||||
result.ISRC = metadata.ISRC
|
||||
result.Label = metadata.Label
|
||||
result.Copyright = metadata.Copyright
|
||||
|
||||
quality, err := GetMP3Quality(filePath)
|
||||
if err == nil {
|
||||
@@ -467,6 +475,8 @@ func scanOggFile(filePath string, result *LibraryScanResult, displayNameHint str
|
||||
result.DiscNumber = metadata.DiscNumber
|
||||
result.Genre = metadata.Genre
|
||||
result.ReleaseDate = metadata.Date
|
||||
result.Label = metadata.Label
|
||||
result.Copyright = metadata.Copyright
|
||||
|
||||
quality, err := GetOggQuality(filePath)
|
||||
if err == nil {
|
||||
@@ -508,6 +518,8 @@ func scanAPEFile(filePath string, result *LibraryScanResult, displayNameHint str
|
||||
} else {
|
||||
result.ReleaseDate = metadata.Year
|
||||
}
|
||||
result.Label = metadata.Label
|
||||
result.Copyright = metadata.Copyright
|
||||
|
||||
applyDefaultLibraryMetadata(filePath, displayNameHint, result)
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
}
|
||||
if (mounted &&
|
||||
exists &&
|
||||
!_isLocalItem &&
|
||||
!_isCueVirtualTrack &&
|
||||
!_hasLoadedResolvedAudioMetadata) {
|
||||
unawaited(_refreshResolvedAudioMetadataFromFile());
|
||||
}
|
||||
@@ -291,8 +291,9 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
}
|
||||
|
||||
Future<void> _refreshResolvedAudioMetadataFromFile() async {
|
||||
if (_isLocalItem ||
|
||||
_downloadItem == null ||
|
||||
if ((_isLocalItem && _localLibraryItem == null) ||
|
||||
(!_isLocalItem && _downloadItem == null) ||
|
||||
_isCueVirtualTrack ||
|
||||
_hasLoadedResolvedAudioMetadata) {
|
||||
return;
|
||||
}
|
||||
@@ -338,9 +339,10 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
(copyright == null || copyright!.isEmpty);
|
||||
|
||||
final shouldPersistResolvedAudioMetadata =
|
||||
resolvedBitDepth != null ||
|
||||
resolvedSampleRate != null ||
|
||||
(isPlaceholderQualityLabel(_quality) && resolvedQuality != null);
|
||||
!_isLocalItem &&
|
||||
(resolvedBitDepth != null ||
|
||||
resolvedSampleRate != null ||
|
||||
(isPlaceholderQualityLabel(_quality) && resolvedQuality != null));
|
||||
|
||||
if ((resolvedBitDepth != null ||
|
||||
resolvedSampleRate != null ||
|
||||
@@ -517,10 +519,10 @@ class _TrackMetadataScreenState extends ConsumerState<TrackMetadataScreen> {
|
||||
(_isLocalItem ? _localLibraryItem!.genre : _downloadItem!.genre);
|
||||
String? get label =>
|
||||
_editedMetadata?['label']?.toString() ??
|
||||
(_isLocalItem ? null : _downloadItem!.label);
|
||||
(_isLocalItem ? _localLibraryItem!.label : _downloadItem!.label);
|
||||
String? get copyright =>
|
||||
_editedMetadata?['copyright']?.toString() ??
|
||||
(_isLocalItem ? null : _downloadItem!.copyright);
|
||||
(_isLocalItem ? _localLibraryItem!.copyright : _downloadItem!.copyright);
|
||||
int? get duration =>
|
||||
_readPositiveInt(_editedMetadata?['duration']) ??
|
||||
(_isLocalItem ? _localLibraryItem!.duration : _downloadItem!.duration);
|
||||
|
||||
@@ -1760,9 +1760,13 @@ class FFmpegService {
|
||||
if (value != '0') vorbis['DISCNUMBER'] = value;
|
||||
break;
|
||||
case 'DATE':
|
||||
case 'YEAR':
|
||||
vorbis['DATE'] = value;
|
||||
break;
|
||||
case 'YEAR':
|
||||
if (!vorbis.containsKey('DATE') || vorbis['DATE']!.isEmpty) {
|
||||
vorbis['DATE'] = value;
|
||||
}
|
||||
break;
|
||||
case 'GENRE':
|
||||
vorbis['GENRE'] = value;
|
||||
break;
|
||||
@@ -1921,9 +1925,13 @@ class FFmpegService {
|
||||
m4aMap['disc'] = value;
|
||||
break;
|
||||
case 'DATE':
|
||||
case 'YEAR':
|
||||
m4aMap['date'] = value;
|
||||
break;
|
||||
case 'YEAR':
|
||||
if (!m4aMap.containsKey('date') || m4aMap['date']!.isEmpty) {
|
||||
m4aMap['date'] = value;
|
||||
}
|
||||
break;
|
||||
case 'GENRE':
|
||||
m4aMap['genre'] = value;
|
||||
break;
|
||||
@@ -2004,9 +2012,13 @@ class FFmpegService {
|
||||
}
|
||||
break;
|
||||
case 'DATE':
|
||||
case 'YEAR':
|
||||
id3Map['date'] = value;
|
||||
break;
|
||||
case 'YEAR':
|
||||
if (!id3Map.containsKey('date') || id3Map['date']!.isEmpty) {
|
||||
id3Map['date'] = value;
|
||||
}
|
||||
break;
|
||||
case 'ISRC':
|
||||
id3Map['TSRC'] = value;
|
||||
break;
|
||||
|
||||
@@ -27,6 +27,8 @@ class LocalLibraryItem {
|
||||
final int? sampleRate;
|
||||
final int? bitrate; // kbps, for lossy formats (mp3, opus, ogg)
|
||||
final String? genre;
|
||||
final String? label;
|
||||
final String? copyright;
|
||||
final String? format; // flac, mp3, opus, m4a
|
||||
|
||||
const LocalLibraryItem({
|
||||
@@ -48,6 +50,8 @@ class LocalLibraryItem {
|
||||
this.sampleRate,
|
||||
this.bitrate,
|
||||
this.genre,
|
||||
this.label,
|
||||
this.copyright,
|
||||
this.format,
|
||||
});
|
||||
|
||||
@@ -70,6 +74,8 @@ class LocalLibraryItem {
|
||||
'sampleRate': sampleRate,
|
||||
'bitrate': bitrate,
|
||||
'genre': genre,
|
||||
'label': label,
|
||||
'copyright': copyright,
|
||||
'format': format,
|
||||
};
|
||||
|
||||
@@ -93,6 +99,8 @@ class LocalLibraryItem {
|
||||
sampleRate: json['sampleRate'] as int?,
|
||||
bitrate: (json['bitrate'] as num?)?.toInt(),
|
||||
genre: json['genre'] as String?,
|
||||
label: json['label'] as String?,
|
||||
copyright: json['copyright'] as String?,
|
||||
format: json['format'] as String?,
|
||||
);
|
||||
|
||||
@@ -122,7 +130,7 @@ class LibraryDatabase {
|
||||
|
||||
return await openDatabase(
|
||||
path,
|
||||
version: 4,
|
||||
version: 5,
|
||||
onConfigure: (db) async {
|
||||
await db.rawQuery('PRAGMA journal_mode = WAL');
|
||||
await db.execute('PRAGMA synchronous = NORMAL');
|
||||
@@ -155,6 +163,8 @@ class LibraryDatabase {
|
||||
sample_rate INTEGER,
|
||||
bitrate INTEGER,
|
||||
genre TEXT,
|
||||
label TEXT,
|
||||
copyright TEXT,
|
||||
format TEXT
|
||||
)
|
||||
''');
|
||||
@@ -190,6 +200,12 @@ class LibraryDatabase {
|
||||
await db.execute('ALTER TABLE library ADD COLUMN bitrate INTEGER');
|
||||
_log.i('Added bitrate column for lossy format quality');
|
||||
}
|
||||
|
||||
if (oldVersion < 5) {
|
||||
await db.execute('ALTER TABLE library ADD COLUMN label TEXT');
|
||||
await db.execute('ALTER TABLE library ADD COLUMN copyright TEXT');
|
||||
_log.i('Added label/copyright columns');
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _jsonToDbRow(Map<String, dynamic> json) {
|
||||
@@ -212,6 +228,8 @@ class LibraryDatabase {
|
||||
'sample_rate': json['sampleRate'],
|
||||
'bitrate': json['bitrate'],
|
||||
'genre': json['genre'],
|
||||
'label': json['label'],
|
||||
'copyright': json['copyright'],
|
||||
'format': json['format'],
|
||||
};
|
||||
}
|
||||
@@ -236,6 +254,8 @@ class LibraryDatabase {
|
||||
'sampleRate': row['sample_rate'],
|
||||
'bitrate': row['bitrate'],
|
||||
'genre': row['genre'],
|
||||
'label': row['label'],
|
||||
'copyright': row['copyright'],
|
||||
'format': row['format'],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user