diff --git a/lib/i18n/da.i18n.json b/lib/i18n/da.i18n.json index b11d7bc9..bd7c2982 100644 --- a/lib/i18n/da.i18n.json +++ b/lib/i18n/da.i18n.json @@ -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", diff --git a/lib/i18n/de.i18n.json b/lib/i18n/de.i18n.json index c9d9424d..1add250f 100644 --- a/lib/i18n/de.i18n.json +++ b/lib/i18n/de.i18n.json @@ -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", diff --git a/lib/i18n/en.i18n.json b/lib/i18n/en.i18n.json index de12b94f..b84915da 100644 --- a/lib/i18n/en.i18n.json +++ b/lib/i18n/en.i18n.json @@ -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", diff --git a/lib/i18n/es.i18n.json b/lib/i18n/es.i18n.json index 807f8f74..a58b8bad 100644 --- a/lib/i18n/es.i18n.json +++ b/lib/i18n/es.i18n.json @@ -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", diff --git a/lib/i18n/fr.i18n.json b/lib/i18n/fr.i18n.json index 4be3d7ce..ea5cba42 100644 --- a/lib/i18n/fr.i18n.json +++ b/lib/i18n/fr.i18n.json @@ -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", diff --git a/lib/i18n/it.i18n.json b/lib/i18n/it.i18n.json index f2f02914..5e0754a8 100644 --- a/lib/i18n/it.i18n.json +++ b/lib/i18n/it.i18n.json @@ -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", diff --git a/lib/i18n/ja.i18n.json b/lib/i18n/ja.i18n.json index 03328907..247e8d2d 100644 --- a/lib/i18n/ja.i18n.json +++ b/lib/i18n/ja.i18n.json @@ -733,7 +733,12 @@ "pauseAll": "すべて一時停止", "resumeAll": "すべて再開", "deleteAll": "すべて削除", - "selectVersion": "バージョンを選択" + "selectVersion": "バージョンを選択", + "allEpisodes": "すべてのエピソード", + "unwatchedOnly": "未視聴のみ", + "nextNUnwatched": "次の${count}件の未視聴", + "customAmount": "数を指定...", + "howManyEpisodes": "何エピソード?" }, "shaders": { "title": "シェーダー", diff --git a/lib/i18n/ko.i18n.json b/lib/i18n/ko.i18n.json index f710915d..148812a9 100644 --- a/lib/i18n/ko.i18n.json +++ b/lib/i18n/ko.i18n.json @@ -733,7 +733,12 @@ "pauseAll": "모두 일시정지", "resumeAll": "모두 재개", "deleteAll": "모두 삭제", - "selectVersion": "버전 선택" + "selectVersion": "버전 선택", + "allEpisodes": "모든 에피소드", + "unwatchedOnly": "시청하지 않은 것만", + "nextNUnwatched": "다음 ${count}개 미시청", + "customAmount": "직접 입력...", + "howManyEpisodes": "몇 개의 에피소드?" }, "shaders": { "title": "셰이더", diff --git a/lib/i18n/nb.i18n.json b/lib/i18n/nb.i18n.json index 61804a8a..d2af43e1 100644 --- a/lib/i18n/nb.i18n.json +++ b/lib/i18n/nb.i18n.json @@ -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", diff --git a/lib/i18n/nl.i18n.json b/lib/i18n/nl.i18n.json index 71703bbd..0935bbd7 100644 --- a/lib/i18n/nl.i18n.json +++ b/lib/i18n/nl.i18n.json @@ -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", diff --git a/lib/i18n/pl.i18n.json b/lib/i18n/pl.i18n.json index 99ecb4e4..5f0cbfeb 100644 --- a/lib/i18n/pl.i18n.json +++ b/lib/i18n/pl.i18n.json @@ -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", diff --git a/lib/i18n/pt.i18n.json b/lib/i18n/pt.i18n.json index 2757acba..c1a0f447 100644 --- a/lib/i18n/pt.i18n.json +++ b/lib/i18n/pt.i18n.json @@ -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", diff --git a/lib/i18n/ru.i18n.json b/lib/i18n/ru.i18n.json index 089cf85a..c412d7f5 100644 --- a/lib/i18n/ru.i18n.json +++ b/lib/i18n/ru.i18n.json @@ -733,7 +733,12 @@ "pauseAll": "Приостановить все", "resumeAll": "Возобновить все", "deleteAll": "Удалить все", - "selectVersion": "Выбрать версию" + "selectVersion": "Выбрать версию", + "allEpisodes": "Все эпизоды", + "unwatchedOnly": "Только непросмотренные", + "nextNUnwatched": "Следующие ${count} непросмотренных", + "customAmount": "Указать количество...", + "howManyEpisodes": "Сколько эпизодов?" }, "shaders": { "title": "Шейдеры", diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 5e941b37..48f0bb6b 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -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 diff --git a/lib/i18n/strings_da.g.dart b/lib/i18n/strings_da.g.dart index 8674da5d..ddf2aa0f 100644 --- a/lib/i18n/strings_da.g.dart +++ b/lib/i18n/strings_da.g.dart @@ -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', diff --git a/lib/i18n/strings_de.g.dart b/lib/i18n/strings_de.g.dart index 906fe60b..cc832305 100644 --- a/lib/i18n/strings_de.g.dart +++ b/lib/i18n/strings_de.g.dart @@ -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', diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index 95de39fa..b423aa30 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -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', diff --git a/lib/i18n/strings_es.g.dart b/lib/i18n/strings_es.g.dart index bc4aa83c..f63fe6eb 100644 --- a/lib/i18n/strings_es.g.dart +++ b/lib/i18n/strings_es.g.dart @@ -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', diff --git a/lib/i18n/strings_fr.g.dart b/lib/i18n/strings_fr.g.dart index 5c575e94..75b93f94 100644 --- a/lib/i18n/strings_fr.g.dart +++ b/lib/i18n/strings_fr.g.dart @@ -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', diff --git a/lib/i18n/strings_it.g.dart b/lib/i18n/strings_it.g.dart index fabe3483..60b7924e 100644 --- a/lib/i18n/strings_it.g.dart +++ b/lib/i18n/strings_it.g.dart @@ -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', diff --git a/lib/i18n/strings_ja.g.dart b/lib/i18n/strings_ja.g.dart index 641e04aa..84981f11 100644 --- a/lib/i18n/strings_ja.g.dart +++ b/lib/i18n/strings_ja.g.dart @@ -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画像スケーリング', diff --git a/lib/i18n/strings_ko.g.dart b/lib/i18n/strings_ko.g.dart index 1b4a1674..e0a90301 100644 --- a/lib/i18n/strings_ko.g.dart +++ b/lib/i18n/strings_ko.g.dart @@ -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 이미지 스케일링', diff --git a/lib/i18n/strings_nb.g.dart b/lib/i18n/strings_nb.g.dart index e04f127d..510e5326 100644 --- a/lib/i18n/strings_nb.g.dart +++ b/lib/i18n/strings_nb.g.dart @@ -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', diff --git a/lib/i18n/strings_nl.g.dart b/lib/i18n/strings_nl.g.dart index 710b1cdf..4ddc24d5 100644 --- a/lib/i18n/strings_nl.g.dart +++ b/lib/i18n/strings_nl.g.dart @@ -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', diff --git a/lib/i18n/strings_pl.g.dart b/lib/i18n/strings_pl.g.dart index a44d43ae..cade86a0 100644 --- a/lib/i18n/strings_pl.g.dart +++ b/lib/i18n/strings_pl.g.dart @@ -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', diff --git a/lib/i18n/strings_pt.g.dart b/lib/i18n/strings_pt.g.dart index b50fe32f..c4f0f9b6 100644 --- a/lib/i18n/strings_pt.g.dart +++ b/lib/i18n/strings_pt.g.dart @@ -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', diff --git a/lib/i18n/strings_ru.g.dart b/lib/i18n/strings_ru.g.dart index 86f8e6f6..de4f70e6 100644 --- a/lib/i18n/strings_ru.g.dart +++ b/lib/i18n/strings_ru.g.dart @@ -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 для более чёткого видео', diff --git a/lib/i18n/strings_sv.g.dart b/lib/i18n/strings_sv.g.dart index 38b4cdf4..f8b8e211 100644 --- a/lib/i18n/strings_sv.g.dart +++ b/lib/i18n/strings_sv.g.dart @@ -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', diff --git a/lib/i18n/strings_zh.g.dart b/lib/i18n/strings_zh.g.dart index 26faa419..740d151f 100644 --- a/lib/i18n/strings_zh.g.dart +++ b/lib/i18n/strings_zh.g.dart @@ -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' => '创建播放列表', diff --git a/lib/i18n/sv.i18n.json b/lib/i18n/sv.i18n.json index 80bd21a7..e8822954 100644 --- a/lib/i18n/sv.i18n.json +++ b/lib/i18n/sv.i18n.json @@ -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", diff --git a/lib/i18n/zh.i18n.json b/lib/i18n/zh.i18n.json index ca4c2972..18cff86a 100644 --- a/lib/i18n/zh.i18n.json +++ b/lib/i18n/zh.i18n.json @@ -630,7 +630,12 @@ "pauseAll": "全部暂停", "resumeAll": "全部继续", "deleteAll": "全部删除", - "selectVersion": "选择版本" + "selectVersion": "选择版本", + "allEpisodes": "所有剧集", + "unwatchedOnly": "仅未观看", + "nextNUnwatched": "接下来 ${count} 集未观看", + "customAmount": "自定义数量...", + "howManyEpisodes": "下载几集?" }, "playlists": { "title": "播放列表", diff --git a/lib/providers/download_provider.dart b/lib/providers/download_provider.dart index fb9229d2..7f9ea009 100644 --- a/lib/providers/download_provider.dart +++ b/lib/providers/download_provider.dart @@ -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 _queueShowDownload(PlexMetadata show, PlexClient client, {DownloadVersionConfig? versionConfig}) async { + Future _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 _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++; diff --git a/lib/screens/media_detail_screen.dart b/lib/screens/media_detail_screen.dart index 238ad5c1..9f546d4d 100644 --- a/lib/screens/media_detail_screen.dart +++ b/lib/screens/media_detail_screen.dart @@ -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 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); diff --git a/lib/utils/dialogs.dart b/lib/utils/dialogs.dart index 919be326..5097e1dc 100644 --- a/lib/utils/dialogs.dart +++ b/lib/utils/dialogs.dart @@ -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 showOptionPickerDialog( required String title, required List<({IconData icon, String label, T value})> options, }) { + final focusFirstItem = InputModeTracker.isKeyboardMode(context); return showDialog( context: context, - builder: (context) => _OptionPickerDialog(title: title, options: options), + builder: (context) => _OptionPickerDialog(title: title, options: options, focusFirstItem: focusFirstItem), ); } class _OptionPickerDialog 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> createState() => _OptionPickerDialogState(); @@ -304,7 +307,9 @@ class _OptionPickerDialogState extends State<_OptionPickerDialog> { 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 extends State<_OptionPickerDialog> { 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), diff --git a/lib/utils/download_utils.dart b/lib/utils/download_utils.dart new file mode 100644 index 00000000..87a1463c --- /dev/null +++ b/lib/utils/download_utils.dart @@ -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 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 _showEpisodeCountDialog(BuildContext context) { + final autoFocus = InputModeTracker.isKeyboardMode(context); + return showDialog( + 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)), + ), + ], + ); + } +} diff --git a/lib/widgets/media_context_menu.dart b/lib/widgets/media_context_menu.dart index d39877c0..a080bba6 100644 --- a/lib/widgets/media_context_menu.dart +++ b/lib/widgets/media_context_menu.dart @@ -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 { 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);