feat(player): add play-next countdown, gesture toggles, and deinterlacing
Users could not adjust the five-second Play Next timer, switch off the edge-swipe/pinch gestures they trigger by accident, deinterlace DVD-era content, or keep always-on-top across episodes. Play Next Countdown (0-30 s) joins Auto-Play & Skip; zero skips the prompt and starts the next episode immediately, while the transient- retry prompt keeps its fixed five-second spacing. Brightness swipe, volume swipe, and pinch-to-zoom each get a switch in a mobile-only Gestures group; a disabled edge also stops stealing the content-strip drag. A Deinterlacing toggle (mpv-only — ExoPlayer has no filter chain) sets mpv's deinterlace=auto at player init. The player's always-on-top toggle now persists via a pref that is re-applied when the next player opens, including autoplay episode transitions; the window flag is still dropped when the player closes. Also resolves the reorg TODO markers per triage: hold-to-speed lock (#1046) will be gesture behavior rather than a setting, metadata language (#1668) and delete re-authentication (#1924) are rejected, and always-on-top is player-persisted state, not a Window setting (#1386). close #1827 close #1810 close #2149 close #931
This commit is contained in:
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Girişi avtomatik ötür",
|
||||
"autoSkipIntroDescription": "Bir neçə saniyədən sonra giriş işarələrini avtomatik ötür",
|
||||
"autoSkipCredits": "Titrləri avtomatik ötür",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Daxili DV7-ni məcburi et",
|
||||
"dvConversionDv81Description": "Dolby Vision profile 8.1-ə çevrilməni məcburi et",
|
||||
"dvConversionHevcStripDescription": "Dolby Vision təbəqələrini sil və sadə HEVC kimi təqdim et",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Açılışda profil soruş",
|
||||
"requireProfileSelectionOnOpenDescription": "Tətbiq hər dəfə açıldıqda profil seçimini göstər",
|
||||
"forceTvMode": "TV rejimini məcburi et",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Sarğı və vaxt tənzimləməsi",
|
||||
"behavior": "Davranış",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Pleyer dəyişikliklərini yadda saxla",
|
||||
"rememberPlayerChangesDescription": "Oxutma zamanı edilən dəyişikliklərin harada saxlanacağı və yenidən tətbiq ediləcəyi",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Автоматично прескачане на интро",
|
||||
"autoSkipIntroDescription": "Автоматично прескачай интро маркери след няколко секунди",
|
||||
"autoSkipCredits": "Автоматично прескачане на финални надписи",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Принуждава директно възпроизвеждане на DV7 и изключва повторния опит за преобразуване",
|
||||
"dvConversionDv81Description": "Принуждава директно преобразуване на RPU към Dolby Vision Profile 8.1",
|
||||
"dvConversionHevcStripDescription": "Премахва слоевете Dolby Vision RPU/EL и подава обикновен HEVC поток",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Питай за профил при отваряне на приложението",
|
||||
"requireProfileSelectionOnOpenDescription": "Показвай избор на профил всеки път при отваряне на приложението",
|
||||
"forceTvMode": "Принуди TV режим",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Търсене и време",
|
||||
"behavior": "Поведение",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Запомняне на промените в плейъра",
|
||||
"rememberPlayerChangesDescription": "Къде се записва и откъде се прилага отново промяна, направена по време на възпроизвеждане",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Spring intro over automatisk",
|
||||
"autoSkipIntroDescription": "Spring automatisk intromarkører over efter få sekunder",
|
||||
"autoSkipCredits": "Spring rulletekster over automatisk",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Gennemtving indbygget DV7-understøttelse, og undlad at forsøge DV-konvertering igen",
|
||||
"dvConversionDv81Description": "Tving inline RPU-konvertering til Dolby Vision profil 8.1",
|
||||
"dvConversionHevcStripDescription": "Fjern Dolby Vision RPU/EL-lag og brug almindelig HEVC",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Spørg om profil ved åbning",
|
||||
"requireProfileSelectionOnOpenDescription": "Vis profilvalg hver gang appen åbnes",
|
||||
"forceTvMode": "Gennemtving TV-tilstand",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Søgning og timing",
|
||||
"behavior": "Adfærd",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Husk afspillerændringer",
|
||||
"rememberPlayerChangesDescription": "Hvor en ændring under afspilning gemmes og anvendes igen",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Intro automatisch überspringen",
|
||||
"autoSkipIntroDescription": "Intro-Marker nach wenigen Sekunden automatisch überspringen",
|
||||
"autoSkipCredits": "Abspann automatisch überspringen",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Natives DV7 erzwingen und einen erneuten DV-Konvertierungsversuch unterdrücken",
|
||||
"dvConversionDv81Description": "Inline-RPU-Konvertierung in Dolby-Vision-Profil 8.1 erzwingen",
|
||||
"dvConversionHevcStripDescription": "Dolby-Vision-RPU/EL-Schichten entfernen und reines HEVC ausgeben",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Profil beim Öffnen abfragen",
|
||||
"requireProfileSelectionOnOpenDescription": "Profilauswahl bei jedem Öffnen der App anzeigen",
|
||||
"forceTvMode": "TV-Modus erzwingen",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Spulen & Timing",
|
||||
"behavior": "Verhalten",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Playeränderungen merken",
|
||||
"rememberPlayerChangesDescription": "Wo eine während der Wiedergabe vorgenommene Änderung gespeichert und erneut angewendet wird",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "Auto-Play & Skip",
|
||||
"autoPlayNextEpisode": "Auto-Play Next Episode",
|
||||
"autoPlayNextEpisodeDescription": "Start the next episode automatically when one ends",
|
||||
"playNextCountdown": "Play Next Countdown",
|
||||
"playNextCountdownImmediate": "Play immediately",
|
||||
"autoSkipIntro": "Auto Skip Intro",
|
||||
"autoSkipIntroDescription": "Automatically skip intro markers after a few seconds",
|
||||
"autoSkipCredits": "Auto Skip Credits",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Force native DV7 and suppress DV conversion retry",
|
||||
"dvConversionDv81Description": "Force inline RPU conversion to Dolby Vision profile 8.1",
|
||||
"dvConversionHevcStripDescription": "Strip Dolby Vision RPU/EL layers and present plain HEVC",
|
||||
"deinterlace": "Deinterlacing",
|
||||
"deinterlaceDescription": "Remove combing artifacts from interlaced video (mpv player only)",
|
||||
"requireProfileSelectionOnOpen": "Ask for profile on app open",
|
||||
"requireProfileSelectionOnOpenDescription": "Show profile selection every time the app is opened",
|
||||
"forceTvMode": "Force TV mode",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "Subtitles",
|
||||
"seekAndTiming": "Seek & Timing",
|
||||
"behavior": "Behavior",
|
||||
"gestures": "Gestures",
|
||||
"gestureBrightnessSwipe": "Brightness Swipe",
|
||||
"gestureBrightnessSwipeDescription": "Swipe up or down on the left edge to adjust brightness",
|
||||
"gestureVolumeSwipe": "Volume Swipe",
|
||||
"gestureVolumeSwipeDescription": "Swipe up or down on the right edge to adjust volume",
|
||||
"gesturePinchToZoom": "Pinch to Zoom",
|
||||
"gesturePinchToZoomDescription": "Pinch on the video to zoom in or out",
|
||||
"controls": "Controls",
|
||||
"rememberPlayerChanges": "Remember Player Changes",
|
||||
"rememberPlayerChangesDescription": "Where a change made during playback is saved and reapplied from",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Saltar introducción automáticamente",
|
||||
"autoSkipIntroDescription": "Saltar automáticamente los marcadores de introducción después de unos segundos",
|
||||
"autoSkipCredits": "Saltar créditos automáticamente",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Forzar DV7 nativo y suprimir el reintento de conversión DV",
|
||||
"dvConversionDv81Description": "Forzar la conversión de RPU en línea al perfil 8.1 de Dolby Vision",
|
||||
"dvConversionHevcStripDescription": "Eliminar las capas RPU/EL de Dolby Vision y presentar HEVC convencional",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Pedir perfil al abrir la aplicación",
|
||||
"requireProfileSelectionOnOpenDescription": "Mostrar selección de perfil cada vez que se abre la aplicación",
|
||||
"forceTvMode": "Forzar modo TV",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Desplazamiento y tiempos",
|
||||
"behavior": "Comportamiento",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Recordar cambios del reproductor",
|
||||
"rememberPlayerChangesDescription": "Dónde se guarda y se vuelve a aplicar un cambio realizado durante la reproducción",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Passer automatiquement l’introduction",
|
||||
"autoSkipIntroDescription": "Passer automatiquement les marqueurs d’introduction après quelques secondes",
|
||||
"autoSkipCredits": "Passer automatiquement le générique",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Forcer le DV7 natif et bloquer la nouvelle tentative de conversion DV",
|
||||
"dvConversionDv81Description": "Forcer la conversion RPU intégrée vers le profil 8.1 de Dolby Vision",
|
||||
"dvConversionHevcStripDescription": "Supprimer les couches RPU/EL Dolby Vision et présenter du HEVC simple",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Demander le profil à l'ouverture",
|
||||
"requireProfileSelectionOnOpenDescription": "Afficher la sélection de profil à chaque ouverture de l'application",
|
||||
"forceTvMode": "Forcer le mode TV",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Déplacement et minutage",
|
||||
"behavior": "Comportement",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Mémoriser les modifications du lecteur",
|
||||
"rememberPlayerChangesDescription": "Où une modification effectuée pendant la lecture est enregistrée et réappliquée",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Intró automatikus átugrása",
|
||||
"autoSkipIntroDescription": "Az intrójelölők automatikus átugrása néhány másodperc után",
|
||||
"autoSkipCredits": "Stáblista automatikus átugrása",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "A natív DV7 kényszerítése és a DV-átalakítási újrapróbálkozás letiltása",
|
||||
"dvConversionDv81Description": "A közvetlen RPU-átalakítás kényszerítése Dolby Vision Profile 8.1-re",
|
||||
"dvConversionHevcStripDescription": "A Dolby Vision RPU/EL-rétegek eltávolítása és egyszerű HEVC-ként való megjelenítés",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Profil kérése az alkalmazás megnyitásakor",
|
||||
"requireProfileSelectionOnOpenDescription": "Profilválasztó megjelenítése minden alkalommal, amikor az alkalmazást megnyitod",
|
||||
"forceTvMode": "TV-mód kényszerítése",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Tekerés és időzítés",
|
||||
"behavior": "Viselkedés",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Lejátszó módosításainak megjegyzése",
|
||||
"rememberPlayerChangesDescription": "A lejátszás közben végzett módosítások mentési és újbóli alkalmazási helye",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Salta automaticamente la sigla iniziale",
|
||||
"autoSkipIntroDescription": "Salta automaticamente i marcatori della sigla iniziale dopo alcuni secondi",
|
||||
"autoSkipCredits": "Salta automaticamente i titoli di coda",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Forza il DV7 nativo e impedisce nuovi tentativi di conversione DV",
|
||||
"dvConversionDv81Description": "Forza la conversione RPU diretta al profilo Dolby Vision 8.1",
|
||||
"dvConversionHevcStripDescription": "Rimuove i livelli RPU/EL di Dolby Vision e riproduce il video come semplice HEVC",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Chiedi di scegliere il profilo all'apertura",
|
||||
"requireProfileSelectionOnOpenDescription": "Mostra la selezione del profilo ogni volta che l'app viene aperta",
|
||||
"forceTvMode": "Forza modalità TV",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Avanzamento e tempi",
|
||||
"behavior": "Comportamento",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Ricorda le modifiche del lettore",
|
||||
"rememberPlayerChangesDescription": "Dove viene salvata e riapplicata una modifica effettuata durante la riproduzione",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "イントロを自動スキップ",
|
||||
"autoSkipIntroDescription": "数秒後にイントロマーカーを自動的にスキップ",
|
||||
"autoSkipCredits": "クレジットを自動スキップ",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "ネイティブ DV7 を強制し、DV 変換の再試行を抑制します",
|
||||
"dvConversionDv81Description": "Dolby Vision プロファイル 8.1 へのインライン RPU 変換を強制します",
|
||||
"dvConversionHevcStripDescription": "Dolby Vision の RPU/EL レイヤーを削除し、通常の HEVC として扱います",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "アプリ起動時にプロフィールを確認",
|
||||
"requireProfileSelectionOnOpenDescription": "アプリを開くたびにプロフィール選択を表示",
|
||||
"forceTvMode": "TVモードを強制",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "シークとタイミング",
|
||||
"behavior": "動作",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "プレーヤーの変更を記憶",
|
||||
"rememberPlayerChangesDescription": "再生中に行った変更を保存し、再適用する場所",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Киріс бөлімді (Intro) автоматты өткізу",
|
||||
"autoSkipIntroDescription": "Бірнеше секундтан кейін киріс белгілерін автоматты өткізу",
|
||||
"autoSkipCredits": "Титрлерді автоматты өткізу",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Ішкі DV7 режимін мәжбүрлеу",
|
||||
"dvConversionDv81Description": "Dolby Vision profile 8.1 форматына түрлендіру",
|
||||
"dvConversionHevcStripDescription": "Dolby Vision қабаттарын алып тастап, HEVC ретінде көрсету",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Ашқанда профильді сұрау",
|
||||
"requireProfileSelectionOnOpenDescription": "Қолданба ашылған сайын профильді таңдауды көрсету",
|
||||
"forceTvMode": "TV режимін мәжбүрлеу",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Айналдыру және уақытты реттеу",
|
||||
"behavior": "Мінез-құлық",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Ойнатқыш өзгерістерін есте сақтау",
|
||||
"rememberPlayerChangesDescription": "Ойнату кезінде жасалған өзгеріс сақталатын және қайта қолданылатын орын",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "자동으로 오프닝 건너뛰기",
|
||||
"autoSkipIntroDescription": "몇 초 후 오프닝을 자동으로 건너뛰기",
|
||||
"autoSkipCredits": "자동으로 엔딩 건너뛰기",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "네이티브 DV7을 강제하고 DV 변환 재시도를 억제합니다",
|
||||
"dvConversionDv81Description": "Dolby Vision 프로필 8.1로 인라인 RPU 변환을 강제합니다",
|
||||
"dvConversionHevcStripDescription": "Dolby Vision RPU/EL 레이어를 제거하고 일반 HEVC로 제공합니다",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "앱 실행 시 프로필 선택",
|
||||
"requireProfileSelectionOnOpenDescription": "앱을 열 때마다 프로필 선택 화면을 표시합니다",
|
||||
"forceTvMode": "TV 모드 강제 사용",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "탐색 및 타이밍",
|
||||
"behavior": "동작",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "플레이어 변경 사항 기억",
|
||||
"rememberPlayerChangesDescription": "재생 중 변경한 사항을 저장하고 다시 적용할 위치",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Hopp over intro automatisk",
|
||||
"autoSkipIntroDescription": "Hopp automatisk over intromarkører etter noen sekunder",
|
||||
"autoSkipCredits": "Hopp over rulletekst automatisk",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Tving opprinnelig DV7-avspilling og ikke prøv DV-konvertering på nytt",
|
||||
"dvConversionDv81Description": "Tving direkte RPU-konvertering til Dolby Vision-profil 8.1",
|
||||
"dvConversionHevcStripDescription": "Fjern Dolby Vision RPU/EL-lag og lever som vanlig HEVC",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Spør om profil ved appåpning",
|
||||
"requireProfileSelectionOnOpenDescription": "Vis profilvalg hver gang appen åpnes",
|
||||
"forceTvMode": "Tving TV-modus",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Spoling og tidsinnstillinger",
|
||||
"behavior": "Oppførsel",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Husk endringer i spilleren",
|
||||
"rememberPlayerChangesDescription": "Hvor en endring under avspilling lagres og brukes på nytt",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Intro automatisch overslaan",
|
||||
"autoSkipIntroDescription": "Intromarkeringen na enkele seconden automatisch overslaan",
|
||||
"autoSkipCredits": "Aftiteling automatisch overslaan",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Dwing native DV7 af en voorkom een nieuwe poging met DV-conversie",
|
||||
"dvConversionDv81Description": "Dwing directe RPU-conversie naar Dolby Vision-profiel 8.1 af",
|
||||
"dvConversionHevcStripDescription": "Verwijder Dolby Vision RPU/EL-lagen en bied gewone HEVC aan",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Vraag om profiel bij openen",
|
||||
"requireProfileSelectionOnOpenDescription": "Toon profielselectie telkens wanneer de app wordt geopend",
|
||||
"forceTvMode": "Tv-modus afdwingen",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Spoelen en timing",
|
||||
"behavior": "Gedrag",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Spelerwijzigingen onthouden",
|
||||
"rememberPlayerChangesDescription": "Waar een wijziging tijdens het afspelen wordt opgeslagen en opnieuw toegepast",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Automatyczne pomijanie intro",
|
||||
"autoSkipIntroDescription": "Automatycznie pomijaj znaczniki intro po kilku sekundach",
|
||||
"autoSkipCredits": "Automatyczne pomijanie napisów końcowych",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Wymuś natywne DV7 i wyłącz ponowną próbę konwersji DV",
|
||||
"dvConversionDv81Description": "Wymuś wbudowaną konwersję RPU do profilu Dolby Vision 8.1",
|
||||
"dvConversionHevcStripDescription": "Usuń warstwy Dolby Vision RPU/EL i przedstaw zwykłe HEVC",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Pytaj o profil przy otwarciu aplikacji",
|
||||
"requireProfileSelectionOnOpenDescription": "Pokaż wybór profilu za każdym razem, gdy aplikacja jest otwierana",
|
||||
"forceTvMode": "Wymuś tryb TV",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Przewijanie i czas",
|
||||
"behavior": "Zachowanie",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Zapamiętuj zmiany odtwarzacza",
|
||||
"rememberPlayerChangesDescription": "Miejsce zapisywania i ponownego stosowania zmian dokonanych podczas odtwarzania",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Pular introdução automaticamente",
|
||||
"autoSkipIntroDescription": "Pular marcadores de introdução automaticamente após alguns segundos",
|
||||
"autoSkipCredits": "Pular créditos automaticamente",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Forçar DV7 nativo e impedir uma nova tentativa de conversão de DV",
|
||||
"dvConversionDv81Description": "Forçar a conversão RPU integrada para Dolby Vision perfil 8.1",
|
||||
"dvConversionHevcStripDescription": "Remover as camadas RPU/EL do Dolby Vision e apresentar HEVC sem Dolby Vision",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Pedir perfil ao abrir o app",
|
||||
"requireProfileSelectionOnOpenDescription": "Mostrar a seleção de perfil sempre que o app for aberto",
|
||||
"forceTvMode": "Forçar modo TV",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Busca e tempo",
|
||||
"behavior": "Comportamento",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Lembrar alterações do reprodutor",
|
||||
"rememberPlayerChangesDescription": "Onde uma alteração feita durante a reprodução é salva e reaplicada",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Автопропуск вступления",
|
||||
"autoSkipIntroDescription": "Автоматически пропускать маркеры вступления через несколько секунд",
|
||||
"autoSkipCredits": "Автопропуск титров",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Принудительно использовать нативный DV7 и не повторять DV-конвертацию",
|
||||
"dvConversionDv81Description": "Принудительно выполнять inline-конвертацию RPU в Dolby Vision профиль 8.1",
|
||||
"dvConversionHevcStripDescription": "Удалять слои Dolby Vision RPU/EL и передавать обычный HEVC",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Запрашивать профиль при запуске",
|
||||
"requireProfileSelectionOnOpenDescription": "Показывать выбор профиля при каждом открытии приложения",
|
||||
"forceTvMode": "Принудительный режим ТВ",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Перемотка и время",
|
||||
"behavior": "Поведение",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Запоминать изменения плеера",
|
||||
"rememberPlayerChangesDescription": "Где сохраняется и откуда повторно применяется изменение, сделанное во время воспроизведения",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 22
|
||||
/// Strings: 41573 (1889 per locale)
|
||||
/// Strings: 41584 (1890 per locale)
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
@@ -837,6 +837,12 @@ class Translations$settings$en {
|
||||
/// en: 'Start the next episode automatically when one ends'
|
||||
String get autoPlayNextEpisodeDescription => 'Start the next episode automatically when one ends';
|
||||
|
||||
/// en: 'Play Next Countdown'
|
||||
String get playNextCountdown => 'Play Next Countdown';
|
||||
|
||||
/// en: 'Play immediately'
|
||||
String get playNextCountdownImmediate => 'Play immediately';
|
||||
|
||||
/// en: 'Auto Skip Intro'
|
||||
String get autoSkipIntro => 'Auto Skip Intro';
|
||||
|
||||
@@ -1059,6 +1065,12 @@ class Translations$settings$en {
|
||||
/// en: 'Strip Dolby Vision RPU/EL layers and present plain HEVC'
|
||||
String get dvConversionHevcStripDescription => 'Strip Dolby Vision RPU/EL layers and present plain HEVC';
|
||||
|
||||
/// en: 'Deinterlacing'
|
||||
String get deinterlace => 'Deinterlacing';
|
||||
|
||||
/// en: 'Remove combing artifacts from interlaced video (mpv player only)'
|
||||
String get deinterlaceDescription => 'Remove combing artifacts from interlaced video (mpv player only)';
|
||||
|
||||
/// en: 'Ask for profile on app open'
|
||||
String get requireProfileSelectionOnOpen => 'Ask for profile on app open';
|
||||
|
||||
@@ -1161,6 +1173,27 @@ class Translations$settings$en {
|
||||
/// en: 'Behavior'
|
||||
String get behavior => 'Behavior';
|
||||
|
||||
/// en: 'Gestures'
|
||||
String get gestures => 'Gestures';
|
||||
|
||||
/// en: 'Brightness Swipe'
|
||||
String get gestureBrightnessSwipe => 'Brightness Swipe';
|
||||
|
||||
/// en: 'Swipe up or down on the left edge to adjust brightness'
|
||||
String get gestureBrightnessSwipeDescription => 'Swipe up or down on the left edge to adjust brightness';
|
||||
|
||||
/// en: 'Volume Swipe'
|
||||
String get gestureVolumeSwipe => 'Volume Swipe';
|
||||
|
||||
/// en: 'Swipe up or down on the right edge to adjust volume'
|
||||
String get gestureVolumeSwipeDescription => 'Swipe up or down on the right edge to adjust volume';
|
||||
|
||||
/// en: 'Pinch to Zoom'
|
||||
String get gesturePinchToZoom => 'Pinch to Zoom';
|
||||
|
||||
/// en: 'Pinch on the video to zoom in or out'
|
||||
String get gesturePinchToZoomDescription => 'Pinch on the video to zoom in or out';
|
||||
|
||||
/// en: 'Controls'
|
||||
String get controls => 'Controls';
|
||||
|
||||
@@ -7103,6 +7136,8 @@ extension on Translations {
|
||||
'settings.autoPlayAndSkip' => 'Auto-Play & Skip',
|
||||
'settings.autoPlayNextEpisode' => 'Auto-Play Next Episode',
|
||||
'settings.autoPlayNextEpisodeDescription' => 'Start the next episode automatically when one ends',
|
||||
'settings.playNextCountdown' => 'Play Next Countdown',
|
||||
'settings.playNextCountdownImmediate' => 'Play immediately',
|
||||
'settings.autoSkipIntro' => 'Auto Skip Intro',
|
||||
'settings.autoSkipIntroDescription' => 'Automatically skip intro markers after a few seconds',
|
||||
'settings.autoSkipCredits' => 'Auto Skip Credits',
|
||||
@@ -7177,6 +7212,8 @@ extension on Translations {
|
||||
'settings.dvConversionNativeDescription' => 'Force native DV7 and suppress DV conversion retry',
|
||||
'settings.dvConversionDv81Description' => 'Force inline RPU conversion to Dolby Vision profile 8.1',
|
||||
'settings.dvConversionHevcStripDescription' => 'Strip Dolby Vision RPU/EL layers and present plain HEVC',
|
||||
'settings.deinterlace' => 'Deinterlacing',
|
||||
'settings.deinterlaceDescription' => 'Remove combing artifacts from interlaced video (mpv player only)',
|
||||
'settings.requireProfileSelectionOnOpen' => 'Ask for profile on app open',
|
||||
'settings.requireProfileSelectionOnOpenDescription' => 'Show profile selection every time the app is opened',
|
||||
'settings.forceTvMode' => 'Force TV mode',
|
||||
@@ -7211,6 +7248,13 @@ extension on Translations {
|
||||
'settings.subtitles' => 'Subtitles',
|
||||
'settings.seekAndTiming' => 'Seek & Timing',
|
||||
'settings.behavior' => 'Behavior',
|
||||
'settings.gestures' => 'Gestures',
|
||||
'settings.gestureBrightnessSwipe' => 'Brightness Swipe',
|
||||
'settings.gestureBrightnessSwipeDescription' => 'Swipe up or down on the left edge to adjust brightness',
|
||||
'settings.gestureVolumeSwipe' => 'Volume Swipe',
|
||||
'settings.gestureVolumeSwipeDescription' => 'Swipe up or down on the right edge to adjust volume',
|
||||
'settings.gesturePinchToZoom' => 'Pinch to Zoom',
|
||||
'settings.gesturePinchToZoomDescription' => 'Pinch on the video to zoom in or out',
|
||||
'settings.controls' => 'Controls',
|
||||
'settings.rememberPlayerChanges' => 'Remember Player Changes',
|
||||
'settings.rememberPlayerChangesDescription' => 'Where a change made during playback is saved and reapplied from',
|
||||
@@ -7352,6 +7396,8 @@ extension on Translations {
|
||||
'fileInfo.sourceKind' => 'Source Kind',
|
||||
'fileInfo.optimizedVersion' => 'Optimized Version',
|
||||
'fileInfo.optimizationTarget' => 'Optimization Target',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'fileInfo.deletedAt' => 'Deleted',
|
||||
'fileInfo.remoteSource' => 'Remote Source',
|
||||
'fileInfo.infiniteStream' => 'Infinite Stream',
|
||||
@@ -7363,8 +7409,6 @@ extension on Translations {
|
||||
'fileInfo.fileId' => 'File ID',
|
||||
'fileInfo.defaultAudioTrack' => 'Default Audio Track',
|
||||
'fileInfo.defaultSubtitleTrack' => 'Default Subtitle Track',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'fileInfo.subtitlesOff' => 'Off',
|
||||
'fileInfo.flagDefault' => 'Default',
|
||||
'fileInfo.flagForced' => 'Forced',
|
||||
@@ -7866,6 +7910,8 @@ extension on Translations {
|
||||
'libraries.filterCategories.unplayed' => 'Unplayed',
|
||||
'libraries.filterCategories.favorites' => 'Favorites',
|
||||
'libraries.sortLabels.title' => 'Title',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'libraries.sortLabels.dateAdded' => 'Date Added',
|
||||
'libraries.sortLabels.releaseDate' => 'Release Date',
|
||||
'libraries.sortLabels.rating' => 'Rating',
|
||||
@@ -7877,8 +7923,6 @@ extension on Translations {
|
||||
'libraries.sortLabels.productionYear' => 'Production Year',
|
||||
'libraries.sortLabels.runtime' => 'Runtime',
|
||||
'libraries.sortLabels.officialRating' => 'Official Rating',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'libraries.sortLabels.premiereDate' => 'Premiere Date',
|
||||
'libraries.sortLabels.startDate' => 'Start Date',
|
||||
'libraries.sortLabels.airTime' => 'Air Time',
|
||||
@@ -8380,6 +8424,8 @@ extension on Translations {
|
||||
'downloads.syncRuleAvailable' => 'Available',
|
||||
'downloads.syncRuleOffline' => 'Offline',
|
||||
'downloads.syncRuleSignInRequired' => 'Sign in required',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'downloads.syncRuleNotAvailableForProfile' => 'Not available for current profile',
|
||||
'downloads.syncRuleUnknownServer' => 'Unknown server',
|
||||
'downloads.syncRuleListCreated' => 'Sync rule created',
|
||||
@@ -8391,8 +8437,6 @@ extension on Translations {
|
||||
'downloads.backgroundWarning.sheetIntro' => 'Android is preventing Plezy from downloading reliably in the background.',
|
||||
'downloads.backgroundWarning.sheetIntroDegraded' => 'Your device is limiting when Plezy can download in the background.',
|
||||
'downloads.backgroundWarning.reasonBackgroundRestricted' => 'Plezy\'s background usage is restricted. Set its battery or background usage to "Unrestricted".',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'downloads.backgroundWarning.reasonStandbyRestricted' => 'Android has put Plezy in a restricted standby state. Set its battery usage to "Unrestricted".',
|
||||
'downloads.backgroundWarning.reasonDownloadChannelBlocked' => 'Download notifications are turned off, so progress and controls may be unavailable.',
|
||||
'downloads.backgroundWarning.reasonNotificationsDisabled' => 'Notifications are turned off. On Android 13 or newer, they are required for long background downloads.',
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Hoppa över intro automatiskt",
|
||||
"autoSkipIntroDescription": "Hoppa automatiskt över intromarkörer efter några sekunder",
|
||||
"autoSkipCredits": "Hoppa över eftertexter automatiskt",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Tvinga inbyggd DV7 och förhindra nya försök med DV-konvertering",
|
||||
"dvConversionDv81Description": "Tvinga direkt RPU-konvertering till Dolby Vision-profil 8.1",
|
||||
"dvConversionHevcStripDescription": "Ta bort Dolby Visions RPU-/EL-lager och använd vanlig HEVC",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Fråga efter profil vid appstart",
|
||||
"requireProfileSelectionOnOpenDescription": "Visa profilval varje gång appen öppnas",
|
||||
"forceTvMode": "Tvinga TV-läge",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Spolning och tidsinställningar",
|
||||
"behavior": "Beteende",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Kom ihåg spelarändringar",
|
||||
"rememberPlayerChangesDescription": "Var en ändring under uppspelning sparas och tillämpas igen",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Jeneriği Otomatik Atla",
|
||||
"autoSkipIntroDescription": "Kaç saniye sonra jenerik işaretçilerini otomatik atla",
|
||||
"autoSkipCredits": "Emeği Geçenleri Otomatik Atla",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Yerel DV7'yi zorla ve DV dönüştürme yeniden denemesini engelle",
|
||||
"dvConversionDv81Description": "Dolby Vision profile 8.1'e satır içi RPU dönüştürmeyi zorla",
|
||||
"dvConversionHevcStripDescription": "Dolby Vision RPU/EL katmanlarını soy ve düz HEVC olarak sun",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Uygulama açılışında profil sor",
|
||||
"requireProfileSelectionOnOpenDescription": "Uygulama her açıldığında profil seçimini göster",
|
||||
"forceTvMode": "TV modunu zorla",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Sarılma ve Zamanlama",
|
||||
"behavior": "Davranış",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Oynatıcı değişikliklerini hatırla",
|
||||
"rememberPlayerChangesDescription": "Oynatma sırasında yapılan değişikliklerin kaydedilip yeniden uygulanacağı yer",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "Kirish qismini (Intro) avtomatik oʻtkazish",
|
||||
"autoSkipIntroDescription": "Bir necha soniyadan soʻng kirish qismlarini avtomatik oʻtkazish",
|
||||
"autoSkipCredits": "Titrlarni avtomatik oʻtkazish",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "Ichki DV7 rejimini majburlash",
|
||||
"dvConversionDv81Description": "Dolby Vision profile 8.1 formatiga oʻtkazish",
|
||||
"dvConversionHevcStripDescription": "Dolby Vision qatlamlarini olib tashlash va HEVC sifatida koʻrsatish",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "Ochilganda profilni soʻrash",
|
||||
"requireProfileSelectionOnOpenDescription": "Ilova ochilgan har safar profilni tanlashni koʻrsatish",
|
||||
"forceTvMode": "TV rejimini majburlash",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "Oʻtkazish va vaqt sozlamalari",
|
||||
"behavior": "Xatti-harakat",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "Pleyer oʻzgarishlarini eslab qolish",
|
||||
"rememberPlayerChangesDescription": "Ijro vaqtida qilingan oʻzgarish qayerda saqlanishi va qayta qoʻllanishi",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "自動跳過片頭",
|
||||
"autoSkipIntroDescription": "幾秒鐘後自動跳過片頭標記",
|
||||
"autoSkipCredits": "自動跳過片尾",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "強制使用原生 DV7 並停用 DV 轉換重試",
|
||||
"dvConversionDv81Description": "強制將內嵌的 RPU 轉換為 Dolby Vision Profile 8.1",
|
||||
"dvConversionHevcStripDescription": "移除 Dolby Vision RPU/EL 層,並以一般 HEVC 呈現",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "開啟應用程式時要求選擇使用者",
|
||||
"requireProfileSelectionOnOpenDescription": "每次開啟應用程式時顯示使用者設定檔選擇畫面",
|
||||
"forceTvMode": "強制 TV 模式",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "跳轉與計時",
|
||||
"behavior": "行為",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "記住播放器變更",
|
||||
"rememberPlayerChangesDescription": "播放期間所做的變更要儲存並從何處重新套用",
|
||||
|
||||
@@ -266,6 +266,8 @@
|
||||
"autoPlayAndSkip": "",
|
||||
"autoPlayNextEpisode": "",
|
||||
"autoPlayNextEpisodeDescription": "",
|
||||
"playNextCountdown": "",
|
||||
"playNextCountdownImmediate": "",
|
||||
"autoSkipIntro": "自动跳过片头",
|
||||
"autoSkipIntroDescription": "几秒钟后自动跳过片头标记",
|
||||
"autoSkipCredits": "自动跳过片尾",
|
||||
@@ -340,6 +342,8 @@
|
||||
"dvConversionNativeDescription": "强制原生 DV7 并禁止重试 DV 转换",
|
||||
"dvConversionDv81Description": "强制内联 RPU 转换为 Dolby Vision Profile 8.1",
|
||||
"dvConversionHevcStripDescription": "移除 Dolby Vision RPU/EL 层并呈现普通 HEVC",
|
||||
"deinterlace": "",
|
||||
"deinterlaceDescription": "",
|
||||
"requireProfileSelectionOnOpen": "打开应用时选择用户资料",
|
||||
"requireProfileSelectionOnOpenDescription": "每次打开应用时都显示用户资料选择界面",
|
||||
"forceTvMode": "强制 TV 模式",
|
||||
@@ -374,6 +378,13 @@
|
||||
"subtitles": "",
|
||||
"seekAndTiming": "跳转与计时",
|
||||
"behavior": "行为",
|
||||
"gestures": "",
|
||||
"gestureBrightnessSwipe": "",
|
||||
"gestureBrightnessSwipeDescription": "",
|
||||
"gestureVolumeSwipe": "",
|
||||
"gestureVolumeSwipeDescription": "",
|
||||
"gesturePinchToZoom": "",
|
||||
"gesturePinchToZoomDescription": "",
|
||||
"controls": "",
|
||||
"rememberPlayerChanges": "记住播放器更改",
|
||||
"rememberPlayerChangesDescription": "播放期间所做的更改保存并重新应用的位置",
|
||||
|
||||
@@ -32,15 +32,7 @@ class GeneralSettingsScreen extends StatelessWidget {
|
||||
return SettingsPage(
|
||||
title: Text(t.settings.general),
|
||||
children: [
|
||||
SettingsGroup(
|
||||
title: t.settings.languageAndRegion,
|
||||
children: [
|
||||
_languageSelector(context),
|
||||
// TODO: Metadata language for Plex discover/metadata queries (#1668)
|
||||
// goes here once the client threads a language override through
|
||||
// the catalog/watchlist requests.
|
||||
],
|
||||
),
|
||||
SettingsGroup(title: t.settings.languageAndRegion, children: [_languageSelector(context)]),
|
||||
|
||||
SettingsGroup(
|
||||
title: t.settings.startup,
|
||||
@@ -77,9 +69,6 @@ class GeneralSettingsScreen extends StatelessWidget {
|
||||
title: t.settings.startInFullscreen,
|
||||
subtitle: t.settings.startInFullscreenDescription,
|
||||
),
|
||||
// TODO: Always-on-top selector (Off / Always / While playing)
|
||||
// (#1386, #931) goes here; needs a persisted pref replacing the
|
||||
// session-only window flag.
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
@@ -66,9 +66,6 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
||||
(svc.read(SettingsService.matchRefreshRate) || svc.read(SettingsService.matchDynamicRange))) ||
|
||||
(Platform.isAndroid &&
|
||||
(svc.read(SettingsService.matchContentFrameRate) || svc.read(SettingsService.matchContentResolution)));
|
||||
// Every Video & Display tile is platform-gated; skip the group where
|
||||
// none can appear so it does not render as an empty card.
|
||||
final hasVideoDisplayGroup = Platform.isAndroid || Platform.isWindows || showDisplaySwitchDelay;
|
||||
|
||||
return SettingsPage(
|
||||
title: Text(t.settings.videoPlayback),
|
||||
@@ -86,24 +83,22 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
||||
],
|
||||
),
|
||||
|
||||
if (hasVideoDisplayGroup)
|
||||
SettingsGroup(
|
||||
title: t.settings.videoAndDisplay,
|
||||
children: [
|
||||
if (Platform.isAndroid) _matchContentFrameRateTile(),
|
||||
if (Platform.isAndroid && PlatformDetector.isTV()) _matchContentResolutionTile(),
|
||||
if (Platform.isWindows) _matchRefreshRateTile(),
|
||||
if (Platform.isWindows) _matchDynamicRangeTile(),
|
||||
if (showDisplaySwitchDelay) _displaySwitchDelayTile(),
|
||||
if (Platform.isAndroid) _dvConversionModeTile(),
|
||||
// TODO: Deinterlace toggle (#2149) goes here, mpv-only —
|
||||
// record ExoPlayer as intentionally unsupported and drop the
|
||||
// hasVideoDisplayGroup gate once the tile exists on every
|
||||
// mpv platform.
|
||||
// TODO: "Extend video into display cutout" toggle (#1769)
|
||||
// goes here, Android-only.
|
||||
],
|
||||
),
|
||||
SettingsGroup(
|
||||
title: t.settings.videoAndDisplay,
|
||||
children: [
|
||||
if (Platform.isAndroid) _matchContentFrameRateTile(),
|
||||
if (Platform.isAndroid && PlatformDetector.isTV()) _matchContentResolutionTile(),
|
||||
if (Platform.isWindows) _matchRefreshRateTile(),
|
||||
if (Platform.isWindows) _matchDynamicRangeTile(),
|
||||
if (showDisplaySwitchDelay) _displaySwitchDelayTile(),
|
||||
if (Platform.isAndroid) _dvConversionModeTile(),
|
||||
// mpv-only (#2149): ExoPlayer has no filter chain, so the
|
||||
// tile disappears while the ExoPlayer backend is active.
|
||||
if (!exoActive) _deinterlaceTile(),
|
||||
// TODO: "Extend video into display cutout" toggle (#1769)
|
||||
// goes here, Android-only.
|
||||
],
|
||||
),
|
||||
|
||||
SettingsGroup(
|
||||
title: t.settings.audio,
|
||||
@@ -146,9 +141,7 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
||||
_seekAndTimingGroup(),
|
||||
_autoPlayAndSkipGroup(),
|
||||
_behaviorGroup(context, isMobile),
|
||||
// TODO: Gestures group (#1810, #1046) goes here, mobile-only:
|
||||
// per-gesture toggles for volume, brightness, zoom, and the
|
||||
// hold-to-speed gesture during playback.
|
||||
if (isMobile) _gesturesGroup(),
|
||||
_rememberPlayerChangesGroup(),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
@@ -314,8 +307,17 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
||||
title: t.settings.autoPlayNextEpisode,
|
||||
subtitle: t.settings.autoPlayNextEpisodeDescription,
|
||||
),
|
||||
// TODO: "Play next countdown" duration tile (#1827) goes here, beside
|
||||
// autoPlayNextEpisode.
|
||||
SettingNumberTile(
|
||||
pref: SettingsService.playNextCountdown,
|
||||
icon: Symbols.timer_rounded,
|
||||
title: t.settings.playNextCountdown,
|
||||
subtitleBuilder: (v) =>
|
||||
v == 0 ? t.settings.playNextCountdownImmediate : t.settings.secondsUnit(seconds: v.toString()),
|
||||
labelText: t.settings.secondsLabel,
|
||||
suffixText: t.settings.secondsShort,
|
||||
min: 0,
|
||||
max: 30,
|
||||
),
|
||||
// TODO: Replace the two auto-skip switches below with per-marker skip
|
||||
// modes — Off / Show button / Auto (#2138); migrate true→auto,
|
||||
// false→button in SettingsService.
|
||||
@@ -364,6 +366,32 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
||||
],
|
||||
);
|
||||
|
||||
/// Optional touch gestures on the player surface (#1810); the group only
|
||||
/// renders on mobile, matching where the gestures exist.
|
||||
Widget _gesturesGroup() => SettingsGroup(
|
||||
title: t.settings.gestures,
|
||||
children: [
|
||||
SettingSwitchTile(
|
||||
pref: SettingsService.gestureBrightnessSwipe,
|
||||
icon: Symbols.brightness_6_rounded,
|
||||
title: t.settings.gestureBrightnessSwipe,
|
||||
subtitle: t.settings.gestureBrightnessSwipeDescription,
|
||||
),
|
||||
SettingSwitchTile(
|
||||
pref: SettingsService.gestureVolumeSwipe,
|
||||
icon: Symbols.volume_up_rounded,
|
||||
title: t.settings.gestureVolumeSwipe,
|
||||
subtitle: t.settings.gestureVolumeSwipeDescription,
|
||||
),
|
||||
SettingSwitchTile(
|
||||
pref: SettingsService.gesturePinchToZoom,
|
||||
icon: Symbols.pinch_rounded,
|
||||
title: t.settings.gesturePinchToZoom,
|
||||
subtitle: t.settings.gesturePinchToZoomDescription,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _playerBackendSelector() => SettingSegmentedTile<bool>(
|
||||
pref: SettingsService.useExoPlayer,
|
||||
icon: Symbols.play_circle_rounded,
|
||||
@@ -436,6 +464,13 @@ class _PlaybackSettingsScreenState extends State<PlaybackSettingsScreen> {
|
||||
subtitle: t.settings.matchDynamicRangeDescription,
|
||||
);
|
||||
|
||||
Widget _deinterlaceTile() => SettingSwitchTile(
|
||||
pref: SettingsService.deinterlace,
|
||||
icon: Symbols.deblur_rounded,
|
||||
title: t.settings.deinterlace,
|
||||
subtitle: t.settings.deinterlaceDescription,
|
||||
);
|
||||
|
||||
Widget _audioPassthroughTile() => SettingSwitchTile(
|
||||
pref: SettingsService.audioPassthrough,
|
||||
icon: Symbols.surround_sound_rounded,
|
||||
|
||||
@@ -435,8 +435,6 @@ class _SettingsScreenState extends State<SettingsScreen> with FocusableTab, Moun
|
||||
subtitle: t.settings.autoRemoveWatchedDownloadsDescription,
|
||||
),
|
||||
if (_backgroundWorkDiagnostics.isSupported) _buildBackgroundDownloadsTile(),
|
||||
// TODO: "Require authentication to delete downloads" toggle (#1924)
|
||||
// goes here, backed by local device authentication.
|
||||
// TODO: "Remove orphaned downloads" toggle (#1413) goes here, next to
|
||||
// autoRemoveWatchedDownloads.
|
||||
],
|
||||
|
||||
@@ -142,6 +142,8 @@ extension _VideoPlayerBuildMethods on VideoPlayerScreenState {
|
||||
}
|
||||
|
||||
void _startMobileZoomGesture() {
|
||||
// Pinch-to-zoom is one of the optional touch gestures (#1810).
|
||||
if (!SettingsService.instance.read(SettingsService.gesturePinchToZoom)) return;
|
||||
final filterManager = _videoFilterManager;
|
||||
if (filterManager == null || _isPinchZooming) return;
|
||||
|
||||
|
||||
@@ -90,15 +90,18 @@ extension _VideoPlayerPlaybackPromptMethods on VideoPlayerScreenState {
|
||||
final settings = await SettingsService.getInstance();
|
||||
if (!mounted) return;
|
||||
final autoPlayEnabled = settings.read(SettingsService.autoPlayNextEpisode);
|
||||
final countdownSeconds = settings.read(SettingsService.playNextCountdown);
|
||||
|
||||
if (skipAutoPlayCountdown && autoPlayEnabled) {
|
||||
// A zero countdown (#1827) behaves like the PiP path above: no prompt,
|
||||
// straight into the next episode.
|
||||
if (autoPlayEnabled && (skipAutoPlayCountdown || countdownSeconds == 0)) {
|
||||
unawaited(_playNext());
|
||||
return;
|
||||
}
|
||||
|
||||
_setPlayerState(() {
|
||||
_episode.showPlayNextDialog = true;
|
||||
_episode.autoPlayCountdown.value = autoPlayEnabled ? 5 : -1;
|
||||
_episode.autoPlayCountdown.value = autoPlayEnabled ? countdownSeconds : -1;
|
||||
});
|
||||
|
||||
// Auto-focus Play Next button on TV when dialog appears (only in keyboard/TV mode)
|
||||
@@ -176,6 +179,9 @@ extension _VideoPlayerPlaybackPromptMethods on VideoPlayerScreenState {
|
||||
|
||||
_setPlayerState(() {
|
||||
_episode.showPlayNextDialog = true;
|
||||
// Deliberately not [SettingsService.playNextCountdown]: this countdown
|
||||
// spaces transient-failure retries (see completion_latch.dart), so a
|
||||
// user preference of 0 must not collapse it into a hot retry loop.
|
||||
_episode.autoPlayCountdown.value = countdown ? 5 : -1;
|
||||
});
|
||||
|
||||
|
||||
@@ -1346,6 +1346,18 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
||||
}
|
||||
await currentPlayer.setProperty('hwdec', _getHwdecValue(enableHardwareDecoding));
|
||||
|
||||
// Deinterlacing (#2149) is mpv-only by design — ExoPlayer has no filter
|
||||
// chain. `auto` deinterlaces only content flagged interlaced. Wrapped:
|
||||
// a preference must never abort player initialization (an older core
|
||||
// that rejects `auto` just keeps its default).
|
||||
if (!(Platform.isAndroid && useExoPlayer) && settingsService.read(SettingsService.deinterlace)) {
|
||||
try {
|
||||
await currentPlayer.setProperty('deinterlace', 'auto');
|
||||
} catch (e) {
|
||||
appLogger.w('VideoPlayerScreen: deinterlace not applied', error: e);
|
||||
}
|
||||
}
|
||||
|
||||
// Subtitle styling is a preference, never a reason to fail playback.
|
||||
// mpv 0.40's OPT_COLOR parser accepts only #RRGGBB/#AARRGGBB (or
|
||||
// r/g/b/a floats), so a stored colour that does not parse would make
|
||||
|
||||
@@ -540,6 +540,27 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
static const musicVolume = DoublePref('music_volume', defaultValue: 100.0);
|
||||
static const autoPlayNextEpisode = BoolPref('auto_play_next_episode', defaultValue: true);
|
||||
|
||||
/// Seconds the Play Next prompt counts down before auto-advancing (#1827).
|
||||
/// 0 skips the prompt entirely and starts the next episode immediately.
|
||||
/// Only consulted while [autoPlayNextEpisode] is on.
|
||||
static final playNextCountdown = IntPref('play_next_countdown', defaultValue: 5, transform: (v) => v.clamp(0, 30));
|
||||
|
||||
/// Touch gestures on the player surface (#1810). Each defaults on; the
|
||||
/// edge swipes gate [MobileEdgeAdjustmentTracker] tracking and the zoom
|
||||
/// pref gates the pinch recognizer in the player screen.
|
||||
static const gestureBrightnessSwipe = BoolPref('gesture_brightness_swipe', defaultValue: true);
|
||||
static const gestureVolumeSwipe = BoolPref('gesture_volume_swipe', defaultValue: true);
|
||||
static const gesturePinchToZoom = BoolPref('gesture_pinch_to_zoom', defaultValue: true);
|
||||
|
||||
/// Deinterlace interlaced video via mpv's `deinterlace=auto` (#2149).
|
||||
/// mpv-only by design: ExoPlayer has no filter chain.
|
||||
static const deinterlace = BoolPref('deinterlace');
|
||||
|
||||
/// Remembered state of the player's always-on-top toggle (#931). The window
|
||||
/// flag itself is only held while a player is open; this pref re-applies it
|
||||
/// on the next playback (including autoplay episode transitions).
|
||||
static const playerAlwaysOnTop = BoolPref('player_always_on_top');
|
||||
|
||||
/// Where Specials (season 0) land in the episode watch order (#1416/#1952).
|
||||
/// Consumed by [sortEpisodesByWatchOrder] (Jellyfin online queue, offline
|
||||
/// next/prev, download/sync "next N", offline OnDeck, Plex fallback queue)
|
||||
@@ -1089,6 +1110,12 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
dvConversionMode,
|
||||
musicVolume,
|
||||
autoPlayNextEpisode,
|
||||
playNextCountdown,
|
||||
gestureBrightnessSwipe,
|
||||
gestureVolumeSwipe,
|
||||
gesturePinchToZoom,
|
||||
deinterlace,
|
||||
playerAlwaysOnTop,
|
||||
specialsOrdering,
|
||||
useExoPlayer,
|
||||
startupSection,
|
||||
|
||||
@@ -68,14 +68,24 @@ class MobileEdgeAdjustmentTracker {
|
||||
bool get isActive => _active;
|
||||
MobileEdgeAdjustmentSide? get side => _side;
|
||||
|
||||
MobileEdgeAdjustmentEvent pointerDown(int pointer, Offset position, Size size) {
|
||||
/// [isSideEnabled] filters which edges may start a gesture (#1810). The
|
||||
/// pointer is still registered either way so multi-touch chord blocking
|
||||
/// keeps seeing every finger.
|
||||
MobileEdgeAdjustmentEvent pointerDown(
|
||||
int pointer,
|
||||
Offset position,
|
||||
Size size, {
|
||||
bool Function(MobileEdgeAdjustmentSide side)? isSideEnabled,
|
||||
}) {
|
||||
_activePointers.add(pointer);
|
||||
if (_blockedUntilAllPointersUp) return const MobileEdgeAdjustmentEvent.none();
|
||||
if (_activePointers.length > 1) return _cancelTracking(clearPointers: false, blockUntilAllPointersUp: true);
|
||||
if (_trackedPointer != null) return const MobileEdgeAdjustmentEvent.none();
|
||||
|
||||
final side = mobileEdgeAdjustmentZoneForPosition(position: position, size: size);
|
||||
if (side == null) return const MobileEdgeAdjustmentEvent.none();
|
||||
if (side == null || (isSideEnabled != null && !isSideEnabled(side))) {
|
||||
return const MobileEdgeAdjustmentEvent.none();
|
||||
}
|
||||
|
||||
_trackedPointer = pointer;
|
||||
_startPosition = position;
|
||||
|
||||
@@ -279,7 +279,12 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
|
||||
final hit = _edgeAdjustmentSurfaceHit(event.position);
|
||||
_handleEdgeAdjustmentEvent(
|
||||
_mobileTouchGesturesAllowed && hit != null
|
||||
? _edgeAdjustmentTracker.pointerDown(event.pointer, hit.position, hit.size)
|
||||
? _edgeAdjustmentTracker.pointerDown(
|
||||
event.pointer,
|
||||
hit.position,
|
||||
hit.size,
|
||||
isSideEnabled: _edgeAdjustmentGestureEnabled,
|
||||
)
|
||||
: const MobileEdgeAdjustmentEvent.none(),
|
||||
);
|
||||
}
|
||||
@@ -347,12 +352,22 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
|
||||
return (position: renderObject.globalToLocal(globalPosition), size: renderObject.size);
|
||||
}
|
||||
|
||||
/// Whether a global touch position sits in an edge zone that can actually
|
||||
/// start a gesture — a disabled swipe (#1810) must not keep stealing the
|
||||
/// content-strip drag.
|
||||
bool _isGlobalPositionInEdgeAdjustmentZone(Offset globalPosition) {
|
||||
final hit = _edgeAdjustmentSurfaceHit(globalPosition);
|
||||
if (hit == null) return false;
|
||||
return mobileEdgeAdjustmentZoneForPosition(position: hit.position, size: hit.size) != null;
|
||||
final side = mobileEdgeAdjustmentZoneForPosition(position: hit.position, size: hit.size);
|
||||
return side != null && _edgeAdjustmentGestureEnabled(side);
|
||||
}
|
||||
|
||||
/// Left edge adjusts brightness, right edge volume — mirror that split for
|
||||
/// the per-gesture prefs.
|
||||
bool _edgeAdjustmentGestureEnabled(MobileEdgeAdjustmentSide side) => SettingsService.instance.read(
|
||||
side == MobileEdgeAdjustmentSide.left ? SettingsService.gestureBrightnessSwipe : SettingsService.gestureVolumeSwipe,
|
||||
);
|
||||
|
||||
void _refreshDeviceAdjustmentValues() {
|
||||
unawaited(_readEdgeAdjustmentValue(MobileEdgeAdjustmentSide.left));
|
||||
unawaited(_readEdgeAdjustmentValue(MobileEdgeAdjustmentSide.right));
|
||||
|
||||
@@ -191,9 +191,15 @@ extension _PlexVideoControlsVisibilityMethods on _PlexVideoControlsState {
|
||||
await FullscreenStateManager().toggleFullscreen();
|
||||
}
|
||||
|
||||
/// Initialize always-on-top state from window manager (desktop only)
|
||||
/// Initialize always-on-top state (desktop only). The toggle is remembered
|
||||
/// across player sessions via [SettingsService.playerAlwaysOnTop] (#931) —
|
||||
/// including episode transitions, which rebuild these controls — while the
|
||||
/// window flag itself is only held while a player is open (dispose drops
|
||||
/// the flag without touching the pref).
|
||||
Future<void> _initAlwaysOnTopState() async {
|
||||
final isOnTop = await windowManager.isAlwaysOnTop();
|
||||
final remembered = SettingsService.instance.read(SettingsService.playerAlwaysOnTop);
|
||||
if (remembered) await windowManager.setAlwaysOnTop(true);
|
||||
final isOnTop = remembered || await windowManager.isAlwaysOnTop();
|
||||
if (mounted && isOnTop != _isAlwaysOnTop) {
|
||||
_setControlsState(() {
|
||||
_isAlwaysOnTop = isOnTop;
|
||||
@@ -207,6 +213,7 @@ extension _PlexVideoControlsVisibilityMethods on _PlexVideoControlsState {
|
||||
|
||||
final newValue = !_isAlwaysOnTop;
|
||||
await windowManager.setAlwaysOnTop(newValue);
|
||||
unawaited(SettingsService.instance.write(SettingsService.playerAlwaysOnTop, newValue));
|
||||
if (!mounted) return;
|
||||
_setControlsState(() {
|
||||
_isAlwaysOnTop = newValue;
|
||||
|
||||
@@ -68,4 +68,93 @@ void main() {
|
||||
expect(settings.prefs.getString(SettingsService.shaderPresetScope.key), 'library');
|
||||
expect(find.descendant(of: tile, matching: find.textContaining('Per library')), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('changes the play next countdown and labels zero as immediate', (tester) async {
|
||||
tester.view.devicePixelRatio = 1;
|
||||
tester.view.physicalSize = const Size(1000, 1400);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(theme: monoTheme(dark: true), home: const PlaybackSettingsScreen()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final title = find.text('Play Next Countdown');
|
||||
await tester.scrollUntilVisible(title, 500, scrollable: find.byType(Scrollable).first);
|
||||
await tester.ensureVisible(title);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(title);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byType(TextField), '0');
|
||||
await tester.tap(find.text('Save'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final settings = SettingsService.instance;
|
||||
expect(settings.read(SettingsService.playNextCountdown), 0);
|
||||
final tile = find.widgetWithText(ListTile, 'Play Next Countdown');
|
||||
expect(find.descendant(of: tile, matching: find.text('Play immediately')), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('toggles deinterlacing on the mpv path', (tester) async {
|
||||
tester.view.devicePixelRatio = 1;
|
||||
tester.view.physicalSize = const Size(1000, 1400);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
|
||||
await tester.pumpWidget(MaterialApp(theme: monoTheme(dark: true), home: const PlaybackSettingsScreen()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final title = find.text('Deinterlacing');
|
||||
await tester.scrollUntilVisible(title, 500, scrollable: find.byType(Scrollable).first);
|
||||
await tester.ensureVisible(title);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(title);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(SettingsService.instance.read(SettingsService.deinterlace), isTrue);
|
||||
});
|
||||
|
||||
testWidgets('gesture toggles render on mobile and persist', (tester) async {
|
||||
tester.view.devicePixelRatio = 1;
|
||||
tester.view.physicalSize = const Size(1000, 1400);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: monoTheme(dark: true).copyWith(platform: TargetPlatform.android),
|
||||
home: const PlaybackSettingsScreen(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final title = find.text('Volume Swipe');
|
||||
await tester.scrollUntilVisible(title, 500, scrollable: find.byType(Scrollable).first);
|
||||
await tester.ensureVisible(title);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(title);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final settings = SettingsService.instance;
|
||||
expect(settings.read(SettingsService.gestureVolumeSwipe), isFalse);
|
||||
expect(settings.read(SettingsService.gestureBrightnessSwipe), isTrue);
|
||||
expect(settings.read(SettingsService.gesturePinchToZoom), isTrue);
|
||||
});
|
||||
|
||||
testWidgets('gesture toggles stay hidden on non-mobile layouts', (tester) async {
|
||||
tester.view.devicePixelRatio = 1;
|
||||
tester.view.physicalSize = const Size(1000, 1400);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: monoTheme(dark: true).copyWith(platform: TargetPlatform.macOS),
|
||||
home: const PlaybackSettingsScreen(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Gestures'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,6 +34,43 @@ void main() {
|
||||
expect(event.deltaFraction, greaterThan(0));
|
||||
});
|
||||
|
||||
test('a disabled side never becomes a candidate', () {
|
||||
final tracker = MobileEdgeAdjustmentTracker(verticalSlop: 10, verticalDominance: 1.5);
|
||||
final event = tracker.pointerDown(
|
||||
1,
|
||||
const Offset(40, 300),
|
||||
size,
|
||||
isSideEnabled: (side) => side != MobileEdgeAdjustmentSide.left,
|
||||
);
|
||||
expect(event.type, MobileEdgeAdjustmentEventType.none);
|
||||
expect(tracker.pointerMove(1, const Offset(40, 200)).type, MobileEdgeAdjustmentEventType.none);
|
||||
});
|
||||
|
||||
test('a pointer on a disabled side still blocks the enabled side as a second finger', () {
|
||||
final tracker = MobileEdgeAdjustmentTracker(verticalSlop: 10, verticalDominance: 1.5);
|
||||
bool onlyRight(MobileEdgeAdjustmentSide side) => side == MobileEdgeAdjustmentSide.right;
|
||||
|
||||
expect(
|
||||
tracker.pointerDown(1, const Offset(40, 300), size, isSideEnabled: onlyRight).type,
|
||||
MobileEdgeAdjustmentEventType.none,
|
||||
);
|
||||
// The disabled-side finger is still down, so a second finger on the
|
||||
// enabled edge is a chord: nothing tracks and it can never activate.
|
||||
expect(
|
||||
tracker.pointerDown(2, const Offset(960, 300), size, isSideEnabled: onlyRight).type,
|
||||
MobileEdgeAdjustmentEventType.none,
|
||||
);
|
||||
expect(tracker.pointerMove(2, const Offset(960, 200)).type, MobileEdgeAdjustmentEventType.none);
|
||||
tracker.pointerUp(1, const Offset(40, 300));
|
||||
tracker.pointerUp(2, const Offset(960, 300));
|
||||
|
||||
// With all fingers lifted the enabled side tracks normally again.
|
||||
expect(
|
||||
tracker.pointerDown(3, const Offset(960, 300), size, isSideEnabled: onlyRight).type,
|
||||
MobileEdgeAdjustmentEventType.candidate,
|
||||
);
|
||||
});
|
||||
|
||||
test('cancels horizontal movement from the edge', () {
|
||||
final tracker = MobileEdgeAdjustmentTracker(verticalSlop: 10, verticalDominance: 1.5);
|
||||
tracker.pointerDown(1, const Offset(40, 300), size);
|
||||
|
||||
Reference in New Issue
Block a user