feat: add skipLyrics manifest field for extensions to opt out of lyrics fetching

This commit is contained in:
zarzet
2026-04-13 23:32:16 +07:00
parent 89603af1f1
commit 53a4773480
4 changed files with 81 additions and 5 deletions
+73 -5
View File
@@ -2215,6 +2215,27 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
return _isrcRegex.hasMatch(value.toUpperCase());
}
/// Returns true if any enabled extension matching [source] or [service]
/// declares `skipLyrics: true` in its manifest.
bool _shouldSkipLyrics(
ExtensionState extensionState,
String? source,
String? service,
) {
final candidates = <String>{};
if (source != null && source.isNotEmpty) {
candidates.add(source.trim().toLowerCase());
}
if (service != null && service.isNotEmpty) {
candidates.add(service.trim().toLowerCase());
}
if (candidates.isEmpty) return false;
return extensionState.extensions.any(
(e) =>
e.enabled && e.skipLyrics && candidates.contains(e.id.toLowerCase()),
);
}
String _newQueueItemId(Track track, {Set<String>? takenIds}) {
final trimmedIsrc = track.isrc?.trim();
final trimmedTrackId = track.id.trim();
@@ -3227,6 +3248,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
String? genre,
String? label,
String? copyright,
String? downloadService,
bool writeExternalLrc = true,
}) async {
final settings = ref.read(settingsProvider);
@@ -3314,11 +3336,19 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
_log.d('Metadata map content: $metadata');
final lyricsMode = settings.lyricsMode;
final extensionState = ref.read(extensionProvider);
final skipLyrics = _shouldSkipLyrics(
extensionState,
track.source,
downloadService,
);
final shouldEmbedLyrics =
settings.embedLyrics &&
!skipLyrics &&
(lyricsMode == 'embed' || lyricsMode == 'both');
final shouldSaveExternalLyrics =
settings.embedLyrics &&
!skipLyrics &&
(lyricsMode == 'external' || lyricsMode == 'both');
final shouldFetchLyrics = shouldEmbedLyrics || shouldSaveExternalLyrics;
String? lrcContent;
@@ -3453,6 +3483,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
String? genre,
String? label,
String? copyright,
String? downloadService,
}) async {
final settings = ref.read(settingsProvider);
if (!settings.embedMetadata) {
@@ -3541,9 +3572,16 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
_log.d('MP3 Metadata map content: $metadata');
final lyricsMode = settings.lyricsMode;
final shouldEmbed = lyricsMode == 'embed' || lyricsMode == 'both';
final mp3ExtState = ref.read(extensionProvider);
final mp3SkipLyrics = _shouldSkipLyrics(
mp3ExtState,
track.source,
downloadService,
);
final shouldEmbed =
!mp3SkipLyrics && (lyricsMode == 'embed' || lyricsMode == 'both');
final shouldSaveExternal =
lyricsMode == 'external' || lyricsMode == 'both';
!mp3SkipLyrics && (lyricsMode == 'external' || lyricsMode == 'both');
if (settings.embedLyrics && (shouldEmbed || shouldSaveExternal)) {
try {
@@ -3639,6 +3677,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
String? genre,
String? label,
String? copyright,
String? downloadService,
}) async {
final settings = ref.read(settingsProvider);
if (!settings.embedMetadata) {
@@ -3724,9 +3763,16 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
_log.d('Opus Metadata map content: $metadata');
final lyricsMode = settings.lyricsMode;
final shouldEmbed = lyricsMode == 'embed' || lyricsMode == 'both';
final opusExtState = ref.read(extensionProvider);
final opusSkipLyrics = _shouldSkipLyrics(
opusExtState,
track.source,
downloadService,
);
final shouldEmbed =
!opusSkipLyrics && (lyricsMode == 'embed' || lyricsMode == 'both');
final shouldSaveExternal =
lyricsMode == 'external' || lyricsMode == 'both';
!opusSkipLyrics && (lyricsMode == 'external' || lyricsMode == 'both');
if (settings.embedLyrics && (shouldEmbed || shouldSaveExternal)) {
try {
@@ -4685,7 +4731,14 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
quality: quality,
embedMetadata: metadataEmbeddingEnabled,
artistTagMode: settings.artistTagMode,
embedLyrics: metadataEmbeddingEnabled && settings.embedLyrics,
embedLyrics:
metadataEmbeddingEnabled &&
settings.embedLyrics &&
!_shouldSkipLyrics(
extensionState,
trackToDownload.source,
item.service,
),
embedMaxQualityCover:
metadataEmbeddingEnabled && settings.maxQualityCover,
trackNumber: normalizedTrackNumber,
@@ -5010,6 +5063,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
} else {
await _embedMetadataToOpus(
@@ -5018,6 +5072,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
}
@@ -5104,6 +5159,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
writeExternalLrc: false,
);
@@ -5197,6 +5253,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
} else {
await _embedMetadataToOpus(
@@ -5205,6 +5262,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
}
_log.d('Metadata embedded successfully');
@@ -5275,6 +5333,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
_log.d('Metadata and cover embedded successfully');
} catch (e) {
@@ -5340,6 +5399,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
} else if (isOpusFile) {
await _embedMetadataToOpus(
@@ -5348,6 +5408,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
} else {
await _embedMetadataAndCover(
@@ -5356,6 +5417,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
writeExternalLrc: false,
);
}
@@ -5420,6 +5482,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
genre: backendGenre ?? genre,
label: backendLabel ?? label,
copyright: backendCopyright,
downloadService: item.service,
);
_log.d('Local FLAC metadata embedding completed');
} catch (e) {
@@ -5500,6 +5563,11 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
final shouldSaveExternalLrc =
metadataEmbeddingEnabled &&
settings.embedLyrics &&
!_shouldSkipLyrics(
extensionState,
trackToDownload.source,
item.service,
) &&
(lyricsMode == 'external' || lyricsMode == 'both');
if (shouldSaveExternalLrc &&
effectiveSafMode &&
+5
View File
@@ -33,6 +33,7 @@ class Extension {
final bool hasDownloadProvider;
final bool hasLyricsProvider;
final bool skipMetadataEnrichment;
final bool skipLyrics;
final SearchBehavior? searchBehavior;
final URLHandler? urlHandler;
final TrackMatching? trackMatching;
@@ -57,6 +58,7 @@ class Extension {
this.hasDownloadProvider = false,
this.hasLyricsProvider = false,
this.skipMetadataEnrichment = false,
this.skipLyrics = false,
this.searchBehavior,
this.urlHandler,
this.trackMatching,
@@ -94,6 +96,7 @@ class Extension {
hasLyricsProvider: json['has_lyrics_provider'] as bool? ?? false,
skipMetadataEnrichment:
json['skip_metadata_enrichment'] as bool? ?? false,
skipLyrics: json['skip_lyrics'] as bool? ?? false,
searchBehavior: json['search_behavior'] != null
? SearchBehavior.fromJson(
json['search_behavior'] as Map<String, dynamic>,
@@ -134,6 +137,7 @@ class Extension {
bool? hasDownloadProvider,
bool? hasLyricsProvider,
bool? skipMetadataEnrichment,
bool? skipLyrics,
SearchBehavior? searchBehavior,
URLHandler? urlHandler,
TrackMatching? trackMatching,
@@ -159,6 +163,7 @@ class Extension {
hasLyricsProvider: hasLyricsProvider ?? this.hasLyricsProvider,
skipMetadataEnrichment:
skipMetadataEnrichment ?? this.skipMetadataEnrichment,
skipLyrics: skipLyrics ?? this.skipLyrics,
searchBehavior: searchBehavior ?? this.searchBehavior,
urlHandler: urlHandler ?? this.urlHandler,
trackMatching: trackMatching ?? this.trackMatching,