From 472ac90fea30169dc04ed0f6a34c6515f8287dd9 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Sun, 22 Mar 2026 09:24:03 +0100 Subject: [PATCH] feat: rewind on resume setting close #735 --- lib/i18n/da.i18n.json | 2 ++ lib/i18n/de.i18n.json | 2 ++ lib/i18n/en.i18n.json | 2 ++ lib/i18n/es.i18n.json | 2 ++ lib/i18n/fr.i18n.json | 2 ++ lib/i18n/it.i18n.json | 2 ++ lib/i18n/ja.i18n.json | 2 ++ lib/i18n/ko.i18n.json | 2 ++ lib/i18n/nb.i18n.json | 2 ++ lib/i18n/nl.i18n.json | 2 ++ lib/i18n/pl.i18n.json | 2 ++ lib/i18n/pt.i18n.json | 2 ++ lib/i18n/ru.i18n.json | 2 ++ lib/i18n/strings.g.dart | 4 +-- lib/i18n/strings_da.g.dart | 8 ++++-- lib/i18n/strings_de.g.dart | 8 ++++-- lib/i18n/strings_en.g.dart | 12 ++++++-- lib/i18n/strings_es.g.dart | 8 ++++-- lib/i18n/strings_fr.g.dart | 8 ++++-- lib/i18n/strings_it.g.dart | 8 ++++-- lib/i18n/strings_ja.g.dart | 8 ++++-- lib/i18n/strings_ko.g.dart | 8 ++++-- lib/i18n/strings_nb.g.dart | 8 ++++-- lib/i18n/strings_nl.g.dart | 8 ++++-- lib/i18n/strings_pl.g.dart | 8 ++++-- lib/i18n/strings_pt.g.dart | 8 ++++-- lib/i18n/strings_ru.g.dart | 8 ++++-- lib/i18n/strings_sv.g.dart | 8 ++++-- lib/i18n/strings_zh.g.dart | 8 ++++-- lib/i18n/sv.i18n.json | 2 ++ lib/i18n/zh.i18n.json | 2 ++ lib/screens/settings/settings_screen.dart | 28 +++++++++++++++++++ lib/screens/video_player_screen.dart | 13 ++++++++- lib/services/settings_service.dart | 10 +++++++ .../video_controls/video_controls.dart | 21 ++++++++++---- 35 files changed, 192 insertions(+), 38 deletions(-) diff --git a/lib/i18n/da.i18n.json b/lib/i18n/da.i18n.json index c70b3582..4ee2b1a3 100644 --- a/lib/i18n/da.i18n.json +++ b/lib/i18n/da.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Tilpas underteksters udseende", "smallSkipDuration": "Kort spring-varighed", "largeSkipDuration": "Lang spring-varighed", + "rewindOnResume": "Spol tilbage ved genoptagelse", + "rewindOnResumeDescription": "Spol tilbage med denne mængde ved genoptagelse af afspilning", "secondsUnit": "${seconds} sekunder", "defaultSleepTimer": "Standard sove-timer", "minutesUnit": "${minutes} minutter", diff --git a/lib/i18n/de.i18n.json b/lib/i18n/de.i18n.json index d986a779..a13d4f22 100644 --- a/lib/i18n/de.i18n.json +++ b/lib/i18n/de.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Aussehen von Untertiteln anpassen", "smallSkipDuration": "Kleine Sprungdauer", "largeSkipDuration": "Große Sprungdauer", + "rewindOnResume": "Zurückspulen bei Fortsetzung", + "rewindOnResumeDescription": "Um diesen Betrag zurückspulen, wenn die Wiedergabe fortgesetzt wird", "secondsUnit": "${seconds} Sekunden", "defaultSleepTimer": "Standard-Sleep-Timer", "minutesUnit": "${minutes} Minuten", diff --git a/lib/i18n/en.i18n.json b/lib/i18n/en.i18n.json index 7da4c9be..29d4b46d 100644 --- a/lib/i18n/en.i18n.json +++ b/lib/i18n/en.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Customize subtitle appearance", "smallSkipDuration": "Small Skip Duration", "largeSkipDuration": "Large Skip Duration", + "rewindOnResume": "Rewind on Resume", + "rewindOnResumeDescription": "Rewind by this amount when resuming playback", "secondsUnit": "${seconds} seconds", "defaultSleepTimer": "Default Sleep Timer", "minutesUnit": "${minutes} minutes", diff --git a/lib/i18n/es.i18n.json b/lib/i18n/es.i18n.json index 61c8af45..aa582e2b 100644 --- a/lib/i18n/es.i18n.json +++ b/lib/i18n/es.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Personalizar la apariencia de los subtítulos", "smallSkipDuration": "Salto pequeño", "largeSkipDuration": "Salto grande", + "rewindOnResume": "Rebobinar al reanudar", + "rewindOnResumeDescription": "Rebobinar esta cantidad al reanudar la reproducción", "secondsUnit": "${seconds} segundos", "defaultSleepTimer": "Temporizador de apagado", "minutesUnit": "${minutes} minutos", diff --git a/lib/i18n/fr.i18n.json b/lib/i18n/fr.i18n.json index 28eecb4b..21248182 100644 --- a/lib/i18n/fr.i18n.json +++ b/lib/i18n/fr.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Personnaliser l'apparence des sous-titres", "smallSkipDuration": "Durée du petit saut", "largeSkipDuration": "Durée du grand saut", + "rewindOnResume": "Rembobiner à la reprise", + "rewindOnResumeDescription": "Rembobiner de cette durée lors de la reprise de la lecture", "secondsUnit": "${seconds} secondes", "defaultSleepTimer": "Minuterie de mise en veille par défaut", "minutesUnit": "${minutes} minutes", diff --git a/lib/i18n/it.i18n.json b/lib/i18n/it.i18n.json index 545c15ec..1096b61d 100644 --- a/lib/i18n/it.i18n.json +++ b/lib/i18n/it.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Personalizza l'aspetto dei sottotitoli", "smallSkipDuration": "Durata skip breve", "largeSkipDuration": "Durata skip lungo", + "rewindOnResume": "Riavvolgi alla ripresa", + "rewindOnResumeDescription": "Riavvolgi di questa quantità alla ripresa della riproduzione", "secondsUnit": "${seconds} secondi", "defaultSleepTimer": "Timer spegnimento predefinito", "minutesUnit": "${minutes} minuti", diff --git a/lib/i18n/ja.i18n.json b/lib/i18n/ja.i18n.json index 0888b91f..4844f19a 100644 --- a/lib/i18n/ja.i18n.json +++ b/lib/i18n/ja.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "字幕の外観をカスタマイズ", "smallSkipDuration": "短いスキップ時間", "largeSkipDuration": "長いスキップ時間", + "rewindOnResume": "再開時に巻き戻し", + "rewindOnResumeDescription": "再生再開時にこの秒数分巻き戻す", "secondsUnit": "${seconds}秒", "defaultSleepTimer": "デフォルトスリープタイマー", "minutesUnit": "${minutes}分", diff --git a/lib/i18n/ko.i18n.json b/lib/i18n/ko.i18n.json index 415d1951..48b47931 100644 --- a/lib/i18n/ko.i18n.json +++ b/lib/i18n/ko.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "자막의 외형을 사용자 설정", "smallSkipDuration": "짧은 건너뛰기 시간", "largeSkipDuration": "긴 건너뛰기 시간", + "rewindOnResume": "재개 시 되감기", + "rewindOnResumeDescription": "재생 재개 시 이 시간만큼 되감기", "secondsUnit": "${seconds}초", "defaultSleepTimer": "기본 취침 타이머", "minutesUnit": "${minutes}분", diff --git a/lib/i18n/nb.i18n.json b/lib/i18n/nb.i18n.json index c22c384c..9315c9ac 100644 --- a/lib/i18n/nb.i18n.json +++ b/lib/i18n/nb.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Tilpass utseendet på undertekster", "smallSkipDuration": "Kort hoppvarighet", "largeSkipDuration": "Lang hoppvarighet", + "rewindOnResume": "Spol tilbake ved gjenopptakelse", + "rewindOnResumeDescription": "Spol tilbake med denne mengden ved gjenopptakelse av avspilling", "secondsUnit": "${seconds} sekunder", "defaultSleepTimer": "Standard søvntimer", "minutesUnit": "${minutes} minutter", diff --git a/lib/i18n/nl.i18n.json b/lib/i18n/nl.i18n.json index ee7fbd4e..cec40d5b 100644 --- a/lib/i18n/nl.i18n.json +++ b/lib/i18n/nl.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Pas ondertitel uiterlijk aan", "smallSkipDuration": "Korte skip duur", "largeSkipDuration": "Lange skip duur", + "rewindOnResume": "Terugspoelen bij hervatten", + "rewindOnResumeDescription": "Spoel dit aantal seconden terug bij het hervatten van afspelen", "secondsUnit": "${seconds} seconden", "defaultSleepTimer": "Standaard slaap timer", "minutesUnit": "bij ${minutes} minuten", diff --git a/lib/i18n/pl.i18n.json b/lib/i18n/pl.i18n.json index 7a9ff04c..a3f07e48 100644 --- a/lib/i18n/pl.i18n.json +++ b/lib/i18n/pl.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Dostosuj wygląd napisów", "smallSkipDuration": "Krótki skok", "largeSkipDuration": "Długi skok", + "rewindOnResume": "Przewiń przy wznowieniu", + "rewindOnResumeDescription": "Przewiń o tę wartość przy wznawianiu odtwarzania", "secondsUnit": "${seconds} sekund", "defaultSleepTimer": "Domyślny wyłącznik czasowy", "minutesUnit": "${minutes} minut", diff --git a/lib/i18n/pt.i18n.json b/lib/i18n/pt.i18n.json index 6ab8c838..c829f6bc 100644 --- a/lib/i18n/pt.i18n.json +++ b/lib/i18n/pt.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Personalizar aparência das legendas", "smallSkipDuration": "Duração do Avanço Curto", "largeSkipDuration": "Duração do Avanço Longo", + "rewindOnResume": "Rebobinar ao retomar", + "rewindOnResumeDescription": "Rebobinar esta quantidade ao retomar a reprodução", "secondsUnit": "${seconds} segundos", "defaultSleepTimer": "Timer de Sono Padrão", "minutesUnit": "${minutes} minutos", diff --git a/lib/i18n/ru.i18n.json b/lib/i18n/ru.i18n.json index b2987d88..a1a3212f 100644 --- a/lib/i18n/ru.i18n.json +++ b/lib/i18n/ru.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Настроить внешний вид субтитров", "smallSkipDuration": "Малая перемотка", "largeSkipDuration": "Большая перемотка", + "rewindOnResume": "Перемотка при возобновлении", + "rewindOnResumeDescription": "Перемотать на это количество секунд при возобновлении воспроизведения", "secondsUnit": "${seconds} секунд", "defaultSleepTimer": "Таймер сна по умолчанию", "minutesUnit": "${minutes} минут", diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 54085f95..439bf8d1 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: 11880 (792 per locale) +/// Strings: 11910 (794 per locale) /// -/// Built on 2026-03-15 at 06:54 UTC +/// Built on 2026-03-22 at 08:19 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 f152769e..2c743146 100644 --- a/lib/i18n/strings_da.g.dart +++ b/lib/i18n/strings_da.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsDa implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Tilpas underteksters udseende'; @override String get smallSkipDuration => 'Kort spring-varighed'; @override String get largeSkipDuration => 'Lang spring-varighed'; + @override String get rewindOnResume => 'Spol tilbage ved genoptagelse'; + @override String get rewindOnResumeDescription => 'Spol tilbage med denne mængde ved genoptagelse af afspilning'; @override String secondsUnit({required Object seconds}) => '${seconds} sekunder'; @override String get defaultSleepTimer => 'Standard sove-timer'; @override String minutesUnit({required Object minutes}) => '${minutes} minutter'; @@ -1404,6 +1406,8 @@ extension on TranslationsDa { 'settings.subtitleStylingDescription' => 'Tilpas underteksters udseende', 'settings.smallSkipDuration' => 'Kort spring-varighed', 'settings.largeSkipDuration' => 'Lang spring-varighed', + 'settings.rewindOnResume' => 'Spol tilbage ved genoptagelse', + 'settings.rewindOnResumeDescription' => 'Spol tilbage med denne mængde ved genoptagelse af afspilning', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} sekunder', 'settings.defaultSleepTimer' => 'Standard sove-timer', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minutter', @@ -1789,10 +1793,10 @@ extension on TranslationsDa { 'collections.title' => 'Samlinger', 'collections.collection' => 'Samling', 'collections.empty' => 'Samlingen er tom', - 'collections.unknownLibrarySection' => 'Kan ikke slette: Ukendt bibliotekssektion', - 'collections.deleteCollection' => 'Slet samling', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'Kan ikke slette: Ukendt bibliotekssektion', + 'collections.deleteCollection' => 'Slet samling', 'collections.deleteConfirm' => ({required Object title}) => 'Er du sikker på, at du vil slette "${title}"? Denne handling kan ikke fortrydes.', 'collections.deleted' => 'Samling slettet', 'collections.deleteFailed' => 'Kunne ikke slette samling', diff --git a/lib/i18n/strings_de.g.dart b/lib/i18n/strings_de.g.dart index aefb87e8..5d783612 100644 --- a/lib/i18n/strings_de.g.dart +++ b/lib/i18n/strings_de.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsDe implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Aussehen von Untertiteln anpassen'; @override String get smallSkipDuration => 'Kleine Sprungdauer'; @override String get largeSkipDuration => 'Große Sprungdauer'; + @override String get rewindOnResume => 'Zurückspulen bei Fortsetzung'; + @override String get rewindOnResumeDescription => 'Um diesen Betrag zurückspulen, wenn die Wiedergabe fortgesetzt wird'; @override String secondsUnit({required Object seconds}) => '${seconds} Sekunden'; @override String get defaultSleepTimer => 'Standard-Sleep-Timer'; @override String minutesUnit({required Object minutes}) => '${minutes} Minuten'; @@ -1404,6 +1406,8 @@ extension on TranslationsDe { 'settings.subtitleStylingDescription' => 'Aussehen von Untertiteln anpassen', 'settings.smallSkipDuration' => 'Kleine Sprungdauer', 'settings.largeSkipDuration' => 'Große Sprungdauer', + 'settings.rewindOnResume' => 'Zurückspulen bei Fortsetzung', + 'settings.rewindOnResumeDescription' => 'Um diesen Betrag zurückspulen, wenn die Wiedergabe fortgesetzt wird', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} Sekunden', 'settings.defaultSleepTimer' => 'Standard-Sleep-Timer', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} Minuten', @@ -1789,10 +1793,10 @@ extension on TranslationsDe { 'downloads.title' => 'Downloads', 'downloads.manage' => 'Verwalten', 'downloads.tvShows' => 'Serien', - 'downloads.movies' => 'Filme', - 'downloads.noDownloads' => 'Noch keine Downloads', _ => null, } ?? switch (path) { + 'downloads.movies' => 'Filme', + 'downloads.noDownloads' => 'Noch keine Downloads', 'downloads.noDownloadsDescription' => 'Heruntergeladene Inhalte werden hier für die Offline-Wiedergabe angezeigt', 'downloads.downloadNow' => 'Herunterladen', 'downloads.deleteDownload' => 'Download löschen', diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index 5be3be2e..5aa40377 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -507,6 +507,12 @@ class TranslationsSettingsEn { /// en: 'Large Skip Duration' String get largeSkipDuration => 'Large Skip Duration'; + /// en: 'Rewind on Resume' + String get rewindOnResume => 'Rewind on Resume'; + + /// en: 'Rewind by this amount when resuming playback' + String get rewindOnResumeDescription => 'Rewind by this amount when resuming playback'; + /// en: '${seconds} seconds' String secondsUnit({required Object seconds}) => '${seconds} seconds'; @@ -2995,6 +3001,8 @@ extension on Translations { 'settings.subtitleStylingDescription' => 'Customize subtitle appearance', 'settings.smallSkipDuration' => 'Small Skip Duration', 'settings.largeSkipDuration' => 'Large Skip Duration', + 'settings.rewindOnResume' => 'Rewind on Resume', + 'settings.rewindOnResumeDescription' => 'Rewind by this amount when resuming playback', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} seconds', 'settings.defaultSleepTimer' => 'Default Sleep Timer', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minutes', @@ -3380,10 +3388,10 @@ extension on Translations { 'collections.title' => 'Collections', 'collections.collection' => 'Collection', 'collections.empty' => 'Collection is empty', - 'collections.unknownLibrarySection' => 'Cannot delete: Unknown library section', - 'collections.deleteCollection' => 'Delete Collection', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'Cannot delete: Unknown library section', + 'collections.deleteCollection' => 'Delete Collection', 'collections.deleteConfirm' => ({required Object title}) => 'Are you sure you want to delete "${title}"? This action cannot be undone.', 'collections.deleted' => 'Collection deleted', 'collections.deleteFailed' => 'Failed to delete collection', diff --git a/lib/i18n/strings_es.g.dart b/lib/i18n/strings_es.g.dart index d6a7098a..ff8ef949 100644 --- a/lib/i18n/strings_es.g.dart +++ b/lib/i18n/strings_es.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsEs implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Personalizar la apariencia de los subtítulos'; @override String get smallSkipDuration => 'Salto pequeño'; @override String get largeSkipDuration => 'Salto grande'; + @override String get rewindOnResume => 'Rebobinar al reanudar'; + @override String get rewindOnResumeDescription => 'Rebobinar esta cantidad al reanudar la reproducción'; @override String secondsUnit({required Object seconds}) => '${seconds} segundos'; @override String get defaultSleepTimer => 'Temporizador de apagado'; @override String minutesUnit({required Object minutes}) => '${minutes} minutos'; @@ -1404,6 +1406,8 @@ extension on TranslationsEs { 'settings.subtitleStylingDescription' => 'Personalizar la apariencia de los subtítulos', 'settings.smallSkipDuration' => 'Salto pequeño', 'settings.largeSkipDuration' => 'Salto grande', + 'settings.rewindOnResume' => 'Rebobinar al reanudar', + 'settings.rewindOnResumeDescription' => 'Rebobinar esta cantidad al reanudar la reproducción', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} segundos', 'settings.defaultSleepTimer' => 'Temporizador de apagado', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minutos', @@ -1789,10 +1793,10 @@ extension on TranslationsEs { 'collections.title' => 'Colecciones', 'collections.collection' => 'Colección', 'collections.empty' => 'La colección está vacía', - 'collections.unknownLibrarySection' => 'No se puede eliminar: Sección de biblioteca desconocida', - 'collections.deleteCollection' => 'Eliminar Colección', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'No se puede eliminar: Sección de biblioteca desconocida', + 'collections.deleteCollection' => 'Eliminar Colección', 'collections.deleteConfirm' => ({required Object title}) => '¿Estás seguro de que quieres eliminar "${title}"? Esta acción no se puede deshacer.', 'collections.deleted' => 'Colección eliminada', 'collections.deleteFailed' => 'Error al eliminar la colección', diff --git a/lib/i18n/strings_fr.g.dart b/lib/i18n/strings_fr.g.dart index b208abf4..a5c65639 100644 --- a/lib/i18n/strings_fr.g.dart +++ b/lib/i18n/strings_fr.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsFr implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Personnaliser l\'apparence des sous-titres'; @override String get smallSkipDuration => 'Durée du petit saut'; @override String get largeSkipDuration => 'Durée du grand saut'; + @override String get rewindOnResume => 'Rembobiner à la reprise'; + @override String get rewindOnResumeDescription => 'Rembobiner de cette durée lors de la reprise de la lecture'; @override String secondsUnit({required Object seconds}) => '${seconds} secondes'; @override String get defaultSleepTimer => 'Minuterie de mise en veille par défaut'; @override String minutesUnit({required Object minutes}) => '${minutes} minutes'; @@ -1404,6 +1406,8 @@ extension on TranslationsFr { 'settings.subtitleStylingDescription' => 'Personnaliser l\'apparence des sous-titres', 'settings.smallSkipDuration' => 'Durée du petit saut', 'settings.largeSkipDuration' => 'Durée du grand saut', + 'settings.rewindOnResume' => 'Rembobiner à la reprise', + 'settings.rewindOnResumeDescription' => 'Rembobiner de cette durée lors de la reprise de la lecture', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} secondes', 'settings.defaultSleepTimer' => 'Minuterie de mise en veille par défaut', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minutes', @@ -1789,10 +1793,10 @@ extension on TranslationsFr { 'collections.title' => 'Collections', 'collections.collection' => 'Collection', 'collections.empty' => 'La collection est vide', - 'collections.unknownLibrarySection' => 'Impossible de supprimer : section de bibliothèque inconnue', - 'collections.deleteCollection' => 'Supprimer la collection', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'Impossible de supprimer : section de bibliothèque inconnue', + 'collections.deleteCollection' => 'Supprimer la collection', 'collections.deleteConfirm' => ({required Object title}) => 'Êtes-vous sûr de vouloir supprimer "${title}" ? Cette action ne peut pas être annulée.', 'collections.deleted' => 'Collection supprimée', 'collections.deleteFailed' => 'Échec de la suppression de la collection', diff --git a/lib/i18n/strings_it.g.dart b/lib/i18n/strings_it.g.dart index 37bcde41..c2065d85 100644 --- a/lib/i18n/strings_it.g.dart +++ b/lib/i18n/strings_it.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsIt implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Personalizza l\'aspetto dei sottotitoli'; @override String get smallSkipDuration => 'Durata skip breve'; @override String get largeSkipDuration => 'Durata skip lungo'; + @override String get rewindOnResume => 'Riavvolgi alla ripresa'; + @override String get rewindOnResumeDescription => 'Riavvolgi di questa quantità alla ripresa della riproduzione'; @override String secondsUnit({required Object seconds}) => '${seconds} secondi'; @override String get defaultSleepTimer => 'Timer spegnimento predefinito'; @override String minutesUnit({required Object minutes}) => '${minutes} minuti'; @@ -1404,6 +1406,8 @@ extension on TranslationsIt { 'settings.subtitleStylingDescription' => 'Personalizza l\'aspetto dei sottotitoli', 'settings.smallSkipDuration' => 'Durata skip breve', 'settings.largeSkipDuration' => 'Durata skip lungo', + 'settings.rewindOnResume' => 'Riavvolgi alla ripresa', + 'settings.rewindOnResumeDescription' => 'Riavvolgi di questa quantità alla ripresa della riproduzione', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} secondi', 'settings.defaultSleepTimer' => 'Timer spegnimento predefinito', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minuti', @@ -1789,10 +1793,10 @@ extension on TranslationsIt { 'downloads.title' => 'Download', 'downloads.manage' => 'Gestisci', 'downloads.tvShows' => 'Serie TV', - 'downloads.movies' => 'Film', - 'downloads.noDownloads' => 'Nessun download', _ => null, } ?? switch (path) { + 'downloads.movies' => 'Film', + 'downloads.noDownloads' => 'Nessun download', 'downloads.noDownloadsDescription' => 'I contenuti scaricati appariranno qui per la visualizzazione offline', 'downloads.downloadNow' => 'Scarica', 'downloads.deleteDownload' => 'Elimina download', diff --git a/lib/i18n/strings_ja.g.dart b/lib/i18n/strings_ja.g.dart index 3be5da64..1607c6f0 100644 --- a/lib/i18n/strings_ja.g.dart +++ b/lib/i18n/strings_ja.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsJa implements TranslationsSettingsEn { @override String get subtitleStylingDescription => '字幕の外観をカスタマイズ'; @override String get smallSkipDuration => '短いスキップ時間'; @override String get largeSkipDuration => '長いスキップ時間'; + @override String get rewindOnResume => '再開時に巻き戻し'; + @override String get rewindOnResumeDescription => '再生再開時にこの秒数分巻き戻す'; @override String secondsUnit({required Object seconds}) => '${seconds}秒'; @override String get defaultSleepTimer => 'デフォルトスリープタイマー'; @override String minutesUnit({required Object minutes}) => '${minutes}分'; @@ -1404,6 +1406,8 @@ extension on TranslationsJa { 'settings.subtitleStylingDescription' => '字幕の外観をカスタマイズ', 'settings.smallSkipDuration' => '短いスキップ時間', 'settings.largeSkipDuration' => '長いスキップ時間', + 'settings.rewindOnResume' => '再開時に巻き戻し', + 'settings.rewindOnResumeDescription' => '再生再開時にこの秒数分巻き戻す', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds}秒', 'settings.defaultSleepTimer' => 'デフォルトスリープタイマー', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes}分', @@ -1789,10 +1793,10 @@ extension on TranslationsJa { 'collections.title' => 'コレクション', 'collections.collection' => 'コレクション', 'collections.empty' => 'コレクションは空です', - 'collections.unknownLibrarySection' => '削除できません:不明なライブラリセクション', - 'collections.deleteCollection' => 'コレクションを削除', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => '削除できません:不明なライブラリセクション', + 'collections.deleteCollection' => 'コレクションを削除', 'collections.deleteConfirm' => ({required Object title}) => '"${title}"を削除してもよろしいですか?この操作は元に戻せません。', 'collections.deleted' => 'コレクションを削除しました', 'collections.deleteFailed' => 'コレクションの削除に失敗しました', diff --git a/lib/i18n/strings_ko.g.dart b/lib/i18n/strings_ko.g.dart index 91c6e34b..6e8c4aa2 100644 --- a/lib/i18n/strings_ko.g.dart +++ b/lib/i18n/strings_ko.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsKo implements TranslationsSettingsEn { @override String get subtitleStylingDescription => '자막의 외형을 사용자 설정'; @override String get smallSkipDuration => '짧은 건너뛰기 시간'; @override String get largeSkipDuration => '긴 건너뛰기 시간'; + @override String get rewindOnResume => '재개 시 되감기'; + @override String get rewindOnResumeDescription => '재생 재개 시 이 시간만큼 되감기'; @override String secondsUnit({required Object seconds}) => '${seconds}초'; @override String get defaultSleepTimer => '기본 취침 타이머'; @override String minutesUnit({required Object minutes}) => '${minutes}분'; @@ -1404,6 +1406,8 @@ extension on TranslationsKo { 'settings.subtitleStylingDescription' => '자막의 외형을 사용자 설정', 'settings.smallSkipDuration' => '짧은 건너뛰기 시간', 'settings.largeSkipDuration' => '긴 건너뛰기 시간', + 'settings.rewindOnResume' => '재개 시 되감기', + 'settings.rewindOnResumeDescription' => '재생 재개 시 이 시간만큼 되감기', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds}초', 'settings.defaultSleepTimer' => '기본 취침 타이머', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes}분', @@ -1789,10 +1793,10 @@ extension on TranslationsKo { 'collections.title' => '컬렉션', 'collections.collection' => '컬렉션', 'collections.empty' => '컬렉션이 비어 있습니다', - 'collections.unknownLibrarySection' => '삭제할 수 없습니다: 알 수 없는 미디어 라이브러리 섹션입니다', - 'collections.deleteCollection' => '컬렉션 삭제', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => '삭제할 수 없습니다: 알 수 없는 미디어 라이브러리 섹션입니다', + 'collections.deleteCollection' => '컬렉션 삭제', 'collections.deleteConfirm' => ({required Object title}) => '"${title}"을(를) 삭제 하시겠습니까? 이 작업은 되돌릴 수 없습니다.', 'collections.deleted' => '컬렉션 삭제됨', 'collections.deleteFailed' => '컬렉션 삭제 실패', diff --git a/lib/i18n/strings_nb.g.dart b/lib/i18n/strings_nb.g.dart index 4cb61c7e..ba4e5a7a 100644 --- a/lib/i18n/strings_nb.g.dart +++ b/lib/i18n/strings_nb.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsNb implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Tilpass utseendet på undertekster'; @override String get smallSkipDuration => 'Kort hoppvarighet'; @override String get largeSkipDuration => 'Lang hoppvarighet'; + @override String get rewindOnResume => 'Spol tilbake ved gjenopptakelse'; + @override String get rewindOnResumeDescription => 'Spol tilbake med denne mengden ved gjenopptakelse av avspilling'; @override String secondsUnit({required Object seconds}) => '${seconds} sekunder'; @override String get defaultSleepTimer => 'Standard søvntimer'; @override String minutesUnit({required Object minutes}) => '${minutes} minutter'; @@ -1404,6 +1406,8 @@ extension on TranslationsNb { 'settings.subtitleStylingDescription' => 'Tilpass utseendet på undertekster', 'settings.smallSkipDuration' => 'Kort hoppvarighet', 'settings.largeSkipDuration' => 'Lang hoppvarighet', + 'settings.rewindOnResume' => 'Spol tilbake ved gjenopptakelse', + 'settings.rewindOnResumeDescription' => 'Spol tilbake med denne mengden ved gjenopptakelse av avspilling', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} sekunder', 'settings.defaultSleepTimer' => 'Standard søvntimer', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minutter', @@ -1789,10 +1793,10 @@ extension on TranslationsNb { 'collections.title' => 'Samlinger', 'collections.collection' => 'Samling', 'collections.empty' => 'Samlingen er tom', - 'collections.unknownLibrarySection' => 'Kan ikke slette: Ukjent bibliotekseksjon', - 'collections.deleteCollection' => 'Slett samling', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'Kan ikke slette: Ukjent bibliotekseksjon', + 'collections.deleteCollection' => 'Slett samling', 'collections.deleteConfirm' => ({required Object title}) => 'Er du sikker på at du vil slette "${title}"? Denne handlingen kan ikke angres.', 'collections.deleted' => 'Samling slettet', 'collections.deleteFailed' => 'Kunne ikke slette samling', diff --git a/lib/i18n/strings_nl.g.dart b/lib/i18n/strings_nl.g.dart index edb87991..1ba076e4 100644 --- a/lib/i18n/strings_nl.g.dart +++ b/lib/i18n/strings_nl.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsNl implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Pas ondertitel uiterlijk aan'; @override String get smallSkipDuration => 'Korte skip duur'; @override String get largeSkipDuration => 'Lange skip duur'; + @override String get rewindOnResume => 'Terugspoelen bij hervatten'; + @override String get rewindOnResumeDescription => 'Spoel dit aantal seconden terug bij het hervatten van afspelen'; @override String secondsUnit({required Object seconds}) => '${seconds} seconden'; @override String get defaultSleepTimer => 'Standaard slaap timer'; @override String minutesUnit({required Object minutes}) => 'bij ${minutes} minuten'; @@ -1404,6 +1406,8 @@ extension on TranslationsNl { 'settings.subtitleStylingDescription' => 'Pas ondertitel uiterlijk aan', 'settings.smallSkipDuration' => 'Korte skip duur', 'settings.largeSkipDuration' => 'Lange skip duur', + 'settings.rewindOnResume' => 'Terugspoelen bij hervatten', + 'settings.rewindOnResumeDescription' => 'Spoel dit aantal seconden terug bij het hervatten van afspelen', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} seconden', 'settings.defaultSleepTimer' => 'Standaard slaap timer', 'settings.minutesUnit' => ({required Object minutes}) => 'bij ${minutes} minuten', @@ -1789,10 +1793,10 @@ extension on TranslationsNl { 'downloads.title' => 'Downloads', 'downloads.manage' => 'Beheren', 'downloads.tvShows' => 'Series', - 'downloads.movies' => 'Films', - 'downloads.noDownloads' => 'Nog geen downloads', _ => null, } ?? switch (path) { + 'downloads.movies' => 'Films', + 'downloads.noDownloads' => 'Nog geen downloads', 'downloads.noDownloadsDescription' => 'Gedownloade content verschijnt hier voor offline weergave', 'downloads.downloadNow' => 'Download', 'downloads.deleteDownload' => 'Download verwijderen', diff --git a/lib/i18n/strings_pl.g.dart b/lib/i18n/strings_pl.g.dart index 962b5585..bb9dff67 100644 --- a/lib/i18n/strings_pl.g.dart +++ b/lib/i18n/strings_pl.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsPl implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Dostosuj wygląd napisów'; @override String get smallSkipDuration => 'Krótki skok'; @override String get largeSkipDuration => 'Długi skok'; + @override String get rewindOnResume => 'Przewiń przy wznowieniu'; + @override String get rewindOnResumeDescription => 'Przewiń o tę wartość przy wznawianiu odtwarzania'; @override String secondsUnit({required Object seconds}) => '${seconds} sekund'; @override String get defaultSleepTimer => 'Domyślny wyłącznik czasowy'; @override String minutesUnit({required Object minutes}) => '${minutes} minut'; @@ -1404,6 +1406,8 @@ extension on TranslationsPl { 'settings.subtitleStylingDescription' => 'Dostosuj wygląd napisów', 'settings.smallSkipDuration' => 'Krótki skok', 'settings.largeSkipDuration' => 'Długi skok', + 'settings.rewindOnResume' => 'Przewiń przy wznowieniu', + 'settings.rewindOnResumeDescription' => 'Przewiń o tę wartość przy wznawianiu odtwarzania', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} sekund', 'settings.defaultSleepTimer' => 'Domyślny wyłącznik czasowy', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minut', @@ -1789,10 +1793,10 @@ extension on TranslationsPl { 'collections.title' => 'Kolekcje', 'collections.collection' => 'Kolekcja', 'collections.empty' => 'Kolekcja jest pusta', - 'collections.unknownLibrarySection' => 'Nie można usunąć: Nieznana sekcja biblioteki', - 'collections.deleteCollection' => 'Usuń kolekcję', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'Nie można usunąć: Nieznana sekcja biblioteki', + 'collections.deleteCollection' => 'Usuń kolekcję', 'collections.deleteConfirm' => ({required Object title}) => 'Czy na pewno chcesz usunąć "${title}"? Tej operacji nie można cofnąć.', 'collections.deleted' => 'Kolekcja usunięta', 'collections.deleteFailed' => 'Nie udało się usunąć kolekcji', diff --git a/lib/i18n/strings_pt.g.dart b/lib/i18n/strings_pt.g.dart index 8cafa68b..0c82084b 100644 --- a/lib/i18n/strings_pt.g.dart +++ b/lib/i18n/strings_pt.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsPt implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Personalizar aparência das legendas'; @override String get smallSkipDuration => 'Duração do Avanço Curto'; @override String get largeSkipDuration => 'Duração do Avanço Longo'; + @override String get rewindOnResume => 'Rebobinar ao retomar'; + @override String get rewindOnResumeDescription => 'Rebobinar esta quantidade ao retomar a reprodução'; @override String secondsUnit({required Object seconds}) => '${seconds} segundos'; @override String get defaultSleepTimer => 'Timer de Sono Padrão'; @override String minutesUnit({required Object minutes}) => '${minutes} minutos'; @@ -1404,6 +1406,8 @@ extension on TranslationsPt { 'settings.subtitleStylingDescription' => 'Personalizar aparência das legendas', 'settings.smallSkipDuration' => 'Duração do Avanço Curto', 'settings.largeSkipDuration' => 'Duração do Avanço Longo', + 'settings.rewindOnResume' => 'Rebobinar ao retomar', + 'settings.rewindOnResumeDescription' => 'Rebobinar esta quantidade ao retomar a reprodução', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} segundos', 'settings.defaultSleepTimer' => 'Timer de Sono Padrão', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minutos', @@ -1789,10 +1793,10 @@ extension on TranslationsPt { 'collections.title' => 'Coleções', 'collections.collection' => 'Coleção', 'collections.empty' => 'A coleção está vazia', - 'collections.unknownLibrarySection' => 'Não é possível excluir: Seção de biblioteca desconhecida', - 'collections.deleteCollection' => 'Excluir Coleção', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'Não é possível excluir: Seção de biblioteca desconhecida', + 'collections.deleteCollection' => 'Excluir Coleção', 'collections.deleteConfirm' => ({required Object title}) => 'Tem certeza que deseja excluir "${title}"? Esta ação não pode ser desfeita.', 'collections.deleted' => 'Coleção excluída', 'collections.deleteFailed' => 'Falha ao excluir coleção', diff --git a/lib/i18n/strings_ru.g.dart b/lib/i18n/strings_ru.g.dart index 1363a532..4d9f6749 100644 --- a/lib/i18n/strings_ru.g.dart +++ b/lib/i18n/strings_ru.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsRu implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Настроить внешний вид субтитров'; @override String get smallSkipDuration => 'Малая перемотка'; @override String get largeSkipDuration => 'Большая перемотка'; + @override String get rewindOnResume => 'Перемотка при возобновлении'; + @override String get rewindOnResumeDescription => 'Перемотать на это количество секунд при возобновлении воспроизведения'; @override String secondsUnit({required Object seconds}) => '${seconds} секунд'; @override String get defaultSleepTimer => 'Таймер сна по умолчанию'; @override String minutesUnit({required Object minutes}) => '${minutes} минут'; @@ -1404,6 +1406,8 @@ extension on TranslationsRu { 'settings.subtitleStylingDescription' => 'Настроить внешний вид субтитров', 'settings.smallSkipDuration' => 'Малая перемотка', 'settings.largeSkipDuration' => 'Большая перемотка', + 'settings.rewindOnResume' => 'Перемотка при возобновлении', + 'settings.rewindOnResumeDescription' => 'Перемотать на это количество секунд при возобновлении воспроизведения', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} секунд', 'settings.defaultSleepTimer' => 'Таймер сна по умолчанию', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} минут', @@ -1789,10 +1793,10 @@ extension on TranslationsRu { 'collections.title' => 'Коллекции', 'collections.collection' => 'Коллекция', 'collections.empty' => 'Коллекция пуста', - 'collections.unknownLibrarySection' => 'Невозможно удалить: неизвестный раздел библиотеки', - 'collections.deleteCollection' => 'Удалить коллекцию', _ => null, } ?? switch (path) { + 'collections.unknownLibrarySection' => 'Невозможно удалить: неизвестный раздел библиотеки', + 'collections.deleteCollection' => 'Удалить коллекцию', 'collections.deleteConfirm' => ({required Object title}) => 'Вы уверены, что хотите удалить "${title}"? Это действие нельзя отменить.', 'collections.deleted' => 'Коллекция удалена', 'collections.deleteFailed' => 'Не удалось удалить коллекцию', diff --git a/lib/i18n/strings_sv.g.dart b/lib/i18n/strings_sv.g.dart index 33c954e5..f1d90b9b 100644 --- a/lib/i18n/strings_sv.g.dart +++ b/lib/i18n/strings_sv.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsSv implements TranslationsSettingsEn { @override String get subtitleStylingDescription => 'Anpassa undertextutseende'; @override String get smallSkipDuration => 'Kort hoppvaraktighet'; @override String get largeSkipDuration => 'Lång hoppvaraktighet'; + @override String get rewindOnResume => 'Spola tillbaka vid återupptagning'; + @override String get rewindOnResumeDescription => 'Spola tillbaka med denna tid vid återupptagning av uppspelning'; @override String secondsUnit({required Object seconds}) => '${seconds} sekunder'; @override String get defaultSleepTimer => 'Standard sovtimer'; @override String minutesUnit({required Object minutes}) => '${minutes} minuter'; @@ -1404,6 +1406,8 @@ extension on TranslationsSv { 'settings.subtitleStylingDescription' => 'Anpassa undertextutseende', 'settings.smallSkipDuration' => 'Kort hoppvaraktighet', 'settings.largeSkipDuration' => 'Lång hoppvaraktighet', + 'settings.rewindOnResume' => 'Spola tillbaka vid återupptagning', + 'settings.rewindOnResumeDescription' => 'Spola tillbaka med denna tid vid återupptagning av uppspelning', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} sekunder', 'settings.defaultSleepTimer' => 'Standard sovtimer', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} minuter', @@ -1789,10 +1793,10 @@ extension on TranslationsSv { 'downloads.title' => 'Nedladdningar', 'downloads.manage' => 'Hantera', 'downloads.tvShows' => 'TV-serier', - 'downloads.movies' => 'Filmer', - 'downloads.noDownloads' => 'Inga nedladdningar ännu', _ => null, } ?? switch (path) { + 'downloads.movies' => 'Filmer', + 'downloads.noDownloads' => 'Inga nedladdningar ännu', 'downloads.noDownloadsDescription' => 'Nedladdat innehåll visas här för offline-visning', 'downloads.downloadNow' => 'Ladda ner', 'downloads.deleteDownload' => 'Ta bort nedladdning', diff --git a/lib/i18n/strings_zh.g.dart b/lib/i18n/strings_zh.g.dart index aae6054f..9f4eb661 100644 --- a/lib/i18n/strings_zh.g.dart +++ b/lib/i18n/strings_zh.g.dart @@ -253,6 +253,8 @@ class _TranslationsSettingsZh implements TranslationsSettingsEn { @override String get subtitleStylingDescription => '调整字幕外观'; @override String get smallSkipDuration => '短跳过时长'; @override String get largeSkipDuration => '长跳过时长'; + @override String get rewindOnResume => '恢复时回退'; + @override String get rewindOnResumeDescription => '恢复播放时回退此时长'; @override String secondsUnit({required Object seconds}) => '${seconds} 秒'; @override String get defaultSleepTimer => '默认睡眠定时器'; @override String minutesUnit({required Object minutes}) => '${minutes} 分钟'; @@ -1404,6 +1406,8 @@ extension on TranslationsZh { 'settings.subtitleStylingDescription' => '调整字幕外观', 'settings.smallSkipDuration' => '短跳过时长', 'settings.largeSkipDuration' => '长跳过时长', + 'settings.rewindOnResume' => '恢复时回退', + 'settings.rewindOnResumeDescription' => '恢复播放时回退此时长', 'settings.secondsUnit' => ({required Object seconds}) => '${seconds} 秒', 'settings.defaultSleepTimer' => '默认睡眠定时器', 'settings.minutesUnit' => ({required Object minutes}) => '${minutes} 分钟', @@ -1789,10 +1793,10 @@ extension on TranslationsZh { 'downloads.title' => '下载', 'downloads.manage' => '管理', 'downloads.tvShows' => '电视剧', - 'downloads.movies' => '电影', - 'downloads.noDownloads' => '暂无下载', _ => null, } ?? switch (path) { + 'downloads.movies' => '电影', + 'downloads.noDownloads' => '暂无下载', 'downloads.noDownloadsDescription' => '下载的内容将在此处显示以供离线观看', 'downloads.downloadNow' => '下载', 'downloads.deleteDownload' => '删除下载', diff --git a/lib/i18n/sv.i18n.json b/lib/i18n/sv.i18n.json index 94f88c3d..95186a47 100644 --- a/lib/i18n/sv.i18n.json +++ b/lib/i18n/sv.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "Anpassa undertextutseende", "smallSkipDuration": "Kort hoppvaraktighet", "largeSkipDuration": "Lång hoppvaraktighet", + "rewindOnResume": "Spola tillbaka vid återupptagning", + "rewindOnResumeDescription": "Spola tillbaka med denna tid vid återupptagning av uppspelning", "secondsUnit": "${seconds} sekunder", "defaultSleepTimer": "Standard sovtimer", "minutesUnit": "${minutes} minuter", diff --git a/lib/i18n/zh.i18n.json b/lib/i18n/zh.i18n.json index 6c62eaba..03510f90 100644 --- a/lib/i18n/zh.i18n.json +++ b/lib/i18n/zh.i18n.json @@ -135,6 +135,8 @@ "subtitleStylingDescription": "调整字幕外观", "smallSkipDuration": "短跳过时长", "largeSkipDuration": "长跳过时长", + "rewindOnResume": "恢复时回退", + "rewindOnResumeDescription": "恢复播放时回退此时长", "secondsUnit": "${seconds} 秒", "defaultSleepTimer": "默认睡眠定时器", "minutesUnit": "${minutes} 分钟", diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index 0231350a..9c86eb00 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -91,6 +91,7 @@ class _SettingsScreenState extends State with FocusableTab { static const _kMpvConfig = 'mpv_config'; static const _kSmallSkipDuration = 'small_skip_duration'; static const _kLargeSkipDuration = 'large_skip_duration'; + static const _kRewindOnResume = 'rewind_on_resume'; static const _kDefaultSleepTimer = 'default_sleep_timer'; static const _kMaxVolume = 'max_volume'; static const _kDiscordRichPresence = 'discord_rich_presence'; @@ -122,6 +123,7 @@ class _SettingsScreenState extends State with FocusableTab { int _bufferSize = 0; int _seekTimeSmall = 10; int _seekTimeLarge = 30; + int _rewindOnResume = 0; int _sleepTimerDuration = 30; bool _rememberTrackSelections = true; bool _clickVideoTogglesPlayback = false; @@ -201,6 +203,7 @@ class _SettingsScreenState extends State with FocusableTab { _bufferSize = _settingsService.getBufferSize(); _seekTimeSmall = _settingsService.getSeekTimeSmall(); _seekTimeLarge = _settingsService.getSeekTimeLarge(); + _rewindOnResume = _settingsService.getRewindOnResume(); _sleepTimerDuration = _settingsService.getSleepTimerDuration(); _rememberTrackSelections = _settingsService.getRememberTrackSelections(); _clickVideoTogglesPlayback = _settingsService.getClickVideoTogglesPlayback(); @@ -794,6 +797,14 @@ class _SettingsScreenState extends State with FocusableTab { trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1), onTap: () => _showSeekTimeLargeDialog(), ), + ListTile( + focusNode: _focusTracker.get(_kRewindOnResume), + leading: const AppIcon(Symbols.replay_rounded, fill: 1), + title: Text(t.settings.rewindOnResume), + subtitle: Text(t.settings.secondsUnit(seconds: _rewindOnResume.toString())), + trailing: const AppIcon(Symbols.chevron_right_rounded, fill: 1), + onTap: () => _showRewindOnResumeDialog(), + ), ListTile( focusNode: _focusTracker.get(_kDefaultSleepTimer), leading: const AppIcon(Symbols.bedtime_rounded, fill: 1), @@ -1625,6 +1636,23 @@ class _SettingsScreenState extends State with FocusableTab { ); } + void _showRewindOnResumeDialog() { + _showNumericInputDialog( + title: t.settings.rewindOnResume, + labelText: t.settings.secondsLabel, + suffixText: t.settings.secondsShort, + min: 0, + max: 10, + currentValue: _rewindOnResume, + onSave: (value) async { + setState(() { + _rewindOnResume = value; + _settingsService.setRewindOnResume(value); + }); + }, + ); + } + void _showSleepTimerDurationDialog() { _showNumericInputDialog( title: t.settings.defaultSleepTimer, diff --git a/lib/screens/video_player_screen.dart b/lib/screens/video_player_screen.dart index 7730c880..b0113ab7 100644 --- a/lib/screens/video_player_screen.dart +++ b/lib/screens/video_player_screen.dart @@ -202,6 +202,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin bool _wasPlayingBeforeInactive = false; bool _hiddenForBackground = false; bool _autoPipEnabled = false; + int _rewindOnResume = 0; /// Whether to skip lifecycle actions because PiP is active or about to start. /// iOS auto-PiP is system-initiated during the background transition, so @@ -396,6 +397,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin final settingsService = await SettingsService.getInstance(); _videoPlayerNavigationEnabled = settingsService.getVideoPlayerNavigationEnabled(); _autoPipEnabled = settingsService.getAutoPip(); + _rewindOnResume = settingsService.getRewindOnResume(); final bufferSizeMB = settingsService.getBufferSize(); final enableHardwareDecoding = settingsService.getEnableHardwareDecoding(); final debugLoggingEnabled = settingsService.getEnableDebugLogging(); @@ -757,7 +759,8 @@ class VideoPlayerScreenState extends State with WidgetsBindin if (event is PlayEvent) { appLogger.d('Media control: Play event received'); - currentPlayer!.play(); + _seekBackForRewind(currentPlayer!); + currentPlayer.play(); _wasPlayingBeforeInactive = false; _updateMediaControlsPlaybackState(); } else if (event is PauseEvent) { @@ -769,6 +772,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin if (currentPlayer!.state.playing) { currentPlayer.pause(); } else { + _seekBackForRewind(currentPlayer); currentPlayer.play(); _wasPlayingBeforeInactive = false; } @@ -1949,6 +1953,12 @@ class VideoPlayerScreenState extends State with WidgetsBindin ); } + Future _seekBackForRewind(Player p) async { + if (_rewindOnResume <= 0) return; + final target = p.state.position - Duration(seconds: _rewindOnResume); + await p.seek(clampSeekPosition(p, target)); + } + Future _restoreMediaControlsAfterResume() async { if (!_isPlayerInitialized || !mounted) return; @@ -1970,6 +1980,7 @@ class VideoPlayerScreenState extends State with WidgetsBindin if (_wasPlayingBeforeInactive) { try { + await _seekBackForRewind(currentPlayer); await currentPlayer.play(); appLogger.d('Video resumed after returning from inactive state'); } catch (e) { diff --git a/lib/services/settings_service.dart b/lib/services/settings_service.dart index 55851531..3069498d 100644 --- a/lib/services/settings_service.dart +++ b/lib/services/settings_service.dart @@ -37,6 +37,7 @@ class SettingsService extends BaseSharedPreferencesService { static const String _keyEpisodePosterMode = 'episode_poster_mode'; static const String _keySeekTimeSmall = 'seek_time_small'; static const String _keySeekTimeLarge = 'seek_time_large'; + static const String _keyRewindOnResume = 'rewind_on_resume'; static const String _keyMediaVersionPreferences = 'media_version_preferences'; static const String _keyShowHeroSection = 'show_hero_section'; static const String _keyUseGlobalHubs = 'use_global_hubs'; @@ -286,6 +287,15 @@ class SettingsService extends BaseSharedPreferencesService { return prefs.getInt(_keySeekTimeLarge) ?? 30; // Default: 30 seconds } + // Rewind on Resume (in seconds, 0 = disabled) + Future setRewindOnResume(int seconds) async { + await prefs.setInt(_keyRewindOnResume, seconds); + } + + int getRewindOnResume() { + return prefs.getInt(_keyRewindOnResume) ?? 0; // Default: 0 (disabled) + } + // Sleep Timer Duration (in minutes) Future setSleepTimerDuration(int minutes) async { await prefs.setInt(_keySleepTimerDuration, minutes); diff --git a/lib/widgets/video_controls/video_controls.dart b/lib/widgets/video_controls/video_controls.dart index f7155ac8..356cd29a 100644 --- a/lib/widgets/video_controls/video_controls.dart +++ b/lib/widgets/video_controls/video_controls.dart @@ -221,6 +221,7 @@ class _PlexVideoControlsState extends State with WindowListen late final FocusNode _focusNode; KeyboardShortcutsService? _keyboardService; int _seekTimeSmall = 10; // Default, loaded from settings + int _rewindOnResume = 0; // Default, loaded from settings int _audioSyncOffset = 0; // Default, loaded from settings int _subtitleSyncOffset = 0; // Default, loaded from settings bool _isRotationLocked = true; // Default locked (landscape only) @@ -553,6 +554,7 @@ class _PlexVideoControlsState extends State with WindowListen if (mounted) { setState(() { _seekTimeSmall = settingsService.getSeekTimeSmall(); + _rewindOnResume = settingsService.getRewindOnResume(); _audioSyncOffset = settingsService.getAudioSyncOffset(); _subtitleSyncOffset = settingsService.getSubtitleSyncOffset(); _isRotationLocked = settingsService.getRotationLocked(); @@ -1167,6 +1169,15 @@ class _PlexVideoControlsState extends State with WindowListen } } + Future _playOrPause() async { + if (!widget.player.state.playing && _rewindOnResume > 0) { + final target = widget.player.state.position - Duration(seconds: _rewindOnResume); + final clamped = clampSeekPosition(widget.player, target); + await widget.player.seek(clamped); + } + await widget.player.playOrPause(); + } + /// Throttled seek for timeline slider - executes immediately then throttles to 200ms void _throttledSeek(Duration position) => _seekThrottle([position]); @@ -1179,7 +1190,7 @@ class _PlexVideoControlsState extends State with WindowListen /// Handle tap in skip zone for desktop mode void _handleTapInSkipZoneDesktop() { if (widget.canControl && _clickVideoTogglesPlayback) { - widget.player.playOrPause(); + _playOrPause(); } _toggleControls(); @@ -1307,7 +1318,7 @@ class _PlexVideoControlsState extends State with WindowListen // Always perform the single-click behavior immediately if (widget.canControl && _clickVideoTogglesPlayback) { - widget.player.playOrPause(); + _playOrPause(); } else { _toggleControls(); } @@ -1582,7 +1593,7 @@ class _PlexVideoControlsState extends State with WindowListen // Handle play/pause globally - works regardless of focus if (_isPlayPauseActivation(event)) { - widget.player.playOrPause(); + _playOrPause(); _showControlsWithFocus(requestFocus: false); return true; // Event handled, stop propagation } @@ -1773,7 +1784,7 @@ class _PlexVideoControlsState extends State with WindowListen if (isPlayPauseKey) { if (_videoPlayerNavigationEnabled || isMobile) { if (_isPlayPauseActivation(event)) { - widget.player.playOrPause(); + _playOrPause(); _showControlsWithFocus(requestFocus: _videoPlayerNavigationEnabled); } } @@ -1805,7 +1816,7 @@ class _PlexVideoControlsState extends State with WindowListen // Handle Select/Enter when controls are hidden: pause and show controls // Only intercept if this Focus node itself has primary focus (not a descendant) if (_isSelectKey(key) && !_showControls && _focusNode.hasPrimaryFocus) { - widget.player.playOrPause(); + _playOrPause(); _showControlsWithFocus(); return KeyEventResult.handled; }