From bc4b4955da0a43a41e1f91c02d06caa56cedfc54 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:45:24 +0200 Subject: [PATCH] feat: auto-remove watched downloads, server watched threshold close #856 --- lib/i18n/da.i18n.json | 3 ++ lib/i18n/de.i18n.json | 3 ++ lib/i18n/en.i18n.json | 3 ++ lib/i18n/es.i18n.json | 3 ++ lib/i18n/fr.i18n.json | 3 ++ lib/i18n/it.i18n.json | 3 ++ lib/i18n/ja.i18n.json | 3 ++ lib/i18n/ko.i18n.json | 3 ++ lib/i18n/nb.i18n.json | 3 ++ lib/i18n/nl.i18n.json | 3 ++ lib/i18n/pl.i18n.json | 3 ++ lib/i18n/pt.i18n.json | 3 ++ lib/i18n/ru.i18n.json | 3 ++ lib/i18n/strings.g.dart | 4 +- lib/i18n/strings_da.g.dart | 10 ++++- lib/i18n/strings_de.g.dart | 10 ++++- lib/i18n/strings_en.g.dart | 16 +++++++- lib/i18n/strings_es.g.dart | 10 ++++- lib/i18n/strings_fr.g.dart | 10 ++++- lib/i18n/strings_it.g.dart | 10 ++++- lib/i18n/strings_ja.g.dart | 10 ++++- lib/i18n/strings_ko.g.dart | 10 ++++- lib/i18n/strings_nb.g.dart | 10 ++++- lib/i18n/strings_nl.g.dart | 10 ++++- lib/i18n/strings_pl.g.dart | 10 ++++- lib/i18n/strings_pt.g.dart | 10 ++++- lib/i18n/strings_ru.g.dart | 10 ++++- lib/i18n/strings_sv.g.dart | 10 ++++- lib/i18n/strings_zh.g.dart | 10 ++++- lib/i18n/sv.i18n.json | 3 ++ lib/i18n/zh.i18n.json | 3 ++ lib/main.dart | 14 ++++++- lib/providers/download_provider.dart | 29 ++++++++++++++ lib/providers/offline_watch_provider.dart | 28 +++++++++++++ lib/screens/settings/settings_screen.dart | 14 +++++++ lib/services/multi_server_manager.dart | 7 ++++ lib/services/offline_watch_sync_service.dart | 28 +++++++++---- lib/services/plex_client.dart | 41 +++++++++++++++++--- lib/services/settings_service.dart | 20 ++++++++++ 39 files changed, 339 insertions(+), 47 deletions(-) diff --git a/lib/i18n/da.i18n.json b/lib/i18n/da.i18n.json index cd966c65..fef656a3 100644 --- a/lib/i18n/da.i18n.json +++ b/lib/i18n/da.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Kunne ikke vælge mappe", "downloadOnWifiOnly": "Download kun på WiFi", "downloadOnWifiOnlyDescription": "Forhindre downloads på mobildata", + "autoRemoveWatchedDownloads": "Fjern sete downloads automatisk", + "autoRemoveWatchedDownloadsDescription": "Slet automatisk downloadede episoder og film, når de markeres som set", "cellularDownloadBlocked": "Downloads er deaktiveret på mobildata. Opret forbindelse til WiFi eller ændr indstillingen.", "maxVolume": "Maksimal lydstyrke", "maxVolumeDescription": "Tillad lydstyrkeforstærkning over 100% for stille medier", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Markeret som uset", "markedAsWatchedOffline": "Markeret som set (synkroniseres online)", "markedAsUnwatchedOffline": "Markeret som uset (synkroniseres online)", + "autoRemovedWatchedDownload": "Automatisk fjernet: ${title}", "removedFromContinueWatching": "Fjernet fra Fortsæt med at se", "errorLoading": "Fejl: ${error}", "fileInfoNotAvailable": "Filinfo ikke tilgængelig", diff --git a/lib/i18n/de.i18n.json b/lib/i18n/de.i18n.json index c904a357..573c32ae 100644 --- a/lib/i18n/de.i18n.json +++ b/lib/i18n/de.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Ordnerauswahl fehlgeschlagen", "downloadOnWifiOnly": "Nur über WLAN herunterladen", "downloadOnWifiOnlyDescription": "Downloads über mobile Daten verhindern", + "autoRemoveWatchedDownloads": "Gesehene Downloads automatisch entfernen", + "autoRemoveWatchedDownloadsDescription": "Heruntergeladene Episoden und Filme automatisch löschen, wenn sie als gesehen markiert werden", "cellularDownloadBlocked": "Downloads sind über mobile Daten deaktiviert. Verbinde dich mit einem WLAN oder ändere die Einstellung.", "maxVolume": "Maximale Lautstärke", "maxVolumeDescription": "Lautstärke über 100% für leise Medien erlauben", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Als ungesehen markiert", "markedAsWatchedOffline": "Als gesehen markiert (wird synchronisiert, wenn online)", "markedAsUnwatchedOffline": "Als ungesehen markiert (wird synchronisiert, wenn online)", + "autoRemovedWatchedDownload": "Automatisch entfernt: ${title}", "removedFromContinueWatching": "Aus ‚Weiterschauen' entfernt", "errorLoading": "Fehler: ${error}", "fileInfoNotAvailable": "Dateiinfo nicht verfügbar", diff --git a/lib/i18n/en.i18n.json b/lib/i18n/en.i18n.json index d557ee76..3d08b040 100644 --- a/lib/i18n/en.i18n.json +++ b/lib/i18n/en.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Failed to select folder", "downloadOnWifiOnly": "Download on WiFi only", "downloadOnWifiOnlyDescription": "Prevent downloads when on cellular data", + "autoRemoveWatchedDownloads": "Auto-remove watched downloads", + "autoRemoveWatchedDownloadsDescription": "Automatically delete downloaded episodes and movies when marked as watched", "cellularDownloadBlocked": "Downloads are disabled on cellular data. Connect to WiFi or change the setting.", "maxVolume": "Maximum Volume", "maxVolumeDescription": "Allow volume boost above 100% for quiet media", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Marked as unwatched", "markedAsWatchedOffline": "Marked as watched (will sync when online)", "markedAsUnwatchedOffline": "Marked as unwatched (will sync when online)", + "autoRemovedWatchedDownload": "Auto-removed: ${title}", "removedFromContinueWatching": "Removed from Continue Watching", "errorLoading": "Error: ${error}", "fileInfoNotAvailable": "File information not available", diff --git a/lib/i18n/es.i18n.json b/lib/i18n/es.i18n.json index 4ef8cdd5..96f0d662 100644 --- a/lib/i18n/es.i18n.json +++ b/lib/i18n/es.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Error al seleccionar la carpeta", "downloadOnWifiOnly": "Descargar solo con WiFi", "downloadOnWifiOnlyDescription": "Evitar descargas cuando se usan datos móviles", + "autoRemoveWatchedDownloads": "Eliminar descargas vistas automáticamente", + "autoRemoveWatchedDownloadsDescription": "Eliminar automáticamente episodios y películas descargados cuando se marquen como vistos", "cellularDownloadBlocked": "Las descargas están desactivadas en datos móviles. Conéctate a una red WiFi o cambia la configuración.", "maxVolume": "Volumen Máximo", "maxVolumeDescription": "Permitir aumento de volumen por encima del 100% para medios con sonido bajo", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Marcado como no visto", "markedAsWatchedOffline": "Marcado como visto (se sincronizará al estar en línea)", "markedAsUnwatchedOffline": "Marcado como no visto (se sincronizará al estar en línea)", + "autoRemovedWatchedDownload": "Eliminado automáticamente: ${title}", "removedFromContinueWatching": "Eliminado de Seguir Viendo", "errorLoading": "Error: ${error}", "fileInfoNotAvailable": "Información de archivo no disponible", diff --git a/lib/i18n/fr.i18n.json b/lib/i18n/fr.i18n.json index 6e9ceccb..beabdfa9 100644 --- a/lib/i18n/fr.i18n.json +++ b/lib/i18n/fr.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Échec de la sélection du dossier", "downloadOnWifiOnly": "Télécharger uniquement via WiFi", "downloadOnWifiOnlyDescription": "Empêcher les téléchargements lorsque vous utilisez les données cellulaires", + "autoRemoveWatchedDownloads": "Supprimer automatiquement les téléchargements vus", + "autoRemoveWatchedDownloadsDescription": "Supprimer automatiquement les épisodes et films téléchargés lorsqu'ils sont marqués comme vus", "cellularDownloadBlocked": "Les téléchargements sont désactivés sur les données cellulaires. Connectez-vous au Wi-Fi ou modifiez le paramètre.", "maxVolume": "Volume maximal", "maxVolumeDescription": "Autoriser l'augmentation du volume au-delà de 100 % pour les médias silencieux", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Marqué comme non vu", "markedAsWatchedOffline": "Marqué comme vu (se synchronisera lorsque vous serez en ligne)", "markedAsUnwatchedOffline": "Marqué comme non vu (sera synchronisé lorsque vous serez en ligne)", + "autoRemovedWatchedDownload": "Supprimé automatiquement : ${title}", "removedFromContinueWatching": "Supprimer de \"Continuer à regarder\"", "errorLoading": "Erreur: ${error}", "fileInfoNotAvailable": "Informations sur le fichier non disponibles", diff --git a/lib/i18n/it.i18n.json b/lib/i18n/it.i18n.json index 47a3bc1e..6bddaf81 100644 --- a/lib/i18n/it.i18n.json +++ b/lib/i18n/it.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Impossibile selezionare la cartella", "downloadOnWifiOnly": "Scarica solo con WiFi", "downloadOnWifiOnlyDescription": "Impedisci i download quando si utilizza la rete dati cellulare", + "autoRemoveWatchedDownloads": "Rimuovi automaticamente i download visti", + "autoRemoveWatchedDownloadsDescription": "Elimina automaticamente episodi e film scaricati quando vengono segnati come visti", "cellularDownloadBlocked": "I download sono disabilitati sulla rete dati cellulare. Connettiti al WiFi o modifica l'impostazione.", "maxVolume": "Volume massimo", "maxVolumeDescription": "Consenti volume superiore al 100% per contenuti audio bassi", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Segna come non visto", "markedAsWatchedOffline": "Segnato come visto (sincronizzato online)", "markedAsUnwatchedOffline": "Segnato come non visto (sincronizzato online)", + "autoRemovedWatchedDownload": "Rimosso automaticamente: ${title}", "removedFromContinueWatching": "Rimosso da Continua a guardare", "errorLoading": "Errore: ${error}", "fileInfoNotAvailable": "Informazioni sul file non disponibili", diff --git a/lib/i18n/ja.i18n.json b/lib/i18n/ja.i18n.json index 1a1f69b4..d638d02f 100644 --- a/lib/i18n/ja.i18n.json +++ b/lib/i18n/ja.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "フォルダの選択に失敗しました", "downloadOnWifiOnly": "WiFiのみでダウンロード", "downloadOnWifiOnlyDescription": "モバイルデータ通信時のダウンロードを防止", + "autoRemoveWatchedDownloads": "視聴済みダウンロードの自動削除", + "autoRemoveWatchedDownloadsDescription": "視聴済みにしたエピソードや映画のダウンロードを自動的に削除", "cellularDownloadBlocked": "モバイルデータ通信ではダウンロードが無効です。WiFiに接続するか設定を変更してください。", "maxVolume": "最大音量", "maxVolumeDescription": "静かなメディアに対して100%以上の音量ブーストを許可", @@ -411,6 +413,7 @@ "markedAsUnwatched": "未視聴にしました", "markedAsWatchedOffline": "視聴済みにしました(オンライン時に同期)", "markedAsUnwatchedOffline": "未視聴にしました(オンライン時に同期)", + "autoRemovedWatchedDownload": "自動削除: ${title}", "removedFromContinueWatching": "視聴中から削除しました", "errorLoading": "エラー: ${error}", "fileInfoNotAvailable": "ファイル情報が利用できません", diff --git a/lib/i18n/ko.i18n.json b/lib/i18n/ko.i18n.json index 8ef72239..fa6da526 100644 --- a/lib/i18n/ko.i18n.json +++ b/lib/i18n/ko.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "폴더 선택 실패", "downloadOnWifiOnly": "WiFi 연결 시에만 다운로드", "downloadOnWifiOnlyDescription": "셀룰러 데이터 사용 시 다운로드 불가", + "autoRemoveWatchedDownloads": "시청한 다운로드 자동 삭제", + "autoRemoveWatchedDownloadsDescription": "시청 완료로 표시된 에피소드와 영화의 다운로드를 자동으로 삭제", "cellularDownloadBlocked": "셀룰러 데이터에서 다운로드가 차단 되었습니다. WiFi에 연결하거나 설정을 변경하세요.", "maxVolume": "최대 볼륨", "maxVolumeDescription": "조용한 미디어를 위해 100% 이상의 볼륨 허용", @@ -411,6 +413,7 @@ "markedAsUnwatched": "시청 안 함으로 표시됨", "markedAsWatchedOffline": "시청 완료로 표시됨 (연결 시 동기화됨)", "markedAsUnwatchedOffline": "미시청으로 표시됨 (연결 시 동기화됨)", + "autoRemovedWatchedDownload": "자동 삭제됨: ${title}", "removedFromContinueWatching": "계속 시청 목록에서 제거됨", "errorLoading": "오류: ${error}", "fileInfoNotAvailable": "파일 정보가 없습니다", diff --git a/lib/i18n/nb.i18n.json b/lib/i18n/nb.i18n.json index fcc9c6c3..dbf89263 100644 --- a/lib/i18n/nb.i18n.json +++ b/lib/i18n/nb.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Kunne ikke velge mappe", "downloadOnWifiOnly": "Last ned kun på WiFi", "downloadOnWifiOnlyDescription": "Forhindre nedlastinger på mobildata", + "autoRemoveWatchedDownloads": "Fjern sette nedlastinger automatisk", + "autoRemoveWatchedDownloadsDescription": "Slett nedlastede episoder og filmer automatisk når de er markert som sett", "cellularDownloadBlocked": "Nedlastinger er deaktivert på mobildata. Koble til WiFi eller endre innstillingen.", "maxVolume": "Maks volum", "maxVolumeDescription": "Tillat volumforsterkning over 100% for stille media", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Merket som usett", "markedAsWatchedOffline": "Merket som sett (synkroniseres når tilkoblet)", "markedAsUnwatchedOffline": "Merket som usett (synkroniseres når tilkoblet)", + "autoRemovedWatchedDownload": "Automatisk fjernet: ${title}", "removedFromContinueWatching": "Fjernet fra Fortsett å se", "errorLoading": "Feil: ${error}", "fileInfoNotAvailable": "Filinformasjon ikke tilgjengelig", diff --git a/lib/i18n/nl.i18n.json b/lib/i18n/nl.i18n.json index f02a6a0f..1720d1f5 100644 --- a/lib/i18n/nl.i18n.json +++ b/lib/i18n/nl.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Kan map niet selecteren", "downloadOnWifiOnly": "Alleen via WiFi downloaden", "downloadOnWifiOnlyDescription": "Voorkom downloads bij gebruik van mobiele data", + "autoRemoveWatchedDownloads": "Bekeken downloads automatisch verwijderen", + "autoRemoveWatchedDownloadsDescription": "Gedownloade afleveringen en films automatisch verwijderen wanneer ze als bekeken zijn gemarkeerd", "cellularDownloadBlocked": "Downloads zijn uitgeschakeld bij mobiele data. Maak verbinding met WiFi of wijzig de instelling.", "maxVolume": "Maximaal volume", "maxVolumeDescription": "Volume boven 100% toestaan voor stille media", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Gemarkeerd als ongekeken", "markedAsWatchedOffline": "Gemarkeerd als gekeken (sync wanneer online)", "markedAsUnwatchedOffline": "Gemarkeerd als ongekeken (sync wanneer online)", + "autoRemovedWatchedDownload": "Automatisch verwijderd: ${title}", "removedFromContinueWatching": "Verwijderd uit Doorgaan met kijken", "errorLoading": "Fout: ${error}", "fileInfoNotAvailable": "Bestand informatie niet beschikbaar", diff --git a/lib/i18n/pl.i18n.json b/lib/i18n/pl.i18n.json index 313c64ca..28ca1046 100644 --- a/lib/i18n/pl.i18n.json +++ b/lib/i18n/pl.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Nie udało się wybrać folderu", "downloadOnWifiOnly": "Pobieraj tylko przez WiFi", "downloadOnWifiOnlyDescription": "Blokuj pobieranie na danych komórkowych", + "autoRemoveWatchedDownloads": "Automatycznie usuwaj obejrzane pobrania", + "autoRemoveWatchedDownloadsDescription": "Automatycznie usuwaj pobrane odcinki i filmy po oznaczeniu jako obejrzane", "cellularDownloadBlocked": "Pobieranie na danych komórkowych jest wyłączone. Połącz się z WiFi lub zmień ustawienie.", "maxVolume": "Maksymalna głośność", "maxVolumeDescription": "Pozwól na wzmocnienie głośności powyżej 100% dla cichych multimediów", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Oznaczono jako nieobejrzane", "markedAsWatchedOffline": "Oznaczono jako obejrzane (zsynchronizuje się po połączeniu)", "markedAsUnwatchedOffline": "Oznaczono jako nieobejrzane (zsynchronizuje się po połączeniu)", + "autoRemovedWatchedDownload": "Automatycznie usunięto: ${title}", "removedFromContinueWatching": "Usunięto z kontynuowania oglądania", "errorLoading": "Błąd: ${error}", "fileInfoNotAvailable": "Informacje o pliku niedostępne", diff --git a/lib/i18n/pt.i18n.json b/lib/i18n/pt.i18n.json index 8fcd3b4c..0bb8d1de 100644 --- a/lib/i18n/pt.i18n.json +++ b/lib/i18n/pt.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Falha ao selecionar pasta", "downloadOnWifiOnly": "Baixar apenas no WiFi", "downloadOnWifiOnlyDescription": "Impedir downloads quando em dados móveis", + "autoRemoveWatchedDownloads": "Remover downloads assistidos automaticamente", + "autoRemoveWatchedDownloadsDescription": "Excluir automaticamente episódios e filmes baixados quando marcados como assistidos", "cellularDownloadBlocked": "Downloads estão desativados em dados móveis. Conecte ao WiFi ou altere a configuração.", "maxVolume": "Volume Máximo", "maxVolumeDescription": "Permitir aumento de volume acima de 100% para mídias silenciosas", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Marcado como não assistido", "markedAsWatchedOffline": "Marcado como assistido (será sincronizado quando online)", "markedAsUnwatchedOffline": "Marcado como não assistido (será sincronizado quando online)", + "autoRemovedWatchedDownload": "Removido automaticamente: ${title}", "removedFromContinueWatching": "Removido de Continuar Assistindo", "errorLoading": "Erro: ${error}", "fileInfoNotAvailable": "Informações do arquivo não disponíveis", diff --git a/lib/i18n/ru.i18n.json b/lib/i18n/ru.i18n.json index d9e9c434..d5105501 100644 --- a/lib/i18n/ru.i18n.json +++ b/lib/i18n/ru.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Не удалось выбрать папку", "downloadOnWifiOnly": "Загружать только по WiFi", "downloadOnWifiOnlyDescription": "Запретить загрузку по мобильным данным", + "autoRemoveWatchedDownloads": "Автоудаление просмотренных загрузок", + "autoRemoveWatchedDownloadsDescription": "Автоматически удалять загруженные эпизоды и фильмы после просмотра", "cellularDownloadBlocked": "Загрузка по мобильным данным отключена. Подключитесь к WiFi или измените настройку.", "maxVolume": "Максимальная громкость", "maxVolumeDescription": "Разрешить усиление громкости выше 100% для тихих медиа", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Отмечено как непросмотренное", "markedAsWatchedOffline": "Отмечено как просмотренное (синхронизируется при подключении)", "markedAsUnwatchedOffline": "Отмечено как непросмотренное (синхронизируется при подключении)", + "autoRemovedWatchedDownload": "Автоудалено: ${title}", "removedFromContinueWatching": "Удалено из «Продолжить просмотр»", "errorLoading": "Ошибка: ${error}", "fileInfoNotAvailable": "Информация о файле недоступна", diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 964b878a..8f79e5fc 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: 13065 (871 per locale) +/// Strings: 13110 (874 per locale) /// -/// Built on 2026-04-13 at 10:11 UTC +/// Built on 2026-04-13 at 20:34 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 658fb224..369dfff5 100644 --- a/lib/i18n/strings_da.g.dart +++ b/lib/i18n/strings_da.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsDa implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Kunne ikke vælge mappe'; @override String get downloadOnWifiOnly => 'Download kun på WiFi'; @override String get downloadOnWifiOnlyDescription => 'Forhindre downloads på mobildata'; + @override String get autoRemoveWatchedDownloads => 'Fjern sete downloads automatisk'; + @override String get autoRemoveWatchedDownloadsDescription => 'Slet automatisk downloadede episoder og film, når de markeres som set'; @override String get cellularDownloadBlocked => 'Downloads er deaktiveret på mobildata. Opret forbindelse til WiFi eller ændr indstillingen.'; @override String get maxVolume => 'Maksimal lydstyrke'; @override String get maxVolumeDescription => 'Tillad lydstyrkeforstærkning over 100% for stille medier'; @@ -565,6 +567,7 @@ class _TranslationsMessagesDa implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Markeret som uset'; @override String get markedAsWatchedOffline => 'Markeret som set (synkroniseres online)'; @override String get markedAsUnwatchedOffline => 'Markeret som uset (synkroniseres online)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Automatisk fjernet: ${title}'; @override String get removedFromContinueWatching => 'Fjernet fra Fortsæt med at se'; @override String errorLoading({required Object error}) => 'Fejl: ${error}'; @override String get fileInfoNotAvailable => 'Filinfo ikke tilgængelig'; @@ -1559,6 +1562,8 @@ extension on TranslationsDa { 'settings.downloadLocationSelectError' => 'Kunne ikke vælge mappe', 'settings.downloadOnWifiOnly' => 'Download kun på WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Forhindre downloads på mobildata', + 'settings.autoRemoveWatchedDownloads' => 'Fjern sete downloads automatisk', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Slet automatisk downloadede episoder og film, når de markeres som set', 'settings.cellularDownloadBlocked' => 'Downloads er deaktiveret på mobildata. Opret forbindelse til WiFi eller ændr indstillingen.', 'settings.maxVolume' => 'Maksimal lydstyrke', 'settings.maxVolumeDescription' => 'Tillad lydstyrkeforstærkning over 100% for stille medier', @@ -1747,6 +1752,7 @@ extension on TranslationsDa { 'messages.markedAsUnwatched' => 'Markeret som uset', 'messages.markedAsWatchedOffline' => 'Markeret som set (synkroniseres online)', 'messages.markedAsUnwatchedOffline' => 'Markeret som uset (synkroniseres online)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisk fjernet: ${title}', 'messages.removedFromContinueWatching' => 'Fjernet fra Fortsæt med at se', 'messages.errorLoading' => ({required Object error}) => 'Fejl: ${error}', 'messages.fileInfoNotAvailable' => 'Filinfo ikke tilgængelig', @@ -1877,11 +1883,11 @@ extension on TranslationsDa { 'libraries.groupings.folders' => 'Mapper', 'about.title' => 'Om', 'about.openSourceLicenses' => 'Open source-licenser', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Version ${version}', 'about.appDescription' => 'En smuk Plex-klient til Flutter', 'about.viewLicensesDescription' => 'Se licenser for tredjepartsbiblioteker', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Kunne ikke forbinde til nogen servere. Tjek dit netværk og prøv igen.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Ingen servere fundet for ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Kunne ikke indlæse servere: ${error}', diff --git a/lib/i18n/strings_de.g.dart b/lib/i18n/strings_de.g.dart index fcefbfbd..b6e0801c 100644 --- a/lib/i18n/strings_de.g.dart +++ b/lib/i18n/strings_de.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsDe implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Ordnerauswahl fehlgeschlagen'; @override String get downloadOnWifiOnly => 'Nur über WLAN herunterladen'; @override String get downloadOnWifiOnlyDescription => 'Downloads über mobile Daten verhindern'; + @override String get autoRemoveWatchedDownloads => 'Gesehene Downloads automatisch entfernen'; + @override String get autoRemoveWatchedDownloadsDescription => 'Heruntergeladene Episoden und Filme automatisch löschen, wenn sie als gesehen markiert werden'; @override String get cellularDownloadBlocked => 'Downloads sind über mobile Daten deaktiviert. Verbinde dich mit einem WLAN oder ändere die Einstellung.'; @override String get maxVolume => 'Maximale Lautstärke'; @override String get maxVolumeDescription => 'Lautstärke über 100% für leise Medien erlauben'; @@ -565,6 +567,7 @@ class _TranslationsMessagesDe implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Als ungesehen markiert'; @override String get markedAsWatchedOffline => 'Als gesehen markiert (wird synchronisiert, wenn online)'; @override String get markedAsUnwatchedOffline => 'Als ungesehen markiert (wird synchronisiert, wenn online)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Automatisch entfernt: ${title}'; @override String get removedFromContinueWatching => 'Aus ‚Weiterschauen\' entfernt'; @override String errorLoading({required Object error}) => 'Fehler: ${error}'; @override String get fileInfoNotAvailable => 'Dateiinfo nicht verfügbar'; @@ -1559,6 +1562,8 @@ extension on TranslationsDe { 'settings.downloadLocationSelectError' => 'Ordnerauswahl fehlgeschlagen', 'settings.downloadOnWifiOnly' => 'Nur über WLAN herunterladen', 'settings.downloadOnWifiOnlyDescription' => 'Downloads über mobile Daten verhindern', + 'settings.autoRemoveWatchedDownloads' => 'Gesehene Downloads automatisch entfernen', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Heruntergeladene Episoden und Filme automatisch löschen, wenn sie als gesehen markiert werden', 'settings.cellularDownloadBlocked' => 'Downloads sind über mobile Daten deaktiviert. Verbinde dich mit einem WLAN oder ändere die Einstellung.', 'settings.maxVolume' => 'Maximale Lautstärke', 'settings.maxVolumeDescription' => 'Lautstärke über 100% für leise Medien erlauben', @@ -1747,6 +1752,7 @@ extension on TranslationsDe { 'messages.markedAsUnwatched' => 'Als ungesehen markiert', 'messages.markedAsWatchedOffline' => 'Als gesehen markiert (wird synchronisiert, wenn online)', 'messages.markedAsUnwatchedOffline' => 'Als ungesehen markiert (wird synchronisiert, wenn online)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisch entfernt: ${title}', 'messages.removedFromContinueWatching' => 'Aus ‚Weiterschauen\' entfernt', 'messages.errorLoading' => ({required Object error}) => 'Fehler: ${error}', 'messages.fileInfoNotAvailable' => 'Dateiinfo nicht verfügbar', @@ -1877,11 +1883,11 @@ extension on TranslationsDe { 'libraries.groupings.folders' => 'Ordner', 'about.title' => 'Über', 'about.openSourceLicenses' => 'Open-Source-Lizenzen', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Version ${version}', 'about.appDescription' => 'Ein schöner Plex-Client für Flutter', 'about.viewLicensesDescription' => 'Lizenzen von Drittanbieter-Bibliotheken anzeigen', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Verbindung zu allen Servern fehlgeschlagen. Bitte Netzwerk prüfen und erneut versuchen.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Keine Server gefunden für ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Server konnten nicht geladen werden: ${error}', diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index e8c91153..ee6ed150 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -706,6 +706,12 @@ class TranslationsSettingsEn { /// en: 'Prevent downloads when on cellular data' String get downloadOnWifiOnlyDescription => 'Prevent downloads when on cellular data'; + /// en: 'Auto-remove watched downloads' + String get autoRemoveWatchedDownloads => 'Auto-remove watched downloads'; + + /// en: 'Automatically delete downloaded episodes and movies when marked as watched' + String get autoRemoveWatchedDownloadsDescription => 'Automatically delete downloaded episodes and movies when marked as watched'; + /// en: 'Downloads are disabled on cellular data. Connect to WiFi or change the setting.' String get cellularDownloadBlocked => 'Downloads are disabled on cellular data. Connect to WiFi or change the setting.'; @@ -1277,6 +1283,9 @@ class TranslationsMessagesEn { /// en: 'Marked as unwatched (will sync when online)' String get markedAsUnwatchedOffline => 'Marked as unwatched (will sync when online)'; + /// en: 'Auto-removed: ${title}' + String autoRemovedWatchedDownload({required Object title}) => 'Auto-removed: ${title}'; + /// en: 'Removed from Continue Watching' String get removedFromContinueWatching => 'Removed from Continue Watching'; @@ -3308,6 +3317,8 @@ extension on Translations { 'settings.downloadLocationSelectError' => 'Failed to select folder', 'settings.downloadOnWifiOnly' => 'Download on WiFi only', 'settings.downloadOnWifiOnlyDescription' => 'Prevent downloads when on cellular data', + 'settings.autoRemoveWatchedDownloads' => 'Auto-remove watched downloads', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Automatically delete downloaded episodes and movies when marked as watched', 'settings.cellularDownloadBlocked' => 'Downloads are disabled on cellular data. Connect to WiFi or change the setting.', 'settings.maxVolume' => 'Maximum Volume', 'settings.maxVolumeDescription' => 'Allow volume boost above 100% for quiet media', @@ -3496,6 +3507,7 @@ extension on Translations { 'messages.markedAsUnwatched' => 'Marked as unwatched', 'messages.markedAsWatchedOffline' => 'Marked as watched (will sync when online)', 'messages.markedAsUnwatchedOffline' => 'Marked as unwatched (will sync when online)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Auto-removed: ${title}', 'messages.removedFromContinueWatching' => 'Removed from Continue Watching', 'messages.errorLoading' => ({required Object error}) => 'Error: ${error}', 'messages.fileInfoNotAvailable' => 'File information not available', @@ -3626,11 +3638,11 @@ extension on Translations { 'libraries.groupings.folders' => 'Folders', 'about.title' => 'About', 'about.openSourceLicenses' => 'Open Source Licenses', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Version ${version}', 'about.appDescription' => 'A beautiful Plex client for Flutter', 'about.viewLicensesDescription' => 'View licenses of third-party libraries', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Failed to connect to any servers. Please check your network and try again.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'No servers found for ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Failed to load servers: ${error}', diff --git a/lib/i18n/strings_es.g.dart b/lib/i18n/strings_es.g.dart index b3d9bce6..7524cb13 100644 --- a/lib/i18n/strings_es.g.dart +++ b/lib/i18n/strings_es.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsEs implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Error al seleccionar la carpeta'; @override String get downloadOnWifiOnly => 'Descargar solo con WiFi'; @override String get downloadOnWifiOnlyDescription => 'Evitar descargas cuando se usan datos móviles'; + @override String get autoRemoveWatchedDownloads => 'Eliminar descargas vistas automáticamente'; + @override String get autoRemoveWatchedDownloadsDescription => 'Eliminar automáticamente episodios y películas descargados cuando se marquen como vistos'; @override String get cellularDownloadBlocked => 'Las descargas están desactivadas en datos móviles. Conéctate a una red WiFi o cambia la configuración.'; @override String get maxVolume => 'Volumen Máximo'; @override String get maxVolumeDescription => 'Permitir aumento de volumen por encima del 100% para medios con sonido bajo'; @@ -565,6 +567,7 @@ class _TranslationsMessagesEs implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Marcado como no visto'; @override String get markedAsWatchedOffline => 'Marcado como visto (se sincronizará al estar en línea)'; @override String get markedAsUnwatchedOffline => 'Marcado como no visto (se sincronizará al estar en línea)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Eliminado automáticamente: ${title}'; @override String get removedFromContinueWatching => 'Eliminado de Seguir Viendo'; @override String errorLoading({required Object error}) => 'Error: ${error}'; @override String get fileInfoNotAvailable => 'Información de archivo no disponible'; @@ -1559,6 +1562,8 @@ extension on TranslationsEs { 'settings.downloadLocationSelectError' => 'Error al seleccionar la carpeta', 'settings.downloadOnWifiOnly' => 'Descargar solo con WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Evitar descargas cuando se usan datos móviles', + 'settings.autoRemoveWatchedDownloads' => 'Eliminar descargas vistas automáticamente', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Eliminar automáticamente episodios y películas descargados cuando se marquen como vistos', 'settings.cellularDownloadBlocked' => 'Las descargas están desactivadas en datos móviles. Conéctate a una red WiFi o cambia la configuración.', 'settings.maxVolume' => 'Volumen Máximo', 'settings.maxVolumeDescription' => 'Permitir aumento de volumen por encima del 100% para medios con sonido bajo', @@ -1747,6 +1752,7 @@ extension on TranslationsEs { 'messages.markedAsUnwatched' => 'Marcado como no visto', 'messages.markedAsWatchedOffline' => 'Marcado como visto (se sincronizará al estar en línea)', 'messages.markedAsUnwatchedOffline' => 'Marcado como no visto (se sincronizará al estar en línea)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Eliminado automáticamente: ${title}', 'messages.removedFromContinueWatching' => 'Eliminado de Seguir Viendo', 'messages.errorLoading' => ({required Object error}) => 'Error: ${error}', 'messages.fileInfoNotAvailable' => 'Información de archivo no disponible', @@ -1877,11 +1883,11 @@ extension on TranslationsEs { 'libraries.groupings.folders' => 'Carpetas', 'about.title' => 'Acerca de', 'about.openSourceLicenses' => 'Licencias de Código Abierto', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Versión ${version}', 'about.appDescription' => 'Un cliente de Plex para Flutter', 'about.viewLicensesDescription' => 'Ver licencias de librerías de terceros', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'No se pudo conectar con ningún servidor. Por favor, comprueba tu conexión e inténtalo de nuevo.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'No se encontraron servidores para ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Error al cargar servidores: ${error}', diff --git a/lib/i18n/strings_fr.g.dart b/lib/i18n/strings_fr.g.dart index 44761354..7cf23cdb 100644 --- a/lib/i18n/strings_fr.g.dart +++ b/lib/i18n/strings_fr.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsFr implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Échec de la sélection du dossier'; @override String get downloadOnWifiOnly => 'Télécharger uniquement via WiFi'; @override String get downloadOnWifiOnlyDescription => 'Empêcher les téléchargements lorsque vous utilisez les données cellulaires'; + @override String get autoRemoveWatchedDownloads => 'Supprimer automatiquement les téléchargements vus'; + @override String get autoRemoveWatchedDownloadsDescription => 'Supprimer automatiquement les épisodes et films téléchargés lorsqu\'ils sont marqués comme vus'; @override String get cellularDownloadBlocked => 'Les téléchargements sont désactivés sur les données cellulaires. Connectez-vous au Wi-Fi ou modifiez le paramètre.'; @override String get maxVolume => 'Volume maximal'; @override String get maxVolumeDescription => 'Autoriser l\'augmentation du volume au-delà de 100 % pour les médias silencieux'; @@ -565,6 +567,7 @@ class _TranslationsMessagesFr implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Marqué comme non vu'; @override String get markedAsWatchedOffline => 'Marqué comme vu (se synchronisera lorsque vous serez en ligne)'; @override String get markedAsUnwatchedOffline => 'Marqué comme non vu (sera synchronisé lorsque vous serez en ligne)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Supprimé automatiquement : ${title}'; @override String get removedFromContinueWatching => 'Supprimer de "Continuer à regarder"'; @override String errorLoading({required Object error}) => 'Erreur: ${error}'; @override String get fileInfoNotAvailable => 'Informations sur le fichier non disponibles'; @@ -1559,6 +1562,8 @@ extension on TranslationsFr { 'settings.downloadLocationSelectError' => 'Échec de la sélection du dossier', 'settings.downloadOnWifiOnly' => 'Télécharger uniquement via WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Empêcher les téléchargements lorsque vous utilisez les données cellulaires', + 'settings.autoRemoveWatchedDownloads' => 'Supprimer automatiquement les téléchargements vus', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Supprimer automatiquement les épisodes et films téléchargés lorsqu\'ils sont marqués comme vus', 'settings.cellularDownloadBlocked' => 'Les téléchargements sont désactivés sur les données cellulaires. Connectez-vous au Wi-Fi ou modifiez le paramètre.', 'settings.maxVolume' => 'Volume maximal', 'settings.maxVolumeDescription' => 'Autoriser l\'augmentation du volume au-delà de 100 % pour les médias silencieux', @@ -1747,6 +1752,7 @@ extension on TranslationsFr { 'messages.markedAsUnwatched' => 'Marqué comme non vu', 'messages.markedAsWatchedOffline' => 'Marqué comme vu (se synchronisera lorsque vous serez en ligne)', 'messages.markedAsUnwatchedOffline' => 'Marqué comme non vu (sera synchronisé lorsque vous serez en ligne)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Supprimé automatiquement : ${title}', 'messages.removedFromContinueWatching' => 'Supprimer de "Continuer à regarder"', 'messages.errorLoading' => ({required Object error}) => 'Erreur: ${error}', 'messages.fileInfoNotAvailable' => 'Informations sur le fichier non disponibles', @@ -1877,11 +1883,11 @@ extension on TranslationsFr { 'libraries.groupings.folders' => 'Dossiers', 'about.title' => 'À propos', 'about.openSourceLicenses' => 'Licences Open Source', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Version ${version}', 'about.appDescription' => 'Un magnifique client Plex pour Flutter', 'about.viewLicensesDescription' => 'Afficher les licences des bibliothèques tierces', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Impossible de se connecter à un serveur. Veuillez vérifier votre connexion réseau et réessayer.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Aucun serveur trouvé pour ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Échec du chargement des serveurs: ${error}', diff --git a/lib/i18n/strings_it.g.dart b/lib/i18n/strings_it.g.dart index a1af6909..ac6ade71 100644 --- a/lib/i18n/strings_it.g.dart +++ b/lib/i18n/strings_it.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsIt implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Impossibile selezionare la cartella'; @override String get downloadOnWifiOnly => 'Scarica solo con WiFi'; @override String get downloadOnWifiOnlyDescription => 'Impedisci i download quando si utilizza la rete dati cellulare'; + @override String get autoRemoveWatchedDownloads => 'Rimuovi automaticamente i download visti'; + @override String get autoRemoveWatchedDownloadsDescription => 'Elimina automaticamente episodi e film scaricati quando vengono segnati come visti'; @override String get cellularDownloadBlocked => 'I download sono disabilitati sulla rete dati cellulare. Connettiti al WiFi o modifica l\'impostazione.'; @override String get maxVolume => 'Volume massimo'; @override String get maxVolumeDescription => 'Consenti volume superiore al 100% per contenuti audio bassi'; @@ -565,6 +567,7 @@ class _TranslationsMessagesIt implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Segna come non visto'; @override String get markedAsWatchedOffline => 'Segnato come visto (sincronizzato online)'; @override String get markedAsUnwatchedOffline => 'Segnato come non visto (sincronizzato online)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Rimosso automaticamente: ${title}'; @override String get removedFromContinueWatching => 'Rimosso da Continua a guardare'; @override String errorLoading({required Object error}) => 'Errore: ${error}'; @override String get fileInfoNotAvailable => 'Informazioni sul file non disponibili'; @@ -1559,6 +1562,8 @@ extension on TranslationsIt { 'settings.downloadLocationSelectError' => 'Impossibile selezionare la cartella', 'settings.downloadOnWifiOnly' => 'Scarica solo con WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Impedisci i download quando si utilizza la rete dati cellulare', + 'settings.autoRemoveWatchedDownloads' => 'Rimuovi automaticamente i download visti', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Elimina automaticamente episodi e film scaricati quando vengono segnati come visti', 'settings.cellularDownloadBlocked' => 'I download sono disabilitati sulla rete dati cellulare. Connettiti al WiFi o modifica l\'impostazione.', 'settings.maxVolume' => 'Volume massimo', 'settings.maxVolumeDescription' => 'Consenti volume superiore al 100% per contenuti audio bassi', @@ -1747,6 +1752,7 @@ extension on TranslationsIt { 'messages.markedAsUnwatched' => 'Segna come non visto', 'messages.markedAsWatchedOffline' => 'Segnato come visto (sincronizzato online)', 'messages.markedAsUnwatchedOffline' => 'Segnato come non visto (sincronizzato online)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Rimosso automaticamente: ${title}', 'messages.removedFromContinueWatching' => 'Rimosso da Continua a guardare', 'messages.errorLoading' => ({required Object error}) => 'Errore: ${error}', 'messages.fileInfoNotAvailable' => 'Informazioni sul file non disponibili', @@ -1877,11 +1883,11 @@ extension on TranslationsIt { 'libraries.groupings.folders' => 'Cartelle', 'about.title' => 'Informazioni', 'about.openSourceLicenses' => 'Licenze Open Source', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Versione ${version}', 'about.appDescription' => 'Un bellissimo client Plex per Flutter', 'about.viewLicensesDescription' => 'Visualizza le licenze delle librerie di terze parti', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Impossibile connettersi a nessun server. Controlla la tua rete e riprova.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Nessun server trovato per ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Impossibile caricare i server: ${error}', diff --git a/lib/i18n/strings_ja.g.dart b/lib/i18n/strings_ja.g.dart index c44e6de8..bc711bed 100644 --- a/lib/i18n/strings_ja.g.dart +++ b/lib/i18n/strings_ja.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsJa implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'フォルダの選択に失敗しました'; @override String get downloadOnWifiOnly => 'WiFiのみでダウンロード'; @override String get downloadOnWifiOnlyDescription => 'モバイルデータ通信時のダウンロードを防止'; + @override String get autoRemoveWatchedDownloads => '視聴済みダウンロードの自動削除'; + @override String get autoRemoveWatchedDownloadsDescription => '視聴済みにしたエピソードや映画のダウンロードを自動的に削除'; @override String get cellularDownloadBlocked => 'モバイルデータ通信ではダウンロードが無効です。WiFiに接続するか設定を変更してください。'; @override String get maxVolume => '最大音量'; @override String get maxVolumeDescription => '静かなメディアに対して100%以上の音量ブーストを許可'; @@ -565,6 +567,7 @@ class _TranslationsMessagesJa implements TranslationsMessagesEn { @override String get markedAsUnwatched => '未視聴にしました'; @override String get markedAsWatchedOffline => '視聴済みにしました(オンライン時に同期)'; @override String get markedAsUnwatchedOffline => '未視聴にしました(オンライン時に同期)'; + @override String autoRemovedWatchedDownload({required Object title}) => '自動削除: ${title}'; @override String get removedFromContinueWatching => '視聴中から削除しました'; @override String errorLoading({required Object error}) => 'エラー: ${error}'; @override String get fileInfoNotAvailable => 'ファイル情報が利用できません'; @@ -1559,6 +1562,8 @@ extension on TranslationsJa { 'settings.downloadLocationSelectError' => 'フォルダの選択に失敗しました', 'settings.downloadOnWifiOnly' => 'WiFiのみでダウンロード', 'settings.downloadOnWifiOnlyDescription' => 'モバイルデータ通信時のダウンロードを防止', + 'settings.autoRemoveWatchedDownloads' => '視聴済みダウンロードの自動削除', + 'settings.autoRemoveWatchedDownloadsDescription' => '視聴済みにしたエピソードや映画のダウンロードを自動的に削除', 'settings.cellularDownloadBlocked' => 'モバイルデータ通信ではダウンロードが無効です。WiFiに接続するか設定を変更してください。', 'settings.maxVolume' => '最大音量', 'settings.maxVolumeDescription' => '静かなメディアに対して100%以上の音量ブーストを許可', @@ -1747,6 +1752,7 @@ extension on TranslationsJa { 'messages.markedAsUnwatched' => '未視聴にしました', 'messages.markedAsWatchedOffline' => '視聴済みにしました(オンライン時に同期)', 'messages.markedAsUnwatchedOffline' => '未視聴にしました(オンライン時に同期)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => '自動削除: ${title}', 'messages.removedFromContinueWatching' => '視聴中から削除しました', 'messages.errorLoading' => ({required Object error}) => 'エラー: ${error}', 'messages.fileInfoNotAvailable' => 'ファイル情報が利用できません', @@ -1877,11 +1883,11 @@ extension on TranslationsJa { 'libraries.groupings.folders' => 'フォルダ', 'about.title' => 'アプリについて', 'about.openSourceLicenses' => 'オープンソースライセンス', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'バージョン ${version}', 'about.appDescription' => 'Flutter製の美しいPlexクライアント', 'about.viewLicensesDescription' => 'サードパーティライブラリのライセンスを表示', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'どのサーバーにも接続できませんでした。ネットワークを確認してもう一度お試しください。', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => '${username} (${email})のサーバーが見つかりません', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'サーバーの読み込みに失敗しました: ${error}', diff --git a/lib/i18n/strings_ko.g.dart b/lib/i18n/strings_ko.g.dart index d07e63ea..1c16150c 100644 --- a/lib/i18n/strings_ko.g.dart +++ b/lib/i18n/strings_ko.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsKo implements TranslationsSettingsEn { @override String get downloadLocationSelectError => '폴더 선택 실패'; @override String get downloadOnWifiOnly => 'WiFi 연결 시에만 다운로드'; @override String get downloadOnWifiOnlyDescription => '셀룰러 데이터 사용 시 다운로드 불가'; + @override String get autoRemoveWatchedDownloads => '시청한 다운로드 자동 삭제'; + @override String get autoRemoveWatchedDownloadsDescription => '시청 완료로 표시된 에피소드와 영화의 다운로드를 자동으로 삭제'; @override String get cellularDownloadBlocked => '셀룰러 데이터에서 다운로드가 차단 되었습니다. WiFi에 연결하거나 설정을 변경하세요.'; @override String get maxVolume => '최대 볼륨'; @override String get maxVolumeDescription => '조용한 미디어를 위해 100% 이상의 볼륨 허용'; @@ -565,6 +567,7 @@ class _TranslationsMessagesKo implements TranslationsMessagesEn { @override String get markedAsUnwatched => '시청 안 함으로 표시됨'; @override String get markedAsWatchedOffline => '시청 완료로 표시됨 (연결 시 동기화됨)'; @override String get markedAsUnwatchedOffline => '미시청으로 표시됨 (연결 시 동기화됨)'; + @override String autoRemovedWatchedDownload({required Object title}) => '자동 삭제됨: ${title}'; @override String get removedFromContinueWatching => '계속 시청 목록에서 제거됨'; @override String errorLoading({required Object error}) => '오류: ${error}'; @override String get fileInfoNotAvailable => '파일 정보가 없습니다'; @@ -1559,6 +1562,8 @@ extension on TranslationsKo { 'settings.downloadLocationSelectError' => '폴더 선택 실패', 'settings.downloadOnWifiOnly' => 'WiFi 연결 시에만 다운로드', 'settings.downloadOnWifiOnlyDescription' => '셀룰러 데이터 사용 시 다운로드 불가', + 'settings.autoRemoveWatchedDownloads' => '시청한 다운로드 자동 삭제', + 'settings.autoRemoveWatchedDownloadsDescription' => '시청 완료로 표시된 에피소드와 영화의 다운로드를 자동으로 삭제', 'settings.cellularDownloadBlocked' => '셀룰러 데이터에서 다운로드가 차단 되었습니다. WiFi에 연결하거나 설정을 변경하세요.', 'settings.maxVolume' => '최대 볼륨', 'settings.maxVolumeDescription' => '조용한 미디어를 위해 100% 이상의 볼륨 허용', @@ -1747,6 +1752,7 @@ extension on TranslationsKo { 'messages.markedAsUnwatched' => '시청 안 함으로 표시됨', 'messages.markedAsWatchedOffline' => '시청 완료로 표시됨 (연결 시 동기화됨)', 'messages.markedAsUnwatchedOffline' => '미시청으로 표시됨 (연결 시 동기화됨)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => '자동 삭제됨: ${title}', 'messages.removedFromContinueWatching' => '계속 시청 목록에서 제거됨', 'messages.errorLoading' => ({required Object error}) => '오류: ${error}', 'messages.fileInfoNotAvailable' => '파일 정보가 없습니다', @@ -1877,11 +1883,11 @@ extension on TranslationsKo { 'libraries.groupings.folders' => '폴더', 'about.title' => '소개', 'about.openSourceLicenses' => '오픈소스 라이선스', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => '버전 ${version}', 'about.appDescription' => '아름다운 Flutter Plex 클라이언트', 'about.viewLicensesDescription' => '타사 라이브러리 라이선스 보기', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => '어떤 서버에도 연결할 수 없습니다. 네트워크를 확인하고 다시 시도하세요.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => '${username} (${email})의 서버를 찾을 수 없습니다.', 'serverSelection.failedToLoadServers' => ({required Object error}) => '서버를 로드할 수 없습니다: ${error}', diff --git a/lib/i18n/strings_nb.g.dart b/lib/i18n/strings_nb.g.dart index 291bd4e2..55e27534 100644 --- a/lib/i18n/strings_nb.g.dart +++ b/lib/i18n/strings_nb.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsNb implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Kunne ikke velge mappe'; @override String get downloadOnWifiOnly => 'Last ned kun på WiFi'; @override String get downloadOnWifiOnlyDescription => 'Forhindre nedlastinger på mobildata'; + @override String get autoRemoveWatchedDownloads => 'Fjern sette nedlastinger automatisk'; + @override String get autoRemoveWatchedDownloadsDescription => 'Slett nedlastede episoder og filmer automatisk når de er markert som sett'; @override String get cellularDownloadBlocked => 'Nedlastinger er deaktivert på mobildata. Koble til WiFi eller endre innstillingen.'; @override String get maxVolume => 'Maks volum'; @override String get maxVolumeDescription => 'Tillat volumforsterkning over 100% for stille media'; @@ -565,6 +567,7 @@ class _TranslationsMessagesNb implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Merket som usett'; @override String get markedAsWatchedOffline => 'Merket som sett (synkroniseres når tilkoblet)'; @override String get markedAsUnwatchedOffline => 'Merket som usett (synkroniseres når tilkoblet)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Automatisk fjernet: ${title}'; @override String get removedFromContinueWatching => 'Fjernet fra Fortsett å se'; @override String errorLoading({required Object error}) => 'Feil: ${error}'; @override String get fileInfoNotAvailable => 'Filinformasjon ikke tilgjengelig'; @@ -1559,6 +1562,8 @@ extension on TranslationsNb { 'settings.downloadLocationSelectError' => 'Kunne ikke velge mappe', 'settings.downloadOnWifiOnly' => 'Last ned kun på WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Forhindre nedlastinger på mobildata', + 'settings.autoRemoveWatchedDownloads' => 'Fjern sette nedlastinger automatisk', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Slett nedlastede episoder og filmer automatisk når de er markert som sett', 'settings.cellularDownloadBlocked' => 'Nedlastinger er deaktivert på mobildata. Koble til WiFi eller endre innstillingen.', 'settings.maxVolume' => 'Maks volum', 'settings.maxVolumeDescription' => 'Tillat volumforsterkning over 100% for stille media', @@ -1747,6 +1752,7 @@ extension on TranslationsNb { 'messages.markedAsUnwatched' => 'Merket som usett', 'messages.markedAsWatchedOffline' => 'Merket som sett (synkroniseres når tilkoblet)', 'messages.markedAsUnwatchedOffline' => 'Merket som usett (synkroniseres når tilkoblet)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisk fjernet: ${title}', 'messages.removedFromContinueWatching' => 'Fjernet fra Fortsett å se', 'messages.errorLoading' => ({required Object error}) => 'Feil: ${error}', 'messages.fileInfoNotAvailable' => 'Filinformasjon ikke tilgjengelig', @@ -1877,11 +1883,11 @@ extension on TranslationsNb { 'libraries.groupings.folders' => 'Mapper', 'about.title' => 'Om', 'about.openSourceLicenses' => 'Åpen kildekode-lisenser', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Versjon ${version}', 'about.appDescription' => 'En vakker Plex-klient for Flutter', 'about.viewLicensesDescription' => 'Vis lisenser for tredjepartsbiblioteker', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Kunne ikke koble til noen servere. Sjekk nettverket ditt og prøv igjen.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Ingen servere funnet for ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Kunne ikke laste servere: ${error}', diff --git a/lib/i18n/strings_nl.g.dart b/lib/i18n/strings_nl.g.dart index 651ff5ff..def3d1ef 100644 --- a/lib/i18n/strings_nl.g.dart +++ b/lib/i18n/strings_nl.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsNl implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Kan map niet selecteren'; @override String get downloadOnWifiOnly => 'Alleen via WiFi downloaden'; @override String get downloadOnWifiOnlyDescription => 'Voorkom downloads bij gebruik van mobiele data'; + @override String get autoRemoveWatchedDownloads => 'Bekeken downloads automatisch verwijderen'; + @override String get autoRemoveWatchedDownloadsDescription => 'Gedownloade afleveringen en films automatisch verwijderen wanneer ze als bekeken zijn gemarkeerd'; @override String get cellularDownloadBlocked => 'Downloads zijn uitgeschakeld bij mobiele data. Maak verbinding met WiFi of wijzig de instelling.'; @override String get maxVolume => 'Maximaal volume'; @override String get maxVolumeDescription => 'Volume boven 100% toestaan voor stille media'; @@ -565,6 +567,7 @@ class _TranslationsMessagesNl implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Gemarkeerd als ongekeken'; @override String get markedAsWatchedOffline => 'Gemarkeerd als gekeken (sync wanneer online)'; @override String get markedAsUnwatchedOffline => 'Gemarkeerd als ongekeken (sync wanneer online)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Automatisch verwijderd: ${title}'; @override String get removedFromContinueWatching => 'Verwijderd uit Doorgaan met kijken'; @override String errorLoading({required Object error}) => 'Fout: ${error}'; @override String get fileInfoNotAvailable => 'Bestand informatie niet beschikbaar'; @@ -1559,6 +1562,8 @@ extension on TranslationsNl { 'settings.downloadLocationSelectError' => 'Kan map niet selecteren', 'settings.downloadOnWifiOnly' => 'Alleen via WiFi downloaden', 'settings.downloadOnWifiOnlyDescription' => 'Voorkom downloads bij gebruik van mobiele data', + 'settings.autoRemoveWatchedDownloads' => 'Bekeken downloads automatisch verwijderen', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Gedownloade afleveringen en films automatisch verwijderen wanneer ze als bekeken zijn gemarkeerd', 'settings.cellularDownloadBlocked' => 'Downloads zijn uitgeschakeld bij mobiele data. Maak verbinding met WiFi of wijzig de instelling.', 'settings.maxVolume' => 'Maximaal volume', 'settings.maxVolumeDescription' => 'Volume boven 100% toestaan voor stille media', @@ -1747,6 +1752,7 @@ extension on TranslationsNl { 'messages.markedAsUnwatched' => 'Gemarkeerd als ongekeken', 'messages.markedAsWatchedOffline' => 'Gemarkeerd als gekeken (sync wanneer online)', 'messages.markedAsUnwatchedOffline' => 'Gemarkeerd als ongekeken (sync wanneer online)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatisch verwijderd: ${title}', 'messages.removedFromContinueWatching' => 'Verwijderd uit Doorgaan met kijken', 'messages.errorLoading' => ({required Object error}) => 'Fout: ${error}', 'messages.fileInfoNotAvailable' => 'Bestand informatie niet beschikbaar', @@ -1877,11 +1883,11 @@ extension on TranslationsNl { 'libraries.groupings.folders' => 'Mappen', 'about.title' => 'Over', 'about.openSourceLicenses' => 'Open Source licenties', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Versie ${version}', 'about.appDescription' => 'Een mooie Plex client voor Flutter', 'about.viewLicensesDescription' => 'Bekijk licenties van third-party bibliotheken', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Kon niet verbinden met servers. Controleer je netwerk en probeer opnieuw.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Geen servers gevonden voor ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Kon servers niet laden: ${error}', diff --git a/lib/i18n/strings_pl.g.dart b/lib/i18n/strings_pl.g.dart index 83991d6e..d19ab61f 100644 --- a/lib/i18n/strings_pl.g.dart +++ b/lib/i18n/strings_pl.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsPl implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Nie udało się wybrać folderu'; @override String get downloadOnWifiOnly => 'Pobieraj tylko przez WiFi'; @override String get downloadOnWifiOnlyDescription => 'Blokuj pobieranie na danych komórkowych'; + @override String get autoRemoveWatchedDownloads => 'Automatycznie usuwaj obejrzane pobrania'; + @override String get autoRemoveWatchedDownloadsDescription => 'Automatycznie usuwaj pobrane odcinki i filmy po oznaczeniu jako obejrzane'; @override String get cellularDownloadBlocked => 'Pobieranie na danych komórkowych jest wyłączone. Połącz się z WiFi lub zmień ustawienie.'; @override String get maxVolume => 'Maksymalna głośność'; @override String get maxVolumeDescription => 'Pozwól na wzmocnienie głośności powyżej 100% dla cichych multimediów'; @@ -565,6 +567,7 @@ class _TranslationsMessagesPl implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Oznaczono jako nieobejrzane'; @override String get markedAsWatchedOffline => 'Oznaczono jako obejrzane (zsynchronizuje się po połączeniu)'; @override String get markedAsUnwatchedOffline => 'Oznaczono jako nieobejrzane (zsynchronizuje się po połączeniu)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Automatycznie usunięto: ${title}'; @override String get removedFromContinueWatching => 'Usunięto z kontynuowania oglądania'; @override String errorLoading({required Object error}) => 'Błąd: ${error}'; @override String get fileInfoNotAvailable => 'Informacje o pliku niedostępne'; @@ -1559,6 +1562,8 @@ extension on TranslationsPl { 'settings.downloadLocationSelectError' => 'Nie udało się wybrać folderu', 'settings.downloadOnWifiOnly' => 'Pobieraj tylko przez WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Blokuj pobieranie na danych komórkowych', + 'settings.autoRemoveWatchedDownloads' => 'Automatycznie usuwaj obejrzane pobrania', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Automatycznie usuwaj pobrane odcinki i filmy po oznaczeniu jako obejrzane', 'settings.cellularDownloadBlocked' => 'Pobieranie na danych komórkowych jest wyłączone. Połącz się z WiFi lub zmień ustawienie.', 'settings.maxVolume' => 'Maksymalna głośność', 'settings.maxVolumeDescription' => 'Pozwól na wzmocnienie głośności powyżej 100% dla cichych multimediów', @@ -1747,6 +1752,7 @@ extension on TranslationsPl { 'messages.markedAsUnwatched' => 'Oznaczono jako nieobejrzane', 'messages.markedAsWatchedOffline' => 'Oznaczono jako obejrzane (zsynchronizuje się po połączeniu)', 'messages.markedAsUnwatchedOffline' => 'Oznaczono jako nieobejrzane (zsynchronizuje się po połączeniu)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatycznie usunięto: ${title}', 'messages.removedFromContinueWatching' => 'Usunięto z kontynuowania oglądania', 'messages.errorLoading' => ({required Object error}) => 'Błąd: ${error}', 'messages.fileInfoNotAvailable' => 'Informacje o pliku niedostępne', @@ -1877,11 +1883,11 @@ extension on TranslationsPl { 'libraries.groupings.folders' => 'Foldery', 'about.title' => 'O aplikacji', 'about.openSourceLicenses' => 'Licencje open source', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Wersja ${version}', 'about.appDescription' => 'Piękny klient Plex na Flutter', 'about.viewLicensesDescription' => 'Zobacz licencje bibliotek zewnętrznych', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Nie udało się połączyć z żadnym serwerem. Sprawdź sieć i spróbuj ponownie.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Nie znaleziono serwerów dla ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Nie udało się załadować serwerów: ${error}', diff --git a/lib/i18n/strings_pt.g.dart b/lib/i18n/strings_pt.g.dart index 80c4a27f..7e3ca7d7 100644 --- a/lib/i18n/strings_pt.g.dart +++ b/lib/i18n/strings_pt.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsPt implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Falha ao selecionar pasta'; @override String get downloadOnWifiOnly => 'Baixar apenas no WiFi'; @override String get downloadOnWifiOnlyDescription => 'Impedir downloads quando em dados móveis'; + @override String get autoRemoveWatchedDownloads => 'Remover downloads assistidos automaticamente'; + @override String get autoRemoveWatchedDownloadsDescription => 'Excluir automaticamente episódios e filmes baixados quando marcados como assistidos'; @override String get cellularDownloadBlocked => 'Downloads estão desativados em dados móveis. Conecte ao WiFi ou altere a configuração.'; @override String get maxVolume => 'Volume Máximo'; @override String get maxVolumeDescription => 'Permitir aumento de volume acima de 100% para mídias silenciosas'; @@ -565,6 +567,7 @@ class _TranslationsMessagesPt implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Marcado como não assistido'; @override String get markedAsWatchedOffline => 'Marcado como assistido (será sincronizado quando online)'; @override String get markedAsUnwatchedOffline => 'Marcado como não assistido (será sincronizado quando online)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Removido automaticamente: ${title}'; @override String get removedFromContinueWatching => 'Removido de Continuar Assistindo'; @override String errorLoading({required Object error}) => 'Erro: ${error}'; @override String get fileInfoNotAvailable => 'Informações do arquivo não disponíveis'; @@ -1559,6 +1562,8 @@ extension on TranslationsPt { 'settings.downloadLocationSelectError' => 'Falha ao selecionar pasta', 'settings.downloadOnWifiOnly' => 'Baixar apenas no WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Impedir downloads quando em dados móveis', + 'settings.autoRemoveWatchedDownloads' => 'Remover downloads assistidos automaticamente', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Excluir automaticamente episódios e filmes baixados quando marcados como assistidos', 'settings.cellularDownloadBlocked' => 'Downloads estão desativados em dados móveis. Conecte ao WiFi ou altere a configuração.', 'settings.maxVolume' => 'Volume Máximo', 'settings.maxVolumeDescription' => 'Permitir aumento de volume acima de 100% para mídias silenciosas', @@ -1747,6 +1752,7 @@ extension on TranslationsPt { 'messages.markedAsUnwatched' => 'Marcado como não assistido', 'messages.markedAsWatchedOffline' => 'Marcado como assistido (será sincronizado quando online)', 'messages.markedAsUnwatchedOffline' => 'Marcado como não assistido (será sincronizado quando online)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Removido automaticamente: ${title}', 'messages.removedFromContinueWatching' => 'Removido de Continuar Assistindo', 'messages.errorLoading' => ({required Object error}) => 'Erro: ${error}', 'messages.fileInfoNotAvailable' => 'Informações do arquivo não disponíveis', @@ -1877,11 +1883,11 @@ extension on TranslationsPt { 'libraries.groupings.folders' => 'Pastas', 'about.title' => 'Sobre', 'about.openSourceLicenses' => 'Licenças Open Source', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Versão ${version}', 'about.appDescription' => 'Um belo cliente Plex para Flutter', 'about.viewLicensesDescription' => 'Ver licenças de bibliotecas de terceiros', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Falha ao conectar a qualquer servidor. Verifique sua rede e tente novamente.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Nenhum servidor encontrado para ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Falha ao carregar servidores: ${error}', diff --git a/lib/i18n/strings_ru.g.dart b/lib/i18n/strings_ru.g.dart index 29c7264d..5be4dbb9 100644 --- a/lib/i18n/strings_ru.g.dart +++ b/lib/i18n/strings_ru.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsRu implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Не удалось выбрать папку'; @override String get downloadOnWifiOnly => 'Загружать только по WiFi'; @override String get downloadOnWifiOnlyDescription => 'Запретить загрузку по мобильным данным'; + @override String get autoRemoveWatchedDownloads => 'Автоудаление просмотренных загрузок'; + @override String get autoRemoveWatchedDownloadsDescription => 'Автоматически удалять загруженные эпизоды и фильмы после просмотра'; @override String get cellularDownloadBlocked => 'Загрузка по мобильным данным отключена. Подключитесь к WiFi или измените настройку.'; @override String get maxVolume => 'Максимальная громкость'; @override String get maxVolumeDescription => 'Разрешить усиление громкости выше 100% для тихих медиа'; @@ -565,6 +567,7 @@ class _TranslationsMessagesRu implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Отмечено как непросмотренное'; @override String get markedAsWatchedOffline => 'Отмечено как просмотренное (синхронизируется при подключении)'; @override String get markedAsUnwatchedOffline => 'Отмечено как непросмотренное (синхронизируется при подключении)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Автоудалено: ${title}'; @override String get removedFromContinueWatching => 'Удалено из «Продолжить просмотр»'; @override String errorLoading({required Object error}) => 'Ошибка: ${error}'; @override String get fileInfoNotAvailable => 'Информация о файле недоступна'; @@ -1559,6 +1562,8 @@ extension on TranslationsRu { 'settings.downloadLocationSelectError' => 'Не удалось выбрать папку', 'settings.downloadOnWifiOnly' => 'Загружать только по WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Запретить загрузку по мобильным данным', + 'settings.autoRemoveWatchedDownloads' => 'Автоудаление просмотренных загрузок', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Автоматически удалять загруженные эпизоды и фильмы после просмотра', 'settings.cellularDownloadBlocked' => 'Загрузка по мобильным данным отключена. Подключитесь к WiFi или измените настройку.', 'settings.maxVolume' => 'Максимальная громкость', 'settings.maxVolumeDescription' => 'Разрешить усиление громкости выше 100% для тихих медиа', @@ -1747,6 +1752,7 @@ extension on TranslationsRu { 'messages.markedAsUnwatched' => 'Отмечено как непросмотренное', 'messages.markedAsWatchedOffline' => 'Отмечено как просмотренное (синхронизируется при подключении)', 'messages.markedAsUnwatchedOffline' => 'Отмечено как непросмотренное (синхронизируется при подключении)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Автоудалено: ${title}', 'messages.removedFromContinueWatching' => 'Удалено из «Продолжить просмотр»', 'messages.errorLoading' => ({required Object error}) => 'Ошибка: ${error}', 'messages.fileInfoNotAvailable' => 'Информация о файле недоступна', @@ -1877,11 +1883,11 @@ extension on TranslationsRu { 'libraries.groupings.folders' => 'Папки', 'about.title' => 'О приложении', 'about.openSourceLicenses' => 'Лицензии открытого ПО', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Версия ${version}', 'about.appDescription' => 'Красивый клиент Plex на Flutter', 'about.viewLicensesDescription' => 'Просмотр лицензий сторонних библиотек', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Не удалось подключиться ни к одному серверу. Проверьте сеть и попробуйте снова.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Серверы не найдены для ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Не удалось загрузить серверы: ${error}', diff --git a/lib/i18n/strings_sv.g.dart b/lib/i18n/strings_sv.g.dart index 54c91adb..f81bc467 100644 --- a/lib/i18n/strings_sv.g.dart +++ b/lib/i18n/strings_sv.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsSv implements TranslationsSettingsEn { @override String get downloadLocationSelectError => 'Kunde inte välja mapp'; @override String get downloadOnWifiOnly => 'Ladda ner endast på WiFi'; @override String get downloadOnWifiOnlyDescription => 'Förhindra nedladdningar vid användning av mobildata'; + @override String get autoRemoveWatchedDownloads => 'Ta bort sedda nedladdningar automatiskt'; + @override String get autoRemoveWatchedDownloadsDescription => 'Ta automatiskt bort nedladdade avsnitt och filmer när de markerats som sedda'; @override String get cellularDownloadBlocked => 'Nedladdningar är inaktiverade på mobildata. Anslut till WiFi eller ändra inställningen.'; @override String get maxVolume => 'Maximal volym'; @override String get maxVolumeDescription => 'Tillåt volym över 100% för tyst media'; @@ -565,6 +567,7 @@ class _TranslationsMessagesSv implements TranslationsMessagesEn { @override String get markedAsUnwatched => 'Markerad som osedd'; @override String get markedAsWatchedOffline => 'Markerad som sedd (synkroniseras när online)'; @override String get markedAsUnwatchedOffline => 'Markerad som osedd (synkroniseras när online)'; + @override String autoRemovedWatchedDownload({required Object title}) => 'Automatiskt borttagen: ${title}'; @override String get removedFromContinueWatching => 'Borttagen från Fortsätt titta'; @override String errorLoading({required Object error}) => 'Fel: ${error}'; @override String get fileInfoNotAvailable => 'Filinformation inte tillgänglig'; @@ -1559,6 +1562,8 @@ extension on TranslationsSv { 'settings.downloadLocationSelectError' => 'Kunde inte välja mapp', 'settings.downloadOnWifiOnly' => 'Ladda ner endast på WiFi', 'settings.downloadOnWifiOnlyDescription' => 'Förhindra nedladdningar vid användning av mobildata', + 'settings.autoRemoveWatchedDownloads' => 'Ta bort sedda nedladdningar automatiskt', + 'settings.autoRemoveWatchedDownloadsDescription' => 'Ta automatiskt bort nedladdade avsnitt och filmer när de markerats som sedda', 'settings.cellularDownloadBlocked' => 'Nedladdningar är inaktiverade på mobildata. Anslut till WiFi eller ändra inställningen.', 'settings.maxVolume' => 'Maximal volym', 'settings.maxVolumeDescription' => 'Tillåt volym över 100% för tyst media', @@ -1747,6 +1752,7 @@ extension on TranslationsSv { 'messages.markedAsUnwatched' => 'Markerad som osedd', 'messages.markedAsWatchedOffline' => 'Markerad som sedd (synkroniseras när online)', 'messages.markedAsUnwatchedOffline' => 'Markerad som osedd (synkroniseras när online)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => 'Automatiskt borttagen: ${title}', 'messages.removedFromContinueWatching' => 'Borttagen från Fortsätt titta', 'messages.errorLoading' => ({required Object error}) => 'Fel: ${error}', 'messages.fileInfoNotAvailable' => 'Filinformation inte tillgänglig', @@ -1877,11 +1883,11 @@ extension on TranslationsSv { 'libraries.groupings.folders' => 'Mappar', 'about.title' => 'Om', 'about.openSourceLicenses' => 'Öppen källkod-licenser', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => 'Version ${version}', 'about.appDescription' => 'En vacker Plex-klient för Flutter', 'about.viewLicensesDescription' => 'Visa licenser för tredjepartsbibliotek', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => 'Misslyckades att ansluta till servrar. Kontrollera ditt nätverk och försök igen.', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => 'Inga servrar hittades för ${username} (${email})', 'serverSelection.failedToLoadServers' => ({required Object error}) => 'Misslyckades att ladda servrar: ${error}', diff --git a/lib/i18n/strings_zh.g.dart b/lib/i18n/strings_zh.g.dart index d75be0e9..f11acc3c 100644 --- a/lib/i18n/strings_zh.g.dart +++ b/lib/i18n/strings_zh.g.dart @@ -320,6 +320,8 @@ class _TranslationsSettingsZh implements TranslationsSettingsEn { @override String get downloadLocationSelectError => '选择文件夹失败'; @override String get downloadOnWifiOnly => '仅在 WiFi 时下载'; @override String get downloadOnWifiOnlyDescription => '使用蜂窝数据时禁止下载'; + @override String get autoRemoveWatchedDownloads => '自动移除已观看的下载'; + @override String get autoRemoveWatchedDownloadsDescription => '当剧集和电影被标记为已观看时自动删除下载内容'; @override String get cellularDownloadBlocked => '蜂窝数据下已禁用下载。请连接 WiFi 或更改设置。'; @override String get maxVolume => '最大音量'; @override String get maxVolumeDescription => '允许音量超过 100% 以适应安静的媒体'; @@ -565,6 +567,7 @@ class _TranslationsMessagesZh implements TranslationsMessagesEn { @override String get markedAsUnwatched => '已标记为未观看'; @override String get markedAsWatchedOffline => '已标记为已观看 (将在联网时同步)'; @override String get markedAsUnwatchedOffline => '已标记为未观看 (将在联网时同步)'; + @override String autoRemovedWatchedDownload({required Object title}) => '已自动移除: ${title}'; @override String get removedFromContinueWatching => '已从继续观看中移除'; @override String errorLoading({required Object error}) => '错误: ${error}'; @override String get fileInfoNotAvailable => '文件信息不可用'; @@ -1559,6 +1562,8 @@ extension on TranslationsZh { 'settings.downloadLocationSelectError' => '选择文件夹失败', 'settings.downloadOnWifiOnly' => '仅在 WiFi 时下载', 'settings.downloadOnWifiOnlyDescription' => '使用蜂窝数据时禁止下载', + 'settings.autoRemoveWatchedDownloads' => '自动移除已观看的下载', + 'settings.autoRemoveWatchedDownloadsDescription' => '当剧集和电影被标记为已观看时自动删除下载内容', 'settings.cellularDownloadBlocked' => '蜂窝数据下已禁用下载。请连接 WiFi 或更改设置。', 'settings.maxVolume' => '最大音量', 'settings.maxVolumeDescription' => '允许音量超过 100% 以适应安静的媒体', @@ -1747,6 +1752,7 @@ extension on TranslationsZh { 'messages.markedAsUnwatched' => '已标记为未观看', 'messages.markedAsWatchedOffline' => '已标记为已观看 (将在联网时同步)', 'messages.markedAsUnwatchedOffline' => '已标记为未观看 (将在联网时同步)', + 'messages.autoRemovedWatchedDownload' => ({required Object title}) => '已自动移除: ${title}', 'messages.removedFromContinueWatching' => '已从继续观看中移除', 'messages.errorLoading' => ({required Object error}) => '错误: ${error}', 'messages.fileInfoNotAvailable' => '文件信息不可用', @@ -1877,11 +1883,11 @@ extension on TranslationsZh { 'libraries.groupings.folders' => '文件夹', 'about.title' => '关于', 'about.openSourceLicenses' => '开源许可证', + _ => null, + } ?? switch (path) { 'about.versionLabel' => ({required Object version}) => '版本 ${version}', 'about.appDescription' => '一款精美的 Flutter Plex 客户端', 'about.viewLicensesDescription' => '查看第三方库的许可证', - _ => null, - } ?? switch (path) { 'serverSelection.allServerConnectionsFailed' => '无法连接到任何服务器。请检查你的网络并重试。', 'serverSelection.noServersFoundForAccount' => ({required Object username, required Object email}) => '未找到 ${username} (${email}) 的服务器', 'serverSelection.failedToLoadServers' => ({required Object error}) => '无法加载服务器: ${error}', diff --git a/lib/i18n/sv.i18n.json b/lib/i18n/sv.i18n.json index f2f30ec2..5e9f974a 100644 --- a/lib/i18n/sv.i18n.json +++ b/lib/i18n/sv.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "Kunde inte välja mapp", "downloadOnWifiOnly": "Ladda ner endast på WiFi", "downloadOnWifiOnlyDescription": "Förhindra nedladdningar vid användning av mobildata", + "autoRemoveWatchedDownloads": "Ta bort sedda nedladdningar automatiskt", + "autoRemoveWatchedDownloadsDescription": "Ta automatiskt bort nedladdade avsnitt och filmer när de markerats som sedda", "cellularDownloadBlocked": "Nedladdningar är inaktiverade på mobildata. Anslut till WiFi eller ändra inställningen.", "maxVolume": "Maximal volym", "maxVolumeDescription": "Tillåt volym över 100% för tyst media", @@ -411,6 +413,7 @@ "markedAsUnwatched": "Markerad som osedd", "markedAsWatchedOffline": "Markerad som sedd (synkroniseras när online)", "markedAsUnwatchedOffline": "Markerad som osedd (synkroniseras när online)", + "autoRemovedWatchedDownload": "Automatiskt borttagen: ${title}", "removedFromContinueWatching": "Borttagen från Fortsätt titta", "errorLoading": "Fel: ${error}", "fileInfoNotAvailable": "Filinformation inte tillgänglig", diff --git a/lib/i18n/zh.i18n.json b/lib/i18n/zh.i18n.json index 4bd03512..ac36714c 100644 --- a/lib/i18n/zh.i18n.json +++ b/lib/i18n/zh.i18n.json @@ -201,6 +201,8 @@ "downloadLocationSelectError": "选择文件夹失败", "downloadOnWifiOnly": "仅在 WiFi 时下载", "downloadOnWifiOnlyDescription": "使用蜂窝数据时禁止下载", + "autoRemoveWatchedDownloads": "自动移除已观看的下载", + "autoRemoveWatchedDownloadsDescription": "当剧集和电影被标记为已观看时自动删除下载内容", "cellularDownloadBlocked": "蜂窝数据下已禁用下载。请连接 WiFi 或更改设置。", "maxVolume": "最大音量", "maxVolumeDescription": "允许音量超过 100% 以适应安静的媒体", @@ -411,6 +413,7 @@ "markedAsUnwatched": "已标记为未观看", "markedAsWatchedOffline": "已标记为已观看 (将在联网时同步)", "markedAsUnwatchedOffline": "已标记为未观看 (将在联网时同步)", + "autoRemovedWatchedDownload": "已自动移除: ${title}", "removedFromContinueWatching": "已从继续观看中移除", "errorLoading": "错误: ${error}", "fileInfoNotAvailable": "文件信息不可用", diff --git a/lib/main.dart b/lib/main.dart index 324049f3..35629ff1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -457,8 +457,18 @@ class _MainAppState extends State with WidgetsBindingObserver { final downloadProvider = context.read(); // Wire up callback to refresh download provider after watch state sync - _offlineWatchSyncService.onWatchStatesRefreshed = () { - downloadProvider.refreshMetadataFromCache(); + _offlineWatchSyncService.onWatchStatesRefreshed = () async { + await downloadProvider.refreshMetadataFromCache(); + final settings = SettingsService.instanceOrNull; + if (settings != null && settings.getAutoRemoveWatchedDownloads()) { + final deleted = await downloadProvider.autoDeleteWatchedDownloads(); + if (deleted.isNotEmpty) { + final msg = deleted.length == 1 + ? t.messages.autoRemovedWatchedDownload(title: deleted.first) + : t.messages.autoRemovedWatchedDownload(title: '${deleted.length} items'); + showGlobalSnackBar(msg); + } + } }; _offlineWatchSyncService.startConnectivityMonitoring(offlineModeProvider); diff --git a/lib/providers/download_provider.dart b/lib/providers/download_provider.dart index 34b463d8..079fa3d7 100644 --- a/lib/providers/download_provider.dart +++ b/lib/providers/download_provider.dart @@ -1025,6 +1025,35 @@ class DownloadProvider extends ChangeNotifier { notifyListeners(); } } + + /// Auto-delete downloaded episodes/movies that are now marked as watched. + /// + /// Only deletes individual episodes and movies, never show/season containers. + Future> autoDeleteWatchedDownloads() async { + final deletedTitles = []; + + final completedKeys = _downloads.entries + .where((e) => e.value.status == DownloadStatus.completed) + .map((e) => e.key) + .toList(); + + for (final globalKey in completedKeys) { + final meta = _metadata[globalKey]; + if (meta == null) continue; + if (!meta.isEpisode && !meta.isMovie) continue; + if (!meta.isWatched) continue; + + try { + appLogger.i('Auto-deleting watched download: ${meta.title} ($globalKey)'); + await deleteDownload(globalKey); + deletedTitles.add(meta.title ?? 'Unknown'); + } catch (e) { + appLogger.w('Failed to auto-delete watched download $globalKey: $e'); + } + } + + return deletedTitles; + } } /// Exception thrown when download is blocked due to cellular-only setting diff --git a/lib/providers/offline_watch_provider.dart b/lib/providers/offline_watch_provider.dart index 1534aa1d..bbe64b8f 100644 --- a/lib/providers/offline_watch_provider.dart +++ b/lib/providers/offline_watch_provider.dart @@ -1,7 +1,13 @@ import 'package:flutter/foundation.dart'; +import '../i18n/strings.g.dart'; +import '../models/download_models.dart'; import '../models/plex_metadata.dart'; import '../services/offline_watch_sync_service.dart'; +import '../services/settings_service.dart'; +import '../utils/app_logger.dart'; +import '../utils/content_utils.dart'; +import '../utils/snackbar_helper.dart'; import '../utils/watch_state_notifier.dart'; import 'download_provider.dart'; import '../utils/global_key_utils.dart'; @@ -175,6 +181,28 @@ class OfflineWatchProvider extends ChangeNotifier { changeType: WatchStateChangeType.watched, ); notifyListeners(); + _autoDeleteIfWatched(serverId, ratingKey); + } + + /// Auto-delete a download if the auto-remove setting is enabled. + void _autoDeleteIfWatched(String serverId, String ratingKey) { + final settings = SettingsService.instanceOrNull; + if (settings == null || !settings.getAutoRemoveWatchedDownloads()) return; + + final globalKey = buildGlobalKey(serverId, ratingKey); + final meta = _downloadProvider.getMetadata(globalKey); + if (meta == null) return; + if (!meta.isEpisode && !meta.isMovie) return; + + final progress = _downloadProvider.downloads[globalKey]; + if (progress?.status != DownloadStatus.completed) return; + + appLogger.i('Auto-deleting locally-watched download: ${meta.title} ($globalKey)'); + _downloadProvider.deleteDownload(globalKey).then((_) { + showGlobalSnackBar(t.messages.autoRemovedWatchedDownload(title: meta.title ?? 'Unknown')); + }, onError: (e) { + appLogger.w('Failed to auto-delete locally-watched download $globalKey: $e'); + }); } /// Mark an item as unwatched while offline. diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index e8ae9b18..efc79751 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -47,6 +47,7 @@ class _SettingsScreenState extends State with FocusableTab { static const _kPlayback = 'playback'; static const _kDownloadLocation = 'download_location'; static const _kDownloadOnWifiOnly = 'download_on_wifi_only'; + static const _kAutoRemoveWatchedDownloads = 'auto_remove_watched_downloads'; static const _kVideoPlayerControls = 'video_player_controls'; static const _kVideoPlayerNavigation = 'video_player_navigation'; static const _kCrashReporting = 'crash_reporting'; @@ -65,6 +66,7 @@ class _SettingsScreenState extends State with FocusableTab { bool _crashReporting = true; bool _enableDebugLogging = false; bool _downloadOnWifiOnly = false; + bool _autoRemoveWatchedDownloads = false; bool _videoPlayerNavigationEnabled = false; String? _customRelayUrl; @@ -121,6 +123,7 @@ class _SettingsScreenState extends State with FocusableTab { _crashReporting = _settingsService.getCrashReporting(); _enableDebugLogging = _settingsService.getEnableDebugLogging(); _downloadOnWifiOnly = _settingsService.getDownloadOnWifiOnly(); + _autoRemoveWatchedDownloads = _settingsService.getAutoRemoveWatchedDownloads(); _videoPlayerNavigationEnabled = _settingsService.getVideoPlayerNavigationEnabled(); _customRelayUrl = _settingsService.getCustomRelayUrl(); _isLoading = false; @@ -249,6 +252,17 @@ class _SettingsScreenState extends State with FocusableTab { await _settingsService.setDownloadOnWifiOnly(value); }, ), + SwitchListTile( + focusNode: _focusTracker.get(_kAutoRemoveWatchedDownloads), + secondary: const AppIcon(Symbols.delete_sweep_rounded, fill: 1), + title: Text(t.settings.autoRemoveWatchedDownloads), + subtitle: Text(t.settings.autoRemoveWatchedDownloadsDescription), + value: _autoRemoveWatchedDownloads, + onChanged: (value) async { + setState(() => _autoRemoveWatchedDownloads = value); + await _settingsService.setAutoRemoveWatchedDownloads(value); + }, + ), ], ); } diff --git a/lib/services/multi_server_manager.dart b/lib/services/multi_server_manager.dart index 79ecea0b..bb10bc6b 100644 --- a/lib/services/multi_server_manager.dart +++ b/lib/services/multi_server_manager.dart @@ -8,6 +8,7 @@ import '../utils/app_logger.dart'; import '../utils/connection_constants.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'plex_auth_service.dart'; +import 'settings_service.dart'; import 'storage_service.dart'; /// Manages multiple Plex server connections simultaneously @@ -217,6 +218,12 @@ class MultiServerManager { onServerConnected?.call(serverId, client); appLogger.i('Successfully connected to ${server.name}'); + // Fire-and-forget: fetch server prefs and cache watched threshold + client.fetchServerPrefs().then((_) { + final threshold = client.watchedThresholdPercent; + SettingsService.instanceOrNull?.setWatchedThreshold(serverId, threshold); + }); + return serverId; } on TimeoutException { appLogger.w('Server connection timed out for ${server.name}'); diff --git a/lib/services/offline_watch_sync_service.dart b/lib/services/offline_watch_sync_service.dart index ee4c6ffe..c6905bc3 100644 --- a/lib/services/offline_watch_sync_service.dart +++ b/lib/services/offline_watch_sync_service.dart @@ -9,13 +9,14 @@ import '../utils/plex_cache_parser.dart'; import 'multi_server_manager.dart'; import 'plex_api_cache.dart'; import 'plex_client.dart'; +import 'settings_service.dart'; /// Service for managing offline watch progress and syncing to Plex servers. /// /// Handles: /// - Queuing progress updates when offline /// - Queuing manual watch/unwatch actions -/// - Auto-marking items as watched at 90% threshold +/// - Auto-marking items as watched at the server's threshold /// - Syncing queued actions when connectivity is restored class OfflineWatchSyncService extends ChangeNotifier { final AppDatabase _database; @@ -32,8 +33,18 @@ class OfflineWatchSyncService extends ChangeNotifier { /// Callback to refresh download provider metadata after sync VoidCallback? onWatchStatesRefreshed; - /// Watch threshold - mark as watched when progress exceeds this percentage - static const double watchedThreshold = 0.9; + /// Get watched threshold for a server. Cascades: + /// 1. Online client's fetched server prefs + /// 2. Cached value in SettingsService + /// 3. Default 90% + double getWatchedThreshold(String serverId) { + final client = _serverManager.getClient(serverId); + if (client != null && client.serverPrefs.isNotEmpty) { + return client.watchedThresholdPercent / 100.0; + } + final cached = SettingsService.instanceOrNull?.getWatchedThreshold(serverId) ?? 90; + return cached / 100.0; + } /// Minimum interval between syncs. /// Mobile: no throttle (always sync on resume for cross-device updates) @@ -150,14 +161,14 @@ class OfflineWatchSyncService extends ChangeNotifier { /// Queue a progress update for later sync. /// /// This is called during offline playback to track the watch position. - /// If progress exceeds 90%, shouldMarkWatched is set to true. + /// If progress exceeds the server's threshold, shouldMarkWatched is set to true. Future queueProgressUpdate({ required String serverId, required String ratingKey, required int viewOffset, required int duration, }) async { - final shouldMarkWatched = isWatchedByProgress(viewOffset, duration); + final shouldMarkWatched = isWatchedByProgress(viewOffset, duration, serverId: serverId); await _database.upsertProgressAction( serverId: serverId, @@ -199,15 +210,16 @@ class OfflineWatchSyncService extends ChangeNotifier { } /// Check if an item should be considered watched based on progress percentage. - bool isWatchedByProgress(int viewOffset, int duration) { + bool isWatchedByProgress(int viewOffset, int duration, {String? serverId}) { if (duration == 0) return false; - return (viewOffset / duration) >= watchedThreshold; + final threshold = serverId != null ? getWatchedThreshold(serverId) : 0.9; + return (viewOffset / duration) >= threshold; } /// Get the local watch status for a media item. /// /// Returns: - /// - `true` if item was marked as watched locally or progress >= 90% + /// - `true` if item was marked as watched locally or progress >= server threshold /// - `false` if item was marked as unwatched locally /// - `null` if no local action exists (use cached server data) Future getLocalWatchStatus(String globalKey) async { diff --git a/lib/services/plex_client.dart b/lib/services/plex_client.dart index 33283718..39da8daf 100644 --- a/lib/services/plex_client.dart +++ b/lib/services/plex_client.dart @@ -129,6 +129,20 @@ class PlexClient { /// EPG providers parsed from /media/providers late final List<({String identifier, String gridEndpoint})> _providerEpg; + /// Server-level preferences fetched from /:/prefs + Map _serverPrefs = {}; + + /// Get all fetched server preferences + Map get serverPrefs => Map.unmodifiable(_serverPrefs); + + /// Get the server's watched threshold percentage (default 90) + int get watchedThresholdPercent { + final value = _serverPrefs['LibraryVideoPlayedThreshold']; + if (value is int) return value; + if (value is String) return int.tryParse(value) ?? 90; + return 90; + } + /// Set offline mode - when true, only cached responses are returned void setOfflineMode(bool offline) { _offlineMode = offline; @@ -1464,11 +1478,8 @@ class PlexClient { return _wrapBoolApiCall(() => _http.delete('/library/metadata/$ratingKey'), 'Failed to delete media item'); } - /// Get preferences for a library section. - /// - /// Returns a map of setting id --> value for all settings in the library. - Future> getLibrarySectionPrefs(String sectionId) async { - final response = await _getWithFailover('/library/sections/$sectionId/prefs'); + /// Parse a Plex Settings response into a map of id --> value. + Map _parseSettingsMap(dynamic response) { final container = _getMediaContainer(response); if (container == null) return {}; final settings = container['Setting']; @@ -1477,6 +1488,26 @@ class PlexClient { return {for (final s in list) s['id'] as String: s['value']}; } + /// Fetch all server-level preferences and store them in [serverPrefs]. + /// + /// Non-blocking: intended to be called fire-and-forget on connect. + Future fetchServerPrefs() async { + try { + final response = await _getWithFailover('/:/prefs'); + _serverPrefs = _parseSettingsMap(response); + } catch (e) { + appLogger.d('Failed to fetch server prefs: $e'); + } + } + + /// Get preferences for a library section. + /// + /// Returns a map of setting id --> value for all settings in the library. + Future> getLibrarySectionPrefs(String sectionId) async { + final response = await _getWithFailover('/library/sections/$sectionId/prefs'); + return _parseSettingsMap(response); + } + /// Get available filters for a library section Future> getLibraryFilters(String sectionId) async { if (sectionId == 'shared') return []; diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart index 81464380..b1f4d35e 100644 --- a/lib/services/settings_service.dart +++ b/lib/services/settings_service.dart @@ -82,6 +82,8 @@ class SettingsService extends BaseSharedPreferencesService { static const String _keyCustomDownloadPath = 'custom_download_path'; static const String _keyCustomDownloadPathType = 'custom_download_path_type'; static const String _keyDownloadOnWifiOnly = 'download_on_wifi_only'; + static const String _keyAutoRemoveWatchedDownloads = 'auto_remove_watched_downloads'; + static const String _prefixWatchedThreshold = 'watched_threshold_'; static const String _keyVideoPlayerNavigationEnabled = 'video_player_navigation_enabled'; static const String _keyShowPerformanceOverlay = 'show_performance_overlay'; static const String _keyAutoHidePerformanceOverlay = 'auto_hide_performance_overlay'; @@ -957,6 +959,24 @@ class SettingsService extends BaseSharedPreferencesService { return prefs.getBool(_keyDownloadOnWifiOnly) ?? false; } + // Auto-remove watched downloads + Future setAutoRemoveWatchedDownloads(bool value) async { + await prefs.setBool(_keyAutoRemoveWatchedDownloads, value); + } + + bool getAutoRemoveWatchedDownloads() { + return prefs.getBool(_keyAutoRemoveWatchedDownloads) ?? false; + } + + // Per-server watched threshold (cached from server prefs) + Future setWatchedThreshold(String serverId, int percent) async { + await prefs.setInt('$_prefixWatchedThreshold$serverId', percent); + } + + int getWatchedThreshold(String serverId) { + return prefs.getInt('$_prefixWatchedThreshold$serverId') ?? 90; + } + // MPV Config (raw text) /// Get the raw MPV config text. Migrates from legacy JSON entries on first read.