feat(convert): optionally retain original files

This commit is contained in:
zarzet
2026-07-16 08:27:24 +07:00
parent d3d34aaec1
commit 491998e5c5
26 changed files with 722 additions and 113 deletions
+27
View File
@@ -4238,6 +4238,18 @@ abstract class AppLocalizations {
/// **'Bitrate'**
String get trackConvertBitrate;
/// Toggle to preserve the source file during conversion
///
/// In en, this message translates to:
/// **'Keep original file'**
String get trackConvertKeepOriginal;
/// Description for preserving the source file during conversion
///
/// In en, this message translates to:
/// **'Add the converted file as a separate library entry'**
String get trackConvertKeepOriginalDescription;
/// Confirmation dialog title
///
/// In en, this message translates to:
@@ -4263,6 +4275,15 @@ abstract class AppLocalizations {
String targetFormat,
);
/// Confirmation message when the source file will be preserved
///
/// In en, this message translates to:
/// **'Convert from {sourceFormat} to {targetFormat}?\n\nThe original file will be kept and the converted file will be added as a separate library entry.'**
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
);
/// Hint shown when converting between lossless formats
///
/// In en, this message translates to:
@@ -4651,6 +4672,12 @@ abstract class AppLocalizations {
/// **'Convert {count} {count, plural, =1{track} other{tracks}} to {format}? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.'**
String selectionBatchConvertConfirmMessageLossless(int count, String format);
/// Batch conversion confirmation when source files will be preserved
///
/// In en, this message translates to:
/// **'Convert {count} {count, plural, =1{track} other{tracks}} to {format}?\n\nOriginal files will be kept and converted files will be added as separate library entries.'**
String selectionBatchConvertConfirmKeepOriginal(int count, String format);
/// Snackbar after batch conversion completes
///
/// In en, this message translates to:
+26
View File
@@ -2379,6 +2379,13 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2399,6 +2406,14 @@ class AppLocalizationsAr extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2678,6 +2693,17 @@ class AppLocalizationsAr extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2408,6 +2408,13 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Konvertierung bestätigen';
@@ -2428,6 +2435,14 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Konvertieren von $sourceFormat in $targetFormat? (kein Qualitätsverlust)\n\nDie Originaldatei wird nach der Konvertierung gelöscht.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Verlustfreie Konvertierung kein Qualitätsverlust';
@@ -2710,6 +2725,17 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Konvertiere $count $_temp0 in $format? (kein Qualitätsverlust)\n\nOriginaldateien werden nach der Konvertierung gelöscht.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return '$success von $total Titeln in $format konvertiert';
+26
View File
@@ -2379,6 +2379,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2399,6 +2406,14 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2678,6 +2693,17 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2379,6 +2379,13 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2399,6 +2406,14 @@ class AppLocalizationsEs extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2678,6 +2693,17 @@ class AppLocalizationsEs extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2439,6 +2439,13 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get trackConvertBitrate => 'Débit binaire';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirmer la conversion';
@@ -2459,6 +2466,14 @@ class AppLocalizationsFr extends AppLocalizations {
return 'Convertir de $sourceFormat vers $targetFormat ? (Sans perte — aucune perte de qualité)\n\nLe fichier d\'origine sera supprimé après la conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Conversion sans perte — aucune perte de qualité';
@@ -2746,6 +2761,17 @@ class AppLocalizationsFr extends AppLocalizations {
return 'Convertir $count $_temp0 au format $format ? (Sans perte — aucune perte de qualité)\n\nLes fichiers d\'origine seront supprimés après la conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return '$success pistes sur $total ont été converties au format $format';
+26
View File
@@ -2379,6 +2379,13 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2399,6 +2406,14 @@ class AppLocalizationsHi extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2678,6 +2693,17 @@ class AppLocalizationsHi extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+20
View File
@@ -2388,6 +2388,13 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Pertahankan file asli';
@override
String get trackConvertKeepOriginalDescription =>
'Tambahkan file hasil konversi sebagai entri library terpisah';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2408,6 +2415,14 @@ class AppLocalizationsId extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Konversi dari $sourceFormat ke $targetFormat?\n\nFile asli akan dipertahankan dan file hasil konversi akan ditambahkan sebagai entri library terpisah.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2687,6 +2702,11 @@ class AppLocalizationsId extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
return 'Konversi $count lagu ke $format?\n\nFile asli akan dipertahankan dan file hasil konversi akan ditambahkan sebagai entri library terpisah.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2368,6 +2368,13 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get trackConvertBitrate => 'ビットレート';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => '変換を確認';
@@ -2388,6 +2395,14 @@ class AppLocalizationsJa extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2667,6 +2682,17 @@ class AppLocalizationsJa extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2315,6 +2315,13 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get trackConvertBitrate => '비트레이트';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => '변환 확인';
@@ -2335,6 +2342,14 @@ class AppLocalizationsKo extends AppLocalizations {
return '$sourceFormat에서 $targetFormat으로 변환하시겠습니까? (무손실 — 음질 손실 없음)\n\n변환 후에 원본 파일이 삭제됩니다';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint => '무손실 변환 — 음질 손실 없음';
@@ -2611,6 +2626,17 @@ class AppLocalizationsKo extends AppLocalizations {
return '$count 개의 $_temp0을 $format으로 변환하시겠습니까? (무손실 — 음질 손실 없음)\n\n변환 후 원본 파일이 삭제됩니다';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return '$total 개 중 $success 개를 $format로 변환 완료';
+26
View File
@@ -2379,6 +2379,13 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2399,6 +2406,14 @@ class AppLocalizationsNl extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2678,6 +2693,17 @@ class AppLocalizationsNl extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2379,6 +2379,13 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2399,6 +2406,14 @@ class AppLocalizationsPt extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2678,6 +2693,17 @@ class AppLocalizationsPt extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2409,6 +2409,13 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get trackConvertBitrate => 'Битрейт';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Подтвердить конвертацию';
@@ -2429,6 +2436,14 @@ class AppLocalizationsRu extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint => 'Конвертация без потери качества';
@@ -2708,6 +2723,17 @@ class AppLocalizationsRu extends AppLocalizations {
return 'Конвертировать $count $_temp0 в $format? (Без потери качества)\n\nОригинальные файлы будут удалены после конвертации.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Конвертировано $success треков $total в $format';
+26
View File
@@ -2405,6 +2405,13 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Dönüştürmeyi Onayla';
@@ -2425,6 +2432,14 @@ class AppLocalizationsTr extends AppLocalizations {
return '$sourceFormat formatından $targetFormat formatına dönüştürmek ister misiniz? (Kayıpsız — kalite kaybı yok)\n\nDönüştürme işleminden sonra orijinal dosya silinecektir.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Kayıpsız dönüştürme — kalite kaybı yok';
@@ -2705,6 +2720,17 @@ class AppLocalizationsTr extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+26
View File
@@ -2413,6 +2413,13 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get trackConvertBitrate => 'Бітрейт';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Підтвердити конверсію';
@@ -2433,6 +2440,14 @@ class AppLocalizationsUk extends AppLocalizations {
return 'Конвертувати з $sourceFormat у $targetFormat? (Lossless — без втрати якості)\n\nОригінальний файл буде видалено після конвертації.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless конвертація — без втрати якості';
@@ -2716,6 +2731,17 @@ class AppLocalizationsUk extends AppLocalizations {
return 'Конвертувати $count $_temp0 у $format? (Lossless — без втрати якості)\n\nОригінальні файли будуть видалені після конвертації.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Конвертовано $success з $total треків у $format';
+26
View File
@@ -2379,6 +2379,13 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get trackConvertBitrate => 'Bitrate';
@override
String get trackConvertKeepOriginal => 'Keep original file';
@override
String get trackConvertKeepOriginalDescription =>
'Add the converted file as a separate library entry';
@override
String get trackConvertConfirmTitle => 'Confirm Conversion';
@@ -2399,6 +2406,14 @@ class AppLocalizationsZh extends AppLocalizations {
return 'Convert from $sourceFormat to $targetFormat? (Lossless — no quality loss)\n\nThe original file will be deleted after conversion.';
}
@override
String trackConvertConfirmKeepOriginal(
String sourceFormat,
String targetFormat,
) {
return 'Convert from $sourceFormat to $targetFormat?\n\nThe original file will be kept and the converted file will be added as a separate library entry.';
}
@override
String get trackConvertLosslessHint =>
'Lossless conversion — no quality loss';
@@ -2678,6 +2693,17 @@ class AppLocalizationsZh extends AppLocalizations {
return 'Convert $count $_temp0 to $format? (Lossless — no quality loss)\n\nOriginal files will be deleted after conversion.';
}
@override
String selectionBatchConvertConfirmKeepOriginal(int count, String format) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: 'tracks',
one: 'track',
);
return 'Convert $count $_temp0 to $format?\n\nOriginal files will be kept and converted files will be added as separate library entries.';
}
@override
String selectionBatchConvertSuccess(int success, int total, String format) {
return 'Converted $success of $total tracks to $format';
+32
View File
@@ -3140,6 +3140,14 @@
"@trackConvertBitrate": {
"description": "Label for bitrate selection"
},
"trackConvertKeepOriginal": "Keep original file",
"@trackConvertKeepOriginal": {
"description": "Toggle to preserve the source file during conversion"
},
"trackConvertKeepOriginalDescription": "Add the converted file as a separate library entry",
"@trackConvertKeepOriginalDescription": {
"description": "Description for preserving the source file during conversion"
},
"trackConvertConfirmTitle": "Confirm Conversion",
"@trackConvertConfirmTitle": {
"description": "Confirmation dialog title"
@@ -3171,6 +3179,18 @@
}
}
},
"trackConvertConfirmKeepOriginal": "Convert from {sourceFormat} to {targetFormat}?\n\nThe original file will be kept and the converted file will be added as a separate library entry.",
"@trackConvertConfirmKeepOriginal": {
"description": "Confirmation message when the source file will be preserved",
"placeholders": {
"sourceFormat": {
"type": "String"
},
"targetFormat": {
"type": "String"
}
}
},
"trackConvertLosslessHint": "Lossless conversion — no quality loss",
"@trackConvertLosslessHint": {
"description": "Hint shown when converting between lossless formats"
@@ -3547,6 +3567,18 @@
}
}
},
"selectionBatchConvertConfirmKeepOriginal": "Convert {count} {count, plural, =1{track} other{tracks}} to {format}?\n\nOriginal files will be kept and converted files will be added as separate library entries.",
"@selectionBatchConvertConfirmKeepOriginal": {
"description": "Batch conversion confirmation when source files will be preserved",
"placeholders": {
"count": {
"type": "int"
},
"format": {
"type": "String"
}
}
},
"selectionBatchConvertSuccess": "Converted {success} of {total} tracks to {format}",
"@selectionBatchConvertSuccess": {
"description": "Snackbar after batch conversion completes",
+33 -1
View File
@@ -5883,5 +5883,37 @@
"extensionsSearchProviderDescription": "Pilih layanan yang digunakan untuk mencari lagu",
"albumFolderAlbumOnly": "Album Saja",
"nowPlayingOpenInExternalPlayer": "Open in external player",
"cueSplitButton": "Split into Tracks"
"cueSplitButton": "Split into Tracks",
"trackConvertKeepOriginal": "Pertahankan file asli",
"@trackConvertKeepOriginal": {
"description": "Toggle untuk mempertahankan file sumber saat konversi"
},
"trackConvertKeepOriginalDescription": "Tambahkan file hasil konversi sebagai entri library terpisah",
"@trackConvertKeepOriginalDescription": {
"description": "Deskripsi opsi mempertahankan file sumber saat konversi"
},
"trackConvertConfirmKeepOriginal": "Konversi dari {sourceFormat} ke {targetFormat}?\n\nFile asli akan dipertahankan dan file hasil konversi akan ditambahkan sebagai entri library terpisah.",
"@trackConvertConfirmKeepOriginal": {
"description": "Konfirmasi konversi ketika file sumber dipertahankan",
"placeholders": {
"sourceFormat": {
"type": "String"
},
"targetFormat": {
"type": "String"
}
}
},
"selectionBatchConvertConfirmKeepOriginal": "Konversi {count} lagu ke {format}?\n\nFile asli akan dipertahankan dan file hasil konversi akan ditambahkan sebagai entri library terpisah.",
"@selectionBatchConvertConfirmKeepOriginal": {
"description": "Konfirmasi konversi batch ketika file sumber dipertahankan",
"placeholders": {
"count": {
"type": "int"
},
"format": {
"type": "String"
}
}
}
}
+54 -43
View File
@@ -263,17 +263,25 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
bitrate,
);
},
onConvert: (format, bitrate, losslessQuality, losslessProcessing) {
Navigator.pop(sheetContext);
_confirmAndConvert(
context: this.context,
sourceFormat: currentFormat,
targetFormat: format,
bitrate: bitrate,
losslessQuality: losslessQuality,
losslessProcessing: losslessProcessing,
);
},
onConvert:
(
format,
bitrate,
losslessQuality,
losslessProcessing,
keepOriginal,
) {
Navigator.pop(sheetContext);
_confirmAndConvert(
context: this.context,
sourceFormat: currentFormat,
targetFormat: format,
bitrate: bitrate,
losslessQuality: losslessQuality,
losslessProcessing: losslessProcessing,
keepOriginal: keepOriginal,
);
},
),
);
}
@@ -738,6 +746,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
const LosslessConversionQuality(),
LosslessConversionProcessing losslessProcessing =
const LosslessConversionProcessing(),
bool keepOriginal = false,
}) {
final isLossless = isLosslessConversionTarget(targetFormat);
showDialog<void>(
@@ -746,7 +755,12 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
return AlertDialog(
title: Text(dialogContext.l10n.trackConvertConfirmTitle),
content: Text(
isLossless && losslessQuality.hasCaps
keepOriginal
? dialogContext.l10n.trackConvertConfirmKeepOriginal(
sourceFormat,
targetFormat,
)
: isLossless && losslessQuality.hasCaps
? dialogContext.l10n.trackConvertConfirmMessageLosslessCapped(
sourceFormat,
targetFormat,
@@ -784,6 +798,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
bitrate: bitrate,
losslessQuality: losslessQuality,
losslessProcessing: losslessProcessing,
keepOriginal: keepOriginal,
);
},
child: Text(dialogContext.l10n.trackConvertFormat),
@@ -801,6 +816,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
const LosslessConversionQuality(),
LosslessConversionProcessing losslessProcessing =
const LosslessConversionProcessing(),
bool keepOriginal = false,
}) async {
if (_isConverting) return;
_setState(() => _isConverting = true);
@@ -874,7 +890,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
metadata: metadata,
coverPath: coverPath,
artistTagMode: ref.read(settingsProvider).artistTagMode,
deleteOriginal: !isSaf,
deleteOriginal: !isSaf && !keepOriginal,
sourceBitDepth: bitDepth,
losslessQuality: losslessQuality,
losslessProcessing: losslessProcessing,
@@ -991,14 +1007,13 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
return;
}
final dotIdx = oldFileName.lastIndexOf('.');
final baseName = dotIdx > 0
? oldFileName.substring(0, dotIdx)
: oldFileName;
final convTarget = convertTargetExtAndMime(targetFormat);
final newExt = convTarget.ext;
final mimeType = convTarget.mime;
final newFileName = '$baseName$newExt';
final newFileName = convertedOutputFileName(
originalFileName: oldFileName,
targetFormat: targetFormat,
keepOriginal: keepOriginal,
);
final safUri = await PlatformBridge.createSafFileFromPath(
treeUri: treeUri,
@@ -1026,7 +1041,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
return;
}
if (!isSameContentUri(cleanFilePath, safUri)) {
if (!keepOriginal && !isSameContentUri(cleanFilePath, safUri)) {
final deletedOriginal = await PlatformBridge.safDelete(
cleanFilePath,
).catchError((_) => false);
@@ -1038,19 +1053,16 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
}
if (!_isLocalItem) {
await HistoryDatabase.instance.updateFilePath(
_downloadItem!.id,
safUri,
newSafFileName: newFileName,
await ConversionLibraryService.persistHistoryConversion(
source: _downloadItem!,
newFilePath: safUri,
newQuality: newQuality,
newFormat: normalizedConvertedAudioFormat(targetFormat),
newBitrate: convertedAudioBitrateKbps(
targetFormat: targetFormat,
bitrate: bitrate,
),
newBitDepth: convertedBitDepth,
newSampleRate: convertedSampleRate,
clearAudioSpecs: !isLosslessOutput,
targetFormat: targetFormat,
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
newSafFileName: newFileName,
);
await ref.read(downloadHistoryProvider.notifier).reloadFromStorage();
} else {
@@ -1061,6 +1073,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
);
await ref.read(localLibraryProvider.notifier).reloadFromStorage();
}
@@ -1075,18 +1088,15 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
}
} else {
if (!_isLocalItem) {
await HistoryDatabase.instance.updateFilePath(
_downloadItem!.id,
newPath,
await ConversionLibraryService.persistHistoryConversion(
source: _downloadItem!,
newFilePath: newPath,
newQuality: newQuality,
newFormat: normalizedConvertedAudioFormat(targetFormat),
newBitrate: convertedAudioBitrateKbps(
targetFormat: targetFormat,
bitrate: bitrate,
),
newBitDepth: convertedBitDepth,
newSampleRate: convertedSampleRate,
clearAudioSpecs: !isLosslessOutput,
targetFormat: targetFormat,
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
);
await ref.read(downloadHistoryProvider.notifier).reloadFromStorage();
} else {
@@ -1097,6 +1107,7 @@ extension _TrackMetadataConvertAndCueSplit on _TrackMetadataScreenState {
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
);
await ref.read(localLibraryProvider.notifier).reloadFromStorage();
}
+1 -1
View File
@@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:file_picker/file_picker.dart';
import 'package:spotiflac_android/services/history_database.dart';
import 'package:spotiflac_android/services/conversion_library_service.dart';
import 'package:spotiflac_android/services/library_database.dart';
import 'package:spotiflac_android/utils/file_access.dart';
import 'package:path_provider/path_provider.dart';
+56 -55
View File
@@ -8,8 +8,8 @@ import 'package:spotiflac_android/models/unified_library_item.dart';
import 'package:spotiflac_android/providers/download_history_provider.dart';
import 'package:spotiflac_android/providers/local_library_provider.dart';
import 'package:spotiflac_android/providers/settings_provider.dart';
import 'package:spotiflac_android/services/conversion_library_service.dart';
import 'package:spotiflac_android/services/ffmpeg_service.dart';
import 'package:spotiflac_android/services/history_database.dart';
import 'package:spotiflac_android/services/library_database.dart';
import 'package:spotiflac_android/services/platform_bridge.dart';
import 'package:spotiflac_android/services/replaygain_service.dart';
@@ -86,20 +86,22 @@ Future<void> showBatchConvertSheet(
confirmLabel: sheetConfirmLabel,
sourceBitDepth: lowestKnownPositiveInt(sourceBitDepths),
sourceSampleRate: lowestKnownPositiveInt(sourceSampleRates),
onConvert: (format, bitrate, losslessQuality, losslessProcessing) {
didStartConversion = true;
Navigator.pop(sheetContext);
_performBatchConversion(
context,
ref,
selectedItems: selectedItems,
targetFormat: format,
bitrate: bitrate,
losslessQuality: losslessQuality,
losslessProcessing: losslessProcessing,
onExitSelectionMode: onExitSelectionMode,
);
},
onConvert:
(format, bitrate, losslessQuality, losslessProcessing, keepOriginal) {
didStartConversion = true;
Navigator.pop(sheetContext);
_performBatchConversion(
context,
ref,
selectedItems: selectedItems,
targetFormat: format,
bitrate: bitrate,
losslessQuality: losslessQuality,
losslessProcessing: losslessProcessing,
keepOriginal: keepOriginal,
onExitSelectionMode: onExitSelectionMode,
);
},
),
);
@@ -115,6 +117,7 @@ Future<void> _performBatchConversion(
LosslessConversionQuality losslessQuality = const LosslessConversionQuality(),
LosslessConversionProcessing losslessProcessing =
const LosslessConversionProcessing(),
bool keepOriginal = false,
required VoidCallback onExitSelectionMode,
}) async {
final convertibleItems = <UnifiedLibraryItem>[];
@@ -150,7 +153,12 @@ Future<void> _performBatchConversion(
builder: (ctx) => AlertDialog(
title: Text(context.l10n.selectionBatchConvertConfirmTitle),
content: Text(
isLossless && losslessQuality.hasCaps
keepOriginal
? context.l10n.selectionBatchConvertConfirmKeepOriginal(
convertibleItems.length,
targetFormat,
)
: isLossless && losslessQuality.hasCaps
? context.l10n.selectionBatchConvertConfirmMessageLosslessCapped(
convertibleItems.length,
targetFormat,
@@ -188,7 +196,6 @@ Future<void> _performBatchConversion(
int successCount = 0;
final total = convertibleItems.length;
final historyDb = HistoryDatabase.instance;
final settings = ref.read(settingsProvider);
final shouldEmbedLyrics =
settings.embedLyrics && settings.lyricsMode != 'external';
@@ -266,7 +273,7 @@ Future<void> _performBatchConversion(
metadata: metadata,
coverPath: coverPath,
artistTagMode: settings.artistTagMode,
deleteOriginal: !isSaf,
deleteOriginal: !isSaf && !keepOriginal,
sourceBitDepth: item.historyItem?.bitDepth ?? item.localItem?.bitDepth,
losslessQuality: losslessQuality,
losslessProcessing: losslessProcessing,
@@ -326,14 +333,13 @@ Future<void> _performBatchConversion(
final relativeDir = hi.safRelativeDir ?? '';
if (treeUri != null && treeUri.isNotEmpty) {
final oldFileName = hi.safFileName ?? '';
final dotIdx = oldFileName.lastIndexOf('.');
final baseName = dotIdx > 0
? oldFileName.substring(0, dotIdx)
: oldFileName;
final convTarget = convertTargetExtAndMime(targetFormat);
final newExt = convTarget.ext;
final mimeType = convTarget.mime;
final newFileName = '$baseName$newExt';
final newFileName = convertedOutputFileName(
originalFileName: oldFileName,
targetFormat: targetFormat,
keepOriginal: keepOriginal,
);
final safUri = await PlatformBridge.createSafFileFromPath(
treeUri: treeUri,
@@ -355,25 +361,22 @@ Future<void> _performBatchConversion(
continue;
}
if (!isSameContentUri(item.filePath, safUri)) {
if (!keepOriginal && !isSameContentUri(item.filePath, safUri)) {
try {
await PlatformBridge.safDelete(item.filePath);
} catch (_) {}
}
await historyDb.updateFilePath(
hi.id,
safUri,
newSafFileName: newFileName,
await ConversionLibraryService.persistHistoryConversion(
source: hi,
newFilePath: safUri,
newQuality: newQuality,
newFormat: normalizedConvertedAudioFormat(targetFormat),
newBitrate: convertedAudioBitrateKbps(
targetFormat: targetFormat,
bitrate: bitrate,
),
newBitDepth: convertedBitDepth,
newSampleRate: convertedSampleRate,
clearAudioSpecs: !isLosslessOutput,
targetFormat: targetFormat,
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
newSafFileName: newFileName,
);
}
try {
@@ -423,14 +426,13 @@ Future<void> _performBatchConversion(
}
if (treeUri != null && oldFileName.isNotEmpty) {
final dotIdx = oldFileName.lastIndexOf('.');
final baseName = dotIdx > 0
? oldFileName.substring(0, dotIdx)
: oldFileName;
final convTarget = convertTargetExtAndMime(targetFormat);
final newExt = convTarget.ext;
final mimeType = convTarget.mime;
final newFileName = '$baseName$newExt';
final newFileName = convertedOutputFileName(
originalFileName: oldFileName,
targetFormat: targetFormat,
keepOriginal: keepOriginal,
);
final safUri = await PlatformBridge.createSafFileFromPath(
treeUri: treeUri,
@@ -452,7 +454,7 @@ Future<void> _performBatchConversion(
continue;
}
if (!isSameContentUri(item.filePath, safUri)) {
if (!keepOriginal && !isSameContentUri(item.filePath, safUri)) {
try {
await PlatformBridge.safDelete(item.filePath);
} catch (_) {}
@@ -464,6 +466,7 @@ Future<void> _performBatchConversion(
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
);
}
@@ -476,18 +479,15 @@ Future<void> _performBatchConversion(
} catch (_) {}
}
} else if (item.historyItem != null) {
await historyDb.updateFilePath(
item.historyItem!.id,
newPath,
await ConversionLibraryService.persistHistoryConversion(
source: item.historyItem!,
newFilePath: newPath,
newQuality: newQuality,
newFormat: normalizedConvertedAudioFormat(targetFormat),
newBitrate: convertedAudioBitrateKbps(
targetFormat: targetFormat,
bitrate: bitrate,
),
newBitDepth: convertedBitDepth,
newSampleRate: convertedSampleRate,
clearAudioSpecs: !isLosslessOutput,
targetFormat: targetFormat,
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
);
} else if (item.localItem != null) {
await LibraryDatabase.instance.replaceWithConvertedItem(
@@ -497,6 +497,7 @@ Future<void> _performBatchConversion(
bitrate: bitrate,
bitDepth: convertedBitDepth,
sampleRate: convertedSampleRate,
keepOriginal: keepOriginal,
);
}
@@ -0,0 +1,54 @@
import 'package:spotiflac_android/providers/download_history_provider.dart';
import 'package:spotiflac_android/services/history_database.dart';
import 'package:spotiflac_android/utils/audio_conversion_utils.dart';
class ConversionLibraryService {
const ConversionLibraryService._();
static Future<void> persistHistoryConversion({
required DownloadHistoryItem source,
required String newFilePath,
required String newQuality,
required String targetFormat,
required String bitrate,
required int? bitDepth,
required int? sampleRate,
required bool keepOriginal,
String? newSafFileName,
}) async {
final convertedBitrate = convertedAudioBitrateKbps(
targetFormat: targetFormat,
bitrate: bitrate,
);
final isLossless = isLosslessConversionTarget(targetFormat);
if (!keepOriginal) {
await HistoryDatabase.instance.updateFilePath(
source.id,
newFilePath,
newSafFileName: newSafFileName,
newQuality: newQuality,
newFormat: normalizedConvertedAudioFormat(targetFormat),
newBitrate: convertedBitrate,
newBitDepth: bitDepth,
newSampleRate: sampleRate,
clearAudioSpecs: !isLossless,
);
return;
}
final converted = source.toJson()
..['id'] = convertedLibraryItemId(source.id, newFilePath)
..['filePath'] = newFilePath
..['downloadedAt'] = DateTime.now().toIso8601String()
..['quality'] = newQuality
..['format'] = normalizedConvertedAudioFormat(targetFormat)
..['bitrate'] = convertedBitrate
..['bitDepth'] = isLossless ? bitDepth : null
..['sampleRate'] = isLossless ? sampleRate : null;
if (newSafFileName != null) {
converted['safFileName'] = newSafFileName;
}
await HistoryDatabase.instance.upsert(converted);
}
}
+13 -10
View File
@@ -1783,6 +1783,7 @@ class LibraryDatabase {
required String bitrate,
int? bitDepth,
int? sampleRate,
bool keepOriginal = false,
}) async {
final db = await database;
final stat = await fileStat(newFilePath);
@@ -1810,16 +1811,18 @@ class LibraryDatabase {
}
await db.transaction((txn) async {
await txn.delete(
'library_path_keys',
where: 'item_id = ?',
whereArgs: [item.id],
);
await txn.delete(
'library',
where: 'id = ? OR file_path = ?',
whereArgs: [item.id, item.filePath],
);
if (!keepOriginal) {
await txn.delete(
'library_path_keys',
where: 'item_id = ?',
whereArgs: [item.id],
);
await txn.delete(
'library',
where: 'id = ? OR file_path = ?',
whereArgs: [item.id, item.filePath],
);
}
await txn.insert(
'library',
_jsonToDbRow(updated),
+21
View File
@@ -332,6 +332,27 @@ String normalizedConvertedAudioFormat(String targetFormat) {
}
}
String convertedOutputFileName({
required String originalFileName,
required String targetFormat,
required bool keepOriginal,
}) {
final dotIndex = originalFileName.lastIndexOf('.');
final baseName = dotIndex > 0
? originalFileName.substring(0, dotIndex)
: originalFileName;
final suffix = keepOriginal ? '_converted' : '';
return '$baseName$suffix${convertTargetExtAndMime(targetFormat).ext}';
}
String convertedLibraryItemId(String sourceId, String filePath) {
var hash = 5381;
for (final codeUnit in filePath.codeUnits) {
hash = (((hash << 5) + hash) ^ codeUnit) & 0x7fffffff;
}
return '$sourceId:converted:${hash.toRadixString(16)}';
}
int? convertedAudioBitrateKbps({
required String targetFormat,
required String bitrate,
+17 -3
View File
@@ -25,6 +25,7 @@ class BatchConvertSheet extends StatefulWidget {
String bitrate,
LosslessConversionQuality losslessQuality,
LosslessConversionProcessing losslessProcessing,
bool keepOriginal,
)
onConvert;
@@ -55,6 +56,7 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
int? _selectedMaxSampleRate;
String _selectedDither = 'none';
String _selectedResampler = 'swr';
bool _keepOriginal = false;
String _defaultBitrateForFormat(String format) {
if (format == 'Opus') return '128k';
@@ -84,9 +86,7 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
);
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.viewInsetsOf(context).bottom,
),
padding: EdgeInsets.only(bottom: MediaQuery.viewInsetsOf(context).bottom),
child: DraggableScrollableSheet(
initialChildSize: 0.85,
minChildSize: 0.5,
@@ -370,6 +370,19 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
),
),
_card(
cs,
child: SwitchListTile(
contentPadding: EdgeInsets.zero,
title: Text(context.l10n.trackConvertKeepOriginal),
subtitle: Text(
context.l10n.trackConvertKeepOriginalDescription,
),
value: _keepOriginal,
onChanged: (value) => setState(() => _keepOriginal = value),
),
),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
@@ -385,6 +398,7 @@ class _BatchConvertSheetState extends State<BatchConvertSheet> {
dither: _selectedDither,
resampler: _selectedResampler,
),
_keepOriginal,
),
icon: const Icon(Icons.swap_horiz),
style: FilledButton.styleFrom(