feat: download options picker for series/seasons
This commit is contained in:
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Pause alle",
|
||||
"resumeAll": "Genoptag alle",
|
||||
"deleteAll": "Slet alle",
|
||||
"selectVersion": "Vælg version"
|
||||
"selectVersion": "Vælg version",
|
||||
"allEpisodes": "Alle episoder",
|
||||
"unwatchedOnly": "Kun usete",
|
||||
"nextNUnwatched": "Næste ${count} usete",
|
||||
"customAmount": "Angiv antal...",
|
||||
"howManyEpisodes": "Hvor mange episoder?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Shadere",
|
||||
|
||||
@@ -630,7 +630,12 @@
|
||||
"pauseAll": "Alle pausieren",
|
||||
"resumeAll": "Alle fortsetzen",
|
||||
"deleteAll": "Alle löschen",
|
||||
"selectVersion": "Version auswählen"
|
||||
"selectVersion": "Version auswählen",
|
||||
"allEpisodes": "Alle Episoden",
|
||||
"unwatchedOnly": "Nur ungesehene",
|
||||
"nextNUnwatched": "Nächste ${count} ungesehene",
|
||||
"customAmount": "Eigene Anzahl...",
|
||||
"howManyEpisodes": "Wie viele Episoden?"
|
||||
},
|
||||
"playlists": {
|
||||
"title": "Wiedergabelisten",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Pause all",
|
||||
"resumeAll": "Resume all",
|
||||
"deleteAll": "Delete all",
|
||||
"selectVersion": "Select Version"
|
||||
"selectVersion": "Select Version",
|
||||
"allEpisodes": "All episodes",
|
||||
"unwatchedOnly": "Unwatched only",
|
||||
"nextNUnwatched": "Next ${count} unwatched",
|
||||
"customAmount": "Custom amount...",
|
||||
"howManyEpisodes": "How many episodes?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Shaders",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Pausar todo",
|
||||
"resumeAll": "Reanudar todo",
|
||||
"deleteAll": "Eliminar todo",
|
||||
"selectVersion": "Seleccionar versión"
|
||||
"selectVersion": "Seleccionar versión",
|
||||
"allEpisodes": "Todos los episodios",
|
||||
"unwatchedOnly": "Solo no vistos",
|
||||
"nextNUnwatched": "Próximos ${count} no vistos",
|
||||
"customAmount": "Cantidad personalizada...",
|
||||
"howManyEpisodes": "¿Cuántos episodios?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Shaders",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Tout mettre en pause",
|
||||
"resumeAll": "Tout reprendre",
|
||||
"deleteAll": "Tout supprimer",
|
||||
"selectVersion": "Sélectionner la version"
|
||||
"selectVersion": "Sélectionner la version",
|
||||
"allEpisodes": "Tous les épisodes",
|
||||
"unwatchedOnly": "Non vus uniquement",
|
||||
"nextNUnwatched": "${count} prochains non vus",
|
||||
"customAmount": "Quantité personnalisée...",
|
||||
"howManyEpisodes": "Combien d'épisodes ?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Shaders",
|
||||
|
||||
@@ -630,7 +630,12 @@
|
||||
"pauseAll": "Metti tutto in pausa",
|
||||
"resumeAll": "Riprendi tutto",
|
||||
"deleteAll": "Elimina tutto",
|
||||
"selectVersion": "Seleziona versione"
|
||||
"selectVersion": "Seleziona versione",
|
||||
"allEpisodes": "Tutti gli episodi",
|
||||
"unwatchedOnly": "Solo non visti",
|
||||
"nextNUnwatched": "Prossimi ${count} non visti",
|
||||
"customAmount": "Quantità personalizzata...",
|
||||
"howManyEpisodes": "Quanti episodi?"
|
||||
},
|
||||
"playlists": {
|
||||
"title": "Playlist",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "すべて一時停止",
|
||||
"resumeAll": "すべて再開",
|
||||
"deleteAll": "すべて削除",
|
||||
"selectVersion": "バージョンを選択"
|
||||
"selectVersion": "バージョンを選択",
|
||||
"allEpisodes": "すべてのエピソード",
|
||||
"unwatchedOnly": "未視聴のみ",
|
||||
"nextNUnwatched": "次の${count}件の未視聴",
|
||||
"customAmount": "数を指定...",
|
||||
"howManyEpisodes": "何エピソード?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "シェーダー",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "모두 일시정지",
|
||||
"resumeAll": "모두 재개",
|
||||
"deleteAll": "모두 삭제",
|
||||
"selectVersion": "버전 선택"
|
||||
"selectVersion": "버전 선택",
|
||||
"allEpisodes": "모든 에피소드",
|
||||
"unwatchedOnly": "시청하지 않은 것만",
|
||||
"nextNUnwatched": "다음 ${count}개 미시청",
|
||||
"customAmount": "직접 입력...",
|
||||
"howManyEpisodes": "몇 개의 에피소드?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "셰이더",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Pause alle",
|
||||
"resumeAll": "Gjenoppta alle",
|
||||
"deleteAll": "Slett alle",
|
||||
"selectVersion": "Velg versjon"
|
||||
"selectVersion": "Velg versjon",
|
||||
"allEpisodes": "Alle episoder",
|
||||
"unwatchedOnly": "Kun usette",
|
||||
"nextNUnwatched": "Neste ${count} usette",
|
||||
"customAmount": "Egendefinert antall...",
|
||||
"howManyEpisodes": "Hvor mange episoder?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Shadere",
|
||||
|
||||
@@ -630,7 +630,12 @@
|
||||
"pauseAll": "Alles pauzeren",
|
||||
"resumeAll": "Alles hervatten",
|
||||
"deleteAll": "Alles verwijderen",
|
||||
"selectVersion": "Versie selecteren"
|
||||
"selectVersion": "Versie selecteren",
|
||||
"allEpisodes": "Alle afleveringen",
|
||||
"unwatchedOnly": "Alleen onbekeken",
|
||||
"nextNUnwatched": "Volgende ${count} onbekeken",
|
||||
"customAmount": "Aangepast aantal...",
|
||||
"howManyEpisodes": "Hoeveel afleveringen?"
|
||||
},
|
||||
"playlists": {
|
||||
"title": "Afspeellijsten",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Wstrzymaj wszystko",
|
||||
"resumeAll": "Wznów wszystko",
|
||||
"deleteAll": "Usuń wszystko",
|
||||
"selectVersion": "Wybierz wersję"
|
||||
"selectVersion": "Wybierz wersję",
|
||||
"allEpisodes": "Wszystkie odcinki",
|
||||
"unwatchedOnly": "Tylko nieobejrzane",
|
||||
"nextNUnwatched": "Następne ${count} nieobejrzanych",
|
||||
"customAmount": "Własna ilość...",
|
||||
"howManyEpisodes": "Ile odcinków?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Shadery",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Pausar todos",
|
||||
"resumeAll": "Retomar todos",
|
||||
"deleteAll": "Excluir todos",
|
||||
"selectVersion": "Selecionar versão"
|
||||
"selectVersion": "Selecionar versão",
|
||||
"allEpisodes": "Todos os episódios",
|
||||
"unwatchedOnly": "Apenas não assistidos",
|
||||
"nextNUnwatched": "Próximos ${count} não assistidos",
|
||||
"customAmount": "Quantidade personalizada...",
|
||||
"howManyEpisodes": "Quantos episódios?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Shaders",
|
||||
|
||||
@@ -733,7 +733,12 @@
|
||||
"pauseAll": "Приостановить все",
|
||||
"resumeAll": "Возобновить все",
|
||||
"deleteAll": "Удалить все",
|
||||
"selectVersion": "Выбрать версию"
|
||||
"selectVersion": "Выбрать версию",
|
||||
"allEpisodes": "Все эпизоды",
|
||||
"unwatchedOnly": "Только непросмотренные",
|
||||
"nextNUnwatched": "Следующие ${count} непросмотренных",
|
||||
"customAmount": "Указать количество...",
|
||||
"howManyEpisodes": "Сколько эпизодов?"
|
||||
},
|
||||
"shaders": {
|
||||
"title": "Шейдеры",
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 15
|
||||
/// Strings: 12750 (850 per locale)
|
||||
/// Strings: 12825 (855 per locale)
|
||||
///
|
||||
/// Built on 2026-04-06 at 14:44 UTC
|
||||
/// Built on 2026-04-07 at 18:27 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsDa implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Genoptag alle';
|
||||
@override String get deleteAll => 'Slet alle';
|
||||
@override String get selectVersion => 'Vælg version';
|
||||
@override String get allEpisodes => 'Alle episoder';
|
||||
@override String get unwatchedOnly => 'Kun usete';
|
||||
@override String nextNUnwatched({required Object count}) => 'Næste ${count} usete';
|
||||
@override String get customAmount => 'Angiv antal...';
|
||||
@override String get howManyEpisodes => 'Hvor mange episoder?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsDa {
|
||||
'downloads.resumeAll' => 'Genoptag alle',
|
||||
'downloads.deleteAll' => 'Slet alle',
|
||||
'downloads.selectVersion' => 'Vælg version',
|
||||
'downloads.allEpisodes' => 'Alle episoder',
|
||||
'downloads.unwatchedOnly' => 'Kun usete',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Næste ${count} usete',
|
||||
'downloads.customAmount' => 'Angiv antal...',
|
||||
'downloads.howManyEpisodes' => 'Hvor mange episoder?',
|
||||
'shaders.title' => 'Shadere',
|
||||
'shaders.noShaderDescription' => 'Ingen videoforbedring',
|
||||
'shaders.nvscalerDescription' => 'NVIDIA-billedskalering for skarpere video',
|
||||
|
||||
@@ -870,6 +870,11 @@ class _TranslationsDownloadsDe implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Alle fortsetzen';
|
||||
@override String get deleteAll => 'Alle löschen';
|
||||
@override String get selectVersion => 'Version auswählen';
|
||||
@override String get allEpisodes => 'Alle Episoden';
|
||||
@override String get unwatchedOnly => 'Nur ungesehene';
|
||||
@override String nextNUnwatched({required Object count}) => 'Nächste ${count} ungesehene';
|
||||
@override String get customAmount => 'Eigene Anzahl...';
|
||||
@override String get howManyEpisodes => 'Wie viele Episoden?';
|
||||
}
|
||||
|
||||
// Path: playlists
|
||||
@@ -1916,6 +1921,11 @@ extension on TranslationsDe {
|
||||
'downloads.resumeAll' => 'Alle fortsetzen',
|
||||
'downloads.deleteAll' => 'Alle löschen',
|
||||
'downloads.selectVersion' => 'Version auswählen',
|
||||
'downloads.allEpisodes' => 'Alle Episoden',
|
||||
'downloads.unwatchedOnly' => 'Nur ungesehene',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Nächste ${count} ungesehene',
|
||||
'downloads.customAmount' => 'Eigene Anzahl...',
|
||||
'downloads.howManyEpisodes' => 'Wie viele Episoden?',
|
||||
'playlists.title' => 'Wiedergabelisten',
|
||||
'playlists.noPlaylists' => 'Keine Wiedergabelisten gefunden',
|
||||
'playlists.create' => 'Wiedergabeliste erstellen',
|
||||
|
||||
@@ -2254,6 +2254,21 @@ class TranslationsDownloadsEn {
|
||||
|
||||
/// en: 'Select Version'
|
||||
String get selectVersion => 'Select Version';
|
||||
|
||||
/// en: 'All episodes'
|
||||
String get allEpisodes => 'All episodes';
|
||||
|
||||
/// en: 'Unwatched only'
|
||||
String get unwatchedOnly => 'Unwatched only';
|
||||
|
||||
/// en: 'Next ${count} unwatched'
|
||||
String nextNUnwatched({required Object count}) => 'Next ${count} unwatched';
|
||||
|
||||
/// en: 'Custom amount...'
|
||||
String get customAmount => 'Custom amount...';
|
||||
|
||||
/// en: 'How many episodes?'
|
||||
String get howManyEpisodes => 'How many episodes?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -3720,6 +3735,11 @@ extension on Translations {
|
||||
'downloads.resumeAll' => 'Resume all',
|
||||
'downloads.deleteAll' => 'Delete all',
|
||||
'downloads.selectVersion' => 'Select Version',
|
||||
'downloads.allEpisodes' => 'All episodes',
|
||||
'downloads.unwatchedOnly' => 'Unwatched only',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Next ${count} unwatched',
|
||||
'downloads.customAmount' => 'Custom amount...',
|
||||
'downloads.howManyEpisodes' => 'How many episodes?',
|
||||
'shaders.title' => 'Shaders',
|
||||
'shaders.noShaderDescription' => 'No video enhancement',
|
||||
'shaders.nvscalerDescription' => 'NVIDIA image scaling for sharper video',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsEs implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Reanudar todo';
|
||||
@override String get deleteAll => 'Eliminar todo';
|
||||
@override String get selectVersion => 'Seleccionar versión';
|
||||
@override String get allEpisodes => 'Todos los episodios';
|
||||
@override String get unwatchedOnly => 'Solo no vistos';
|
||||
@override String nextNUnwatched({required Object count}) => 'Próximos ${count} no vistos';
|
||||
@override String get customAmount => 'Cantidad personalizada...';
|
||||
@override String get howManyEpisodes => '¿Cuántos episodios?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsEs {
|
||||
'downloads.resumeAll' => 'Reanudar todo',
|
||||
'downloads.deleteAll' => 'Eliminar todo',
|
||||
'downloads.selectVersion' => 'Seleccionar versión',
|
||||
'downloads.allEpisodes' => 'Todos los episodios',
|
||||
'downloads.unwatchedOnly' => 'Solo no vistos',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Próximos ${count} no vistos',
|
||||
'downloads.customAmount' => 'Cantidad personalizada...',
|
||||
'downloads.howManyEpisodes' => '¿Cuántos episodios?',
|
||||
'shaders.title' => 'Shaders',
|
||||
'shaders.noShaderDescription' => 'Sin mejora de video',
|
||||
'shaders.nvscalerDescription' => 'Escalado de imagen NVIDIA para un video más nítido',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsFr implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Tout reprendre';
|
||||
@override String get deleteAll => 'Tout supprimer';
|
||||
@override String get selectVersion => 'Sélectionner la version';
|
||||
@override String get allEpisodes => 'Tous les épisodes';
|
||||
@override String get unwatchedOnly => 'Non vus uniquement';
|
||||
@override String nextNUnwatched({required Object count}) => '${count} prochains non vus';
|
||||
@override String get customAmount => 'Quantité personnalisée...';
|
||||
@override String get howManyEpisodes => 'Combien d\'épisodes ?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsFr {
|
||||
'downloads.resumeAll' => 'Tout reprendre',
|
||||
'downloads.deleteAll' => 'Tout supprimer',
|
||||
'downloads.selectVersion' => 'Sélectionner la version',
|
||||
'downloads.allEpisodes' => 'Tous les épisodes',
|
||||
'downloads.unwatchedOnly' => 'Non vus uniquement',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => '${count} prochains non vus',
|
||||
'downloads.customAmount' => 'Quantité personnalisée...',
|
||||
'downloads.howManyEpisodes' => 'Combien d\'épisodes ?',
|
||||
'shaders.title' => 'Shaders',
|
||||
'shaders.noShaderDescription' => 'Aucune amélioration vidéo',
|
||||
'shaders.nvscalerDescription' => 'Mise à l\'échelle NVIDIA pour une vidéo plus nette',
|
||||
|
||||
@@ -870,6 +870,11 @@ class _TranslationsDownloadsIt implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Riprendi tutto';
|
||||
@override String get deleteAll => 'Elimina tutto';
|
||||
@override String get selectVersion => 'Seleziona versione';
|
||||
@override String get allEpisodes => 'Tutti gli episodi';
|
||||
@override String get unwatchedOnly => 'Solo non visti';
|
||||
@override String nextNUnwatched({required Object count}) => 'Prossimi ${count} non visti';
|
||||
@override String get customAmount => 'Quantità personalizzata...';
|
||||
@override String get howManyEpisodes => 'Quanti episodi?';
|
||||
}
|
||||
|
||||
// Path: playlists
|
||||
@@ -1916,6 +1921,11 @@ extension on TranslationsIt {
|
||||
'downloads.resumeAll' => 'Riprendi tutto',
|
||||
'downloads.deleteAll' => 'Elimina tutto',
|
||||
'downloads.selectVersion' => 'Seleziona versione',
|
||||
'downloads.allEpisodes' => 'Tutti gli episodi',
|
||||
'downloads.unwatchedOnly' => 'Solo non visti',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Prossimi ${count} non visti',
|
||||
'downloads.customAmount' => 'Quantità personalizzata...',
|
||||
'downloads.howManyEpisodes' => 'Quanti episodi?',
|
||||
'playlists.title' => 'Playlist',
|
||||
'playlists.noPlaylists' => 'Nessuna playlist trovata',
|
||||
'playlists.create' => 'Crea playlist',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsJa implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'すべて再開';
|
||||
@override String get deleteAll => 'すべて削除';
|
||||
@override String get selectVersion => 'バージョンを選択';
|
||||
@override String get allEpisodes => 'すべてのエピソード';
|
||||
@override String get unwatchedOnly => '未視聴のみ';
|
||||
@override String nextNUnwatched({required Object count}) => '次の${count}件の未視聴';
|
||||
@override String get customAmount => '数を指定...';
|
||||
@override String get howManyEpisodes => '何エピソード?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsJa {
|
||||
'downloads.resumeAll' => 'すべて再開',
|
||||
'downloads.deleteAll' => 'すべて削除',
|
||||
'downloads.selectVersion' => 'バージョンを選択',
|
||||
'downloads.allEpisodes' => 'すべてのエピソード',
|
||||
'downloads.unwatchedOnly' => '未視聴のみ',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => '次の${count}件の未視聴',
|
||||
'downloads.customAmount' => '数を指定...',
|
||||
'downloads.howManyEpisodes' => '何エピソード?',
|
||||
'shaders.title' => 'シェーダー',
|
||||
'shaders.noShaderDescription' => '映像補正なし',
|
||||
'shaders.nvscalerDescription' => 'よりシャープな映像のためのNVIDIA画像スケーリング',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsKo implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => '모두 재개';
|
||||
@override String get deleteAll => '모두 삭제';
|
||||
@override String get selectVersion => '버전 선택';
|
||||
@override String get allEpisodes => '모든 에피소드';
|
||||
@override String get unwatchedOnly => '시청하지 않은 것만';
|
||||
@override String nextNUnwatched({required Object count}) => '다음 ${count}개 미시청';
|
||||
@override String get customAmount => '직접 입력...';
|
||||
@override String get howManyEpisodes => '몇 개의 에피소드?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsKo {
|
||||
'downloads.resumeAll' => '모두 재개',
|
||||
'downloads.deleteAll' => '모두 삭제',
|
||||
'downloads.selectVersion' => '버전 선택',
|
||||
'downloads.allEpisodes' => '모든 에피소드',
|
||||
'downloads.unwatchedOnly' => '시청하지 않은 것만',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => '다음 ${count}개 미시청',
|
||||
'downloads.customAmount' => '직접 입력...',
|
||||
'downloads.howManyEpisodes' => '몇 개의 에피소드?',
|
||||
'shaders.title' => '셰이더',
|
||||
'shaders.noShaderDescription' => '비디오 향상 없음',
|
||||
'shaders.nvscalerDescription' => '더 선명한 비디오를 위한 NVIDIA 이미지 스케일링',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsNb implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Gjenoppta alle';
|
||||
@override String get deleteAll => 'Slett alle';
|
||||
@override String get selectVersion => 'Velg versjon';
|
||||
@override String get allEpisodes => 'Alle episoder';
|
||||
@override String get unwatchedOnly => 'Kun usette';
|
||||
@override String nextNUnwatched({required Object count}) => 'Neste ${count} usette';
|
||||
@override String get customAmount => 'Egendefinert antall...';
|
||||
@override String get howManyEpisodes => 'Hvor mange episoder?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsNb {
|
||||
'downloads.resumeAll' => 'Gjenoppta alle',
|
||||
'downloads.deleteAll' => 'Slett alle',
|
||||
'downloads.selectVersion' => 'Velg versjon',
|
||||
'downloads.allEpisodes' => 'Alle episoder',
|
||||
'downloads.unwatchedOnly' => 'Kun usette',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Neste ${count} usette',
|
||||
'downloads.customAmount' => 'Egendefinert antall...',
|
||||
'downloads.howManyEpisodes' => 'Hvor mange episoder?',
|
||||
'shaders.title' => 'Shadere',
|
||||
'shaders.noShaderDescription' => 'Ingen videoforbedring',
|
||||
'shaders.nvscalerDescription' => 'NVIDIA bildeskalering for skarpere video',
|
||||
|
||||
@@ -870,6 +870,11 @@ class _TranslationsDownloadsNl implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Alles hervatten';
|
||||
@override String get deleteAll => 'Alles verwijderen';
|
||||
@override String get selectVersion => 'Versie selecteren';
|
||||
@override String get allEpisodes => 'Alle afleveringen';
|
||||
@override String get unwatchedOnly => 'Alleen onbekeken';
|
||||
@override String nextNUnwatched({required Object count}) => 'Volgende ${count} onbekeken';
|
||||
@override String get customAmount => 'Aangepast aantal...';
|
||||
@override String get howManyEpisodes => 'Hoeveel afleveringen?';
|
||||
}
|
||||
|
||||
// Path: playlists
|
||||
@@ -1916,6 +1921,11 @@ extension on TranslationsNl {
|
||||
'downloads.resumeAll' => 'Alles hervatten',
|
||||
'downloads.deleteAll' => 'Alles verwijderen',
|
||||
'downloads.selectVersion' => 'Versie selecteren',
|
||||
'downloads.allEpisodes' => 'Alle afleveringen',
|
||||
'downloads.unwatchedOnly' => 'Alleen onbekeken',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Volgende ${count} onbekeken',
|
||||
'downloads.customAmount' => 'Aangepast aantal...',
|
||||
'downloads.howManyEpisodes' => 'Hoeveel afleveringen?',
|
||||
'playlists.title' => 'Afspeellijsten',
|
||||
'playlists.noPlaylists' => 'Geen afspeellijsten gevonden',
|
||||
'playlists.create' => 'Afspeellijst maken',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsPl implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Wznów wszystko';
|
||||
@override String get deleteAll => 'Usuń wszystko';
|
||||
@override String get selectVersion => 'Wybierz wersję';
|
||||
@override String get allEpisodes => 'Wszystkie odcinki';
|
||||
@override String get unwatchedOnly => 'Tylko nieobejrzane';
|
||||
@override String nextNUnwatched({required Object count}) => 'Następne ${count} nieobejrzanych';
|
||||
@override String get customAmount => 'Własna ilość...';
|
||||
@override String get howManyEpisodes => 'Ile odcinków?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsPl {
|
||||
'downloads.resumeAll' => 'Wznów wszystko',
|
||||
'downloads.deleteAll' => 'Usuń wszystko',
|
||||
'downloads.selectVersion' => 'Wybierz wersję',
|
||||
'downloads.allEpisodes' => 'Wszystkie odcinki',
|
||||
'downloads.unwatchedOnly' => 'Tylko nieobejrzane',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Następne ${count} nieobejrzanych',
|
||||
'downloads.customAmount' => 'Własna ilość...',
|
||||
'downloads.howManyEpisodes' => 'Ile odcinków?',
|
||||
'shaders.title' => 'Shadery',
|
||||
'shaders.noShaderDescription' => 'Bez ulepszenia wideo',
|
||||
'shaders.nvscalerDescription' => 'Skalowanie obrazu NVIDIA dla ostrzejszego wideo',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsPt implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Retomar todos';
|
||||
@override String get deleteAll => 'Excluir todos';
|
||||
@override String get selectVersion => 'Selecionar versão';
|
||||
@override String get allEpisodes => 'Todos os episódios';
|
||||
@override String get unwatchedOnly => 'Apenas não assistidos';
|
||||
@override String nextNUnwatched({required Object count}) => 'Próximos ${count} não assistidos';
|
||||
@override String get customAmount => 'Quantidade personalizada...';
|
||||
@override String get howManyEpisodes => 'Quantos episódios?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsPt {
|
||||
'downloads.resumeAll' => 'Retomar todos',
|
||||
'downloads.deleteAll' => 'Excluir todos',
|
||||
'downloads.selectVersion' => 'Selecionar versão',
|
||||
'downloads.allEpisodes' => 'Todos os episódios',
|
||||
'downloads.unwatchedOnly' => 'Apenas não assistidos',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Próximos ${count} não assistidos',
|
||||
'downloads.customAmount' => 'Quantidade personalizada...',
|
||||
'downloads.howManyEpisodes' => 'Quantos episódios?',
|
||||
'shaders.title' => 'Shaders',
|
||||
'shaders.noShaderDescription' => 'Sem aprimoramento de vídeo',
|
||||
'shaders.nvscalerDescription' => 'Escalonamento de imagem NVIDIA para vídeo mais nítido',
|
||||
|
||||
@@ -994,6 +994,11 @@ class _TranslationsDownloadsRu implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Возобновить все';
|
||||
@override String get deleteAll => 'Удалить все';
|
||||
@override String get selectVersion => 'Выбрать версию';
|
||||
@override String get allEpisodes => 'Все эпизоды';
|
||||
@override String get unwatchedOnly => 'Только непросмотренные';
|
||||
@override String nextNUnwatched({required Object count}) => 'Следующие ${count} непросмотренных';
|
||||
@override String get customAmount => 'Указать количество...';
|
||||
@override String get howManyEpisodes => 'Сколько эпизодов?';
|
||||
}
|
||||
|
||||
// Path: shaders
|
||||
@@ -2013,6 +2018,11 @@ extension on TranslationsRu {
|
||||
'downloads.resumeAll' => 'Возобновить все',
|
||||
'downloads.deleteAll' => 'Удалить все',
|
||||
'downloads.selectVersion' => 'Выбрать версию',
|
||||
'downloads.allEpisodes' => 'Все эпизоды',
|
||||
'downloads.unwatchedOnly' => 'Только непросмотренные',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Следующие ${count} непросмотренных',
|
||||
'downloads.customAmount' => 'Указать количество...',
|
||||
'downloads.howManyEpisodes' => 'Сколько эпизодов?',
|
||||
'shaders.title' => 'Шейдеры',
|
||||
'shaders.noShaderDescription' => 'Без улучшения видео',
|
||||
'shaders.nvscalerDescription' => 'Масштабирование NVIDIA для более чёткого видео',
|
||||
|
||||
@@ -870,6 +870,11 @@ class _TranslationsDownloadsSv implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => 'Återuppta alla';
|
||||
@override String get deleteAll => 'Ta bort alla';
|
||||
@override String get selectVersion => 'Välj version';
|
||||
@override String get allEpisodes => 'Alla avsnitt';
|
||||
@override String get unwatchedOnly => 'Endast osedda';
|
||||
@override String nextNUnwatched({required Object count}) => 'Nästa ${count} osedda';
|
||||
@override String get customAmount => 'Ange antal...';
|
||||
@override String get howManyEpisodes => 'Hur många avsnitt?';
|
||||
}
|
||||
|
||||
// Path: playlists
|
||||
@@ -1916,6 +1921,11 @@ extension on TranslationsSv {
|
||||
'downloads.resumeAll' => 'Återuppta alla',
|
||||
'downloads.deleteAll' => 'Ta bort alla',
|
||||
'downloads.selectVersion' => 'Välj version',
|
||||
'downloads.allEpisodes' => 'Alla avsnitt',
|
||||
'downloads.unwatchedOnly' => 'Endast osedda',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => 'Nästa ${count} osedda',
|
||||
'downloads.customAmount' => 'Ange antal...',
|
||||
'downloads.howManyEpisodes' => 'Hur många avsnitt?',
|
||||
'playlists.title' => 'Spellistor',
|
||||
'playlists.noPlaylists' => 'Inga spellistor hittades',
|
||||
'playlists.create' => 'Skapa spellista',
|
||||
|
||||
@@ -870,6 +870,11 @@ class _TranslationsDownloadsZh implements TranslationsDownloadsEn {
|
||||
@override String get resumeAll => '全部继续';
|
||||
@override String get deleteAll => '全部删除';
|
||||
@override String get selectVersion => '选择版本';
|
||||
@override String get allEpisodes => '所有剧集';
|
||||
@override String get unwatchedOnly => '仅未观看';
|
||||
@override String nextNUnwatched({required Object count}) => '接下来 ${count} 集未观看';
|
||||
@override String get customAmount => '自定义数量...';
|
||||
@override String get howManyEpisodes => '下载几集?';
|
||||
}
|
||||
|
||||
// Path: playlists
|
||||
@@ -1916,6 +1921,11 @@ extension on TranslationsZh {
|
||||
'downloads.resumeAll' => '全部继续',
|
||||
'downloads.deleteAll' => '全部删除',
|
||||
'downloads.selectVersion' => '选择版本',
|
||||
'downloads.allEpisodes' => '所有剧集',
|
||||
'downloads.unwatchedOnly' => '仅未观看',
|
||||
'downloads.nextNUnwatched' => ({required Object count}) => '接下来 ${count} 集未观看',
|
||||
'downloads.customAmount' => '自定义数量...',
|
||||
'downloads.howManyEpisodes' => '下载几集?',
|
||||
'playlists.title' => '播放列表',
|
||||
'playlists.noPlaylists' => '未找到播放列表',
|
||||
'playlists.create' => '创建播放列表',
|
||||
|
||||
@@ -630,7 +630,12 @@
|
||||
"pauseAll": "Pausa alla",
|
||||
"resumeAll": "Återuppta alla",
|
||||
"deleteAll": "Ta bort alla",
|
||||
"selectVersion": "Välj version"
|
||||
"selectVersion": "Välj version",
|
||||
"allEpisodes": "Alla avsnitt",
|
||||
"unwatchedOnly": "Endast osedda",
|
||||
"nextNUnwatched": "Nästa ${count} osedda",
|
||||
"customAmount": "Ange antal...",
|
||||
"howManyEpisodes": "Hur många avsnitt?"
|
||||
},
|
||||
"playlists": {
|
||||
"title": "Spellistor",
|
||||
|
||||
@@ -630,7 +630,12 @@
|
||||
"pauseAll": "全部暂停",
|
||||
"resumeAll": "全部继续",
|
||||
"deleteAll": "全部删除",
|
||||
"selectVersion": "选择版本"
|
||||
"selectVersion": "选择版本",
|
||||
"allEpisodes": "所有剧集",
|
||||
"unwatchedOnly": "仅未观看",
|
||||
"nextNUnwatched": "接下来 ${count} 集未观看",
|
||||
"customAmount": "自定义数量...",
|
||||
"howManyEpisodes": "下载几集?"
|
||||
},
|
||||
"playlists": {
|
||||
"title": "播放列表",
|
||||
|
||||
@@ -15,6 +15,10 @@ import '../services/plex_client.dart';
|
||||
import '../utils/app_logger.dart';
|
||||
import '../utils/global_key_utils.dart';
|
||||
|
||||
/// Filter mode for batch downloads (shows/seasons).
|
||||
/// Use [all] to download everything, or [unwatched] with an optional maxCount.
|
||||
enum DownloadFilter { all, unwatched }
|
||||
|
||||
/// Holds Plex thumb path reference for downloaded artwork.
|
||||
/// The actual file path is computed from the hash of serverId + thumb path.
|
||||
class DownloadedArtwork {
|
||||
@@ -599,6 +603,8 @@ class DownloadProvider extends ChangeNotifier {
|
||||
PlexMetadata metadata,
|
||||
PlexClient client, {
|
||||
DownloadVersionConfig? versionConfig,
|
||||
DownloadFilter filter = DownloadFilter.all,
|
||||
int? maxCount,
|
||||
}) async {
|
||||
final globalKey = metadata.globalKey;
|
||||
final config = versionConfig ?? DownloadVersionConfig();
|
||||
@@ -620,10 +626,10 @@ class DownloadProvider extends ChangeNotifier {
|
||||
return queued ? 1 : 0;
|
||||
} else if (mt == PlexMediaType.show) {
|
||||
_metadata[globalKey] = metadata;
|
||||
return await _queueShowDownload(metadata, client, versionConfig: config);
|
||||
return await _queueShowDownload(metadata, client, versionConfig: config, filter: filter, maxCount: maxCount);
|
||||
} else if (mt == PlexMediaType.season) {
|
||||
_metadata[globalKey] = metadata;
|
||||
return await _queueSeasonDownload(metadata, client, versionConfig: config);
|
||||
return await _queueSeasonDownload(metadata, client, versionConfig: config, filter: filter, maxCount: maxCount);
|
||||
} else {
|
||||
throw Exception('Cannot download ${metadata.type}');
|
||||
}
|
||||
@@ -750,16 +756,22 @@ class DownloadProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
/// Queue all episodes from a TV show for download
|
||||
Future<int> _queueShowDownload(PlexMetadata show, PlexClient client, {DownloadVersionConfig? versionConfig}) async {
|
||||
Future<int> _queueShowDownload(PlexMetadata show, PlexClient client,
|
||||
{DownloadVersionConfig? versionConfig, DownloadFilter filter = DownloadFilter.all, int? maxCount}) async {
|
||||
int count = 0;
|
||||
final seasons = await client.getChildren(show.ratingKey);
|
||||
|
||||
await _storeLeafCount(show.globalKey, show);
|
||||
|
||||
int? remaining = maxCount;
|
||||
for (final season in seasons) {
|
||||
if (season.type == 'season') {
|
||||
if (remaining != null && remaining <= 0) break;
|
||||
final seasonWithServer = _ensureServerId(season, show.serverId);
|
||||
count += await _queueSeasonDownload(seasonWithServer, client, versionConfig: versionConfig);
|
||||
final queued = await _queueSeasonDownload(seasonWithServer, client,
|
||||
versionConfig: versionConfig, filter: filter, maxCount: remaining);
|
||||
count += queued;
|
||||
if (remaining != null) remaining -= queued;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,7 +780,7 @@ class DownloadProvider extends ChangeNotifier {
|
||||
|
||||
/// Queue all episodes from a season for download
|
||||
Future<int> _queueSeasonDownload(PlexMetadata season, PlexClient client,
|
||||
{DownloadVersionConfig? versionConfig}) async {
|
||||
{DownloadVersionConfig? versionConfig, DownloadFilter filter = DownloadFilter.all, int? maxCount}) async {
|
||||
int count = 0;
|
||||
final episodes = await client.getChildren(season.ratingKey);
|
||||
|
||||
@@ -776,6 +788,9 @@ class DownloadProvider extends ChangeNotifier {
|
||||
|
||||
for (final episode in episodes) {
|
||||
if (episode.type == 'episode') {
|
||||
if (filter == DownloadFilter.unwatched && episode.isWatched && !episode.hasActiveProgress) continue;
|
||||
if (maxCount != null && count >= maxCount) break;
|
||||
|
||||
final episodeWithServer = _ensureServerId(episode, season.serverId);
|
||||
final queued = await _queueSingleDownload(episodeWithServer, client, versionConfig: versionConfig);
|
||||
if (queued) count++;
|
||||
|
||||
@@ -30,6 +30,7 @@ import '../utils/rating_utils.dart';
|
||||
import '../models/download_models.dart';
|
||||
import '../services/download_storage_service.dart';
|
||||
import '../utils/download_version_utils.dart';
|
||||
import '../utils/download_utils.dart';
|
||||
import '../providers/playback_state_provider.dart';
|
||||
import '../providers/settings_provider.dart';
|
||||
import '../utils/grid_size_calculator.dart';
|
||||
@@ -777,17 +778,19 @@ class _MediaDetailScreenState extends State<MediaDetailScreen>
|
||||
final client = _getClientForMetadata(context);
|
||||
if (client == null) return;
|
||||
|
||||
final versionConfig = await _resolveDownloadVersion(context, metadata, client);
|
||||
if (versionConfig == null || !context.mounted) return;
|
||||
|
||||
try {
|
||||
final count = await downloadProvider.queueDownload(metadata, client, versionConfig: versionConfig);
|
||||
if (context.mounted) {
|
||||
final message = count > 1
|
||||
? t.downloads.episodesQueued(count: count)
|
||||
: t.downloads.downloadQueued;
|
||||
showSuccessSnackBar(context, message);
|
||||
}
|
||||
final count = await showDownloadOptionsAndQueue(
|
||||
context,
|
||||
metadata: metadata,
|
||||
client: client,
|
||||
downloadProvider: downloadProvider,
|
||||
);
|
||||
if (count == null || !context.mounted) return;
|
||||
|
||||
final message = count > 1
|
||||
? t.downloads.episodesQueued(count: count)
|
||||
: t.downloads.downloadQueued;
|
||||
showSuccessSnackBar(context, message);
|
||||
} on CellularDownloadBlockedException {
|
||||
if (context.mounted) {
|
||||
showErrorSnackBar(context, t.settings.cellularDownloadBlocked);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../focus/focusable_button.dart';
|
||||
import '../focus/input_mode_tracker.dart';
|
||||
import '../i18n/strings.g.dart';
|
||||
import '../widgets/app_icon.dart';
|
||||
import '../widgets/focusable_list_tile.dart';
|
||||
@@ -281,17 +282,19 @@ Future<T?> showOptionPickerDialog<T>(
|
||||
required String title,
|
||||
required List<({IconData icon, String label, T value})> options,
|
||||
}) {
|
||||
final focusFirstItem = InputModeTracker.isKeyboardMode(context);
|
||||
return showDialog<T>(
|
||||
context: context,
|
||||
builder: (context) => _OptionPickerDialog<T>(title: title, options: options),
|
||||
builder: (context) => _OptionPickerDialog<T>(title: title, options: options, focusFirstItem: focusFirstItem),
|
||||
);
|
||||
}
|
||||
|
||||
class _OptionPickerDialog<T> extends StatefulWidget {
|
||||
final String title;
|
||||
final List<({IconData icon, String label, T value})> options;
|
||||
final bool focusFirstItem;
|
||||
|
||||
const _OptionPickerDialog({required this.title, required this.options});
|
||||
const _OptionPickerDialog({required this.title, required this.options, this.focusFirstItem = false});
|
||||
|
||||
@override
|
||||
State<_OptionPickerDialog<T>> createState() => _OptionPickerDialogState<T>();
|
||||
@@ -304,7 +307,9 @@ class _OptionPickerDialogState<T> extends State<_OptionPickerDialog<T>> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initialFocusNode = FocusNode(debugLabel: 'OptionPickerInitialFocus');
|
||||
FocusUtils.requestFocusAfterBuild(this, _initialFocusNode);
|
||||
if (widget.focusFirstItem) {
|
||||
FocusUtils.requestFocusAfterBuild(this, _initialFocusNode);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -321,7 +326,7 @@ class _OptionPickerDialogState<T> extends State<_OptionPickerDialog<T>> {
|
||||
children: List.generate(widget.options.length, (index) {
|
||||
final option = widget.options[index];
|
||||
return FocusableListTile(
|
||||
focusNode: index == 0 ? _initialFocusNode : null,
|
||||
focusNode: index == 0 && widget.focusFirstItem ? _initialFocusNode : null,
|
||||
leading: AppIcon(option.icon, fill: 1, size: 24),
|
||||
title: Text(option.label, style: Theme.of(context).textTheme.bodyLarge),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 4),
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import '../focus/focusable_button.dart';
|
||||
import '../focus/input_mode_tracker.dart';
|
||||
import '../i18n/strings.g.dart';
|
||||
import '../models/plex_metadata.dart';
|
||||
import '../providers/download_provider.dart';
|
||||
import '../services/plex_client.dart';
|
||||
import 'dialogs.dart';
|
||||
import 'download_version_utils.dart';
|
||||
|
||||
/// Dialog option for the download picker. Typed to avoid stringly-typed values.
|
||||
enum _DownloadChoice { all, unwatched, next5, next10, custom }
|
||||
|
||||
/// Shows download options dialog for shows/seasons, then queues the download.
|
||||
/// For movies/episodes, queues directly without a dialog.
|
||||
/// Returns the number of items queued, or null if cancelled.
|
||||
Future<int?> showDownloadOptionsAndQueue(
|
||||
BuildContext context, {
|
||||
required PlexMetadata metadata,
|
||||
required PlexClient client,
|
||||
required DownloadProvider downloadProvider,
|
||||
}) async {
|
||||
final mt = metadata.mediaType;
|
||||
|
||||
var filter = DownloadFilter.all;
|
||||
int? maxCount;
|
||||
|
||||
if (mt == PlexMediaType.show || mt == PlexMediaType.season) {
|
||||
final selected = await showOptionPickerDialog<_DownloadChoice>(
|
||||
context,
|
||||
title: t.downloads.downloadNow,
|
||||
options: [
|
||||
(icon: Symbols.download_rounded, label: t.downloads.allEpisodes, value: _DownloadChoice.all),
|
||||
(icon: Symbols.visibility_off_rounded, label: t.downloads.unwatchedOnly, value: _DownloadChoice.unwatched),
|
||||
(icon: Symbols.filter_5_rounded, label: t.downloads.nextNUnwatched(count: 5), value: _DownloadChoice.next5),
|
||||
(icon: Symbols.filter_9_plus_rounded, label: t.downloads.nextNUnwatched(count: 10), value: _DownloadChoice.next10),
|
||||
(icon: Symbols.tune_rounded, label: t.downloads.customAmount, value: _DownloadChoice.custom),
|
||||
],
|
||||
);
|
||||
|
||||
if (selected == null || !context.mounted) return null;
|
||||
|
||||
switch (selected) {
|
||||
case _DownloadChoice.all:
|
||||
break;
|
||||
case _DownloadChoice.unwatched:
|
||||
filter = DownloadFilter.unwatched;
|
||||
case _DownloadChoice.next5:
|
||||
filter = DownloadFilter.unwatched;
|
||||
maxCount = 5;
|
||||
case _DownloadChoice.next10:
|
||||
filter = DownloadFilter.unwatched;
|
||||
maxCount = 10;
|
||||
case _DownloadChoice.custom:
|
||||
final count = await _showEpisodeCountDialog(context);
|
||||
if (count == null || !context.mounted) return null;
|
||||
filter = DownloadFilter.unwatched;
|
||||
maxCount = count;
|
||||
}
|
||||
}
|
||||
|
||||
if (!context.mounted) return null;
|
||||
|
||||
final versionConfig = await resolveDownloadVersion(context, metadata, client);
|
||||
if (versionConfig == null || !context.mounted) return null;
|
||||
|
||||
return await downloadProvider.queueDownload(
|
||||
metadata,
|
||||
client,
|
||||
versionConfig: versionConfig,
|
||||
filter: filter,
|
||||
maxCount: maxCount,
|
||||
);
|
||||
}
|
||||
|
||||
Future<int?> _showEpisodeCountDialog(BuildContext context) {
|
||||
final autoFocus = InputModeTracker.isKeyboardMode(context);
|
||||
return showDialog<int>(
|
||||
context: context,
|
||||
builder: (context) => _EpisodeCountDialog(autoFocus: autoFocus),
|
||||
);
|
||||
}
|
||||
|
||||
class _EpisodeCountDialog extends StatefulWidget {
|
||||
final bool autoFocus;
|
||||
const _EpisodeCountDialog({this.autoFocus = false});
|
||||
|
||||
@override
|
||||
State<_EpisodeCountDialog> createState() => _EpisodeCountDialogState();
|
||||
}
|
||||
|
||||
class _EpisodeCountDialogState extends State<_EpisodeCountDialog> {
|
||||
late final TextEditingController _controller;
|
||||
final _submitFocusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_submitFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _submit() {
|
||||
final count = int.tryParse(_controller.text);
|
||||
if (count != null && count > 0) {
|
||||
Navigator.pop(context, count);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(t.downloads.howManyEpisodes),
|
||||
content: TextField(
|
||||
controller: _controller,
|
||||
autofocus: widget.autoFocus,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
textInputAction: TextInputAction.done,
|
||||
onSubmitted: (_) => _submitFocusNode.requestFocus(),
|
||||
),
|
||||
actions: [
|
||||
FocusableButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: TextButton(onPressed: () => Navigator.pop(context), child: Text(t.common.cancel)),
|
||||
),
|
||||
FocusableButton(
|
||||
focusNode: _submitFocusNode,
|
||||
onPressed: _submit,
|
||||
child: TextButton(onPressed: _submit, child: Text(t.common.ok)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import '../services/play_queue_launcher.dart';
|
||||
import '../models/plex_metadata.dart';
|
||||
import '../models/plex_playlist.dart';
|
||||
import '../utils/download_version_utils.dart';
|
||||
import '../utils/download_utils.dart';
|
||||
import '../utils/content_utils.dart';
|
||||
import '../providers/download_provider.dart';
|
||||
import '../providers/multi_server_provider.dart';
|
||||
@@ -1115,15 +1116,16 @@ class MediaContextMenuState extends State<MediaContextMenu> {
|
||||
final client = _getClientForItem();
|
||||
|
||||
try {
|
||||
final versionConfig = await resolveDownloadVersion(context, metadata, client);
|
||||
if (versionConfig == null) return;
|
||||
if (!context.mounted) return;
|
||||
final count = await showDownloadOptionsAndQueue(
|
||||
context,
|
||||
metadata: metadata,
|
||||
client: client,
|
||||
downloadProvider: downloadProvider,
|
||||
);
|
||||
if (count == null || !context.mounted) return;
|
||||
|
||||
final count = await downloadProvider.queueDownload(metadata, client, versionConfig: versionConfig);
|
||||
if (context.mounted) {
|
||||
final message = count > 1 ? t.downloads.episodesQueued(count: count) : t.downloads.downloadQueued;
|
||||
showSuccessSnackBar(context, message);
|
||||
}
|
||||
final message = count > 1 ? t.downloads.episodesQueued(count: count) : t.downloads.downloadQueued;
|
||||
showSuccessSnackBar(context, message);
|
||||
} on CellularDownloadBlockedException {
|
||||
if (context.mounted) {
|
||||
showErrorSnackBar(context, t.settings.cellularDownloadBlocked);
|
||||
|
||||
Reference in New Issue
Block a user