diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index ae9a4cc0..01af801f 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -4817,6 +4817,132 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'{count, plural, =1{1 playlist} other{{count} playlists}}'** String playlistsCount(int count); + + /// Section title for selective online metadata auto-fill in the edit metadata sheet + /// + /// In en, this message translates to: + /// **'Auto-fill from online'** + String get editMetadataAutoFill; + + /// Description for the auto-fill section + /// + /// In en, this message translates to: + /// **'Select fields to fill automatically from online metadata'** + String get editMetadataAutoFillDesc; + + /// Button label to fetch online metadata and fill selected fields + /// + /// In en, this message translates to: + /// **'Fetch & Fill'** + String get editMetadataAutoFillFetch; + + /// Snackbar shown while searching for online metadata + /// + /// In en, this message translates to: + /// **'Searching online...'** + String get editMetadataAutoFillSearching; + + /// Snackbar when online metadata search returns no results + /// + /// In en, this message translates to: + /// **'No matching metadata found online'** + String get editMetadataAutoFillNoResults; + + /// Snackbar confirming how many fields were auto-filled + /// + /// In en, this message translates to: + /// **'Filled {count} {count, plural, =1{field} other{fields}} from online metadata'** + String editMetadataAutoFillDone(int count); + + /// Snackbar when user taps Fetch without selecting any fields + /// + /// In en, this message translates to: + /// **'Select at least one field to auto-fill'** + String get editMetadataAutoFillNoneSelected; + + /// Chip label for title field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Title'** + String get editMetadataFieldTitle; + + /// Chip label for artist field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Artist'** + String get editMetadataFieldArtist; + + /// Chip label for album field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Album'** + String get editMetadataFieldAlbum; + + /// Chip label for album artist field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Album Artist'** + String get editMetadataFieldAlbumArtist; + + /// Chip label for date field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Date'** + String get editMetadataFieldDate; + + /// Chip label for track number field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Track #'** + String get editMetadataFieldTrackNum; + + /// Chip label for disc number field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Disc #'** + String get editMetadataFieldDiscNum; + + /// Chip label for genre field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Genre'** + String get editMetadataFieldGenre; + + /// Chip label for ISRC field in auto-fill selector + /// + /// In en, this message translates to: + /// **'ISRC'** + String get editMetadataFieldIsrc; + + /// Chip label for label field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Label'** + String get editMetadataFieldLabel; + + /// Chip label for copyright field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Copyright'** + String get editMetadataFieldCopyright; + + /// Chip label for cover art field in auto-fill selector + /// + /// In en, this message translates to: + /// **'Cover Art'** + String get editMetadataFieldCover; + + /// Button to select all fields for auto-fill + /// + /// In en, this message translates to: + /// **'All'** + String get editMetadataSelectAll; + + /// Button to select only fields that are currently empty + /// + /// In en, this message translates to: + /// **'Empty only'** + String get editMetadataSelectEmpty; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index ce489e3d..c3ee5083 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2826,4 +2826,78 @@ class AppLocalizationsDe extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index bd280e94..180810f3 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -2798,4 +2798,78 @@ class AppLocalizationsEn extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 81ce15ff..cea3915b 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -2798,6 +2798,80 @@ class AppLocalizationsEs extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } /// The translations for Spanish Castilian, as used in Spain (`es_ES`). diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index b35b22d0..8270c7c2 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -2800,4 +2800,78 @@ class AppLocalizationsFr extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index e36316e9..1767c438 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -2798,4 +2798,78 @@ class AppLocalizationsHi extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index 68bffbe5..67a9c2fc 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -2805,4 +2805,78 @@ class AppLocalizationsId extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 8145c794..e406ba35 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -2785,4 +2785,78 @@ class AppLocalizationsJa extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 66d21aec..b8085a70 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -2778,4 +2778,78 @@ class AppLocalizationsKo extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index e87906ac..c54e044a 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -2798,4 +2798,78 @@ class AppLocalizationsNl extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 466572ce..ac0f577d 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -2798,6 +2798,80 @@ class AppLocalizationsPt extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } /// The translations for Portuguese, as used in Portugal (`pt_PT`). diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index dd460698..76814d38 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -2857,4 +2857,78 @@ class AppLocalizationsRu extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart index 6e1a37f1..f24d9f88 100644 --- a/lib/l10n/app_localizations_tr.dart +++ b/lib/l10n/app_localizations_tr.dart @@ -2810,4 +2810,78 @@ class AppLocalizationsTr extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 9248d997..d9440cff 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -2798,6 +2798,80 @@ class AppLocalizationsZh extends AppLocalizations { ); return '$_temp0'; } + + @override + String get editMetadataAutoFill => 'Auto-fill from online'; + + @override + String get editMetadataAutoFillDesc => + 'Select fields to fill automatically from online metadata'; + + @override + String get editMetadataAutoFillFetch => 'Fetch & Fill'; + + @override + String get editMetadataAutoFillSearching => 'Searching online...'; + + @override + String get editMetadataAutoFillNoResults => + 'No matching metadata found online'; + + @override + String editMetadataAutoFillDone(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'fields', + one: 'field', + ); + return 'Filled $count $_temp0 from online metadata'; + } + + @override + String get editMetadataAutoFillNoneSelected => + 'Select at least one field to auto-fill'; + + @override + String get editMetadataFieldTitle => 'Title'; + + @override + String get editMetadataFieldArtist => 'Artist'; + + @override + String get editMetadataFieldAlbum => 'Album'; + + @override + String get editMetadataFieldAlbumArtist => 'Album Artist'; + + @override + String get editMetadataFieldDate => 'Date'; + + @override + String get editMetadataFieldTrackNum => 'Track #'; + + @override + String get editMetadataFieldDiscNum => 'Disc #'; + + @override + String get editMetadataFieldGenre => 'Genre'; + + @override + String get editMetadataFieldIsrc => 'ISRC'; + + @override + String get editMetadataFieldLabel => 'Label'; + + @override + String get editMetadataFieldCopyright => 'Copyright'; + + @override + String get editMetadataFieldCover => 'Cover Art'; + + @override + String get editMetadataSelectAll => 'All'; + + @override + String get editMetadataSelectEmpty => 'Empty only'; } /// The translations for Chinese, as used in China (`zh_CN`). diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 3de4ec31..6da406d0 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -3706,5 +3706,94 @@ "type": "int" } } + }, + "editMetadataAutoFill": "Auto-fill from online", + "@editMetadataAutoFill": { + "description": "Section title for selective online metadata auto-fill in the edit metadata sheet" + }, + "editMetadataAutoFillDesc": "Select fields to fill automatically from online metadata", + "@editMetadataAutoFillDesc": { + "description": "Description for the auto-fill section" + }, + "editMetadataAutoFillFetch": "Fetch & Fill", + "@editMetadataAutoFillFetch": { + "description": "Button label to fetch online metadata and fill selected fields" + }, + "editMetadataAutoFillSearching": "Searching online...", + "@editMetadataAutoFillSearching": { + "description": "Snackbar shown while searching for online metadata" + }, + "editMetadataAutoFillNoResults": "No matching metadata found online", + "@editMetadataAutoFillNoResults": { + "description": "Snackbar when online metadata search returns no results" + }, + "editMetadataAutoFillDone": "Filled {count} {count, plural, =1{field} other{fields}} from online metadata", + "@editMetadataAutoFillDone": { + "description": "Snackbar confirming how many fields were auto-filled", + "placeholders": { + "count": { + "type": "int" + } + } + }, + "editMetadataAutoFillNoneSelected": "Select at least one field to auto-fill", + "@editMetadataAutoFillNoneSelected": { + "description": "Snackbar when user taps Fetch without selecting any fields" + }, + "editMetadataFieldTitle": "Title", + "@editMetadataFieldTitle": { + "description": "Chip label for title field in auto-fill selector" + }, + "editMetadataFieldArtist": "Artist", + "@editMetadataFieldArtist": { + "description": "Chip label for artist field in auto-fill selector" + }, + "editMetadataFieldAlbum": "Album", + "@editMetadataFieldAlbum": { + "description": "Chip label for album field in auto-fill selector" + }, + "editMetadataFieldAlbumArtist": "Album Artist", + "@editMetadataFieldAlbumArtist": { + "description": "Chip label for album artist field in auto-fill selector" + }, + "editMetadataFieldDate": "Date", + "@editMetadataFieldDate": { + "description": "Chip label for date field in auto-fill selector" + }, + "editMetadataFieldTrackNum": "Track #", + "@editMetadataFieldTrackNum": { + "description": "Chip label for track number field in auto-fill selector" + }, + "editMetadataFieldDiscNum": "Disc #", + "@editMetadataFieldDiscNum": { + "description": "Chip label for disc number field in auto-fill selector" + }, + "editMetadataFieldGenre": "Genre", + "@editMetadataFieldGenre": { + "description": "Chip label for genre field in auto-fill selector" + }, + "editMetadataFieldIsrc": "ISRC", + "@editMetadataFieldIsrc": { + "description": "Chip label for ISRC field in auto-fill selector" + }, + "editMetadataFieldLabel": "Label", + "@editMetadataFieldLabel": { + "description": "Chip label for label field in auto-fill selector" + }, + "editMetadataFieldCopyright": "Copyright", + "@editMetadataFieldCopyright": { + "description": "Chip label for copyright field in auto-fill selector" + }, + "editMetadataFieldCover": "Cover Art", + "@editMetadataFieldCover": { + "description": "Chip label for cover art field in auto-fill selector" + }, + "editMetadataSelectAll": "All", + "@editMetadataSelectAll": { + "description": "Button to select all fields for auto-fill" + }, + "editMetadataSelectEmpty": "Empty only", + "@editMetadataSelectEmpty": { + "description": "Button to select only fields that are currently empty" } } diff --git a/lib/screens/track_metadata_screen.dart b/lib/screens/track_metadata_screen.dart index cd37ee6a..989b6a47 100644 --- a/lib/screens/track_metadata_screen.dart +++ b/lib/screens/track_metadata_screen.dart @@ -3932,6 +3932,8 @@ class _EditMetadataSheet extends StatefulWidget { class _EditMetadataSheetState extends State<_EditMetadataSheet> { bool _saving = false; bool _showAdvanced = false; + bool _showAutoFill = false; + bool _fetching = false; String? _selectedCoverPath; String? _selectedCoverTempDir; String? _selectedCoverName; @@ -3939,6 +3941,25 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { String? _currentCoverTempDir; bool _loadingCurrentCover = false; + // Auto-fill field selection — which fields the user wants to fetch + final Set _autoFillFields = {}; + + // All auto-fillable fields and their mapping + static const _fieldDefs = { + 'title': 'title', + 'artist': 'artist', + 'album': 'album', + 'album_artist': 'album_artist', + 'date': 'date', + 'track_number': 'track_number', + 'disc_number': 'disc_number', + 'genre': 'genre', + 'isrc': 'isrc', + 'label': 'label', + 'copyright': 'copyright', + 'cover': 'cover', + }; + late final TextEditingController _titleCtrl; late final TextEditingController _artistCtrl; late final TextEditingController _albumCtrl; @@ -4132,6 +4153,286 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { } } + String _fieldLabel(String key) { + final l10n = context.l10n; + switch (key) { + case 'title': + return l10n.editMetadataFieldTitle; + case 'artist': + return l10n.editMetadataFieldArtist; + case 'album': + return l10n.editMetadataFieldAlbum; + case 'album_artist': + return l10n.editMetadataFieldAlbumArtist; + case 'date': + return l10n.editMetadataFieldDate; + case 'track_number': + return l10n.editMetadataFieldTrackNum; + case 'disc_number': + return l10n.editMetadataFieldDiscNum; + case 'genre': + return l10n.editMetadataFieldGenre; + case 'isrc': + return l10n.editMetadataFieldIsrc; + case 'label': + return l10n.editMetadataFieldLabel; + case 'copyright': + return l10n.editMetadataFieldCopyright; + case 'cover': + return l10n.editMetadataFieldCover; + default: + return key; + } + } + + TextEditingController? _controllerForKey(String key) { + switch (key) { + case 'title': + return _titleCtrl; + case 'artist': + return _artistCtrl; + case 'album': + return _albumCtrl; + case 'album_artist': + return _albumArtistCtrl; + case 'date': + return _dateCtrl; + case 'track_number': + return _trackNumCtrl; + case 'disc_number': + return _discNumCtrl; + case 'genre': + return _genreCtrl; + case 'isrc': + return _isrcCtrl; + case 'label': + return _labelCtrl; + case 'copyright': + return _copyrightCtrl; + default: + return null; + } + } + + void _selectAllFields() { + setState(() { + _autoFillFields.addAll(_fieldDefs.keys); + }); + } + + void _selectEmptyFields() { + setState(() { + _autoFillFields.clear(); + for (final key in _fieldDefs.keys) { + if (key == 'cover') { + if (!_hasValue(_currentCoverPath) && !_hasValue(_selectedCoverPath)) { + _autoFillFields.add(key); + } + continue; + } + final ctrl = _controllerForKey(key); + if (ctrl != null && ctrl.text.trim().isEmpty) { + _autoFillFields.add(key); + } + } + }); + } + + Future _fetchAndFill() async { + if (_autoFillFields.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.editMetadataAutoFillNoneSelected)), + ); + return; + } + + setState(() => _fetching = true); + + try { + // Build search query from current field values + final title = _titleCtrl.text.trim(); + final artist = _artistCtrl.text.trim(); + final album = _albumCtrl.text.trim(); + final queryParts = []; + if (title.isNotEmpty) queryParts.add(title); + if (artist.isNotEmpty) queryParts.add(artist); + if (album.isNotEmpty) queryParts.add(album); + + if (queryParts.isEmpty) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.editMetadataAutoFillNoResults), + ), + ); + } + return; + } + + final query = queryParts.join(' '); + final results = await PlatformBridge.searchTracksWithMetadataProviders( + query, + limit: 5, + ); + + if (!mounted) return; + + if (results.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(context.l10n.editMetadataAutoFillNoResults)), + ); + return; + } + + // Pick best match: prefer ISRC match, then first result + final currentIsrc = _isrcCtrl.text.trim().toUpperCase(); + Map? best; + if (currentIsrc.isNotEmpty) { + for (final r in results) { + final candidateIsrc = + (r['isrc']?.toString() ?? '').trim().toUpperCase(); + if (candidateIsrc == currentIsrc) { + best = r; + break; + } + } + } + best ??= results.first; + + // Extract metadata from best match + final enriched = { + 'title': (best['name'] ?? '').toString(), + 'artist': (best['artists'] ?? best['artist'] ?? '').toString(), + 'album': (best['album_name'] ?? best['album'] ?? '').toString(), + 'album_artist': (best['album_artist'] ?? '').toString(), + 'date': (best['release_date'] ?? '').toString(), + 'track_number': (best['track_number'] ?? '').toString(), + 'disc_number': (best['disc_number'] ?? '').toString(), + 'isrc': (best['isrc'] ?? '').toString(), + }; + + // Try to get extended metadata (genre, label, copyright) from Deezer + final trackId = + (best['spotify_id'] ?? best['id'] ?? '').toString(); + final source = (best['source'] ?? best['provider_id'] ?? '').toString(); + + if ((_autoFillFields.contains('genre') || + _autoFillFields.contains('label') || + _autoFillFields.contains('copyright')) && + trackId.isNotEmpty) { + try { + // If source is Deezer, fetch extended metadata directly + Map? extended; + if (source.toLowerCase().contains('deezer')) { + extended = await PlatformBridge.getDeezerExtendedMetadata(trackId); + } else { + // Try ISRC lookup via Deezer for genre/label/copyright + final isrcForLookup = enriched['isrc'] ?? ''; + if (isrcForLookup.isNotEmpty) { + try { + final deezerResult = await PlatformBridge.searchDeezerByISRC( + isrcForLookup, + ); + final deezerTrackId = + (deezerResult['id'] ?? deezerResult['track_id'] ?? '') + .toString(); + if (deezerTrackId.isNotEmpty) { + extended = await PlatformBridge.getDeezerExtendedMetadata( + deezerTrackId, + ); + } + } catch (_) {} + } + } + if (extended != null) { + enriched['genre'] = extended['genre'] ?? ''; + enriched['label'] = extended['label'] ?? ''; + enriched['copyright'] = extended['copyright'] ?? ''; + } + } catch (_) { + // Extended metadata is best-effort + } + } + + if (!mounted) return; + + // Apply selected fields to controllers + var filledCount = 0; + for (final key in _autoFillFields) { + if (key == 'cover') continue; // Handle cover separately below + final value = enriched[key]; + if (value != null && value.isNotEmpty && value != '0' && value != 'null') { + final ctrl = _controllerForKey(key); + if (ctrl != null) { + ctrl.text = value; + filledCount++; + } + } + } + + // Handle cover art download + if (_autoFillFields.contains('cover')) { + final coverUrl = + (best['cover_url'] ?? best['images'] ?? '').toString(); + if (coverUrl.isNotEmpty) { + try { + final tempDir = await Directory.systemTemp.createTemp( + 'autofill_cover_', + ); + final coverOutput = + '${tempDir.path}${Platform.pathSeparator}cover.jpg'; + final response = await HttpClient() + .getUrl(Uri.parse(coverUrl)) + .then((req) => req.close()); + final file = File(coverOutput); + final sink = file.openWrite(); + await response.pipe(sink); + if (await file.exists() && await file.length() > 0) { + await _cleanupSelectedCoverTemp(); + if (mounted) { + setState(() { + _selectedCoverPath = coverOutput; + _selectedCoverTempDir = tempDir.path; + _selectedCoverName = 'Online cover'; + }); + filledCount++; + } + } else { + try { + await tempDir.delete(recursive: true); + } catch (_) {} + } + } catch (_) { + // Cover download is best-effort + } + } + } + + if (mounted) { + setState(() {}); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + filledCount > 0 + ? context.l10n.editMetadataAutoFillDone(filledCount) + : context.l10n.editMetadataAutoFillNoResults, + ), + ), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.snackbarError(e.toString())), + ), + ); + } + } finally { + if (mounted) setState(() => _fetching = false); + } + } + @override void initState() { super.initState(); @@ -4416,6 +4717,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { children: [ const SizedBox(height: 6), _buildCoverEditor(cs), + _buildAutoFillSection(cs), _field('Title', _titleCtrl), _field('Artist', _artistCtrl), _field('Album', _albumCtrl), @@ -4487,6 +4789,183 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { ); } + Widget _buildAutoFillSection(ColorScheme cs) { + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Container( + decoration: BoxDecoration( + color: cs.surfaceContainerHighest.withValues(alpha: 0.5), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: cs.outlineVariant.withValues(alpha: 0.5)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () => setState(() => _showAutoFill = !_showAutoFill), + borderRadius: BorderRadius.circular(12), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 10, + ), + child: Row( + children: [ + Icon( + Icons.travel_explore, + size: 20, + color: cs.primary, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + context.l10n.editMetadataAutoFill, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: cs.onSurface, + fontWeight: FontWeight.w600, + ), + ), + ), + Icon( + _showAutoFill ? Icons.expand_less : Icons.expand_more, + size: 20, + color: cs.onSurfaceVariant, + ), + ], + ), + ), + ), + if (_showAutoFill) ...[ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Text( + context.l10n.editMetadataAutoFillDesc, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: cs.onSurfaceVariant, + ), + ), + ), + const SizedBox(height: 8), + // Quick select buttons + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + _quickSelectButton( + label: context.l10n.editMetadataSelectAll, + onTap: _selectAllFields, + cs: cs, + ), + const SizedBox(width: 8), + _quickSelectButton( + label: context.l10n.editMetadataSelectEmpty, + onTap: _selectEmptyFields, + cs: cs, + ), + ], + ), + ), + const SizedBox(height: 8), + // Field chips + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Wrap( + spacing: 6, + runSpacing: 4, + children: _fieldDefs.keys.map((key) { + final selected = _autoFillFields.contains(key); + return FilterChip( + label: Text(_fieldLabel(key)), + selected: selected, + onSelected: _fetching + ? null + : (val) { + setState(() { + if (val) { + _autoFillFields.add(key); + } else { + _autoFillFields.remove(key); + } + }); + }, + selectedColor: cs.primaryContainer, + checkmarkColor: cs.onPrimaryContainer, + labelStyle: Theme.of(context).textTheme.labelSmall + ?.copyWith( + color: selected + ? cs.onPrimaryContainer + : cs.onSurfaceVariant, + ), + visualDensity: VisualDensity.compact, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ); + }).toList(), + ), + ), + const SizedBox(height: 10), + // Fetch button + Padding( + padding: const EdgeInsets.only( + left: 12, + right: 12, + bottom: 12, + ), + child: SizedBox( + width: double.infinity, + child: FilledButton.icon( + onPressed: + (_fetching || _saving || _autoFillFields.isEmpty) + ? null + : _fetchAndFill, + icon: _fetching + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : const Icon(Icons.auto_fix_high), + label: Text( + _fetching + ? context.l10n.editMetadataAutoFillSearching + : context.l10n.editMetadataAutoFillFetch, + ), + ), + ), + ), + ], + ], + ), + ), + ); + } + + Widget _quickSelectButton({ + required String label, + required VoidCallback onTap, + required ColorScheme cs, + }) { + return InkWell( + onTap: _fetching ? null : onTap, + borderRadius: BorderRadius.circular(16), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + border: Border.all(color: cs.outline.withValues(alpha: 0.5)), + ), + child: Text( + label, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: cs.primary, + ), + ), + ), + ); + } + Widget _buildCoverEditor(ColorScheme cs) { final hasSelectedCover = _hasValue(_selectedCoverPath); final hasCurrentCover = _hasValue(_currentCoverPath);