@@ -122,6 +122,7 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
|
|||||||
private var surfaceContainer: FrameLayout? = null
|
private var surfaceContainer: FrameLayout? = null
|
||||||
private var videoAspectContainer: AspectRatioFrameLayout? = null
|
private var videoAspectContainer: AspectRatioFrameLayout? = null
|
||||||
private var subtitleView: SubtitleView? = null
|
private var subtitleView: SubtitleView? = null
|
||||||
|
private var videoZoomScale: Float = 1.0f
|
||||||
private var assHandler: AssHandler? = null
|
private var assHandler: AssHandler? = null
|
||||||
private var overlayLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
|
private var overlayLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
|
||||||
private var lastVideoSize: VideoSize? = null
|
private var lastVideoSize: VideoSize? = null
|
||||||
@@ -1193,18 +1194,26 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
|
|||||||
val containerHeight = contentView.height
|
val containerHeight = contentView.height
|
||||||
if (containerWidth == 0 || containerHeight == 0) return
|
if (containerWidth == 0 || containerHeight == 0) return
|
||||||
|
|
||||||
// In cover/stretch modes subtitles stay at container size so they never get
|
// In cover/stretch/zoomed-in modes subtitles stay at container size so they
|
||||||
// cropped or distorted. In letterbox mode they follow the video rect so they
|
// never get cropped. In letterbox mode they follow the visible video rect.
|
||||||
// anchor to the bottom of the video (matching MPV's default sub positioning).
|
|
||||||
val isLetterbox = videoAspectContainer?.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIT
|
val isLetterbox = videoAspectContainer?.resizeMode == AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
val (subWidth, subHeight) = if (isLetterbox) {
|
val (subWidth, subHeight) = if (isLetterbox) {
|
||||||
val videoAspect = (videoWidth * pixelRatio) / videoHeight
|
val videoAspect = (videoWidth * pixelRatio) / videoHeight
|
||||||
val containerAspect = containerWidth.toFloat() / containerHeight
|
val containerAspect = containerWidth.toFloat() / containerHeight
|
||||||
if (videoAspect > containerAspect) {
|
val (baseWidth, baseHeight) = if (videoAspect > containerAspect) {
|
||||||
containerWidth to (containerWidth / videoAspect).toInt()
|
containerWidth to (containerWidth / videoAspect).toInt()
|
||||||
} else {
|
} else {
|
||||||
(containerHeight * videoAspect).toInt() to containerHeight
|
(containerHeight * videoAspect).toInt() to containerHeight
|
||||||
}
|
}
|
||||||
|
if (videoZoomScale < 0.999f) {
|
||||||
|
val zoomedWidth = ((baseWidth * videoZoomScale).toInt()).coerceAtLeast(1)
|
||||||
|
val zoomedHeight = ((baseHeight * videoZoomScale).toInt()).coerceAtLeast(1)
|
||||||
|
zoomedWidth to zoomedHeight
|
||||||
|
} else if (videoZoomScale > 1.001f) {
|
||||||
|
containerWidth to containerHeight
|
||||||
|
} else {
|
||||||
|
baseWidth to baseHeight
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
containerWidth to containerHeight
|
containerWidth to containerHeight
|
||||||
}
|
}
|
||||||
@@ -1235,6 +1244,21 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setVideoZoom(scale: Double) {
|
||||||
|
if (disposing) return
|
||||||
|
val clamped = scale.coerceIn(0.5, 2.0).toFloat()
|
||||||
|
activity.runOnUiThread {
|
||||||
|
videoZoomScale = clamped
|
||||||
|
videoAspectContainer?.scaleX = clamped
|
||||||
|
videoAspectContainer?.scaleY = clamped
|
||||||
|
lastVideoSize?.let { vs ->
|
||||||
|
if (vs.width > 0 && vs.height > 0) {
|
||||||
|
updateSubtitleViewSize(vs.width, vs.height, vs.pixelWidthHeightRatio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun emitTrackList() {
|
private fun emitTrackList() {
|
||||||
val player = exoPlayer ?: return
|
val player = exoPlayer ?: return
|
||||||
val tracks = player.currentTracks
|
val tracks = player.currentTracks
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ class ExoPlayerPlugin :
|
|||||||
}
|
}
|
||||||
"setSubtitleStyle" -> handleSetSubtitleStyle(call, result)
|
"setSubtitleStyle" -> handleSetSubtitleStyle(call, result)
|
||||||
"setBoxFitMode" -> handleSetBoxFitMode(call, result)
|
"setBoxFitMode" -> handleSetBoxFitMode(call, result)
|
||||||
|
"setVideoZoom" -> handleSetVideoZoom(call, result)
|
||||||
"setDvConversionMode" -> handleSetDvConversionMode(call, result)
|
"setDvConversionMode" -> handleSetDvConversionMode(call, result)
|
||||||
"observeProperty" -> handleObserveProperty(call, result)
|
"observeProperty" -> handleObserveProperty(call, result)
|
||||||
"setMpvProperty" -> handleSetMpvProperty(call, result)
|
"setMpvProperty" -> handleSetMpvProperty(call, result)
|
||||||
@@ -550,6 +551,22 @@ class ExoPlayerPlugin :
|
|||||||
} ?: result.success(null)
|
} ?: result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleSetVideoZoom(call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
val scale = call.argument<Number>("scale")?.toDouble()
|
||||||
|
if (scale == null) {
|
||||||
|
result.error("INVALID_ARGS", "Missing 'scale'", null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (usingMpvFallback) {
|
||||||
|
result.success(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
playerCore?.setVideoZoom(scale)
|
||||||
|
result.success(null)
|
||||||
|
} ?: result.success(null)
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleSetDvConversionMode(call: MethodCall, result: MethodChannel.Result) {
|
private fun handleSetDvConversionMode(call: MethodCall, result: MethodChannel.Result) {
|
||||||
val mode = call.argument<String>("mode")
|
val mode = call.argument<String>("mode")
|
||||||
if (mode == null) {
|
if (mode == null) {
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Увеличи скоростта",
|
"speedIncrease": "Увеличи скоростта",
|
||||||
"speedDecrease": "Намали скоростта",
|
"speedDecrease": "Намали скоростта",
|
||||||
"speedReset": "Нулирай скоростта",
|
"speedReset": "Нулирай скоростта",
|
||||||
|
"zoomIn": "Увеличи мащаба",
|
||||||
|
"zoomOut": "Намали мащаба",
|
||||||
|
"zoomReset": "Нулирай мащаба",
|
||||||
"subSeekNext": "Отиди до следващ субтитър",
|
"subSeekNext": "Отиди до следващ субтитър",
|
||||||
"subSeekPrev": "Отиди до предишен субтитър",
|
"subSeekPrev": "Отиди до предишен субтитър",
|
||||||
"shaderToggle": "Превключи шейдъри",
|
"shaderToggle": "Превключи шейдъри",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Възпроизвеждане в режим картина в картината",
|
"pipActive": "Възпроизвеждане в режим картина в картината",
|
||||||
"pipFailed": "Режимът картина в картината не успя да стартира",
|
"pipFailed": "Режимът картина в картината не успя да стартира",
|
||||||
"screenshotSaved": "Екранната снимка е запазена",
|
"screenshotSaved": "Екранната снимка е запазена",
|
||||||
|
"zoomPercent": "Мащаб ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Изисква Android 8.0 или по-нова версия",
|
"androidVersion": "Изисква Android 8.0 или по-нова версия",
|
||||||
"iosVersion": "Изисква iOS 15.0 или по-нова версия",
|
"iosVersion": "Изисква iOS 15.0 или по-нова версия",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Скорост на възпроизвеждане",
|
"playbackSpeed": "Скорост на възпроизвеждане",
|
||||||
|
"zoom": "Мащаб",
|
||||||
"sleepTimer": "Таймер за заспиване",
|
"sleepTimer": "Таймер за заспиване",
|
||||||
"audioSync": "Синхронизация на аудио",
|
"audioSync": "Синхронизация на аудио",
|
||||||
"subtitleSync": "Синхронизация на субтитри",
|
"subtitleSync": "Синхронизация на субтитри",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Øg hastighed",
|
"speedIncrease": "Øg hastighed",
|
||||||
"speedDecrease": "Sænk hastighed",
|
"speedDecrease": "Sænk hastighed",
|
||||||
"speedReset": "Nulstil hastighed",
|
"speedReset": "Nulstil hastighed",
|
||||||
|
"zoomIn": "Zoom ind",
|
||||||
|
"zoomOut": "Zoom ud",
|
||||||
|
"zoomReset": "Nulstil zoom",
|
||||||
"subSeekNext": "Søg til næste undertekst",
|
"subSeekNext": "Søg til næste undertekst",
|
||||||
"subSeekPrev": "Søg til forrige undertekst",
|
"subSeekPrev": "Søg til forrige undertekst",
|
||||||
"shaderToggle": "Skift shadere",
|
"shaderToggle": "Skift shadere",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Afspiller i billede-i-billede",
|
"pipActive": "Afspiller i billede-i-billede",
|
||||||
"pipFailed": "Billede-i-billede kunne ikke starte",
|
"pipFailed": "Billede-i-billede kunne ikke starte",
|
||||||
"screenshotSaved": "Skærmbillede gemt",
|
"screenshotSaved": "Skærmbillede gemt",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Kræver Android 8.0 eller nyere",
|
"androidVersion": "Kræver Android 8.0 eller nyere",
|
||||||
"iosVersion": "Kræver iOS 15.0 eller nyere",
|
"iosVersion": "Kræver iOS 15.0 eller nyere",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Afspilningshastighed",
|
"playbackSpeed": "Afspilningshastighed",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Sove-timer",
|
"sleepTimer": "Sove-timer",
|
||||||
"audioSync": "Lydsynkronisering",
|
"audioSync": "Lydsynkronisering",
|
||||||
"subtitleSync": "Undertekstsynkronisering",
|
"subtitleSync": "Undertekstsynkronisering",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Geschwindigkeit erhöhen",
|
"speedIncrease": "Geschwindigkeit erhöhen",
|
||||||
"speedDecrease": "Geschwindigkeit verringern",
|
"speedDecrease": "Geschwindigkeit verringern",
|
||||||
"speedReset": "Geschwindigkeit zurücksetzen",
|
"speedReset": "Geschwindigkeit zurücksetzen",
|
||||||
|
"zoomIn": "Vergrößern",
|
||||||
|
"zoomOut": "Verkleinern",
|
||||||
|
"zoomReset": "Zoom zurücksetzen",
|
||||||
"subSeekNext": "Zum nächsten Untertitel springen",
|
"subSeekNext": "Zum nächsten Untertitel springen",
|
||||||
"subSeekPrev": "Zum vorherigen Untertitel springen",
|
"subSeekPrev": "Zum vorherigen Untertitel springen",
|
||||||
"shaderToggle": "Shader umschalten",
|
"shaderToggle": "Shader umschalten",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Wiedergabe im Bild-in-Bild-Modus",
|
"pipActive": "Wiedergabe im Bild-in-Bild-Modus",
|
||||||
"pipFailed": "Bild-in-Bild konnte nicht gestartet werden",
|
"pipFailed": "Bild-in-Bild konnte nicht gestartet werden",
|
||||||
"screenshotSaved": "Screenshot gespeichert",
|
"screenshotSaved": "Screenshot gespeichert",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Erfordert Android 8.0 oder neuer",
|
"androidVersion": "Erfordert Android 8.0 oder neuer",
|
||||||
"iosVersion": "Erfordert iOS 15.0 oder neuer",
|
"iosVersion": "Erfordert iOS 15.0 oder neuer",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Wiedergabegeschwindigkeit",
|
"playbackSpeed": "Wiedergabegeschwindigkeit",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Schlaftimer",
|
"sleepTimer": "Schlaftimer",
|
||||||
"audioSync": "Audio-Synchronisation",
|
"audioSync": "Audio-Synchronisation",
|
||||||
"subtitleSync": "Untertitel-Synchronisation",
|
"subtitleSync": "Untertitel-Synchronisation",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Increase Speed",
|
"speedIncrease": "Increase Speed",
|
||||||
"speedDecrease": "Decrease Speed",
|
"speedDecrease": "Decrease Speed",
|
||||||
"speedReset": "Reset Speed",
|
"speedReset": "Reset Speed",
|
||||||
|
"zoomIn": "Zoom In",
|
||||||
|
"zoomOut": "Zoom Out",
|
||||||
|
"zoomReset": "Reset Zoom",
|
||||||
"subSeekNext": "Seek to Next Subtitle",
|
"subSeekNext": "Seek to Next Subtitle",
|
||||||
"subSeekPrev": "Seek to Previous Subtitle",
|
"subSeekPrev": "Seek to Previous Subtitle",
|
||||||
"shaderToggle": "Toggle Shaders",
|
"shaderToggle": "Toggle Shaders",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Playing in Picture-in-Picture",
|
"pipActive": "Playing in Picture-in-Picture",
|
||||||
"pipFailed": "Picture-in-picture failed to start",
|
"pipFailed": "Picture-in-picture failed to start",
|
||||||
"screenshotSaved": "Screenshot saved",
|
"screenshotSaved": "Screenshot saved",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Requires Android 8.0 or newer",
|
"androidVersion": "Requires Android 8.0 or newer",
|
||||||
"iosVersion": "Requires iOS 15.0 or newer",
|
"iosVersion": "Requires iOS 15.0 or newer",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Playback Speed",
|
"playbackSpeed": "Playback Speed",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Sleep Timer",
|
"sleepTimer": "Sleep Timer",
|
||||||
"audioSync": "Audio Sync",
|
"audioSync": "Audio Sync",
|
||||||
"subtitleSync": "Subtitle Sync",
|
"subtitleSync": "Subtitle Sync",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Aumentar Velocidad",
|
"speedIncrease": "Aumentar Velocidad",
|
||||||
"speedDecrease": "Disminuir Velocidad",
|
"speedDecrease": "Disminuir Velocidad",
|
||||||
"speedReset": "Restablecer Velocidad",
|
"speedReset": "Restablecer Velocidad",
|
||||||
|
"zoomIn": "Acercar",
|
||||||
|
"zoomOut": "Alejar",
|
||||||
|
"zoomReset": "Restablecer zoom",
|
||||||
"subSeekNext": "Ir al Siguiente Subtítulo",
|
"subSeekNext": "Ir al Siguiente Subtítulo",
|
||||||
"subSeekPrev": "Ir al Anterior Subtítulo",
|
"subSeekPrev": "Ir al Anterior Subtítulo",
|
||||||
"shaderToggle": "Alternar Shaders",
|
"shaderToggle": "Alternar Shaders",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Reproduciendo en Imagen en Imagen",
|
"pipActive": "Reproduciendo en Imagen en Imagen",
|
||||||
"pipFailed": "Error al iniciar Imagen en Imagen",
|
"pipFailed": "Error al iniciar Imagen en Imagen",
|
||||||
"screenshotSaved": "Captura de pantalla guardada",
|
"screenshotSaved": "Captura de pantalla guardada",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Requiere Android 8.0 o más reciente",
|
"androidVersion": "Requiere Android 8.0 o más reciente",
|
||||||
"iosVersion": "Requiere iOS 15.0 o más reciente",
|
"iosVersion": "Requiere iOS 15.0 o más reciente",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Velocidad de reproducción",
|
"playbackSpeed": "Velocidad de reproducción",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Temporizador de apagado",
|
"sleepTimer": "Temporizador de apagado",
|
||||||
"audioSync": "Sincronización de audio",
|
"audioSync": "Sincronización de audio",
|
||||||
"subtitleSync": "Sincronización de subtítulos",
|
"subtitleSync": "Sincronización de subtítulos",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Augmenter la vitesse",
|
"speedIncrease": "Augmenter la vitesse",
|
||||||
"speedDecrease": "Réduire la vitesse",
|
"speedDecrease": "Réduire la vitesse",
|
||||||
"speedReset": "Réinitialiser la vitesse",
|
"speedReset": "Réinitialiser la vitesse",
|
||||||
|
"zoomIn": "Zoom avant",
|
||||||
|
"zoomOut": "Zoom arrière",
|
||||||
|
"zoomReset": "Réinitialiser le zoom",
|
||||||
"subSeekNext": "Rechercher le sous-titre suivant",
|
"subSeekNext": "Rechercher le sous-titre suivant",
|
||||||
"subSeekPrev": "Rechercher le sous-titre précédent",
|
"subSeekPrev": "Rechercher le sous-titre précédent",
|
||||||
"shaderToggle": "Activer/désactiver les shaders",
|
"shaderToggle": "Activer/désactiver les shaders",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Lecture en mode image dans l'image",
|
"pipActive": "Lecture en mode image dans l'image",
|
||||||
"pipFailed": "Échec du démarrage du mode image dans l'image",
|
"pipFailed": "Échec du démarrage du mode image dans l'image",
|
||||||
"screenshotSaved": "Capture d'écran enregistrée",
|
"screenshotSaved": "Capture d'écran enregistrée",
|
||||||
|
"zoomPercent": "Zoom ${percent} %",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Nécessite Android 8.0 ou plus récent",
|
"androidVersion": "Nécessite Android 8.0 ou plus récent",
|
||||||
"iosVersion": "Nécessite iOS 15.0 ou plus récent",
|
"iosVersion": "Nécessite iOS 15.0 ou plus récent",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Vitesse de lecture",
|
"playbackSpeed": "Vitesse de lecture",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Minuterie de mise en veille",
|
"sleepTimer": "Minuterie de mise en veille",
|
||||||
"audioSync": "Synchronisation audio",
|
"audioSync": "Synchronisation audio",
|
||||||
"subtitleSync": "Synchronisation des sous-titres",
|
"subtitleSync": "Synchronisation des sous-titres",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Aumenta velocità",
|
"speedIncrease": "Aumenta velocità",
|
||||||
"speedDecrease": "Diminuisci velocità",
|
"speedDecrease": "Diminuisci velocità",
|
||||||
"speedReset": "Ripristina velocità",
|
"speedReset": "Ripristina velocità",
|
||||||
|
"zoomIn": "Aumenta zoom",
|
||||||
|
"zoomOut": "Riduci zoom",
|
||||||
|
"zoomReset": "Ripristina zoom",
|
||||||
"subSeekNext": "Vai al sottotitolo successivo",
|
"subSeekNext": "Vai al sottotitolo successivo",
|
||||||
"subSeekPrev": "Vai al sottotitolo precedente",
|
"subSeekPrev": "Vai al sottotitolo precedente",
|
||||||
"shaderToggle": "Attiva/disattiva shader",
|
"shaderToggle": "Attiva/disattiva shader",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Riproduzione in Picture-in-Picture",
|
"pipActive": "Riproduzione in Picture-in-Picture",
|
||||||
"pipFailed": "Impossibile avviare la modalità Picture-in-Picture",
|
"pipFailed": "Impossibile avviare la modalità Picture-in-Picture",
|
||||||
"screenshotSaved": "Schermata salvata",
|
"screenshotSaved": "Schermata salvata",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Richiede Android 8.0 o versioni successive",
|
"androidVersion": "Richiede Android 8.0 o versioni successive",
|
||||||
"iosVersion": "Richiede iOS 15.0 o versioni successive",
|
"iosVersion": "Richiede iOS 15.0 o versioni successive",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Velocità di riproduzione",
|
"playbackSpeed": "Velocità di riproduzione",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Timer di spegnimento",
|
"sleepTimer": "Timer di spegnimento",
|
||||||
"audioSync": "Sincronizzazione audio",
|
"audioSync": "Sincronizzazione audio",
|
||||||
"subtitleSync": "Sincronizzazione sottotitoli",
|
"subtitleSync": "Sincronizzazione sottotitoli",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "速度を上げる",
|
"speedIncrease": "速度を上げる",
|
||||||
"speedDecrease": "速度を下げる",
|
"speedDecrease": "速度を下げる",
|
||||||
"speedReset": "速度をリセット",
|
"speedReset": "速度をリセット",
|
||||||
|
"zoomIn": "ズームイン",
|
||||||
|
"zoomOut": "ズームアウト",
|
||||||
|
"zoomReset": "ズームをリセット",
|
||||||
"subSeekNext": "次の字幕にシーク",
|
"subSeekNext": "次の字幕にシーク",
|
||||||
"subSeekPrev": "前の字幕にシーク",
|
"subSeekPrev": "前の字幕にシーク",
|
||||||
"shaderToggle": "シェーダー切替",
|
"shaderToggle": "シェーダー切替",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "ピクチャーインピクチャーで再生中",
|
"pipActive": "ピクチャーインピクチャーで再生中",
|
||||||
"pipFailed": "ピクチャーインピクチャーの開始に失敗しました",
|
"pipFailed": "ピクチャーインピクチャーの開始に失敗しました",
|
||||||
"screenshotSaved": "スクリーンショットを保存しました",
|
"screenshotSaved": "スクリーンショットを保存しました",
|
||||||
|
"zoomPercent": "ズーム ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Android 8.0以降が必要です",
|
"androidVersion": "Android 8.0以降が必要です",
|
||||||
"iosVersion": "iOS 15.0以降が必要です",
|
"iosVersion": "iOS 15.0以降が必要です",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "再生速度",
|
"playbackSpeed": "再生速度",
|
||||||
|
"zoom": "ズーム",
|
||||||
"sleepTimer": "スリープタイマー",
|
"sleepTimer": "スリープタイマー",
|
||||||
"audioSync": "音声同期",
|
"audioSync": "音声同期",
|
||||||
"subtitleSync": "字幕同期",
|
"subtitleSync": "字幕同期",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "속도 높이기",
|
"speedIncrease": "속도 높이기",
|
||||||
"speedDecrease": "속도 낮추기",
|
"speedDecrease": "속도 낮추기",
|
||||||
"speedReset": "속도 초기화",
|
"speedReset": "속도 초기화",
|
||||||
|
"zoomIn": "확대",
|
||||||
|
"zoomOut": "축소",
|
||||||
|
"zoomReset": "확대/축소 초기화",
|
||||||
"subSeekNext": "다음 자막으로 이동",
|
"subSeekNext": "다음 자막으로 이동",
|
||||||
"subSeekPrev": "이전 자막으로 이동",
|
"subSeekPrev": "이전 자막으로 이동",
|
||||||
"shaderToggle": "셰이더 전환",
|
"shaderToggle": "셰이더 전환",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "화면 속 화면으로 재생 중",
|
"pipActive": "화면 속 화면으로 재생 중",
|
||||||
"pipFailed": "화면 속 화면 모드를 시작할 수 없습니다",
|
"pipFailed": "화면 속 화면 모드를 시작할 수 없습니다",
|
||||||
"screenshotSaved": "스크린샷 저장됨",
|
"screenshotSaved": "스크린샷 저장됨",
|
||||||
|
"zoomPercent": "확대/축소 ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Android 8.0 이상이 필요합니다",
|
"androidVersion": "Android 8.0 이상이 필요합니다",
|
||||||
"iosVersion": "iOS 15.0 이상이 필요합니다",
|
"iosVersion": "iOS 15.0 이상이 필요합니다",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "재생 속도",
|
"playbackSpeed": "재생 속도",
|
||||||
|
"zoom": "확대/축소",
|
||||||
"sleepTimer": "취침 타이머",
|
"sleepTimer": "취침 타이머",
|
||||||
"audioSync": "오디오 동기화",
|
"audioSync": "오디오 동기화",
|
||||||
"subtitleSync": "자막 동기화",
|
"subtitleSync": "자막 동기화",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Øk hastighet",
|
"speedIncrease": "Øk hastighet",
|
||||||
"speedDecrease": "Reduser hastighet",
|
"speedDecrease": "Reduser hastighet",
|
||||||
"speedReset": "Tilbakestill hastighet",
|
"speedReset": "Tilbakestill hastighet",
|
||||||
|
"zoomIn": "Zoom inn",
|
||||||
|
"zoomOut": "Zoom ut",
|
||||||
|
"zoomReset": "Tilbakestill zoom",
|
||||||
"subSeekNext": "Spol til neste undertekst",
|
"subSeekNext": "Spol til neste undertekst",
|
||||||
"subSeekPrev": "Spol til forrige undertekst",
|
"subSeekPrev": "Spol til forrige undertekst",
|
||||||
"shaderToggle": "Veksle shadere",
|
"shaderToggle": "Veksle shadere",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Spiller i bilde-i-bilde",
|
"pipActive": "Spiller i bilde-i-bilde",
|
||||||
"pipFailed": "Bilde-i-bilde kunne ikke starte",
|
"pipFailed": "Bilde-i-bilde kunne ikke starte",
|
||||||
"screenshotSaved": "Skjermbilde lagret",
|
"screenshotSaved": "Skjermbilde lagret",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Krever Android 8.0 eller nyere",
|
"androidVersion": "Krever Android 8.0 eller nyere",
|
||||||
"iosVersion": "Krever iOS 15.0 eller nyere",
|
"iosVersion": "Krever iOS 15.0 eller nyere",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Avspillingshastighet",
|
"playbackSpeed": "Avspillingshastighet",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Søvntimer",
|
"sleepTimer": "Søvntimer",
|
||||||
"audioSync": "Lydsynkronisering",
|
"audioSync": "Lydsynkronisering",
|
||||||
"subtitleSync": "Undertekstsynkronisering",
|
"subtitleSync": "Undertekstsynkronisering",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Snelheid verhogen",
|
"speedIncrease": "Snelheid verhogen",
|
||||||
"speedDecrease": "Snelheid verlagen",
|
"speedDecrease": "Snelheid verlagen",
|
||||||
"speedReset": "Snelheid resetten",
|
"speedReset": "Snelheid resetten",
|
||||||
|
"zoomIn": "Inzoomen",
|
||||||
|
"zoomOut": "Uitzoomen",
|
||||||
|
"zoomReset": "Zoom resetten",
|
||||||
"subSeekNext": "Naar volgende ondertitel",
|
"subSeekNext": "Naar volgende ondertitel",
|
||||||
"subSeekPrev": "Naar vorige ondertitel",
|
"subSeekPrev": "Naar vorige ondertitel",
|
||||||
"shaderToggle": "Shaders aan/uit",
|
"shaderToggle": "Shaders aan/uit",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Afspelen in beeld-in-beeld",
|
"pipActive": "Afspelen in beeld-in-beeld",
|
||||||
"pipFailed": "Beeld-in-beeld kon niet worden gestart",
|
"pipFailed": "Beeld-in-beeld kon niet worden gestart",
|
||||||
"screenshotSaved": "Schermafbeelding opgeslagen",
|
"screenshotSaved": "Schermafbeelding opgeslagen",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Vereist Android 8.0 of nieuwer",
|
"androidVersion": "Vereist Android 8.0 of nieuwer",
|
||||||
"iosVersion": "Vereist iOS 15.0 of nieuwer",
|
"iosVersion": "Vereist iOS 15.0 of nieuwer",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Afspeelsnelheid",
|
"playbackSpeed": "Afspeelsnelheid",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Slaaptimer",
|
"sleepTimer": "Slaaptimer",
|
||||||
"audioSync": "Audio synchronisatie",
|
"audioSync": "Audio synchronisatie",
|
||||||
"subtitleSync": "Ondertitel synchronisatie",
|
"subtitleSync": "Ondertitel synchronisatie",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Zwiększ prędkość",
|
"speedIncrease": "Zwiększ prędkość",
|
||||||
"speedDecrease": "Zmniejsz prędkość",
|
"speedDecrease": "Zmniejsz prędkość",
|
||||||
"speedReset": "Zresetuj prędkość",
|
"speedReset": "Zresetuj prędkość",
|
||||||
|
"zoomIn": "Powiększ",
|
||||||
|
"zoomOut": "Pomniejsz",
|
||||||
|
"zoomReset": "Zresetuj zoom",
|
||||||
"subSeekNext": "Przewiń do następnego napisu",
|
"subSeekNext": "Przewiń do następnego napisu",
|
||||||
"subSeekPrev": "Przewiń do poprzedniego napisu",
|
"subSeekPrev": "Przewiń do poprzedniego napisu",
|
||||||
"shaderToggle": "Przełącz shadery",
|
"shaderToggle": "Przełącz shadery",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Odtwarzanie w trybie obraz w obrazie",
|
"pipActive": "Odtwarzanie w trybie obraz w obrazie",
|
||||||
"pipFailed": "Nie udało się uruchomić trybu obraz w obrazie",
|
"pipFailed": "Nie udało się uruchomić trybu obraz w obrazie",
|
||||||
"screenshotSaved": "Zrzut ekranu zapisany",
|
"screenshotSaved": "Zrzut ekranu zapisany",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Wymaga Androida 8.0 lub nowszego",
|
"androidVersion": "Wymaga Androida 8.0 lub nowszego",
|
||||||
"iosVersion": "Wymaga iOS 15.0 lub nowszego",
|
"iosVersion": "Wymaga iOS 15.0 lub nowszego",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Prędkość odtwarzania",
|
"playbackSpeed": "Prędkość odtwarzania",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Wyłącznik czasowy",
|
"sleepTimer": "Wyłącznik czasowy",
|
||||||
"audioSync": "Synchronizacja audio",
|
"audioSync": "Synchronizacja audio",
|
||||||
"subtitleSync": "Synchronizacja napisów",
|
"subtitleSync": "Synchronizacja napisów",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Aumentar Velocidade",
|
"speedIncrease": "Aumentar Velocidade",
|
||||||
"speedDecrease": "Diminuir Velocidade",
|
"speedDecrease": "Diminuir Velocidade",
|
||||||
"speedReset": "Redefinir Velocidade",
|
"speedReset": "Redefinir Velocidade",
|
||||||
|
"zoomIn": "Aumentar zoom",
|
||||||
|
"zoomOut": "Diminuir zoom",
|
||||||
|
"zoomReset": "Redefinir zoom",
|
||||||
"subSeekNext": "Ir para Próxima Legenda",
|
"subSeekNext": "Ir para Próxima Legenda",
|
||||||
"subSeekPrev": "Ir para Legenda Anterior",
|
"subSeekPrev": "Ir para Legenda Anterior",
|
||||||
"shaderToggle": "Alternar Shaders",
|
"shaderToggle": "Alternar Shaders",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Reproduzindo em Picture-in-Picture",
|
"pipActive": "Reproduzindo em Picture-in-Picture",
|
||||||
"pipFailed": "Falha ao iniciar picture-in-picture",
|
"pipFailed": "Falha ao iniciar picture-in-picture",
|
||||||
"screenshotSaved": "Captura de tela salva",
|
"screenshotSaved": "Captura de tela salva",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Requer Android 8.0 ou superior",
|
"androidVersion": "Requer Android 8.0 ou superior",
|
||||||
"iosVersion": "Requer iOS 15.0 ou superior",
|
"iosVersion": "Requer iOS 15.0 ou superior",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Velocidade de Reprodução",
|
"playbackSpeed": "Velocidade de Reprodução",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Timer de Sono",
|
"sleepTimer": "Timer de Sono",
|
||||||
"audioSync": "Sincronia de Áudio",
|
"audioSync": "Sincronia de Áudio",
|
||||||
"subtitleSync": "Sincronia de Legendas",
|
"subtitleSync": "Sincronia de Legendas",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Увеличить скорость",
|
"speedIncrease": "Увеличить скорость",
|
||||||
"speedDecrease": "Уменьшить скорость",
|
"speedDecrease": "Уменьшить скорость",
|
||||||
"speedReset": "Сбросить скорость",
|
"speedReset": "Сбросить скорость",
|
||||||
|
"zoomIn": "Увеличить масштаб",
|
||||||
|
"zoomOut": "Уменьшить масштаб",
|
||||||
|
"zoomReset": "Сбросить масштаб",
|
||||||
"subSeekNext": "К следующему субтитру",
|
"subSeekNext": "К следующему субтитру",
|
||||||
"subSeekPrev": "К предыдущему субтитру",
|
"subSeekPrev": "К предыдущему субтитру",
|
||||||
"shaderToggle": "Вкл./выкл. шейдеры",
|
"shaderToggle": "Вкл./выкл. шейдеры",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Воспроизводится в режиме «картинка в картинке»",
|
"pipActive": "Воспроизводится в режиме «картинка в картинке»",
|
||||||
"pipFailed": "Не удалось запустить режим «картинка в картинке»",
|
"pipFailed": "Не удалось запустить режим «картинка в картинке»",
|
||||||
"screenshotSaved": "Снимок экрана сохранён",
|
"screenshotSaved": "Снимок экрана сохранён",
|
||||||
|
"zoomPercent": "Масштаб ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Требуется Android 8.0 или новее",
|
"androidVersion": "Требуется Android 8.0 или новее",
|
||||||
"iosVersion": "Требуется iOS 15.0 или новее",
|
"iosVersion": "Требуется iOS 15.0 или новее",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Скорость воспроизведения",
|
"playbackSpeed": "Скорость воспроизведения",
|
||||||
|
"zoom": "Масштаб",
|
||||||
"sleepTimer": "Таймер сна",
|
"sleepTimer": "Таймер сна",
|
||||||
"audioSync": "Синхронизация аудио",
|
"audioSync": "Синхронизация аудио",
|
||||||
"subtitleSync": "Синхронизация субтитров",
|
"subtitleSync": "Синхронизация субтитров",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
/// To regenerate, run: `dart run slang`
|
/// To regenerate, run: `dart run slang`
|
||||||
///
|
///
|
||||||
/// Locales: 16
|
/// Locales: 16
|
||||||
/// Strings: 18864 (1179 per locale)
|
/// Strings: 18944 (1184 per locale)
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint, unused_import
|
// ignore_for_file: type=lint, unused_import
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsBg extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Възпроизвеждане в режим картина в картината';
|
@override String get pipActive => 'Възпроизвеждане в режим картина в картината';
|
||||||
@override String get pipFailed => 'Режимът картина в картината не успя да стартира';
|
@override String get pipFailed => 'Режимът картина в картината не успя да стартира';
|
||||||
@override String get screenshotSaved => 'Екранната снимка е запазена';
|
@override String get screenshotSaved => 'Екранната снимка е запазена';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Мащаб ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsBg pipErrors = _TranslationsVideoControlsPipErrorsBg._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsBg pipErrors = _TranslationsVideoControlsPipErrorsBg._(_root);
|
||||||
@override String get chapters => 'Глави';
|
@override String get chapters => 'Глави';
|
||||||
@override String get noChaptersAvailable => 'Няма налични глави';
|
@override String get noChaptersAvailable => 'Няма налични глави';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsBg extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Скорост на възпроизвеждане';
|
@override String get playbackSpeed => 'Скорост на възпроизвеждане';
|
||||||
|
@override String get zoom => 'Мащаб';
|
||||||
@override String get sleepTimer => 'Таймер за заспиване';
|
@override String get sleepTimer => 'Таймер за заспиване';
|
||||||
@override String get audioSync => 'Синхронизация на аудио';
|
@override String get audioSync => 'Синхронизация на аудио';
|
||||||
@override String get subtitleSync => 'Синхронизация на субтитри';
|
@override String get subtitleSync => 'Синхронизация на субтитри';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsBg extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Увеличи скоростта';
|
@override String get speedIncrease => 'Увеличи скоростта';
|
||||||
@override String get speedDecrease => 'Намали скоростта';
|
@override String get speedDecrease => 'Намали скоростта';
|
||||||
@override String get speedReset => 'Нулирай скоростта';
|
@override String get speedReset => 'Нулирай скоростта';
|
||||||
|
@override String get zoomIn => 'Увеличи мащаба';
|
||||||
|
@override String get zoomOut => 'Намали мащаба';
|
||||||
|
@override String get zoomReset => 'Нулирай мащаба';
|
||||||
@override String get subSeekNext => 'Отиди до следващ субтитър';
|
@override String get subSeekNext => 'Отиди до следващ субтитър';
|
||||||
@override String get subSeekPrev => 'Отиди до предишен субтитър';
|
@override String get subSeekPrev => 'Отиди до предишен субтитър';
|
||||||
@override String get shaderToggle => 'Превключи шейдъри';
|
@override String get shaderToggle => 'Превключи шейдъри';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsBg {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Увеличи скоростта',
|
'hotkeys.actions.speedIncrease' => 'Увеличи скоростта',
|
||||||
'hotkeys.actions.speedDecrease' => 'Намали скоростта',
|
'hotkeys.actions.speedDecrease' => 'Намали скоростта',
|
||||||
'hotkeys.actions.speedReset' => 'Нулирай скоростта',
|
'hotkeys.actions.speedReset' => 'Нулирай скоростта',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Увеличи мащаба',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Намали мащаба',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Нулирай мащаба',
|
||||||
'hotkeys.actions.subSeekNext' => 'Отиди до следващ субтитър',
|
'hotkeys.actions.subSeekNext' => 'Отиди до следващ субтитър',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Отиди до предишен субтитър',
|
'hotkeys.actions.subSeekPrev' => 'Отиди до предишен субтитър',
|
||||||
'hotkeys.actions.shaderToggle' => 'Превключи шейдъри',
|
'hotkeys.actions.shaderToggle' => 'Превключи шейдъри',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsBg {
|
|||||||
'videoControls.pipActive' => 'Възпроизвеждане в режим картина в картината',
|
'videoControls.pipActive' => 'Възпроизвеждане в режим картина в картината',
|
||||||
'videoControls.pipFailed' => 'Режимът картина в картината не успя да стартира',
|
'videoControls.pipFailed' => 'Режимът картина в картината не успя да стартира',
|
||||||
'videoControls.screenshotSaved' => 'Екранната снимка е запазена',
|
'videoControls.screenshotSaved' => 'Екранната снимка е запазена',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Мащаб ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Изисква Android 8.0 или по-нова версия',
|
'videoControls.pipErrors.androidVersion' => 'Изисква Android 8.0 или по-нова версия',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Изисква iOS 15.0 или по-нова версия',
|
'videoControls.pipErrors.iosVersion' => 'Изисква iOS 15.0 или по-нова версия',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Режимът картина в картината е изключен. Включете го в системните настройки.',
|
'videoControls.pipErrors.permissionDisabled' => 'Режимът картина в картината е изключен. Включете го в системните настройки.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsBg {
|
|||||||
'profiles.addPlezyProfile' => 'Добави Plezy профил',
|
'profiles.addPlezyProfile' => 'Добави Plezy профил',
|
||||||
'profiles.switchingProfile' => 'Смяна на профил…',
|
'profiles.switchingProfile' => 'Смяна на профил…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Да се изтрие ли този профил?',
|
'profiles.deleteThisProfileTitle' => 'Да се изтрие ли този профил?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Премахване на ${displayName}. Връзките не се засягат.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Премахване на ${displayName}. Връзките не се засягат.',
|
||||||
'profiles.active' => 'Активен',
|
'profiles.active' => 'Активен',
|
||||||
'profiles.manage' => 'Управление',
|
'profiles.manage' => 'Управление',
|
||||||
'profiles.delete' => 'Изтрий',
|
'profiles.delete' => 'Изтрий',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Изход',
|
'profiles.signOut' => 'Изход',
|
||||||
'profiles.signOutPlexTitle' => 'Изход от Plex?',
|
'profiles.signOutPlexTitle' => 'Изход от Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Премахване на ${displayName} и всички Plex Home потребители? Можете да влезете отново по всяко време.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Премахване на ${displayName} и всички Plex Home потребители? Можете да влезете отново по всяко време.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsBg {
|
|||||||
'companionRemote.remote.audio' => 'Аудио',
|
'companionRemote.remote.audio' => 'Аудио',
|
||||||
'companionRemote.remote.searchHint' => 'Търсене на настолен компютър...',
|
'companionRemote.remote.searchHint' => 'Търсене на настолен компютър...',
|
||||||
'videoSettings.playbackSpeed' => 'Скорост на възпроизвеждане',
|
'videoSettings.playbackSpeed' => 'Скорост на възпроизвеждане',
|
||||||
|
'videoSettings.zoom' => 'Мащаб',
|
||||||
'videoSettings.sleepTimer' => 'Таймер за заспиване',
|
'videoSettings.sleepTimer' => 'Таймер за заспиване',
|
||||||
'videoSettings.audioSync' => 'Синхронизация на аудио',
|
'videoSettings.audioSync' => 'Синхронизация на аудио',
|
||||||
'videoSettings.subtitleSync' => 'Синхронизация на субтитри',
|
'videoSettings.subtitleSync' => 'Синхронизация на субтитри',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsBg {
|
|||||||
'metadataEdit.basicInfo' => 'Основна информация',
|
'metadataEdit.basicInfo' => 'Основна информация',
|
||||||
'metadataEdit.artwork' => 'Обложка',
|
'metadataEdit.artwork' => 'Обложка',
|
||||||
'metadataEdit.advancedSettings' => 'Разширени настройки',
|
'metadataEdit.advancedSettings' => 'Разширени настройки',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Заглавие',
|
'metadataEdit.title' => 'Заглавие',
|
||||||
'metadataEdit.sortTitle' => 'Заглавие за сортиране',
|
'metadataEdit.sortTitle' => 'Заглавие за сортиране',
|
||||||
'metadataEdit.originalTitle' => 'Оригинално заглавие',
|
'metadataEdit.originalTitle' => 'Оригинално заглавие',
|
||||||
'metadataEdit.releaseDate' => 'Дата на излизане',
|
'metadataEdit.releaseDate' => 'Дата на излизане',
|
||||||
'metadataEdit.contentRating' => 'Възрастов рейтинг',
|
'metadataEdit.contentRating' => 'Възрастов рейтинг',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Студио',
|
'metadataEdit.studio' => 'Студио',
|
||||||
'metadataEdit.tagline' => 'Слоган',
|
'metadataEdit.tagline' => 'Слоган',
|
||||||
'metadataEdit.summary' => 'Резюме',
|
'metadataEdit.summary' => 'Резюме',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsDa extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Afspiller i billede-i-billede';
|
@override String get pipActive => 'Afspiller i billede-i-billede';
|
||||||
@override String get pipFailed => 'Billede-i-billede kunne ikke starte';
|
@override String get pipFailed => 'Billede-i-billede kunne ikke starte';
|
||||||
@override String get screenshotSaved => 'Skærmbillede gemt';
|
@override String get screenshotSaved => 'Skærmbillede gemt';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsDa pipErrors = _TranslationsVideoControlsPipErrorsDa._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsDa pipErrors = _TranslationsVideoControlsPipErrorsDa._(_root);
|
||||||
@override String get chapters => 'Kapitler';
|
@override String get chapters => 'Kapitler';
|
||||||
@override String get noChaptersAvailable => 'Ingen kapitler tilgængelige';
|
@override String get noChaptersAvailable => 'Ingen kapitler tilgængelige';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsDa extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Afspilningshastighed';
|
@override String get playbackSpeed => 'Afspilningshastighed';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Sove-timer';
|
@override String get sleepTimer => 'Sove-timer';
|
||||||
@override String get audioSync => 'Lydsynkronisering';
|
@override String get audioSync => 'Lydsynkronisering';
|
||||||
@override String get subtitleSync => 'Undertekstsynkronisering';
|
@override String get subtitleSync => 'Undertekstsynkronisering';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsDa extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Øg hastighed';
|
@override String get speedIncrease => 'Øg hastighed';
|
||||||
@override String get speedDecrease => 'Sænk hastighed';
|
@override String get speedDecrease => 'Sænk hastighed';
|
||||||
@override String get speedReset => 'Nulstil hastighed';
|
@override String get speedReset => 'Nulstil hastighed';
|
||||||
|
@override String get zoomIn => 'Zoom ind';
|
||||||
|
@override String get zoomOut => 'Zoom ud';
|
||||||
|
@override String get zoomReset => 'Nulstil zoom';
|
||||||
@override String get subSeekNext => 'Søg til næste undertekst';
|
@override String get subSeekNext => 'Søg til næste undertekst';
|
||||||
@override String get subSeekPrev => 'Søg til forrige undertekst';
|
@override String get subSeekPrev => 'Søg til forrige undertekst';
|
||||||
@override String get shaderToggle => 'Skift shadere';
|
@override String get shaderToggle => 'Skift shadere';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsDa {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Øg hastighed',
|
'hotkeys.actions.speedIncrease' => 'Øg hastighed',
|
||||||
'hotkeys.actions.speedDecrease' => 'Sænk hastighed',
|
'hotkeys.actions.speedDecrease' => 'Sænk hastighed',
|
||||||
'hotkeys.actions.speedReset' => 'Nulstil hastighed',
|
'hotkeys.actions.speedReset' => 'Nulstil hastighed',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Zoom ind',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Zoom ud',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Nulstil zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Søg til næste undertekst',
|
'hotkeys.actions.subSeekNext' => 'Søg til næste undertekst',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Søg til forrige undertekst',
|
'hotkeys.actions.subSeekPrev' => 'Søg til forrige undertekst',
|
||||||
'hotkeys.actions.shaderToggle' => 'Skift shadere',
|
'hotkeys.actions.shaderToggle' => 'Skift shadere',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsDa {
|
|||||||
'videoControls.pipActive' => 'Afspiller i billede-i-billede',
|
'videoControls.pipActive' => 'Afspiller i billede-i-billede',
|
||||||
'videoControls.pipFailed' => 'Billede-i-billede kunne ikke starte',
|
'videoControls.pipFailed' => 'Billede-i-billede kunne ikke starte',
|
||||||
'videoControls.screenshotSaved' => 'Skærmbillede gemt',
|
'videoControls.screenshotSaved' => 'Skærmbillede gemt',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Kræver Android 8.0 eller nyere',
|
'videoControls.pipErrors.androidVersion' => 'Kræver Android 8.0 eller nyere',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Kræver iOS 15.0 eller nyere',
|
'videoControls.pipErrors.iosVersion' => 'Kræver iOS 15.0 eller nyere',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Billede-i-billede er deaktiveret. Slå det til i systemindstillinger.',
|
'videoControls.pipErrors.permissionDisabled' => 'Billede-i-billede er deaktiveret. Slå det til i systemindstillinger.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsDa {
|
|||||||
'profiles.addPlezyProfile' => 'Tilføj Plezy-profil',
|
'profiles.addPlezyProfile' => 'Tilføj Plezy-profil',
|
||||||
'profiles.switchingProfile' => 'Skifter profil…',
|
'profiles.switchingProfile' => 'Skifter profil…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Slet denne profil?',
|
'profiles.deleteThisProfileTitle' => 'Slet denne profil?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Fjern ${displayName}. Forbindelser påvirkes ikke.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Fjern ${displayName}. Forbindelser påvirkes ikke.',
|
||||||
'profiles.active' => 'Aktiv',
|
'profiles.active' => 'Aktiv',
|
||||||
'profiles.manage' => 'Administrer',
|
'profiles.manage' => 'Administrer',
|
||||||
'profiles.delete' => 'Slet',
|
'profiles.delete' => 'Slet',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Log ud',
|
'profiles.signOut' => 'Log ud',
|
||||||
'profiles.signOutPlexTitle' => 'Log ud af Plex?',
|
'profiles.signOutPlexTitle' => 'Log ud af Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Fjern ${displayName} og alle Plex Home-brugere? Log ind igen når som helst.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Fjern ${displayName} og alle Plex Home-brugere? Log ind igen når som helst.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsDa {
|
|||||||
'companionRemote.remote.audio' => 'Lyd',
|
'companionRemote.remote.audio' => 'Lyd',
|
||||||
'companionRemote.remote.searchHint' => 'Søg på desktop...',
|
'companionRemote.remote.searchHint' => 'Søg på desktop...',
|
||||||
'videoSettings.playbackSpeed' => 'Afspilningshastighed',
|
'videoSettings.playbackSpeed' => 'Afspilningshastighed',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Sove-timer',
|
'videoSettings.sleepTimer' => 'Sove-timer',
|
||||||
'videoSettings.audioSync' => 'Lydsynkronisering',
|
'videoSettings.audioSync' => 'Lydsynkronisering',
|
||||||
'videoSettings.subtitleSync' => 'Undertekstsynkronisering',
|
'videoSettings.subtitleSync' => 'Undertekstsynkronisering',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsDa {
|
|||||||
'metadataEdit.basicInfo' => 'Grundlæggende info',
|
'metadataEdit.basicInfo' => 'Grundlæggende info',
|
||||||
'metadataEdit.artwork' => 'Grafik',
|
'metadataEdit.artwork' => 'Grafik',
|
||||||
'metadataEdit.advancedSettings' => 'Avancerede indstillinger',
|
'metadataEdit.advancedSettings' => 'Avancerede indstillinger',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Titel',
|
'metadataEdit.title' => 'Titel',
|
||||||
'metadataEdit.sortTitle' => 'Sorteringstitel',
|
'metadataEdit.sortTitle' => 'Sorteringstitel',
|
||||||
'metadataEdit.originalTitle' => 'Originaltitel',
|
'metadataEdit.originalTitle' => 'Originaltitel',
|
||||||
'metadataEdit.releaseDate' => 'Udgivelsesdato',
|
'metadataEdit.releaseDate' => 'Udgivelsesdato',
|
||||||
'metadataEdit.contentRating' => 'Aldersgrænse',
|
'metadataEdit.contentRating' => 'Aldersgrænse',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studie',
|
'metadataEdit.studio' => 'Studie',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Resumé',
|
'metadataEdit.summary' => 'Resumé',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsDe extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Wiedergabe im Bild-in-Bild-Modus';
|
@override String get pipActive => 'Wiedergabe im Bild-in-Bild-Modus';
|
||||||
@override String get pipFailed => 'Bild-in-Bild konnte nicht gestartet werden';
|
@override String get pipFailed => 'Bild-in-Bild konnte nicht gestartet werden';
|
||||||
@override String get screenshotSaved => 'Screenshot gespeichert';
|
@override String get screenshotSaved => 'Screenshot gespeichert';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsDe pipErrors = _TranslationsVideoControlsPipErrorsDe._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsDe pipErrors = _TranslationsVideoControlsPipErrorsDe._(_root);
|
||||||
@override String get chapters => 'Kapitel';
|
@override String get chapters => 'Kapitel';
|
||||||
@override String get noChaptersAvailable => 'Keine Kapitel verfügbar';
|
@override String get noChaptersAvailable => 'Keine Kapitel verfügbar';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsDe extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Wiedergabegeschwindigkeit';
|
@override String get playbackSpeed => 'Wiedergabegeschwindigkeit';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Schlaftimer';
|
@override String get sleepTimer => 'Schlaftimer';
|
||||||
@override String get audioSync => 'Audio-Synchronisation';
|
@override String get audioSync => 'Audio-Synchronisation';
|
||||||
@override String get subtitleSync => 'Untertitel-Synchronisation';
|
@override String get subtitleSync => 'Untertitel-Synchronisation';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsDe extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Geschwindigkeit erhöhen';
|
@override String get speedIncrease => 'Geschwindigkeit erhöhen';
|
||||||
@override String get speedDecrease => 'Geschwindigkeit verringern';
|
@override String get speedDecrease => 'Geschwindigkeit verringern';
|
||||||
@override String get speedReset => 'Geschwindigkeit zurücksetzen';
|
@override String get speedReset => 'Geschwindigkeit zurücksetzen';
|
||||||
|
@override String get zoomIn => 'Vergrößern';
|
||||||
|
@override String get zoomOut => 'Verkleinern';
|
||||||
|
@override String get zoomReset => 'Zoom zurücksetzen';
|
||||||
@override String get subSeekNext => 'Zum nächsten Untertitel springen';
|
@override String get subSeekNext => 'Zum nächsten Untertitel springen';
|
||||||
@override String get subSeekPrev => 'Zum vorherigen Untertitel springen';
|
@override String get subSeekPrev => 'Zum vorherigen Untertitel springen';
|
||||||
@override String get shaderToggle => 'Shader umschalten';
|
@override String get shaderToggle => 'Shader umschalten';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsDe {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Geschwindigkeit erhöhen',
|
'hotkeys.actions.speedIncrease' => 'Geschwindigkeit erhöhen',
|
||||||
'hotkeys.actions.speedDecrease' => 'Geschwindigkeit verringern',
|
'hotkeys.actions.speedDecrease' => 'Geschwindigkeit verringern',
|
||||||
'hotkeys.actions.speedReset' => 'Geschwindigkeit zurücksetzen',
|
'hotkeys.actions.speedReset' => 'Geschwindigkeit zurücksetzen',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Vergrößern',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Verkleinern',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Zoom zurücksetzen',
|
||||||
'hotkeys.actions.subSeekNext' => 'Zum nächsten Untertitel springen',
|
'hotkeys.actions.subSeekNext' => 'Zum nächsten Untertitel springen',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Zum vorherigen Untertitel springen',
|
'hotkeys.actions.subSeekPrev' => 'Zum vorherigen Untertitel springen',
|
||||||
'hotkeys.actions.shaderToggle' => 'Shader umschalten',
|
'hotkeys.actions.shaderToggle' => 'Shader umschalten',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsDe {
|
|||||||
'videoControls.pipActive' => 'Wiedergabe im Bild-in-Bild-Modus',
|
'videoControls.pipActive' => 'Wiedergabe im Bild-in-Bild-Modus',
|
||||||
'videoControls.pipFailed' => 'Bild-in-Bild konnte nicht gestartet werden',
|
'videoControls.pipFailed' => 'Bild-in-Bild konnte nicht gestartet werden',
|
||||||
'videoControls.screenshotSaved' => 'Screenshot gespeichert',
|
'videoControls.screenshotSaved' => 'Screenshot gespeichert',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Erfordert Android 8.0 oder neuer',
|
'videoControls.pipErrors.androidVersion' => 'Erfordert Android 8.0 oder neuer',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Erfordert iOS 15.0 oder neuer',
|
'videoControls.pipErrors.iosVersion' => 'Erfordert iOS 15.0 oder neuer',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Bild-in-Bild ist deaktiviert. Aktiviere es in den Systemeinstellungen.',
|
'videoControls.pipErrors.permissionDisabled' => 'Bild-in-Bild ist deaktiviert. Aktiviere es in den Systemeinstellungen.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsDe {
|
|||||||
'profiles.addPlezyProfile' => 'Plezy-Profil hinzufügen',
|
'profiles.addPlezyProfile' => 'Plezy-Profil hinzufügen',
|
||||||
'profiles.switchingProfile' => 'Profil wird gewechselt…',
|
'profiles.switchingProfile' => 'Profil wird gewechselt…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Dieses Profil löschen?',
|
'profiles.deleteThisProfileTitle' => 'Dieses Profil löschen?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '${displayName} entfernen. Verbindungen bleiben unberührt.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '${displayName} entfernen. Verbindungen bleiben unberührt.',
|
||||||
'profiles.active' => 'Aktiv',
|
'profiles.active' => 'Aktiv',
|
||||||
'profiles.manage' => 'Verwalten',
|
'profiles.manage' => 'Verwalten',
|
||||||
'profiles.delete' => 'Löschen',
|
'profiles.delete' => 'Löschen',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Abmelden',
|
'profiles.signOut' => 'Abmelden',
|
||||||
'profiles.signOutPlexTitle' => 'Von Plex abmelden?',
|
'profiles.signOutPlexTitle' => 'Von Plex abmelden?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName} und alle Plex Home-Benutzer entfernen? Du kannst dich jederzeit wieder anmelden.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName} und alle Plex Home-Benutzer entfernen? Du kannst dich jederzeit wieder anmelden.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsDe {
|
|||||||
'companionRemote.remote.audio' => 'Audio',
|
'companionRemote.remote.audio' => 'Audio',
|
||||||
'companionRemote.remote.searchHint' => 'Auf Desktop suchen...',
|
'companionRemote.remote.searchHint' => 'Auf Desktop suchen...',
|
||||||
'videoSettings.playbackSpeed' => 'Wiedergabegeschwindigkeit',
|
'videoSettings.playbackSpeed' => 'Wiedergabegeschwindigkeit',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Schlaftimer',
|
'videoSettings.sleepTimer' => 'Schlaftimer',
|
||||||
'videoSettings.audioSync' => 'Audio-Synchronisation',
|
'videoSettings.audioSync' => 'Audio-Synchronisation',
|
||||||
'videoSettings.subtitleSync' => 'Untertitel-Synchronisation',
|
'videoSettings.subtitleSync' => 'Untertitel-Synchronisation',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsDe {
|
|||||||
'metadataEdit.basicInfo' => 'Grundlegende Infos',
|
'metadataEdit.basicInfo' => 'Grundlegende Infos',
|
||||||
'metadataEdit.artwork' => 'Artwork',
|
'metadataEdit.artwork' => 'Artwork',
|
||||||
'metadataEdit.advancedSettings' => 'Erweiterte Einstellungen',
|
'metadataEdit.advancedSettings' => 'Erweiterte Einstellungen',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Titel',
|
'metadataEdit.title' => 'Titel',
|
||||||
'metadataEdit.sortTitle' => 'Sortiertitel',
|
'metadataEdit.sortTitle' => 'Sortiertitel',
|
||||||
'metadataEdit.originalTitle' => 'Originaltitel',
|
'metadataEdit.originalTitle' => 'Originaltitel',
|
||||||
'metadataEdit.releaseDate' => 'Erscheinungsdatum',
|
'metadataEdit.releaseDate' => 'Erscheinungsdatum',
|
||||||
'metadataEdit.contentRating' => 'Altersfreigabe',
|
'metadataEdit.contentRating' => 'Altersfreigabe',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Zusammenfassung',
|
'metadataEdit.summary' => 'Zusammenfassung',
|
||||||
|
|||||||
@@ -1396,6 +1396,9 @@ class TranslationsVideoControlsEn {
|
|||||||
/// en: 'Screenshot saved'
|
/// en: 'Screenshot saved'
|
||||||
String get screenshotSaved => 'Screenshot saved';
|
String get screenshotSaved => 'Screenshot saved';
|
||||||
|
|
||||||
|
/// en: 'Zoom ${percent}%'
|
||||||
|
String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
|
|
||||||
late final TranslationsVideoControlsPipErrorsEn pipErrors = TranslationsVideoControlsPipErrorsEn.internal(_root);
|
late final TranslationsVideoControlsPipErrorsEn pipErrors = TranslationsVideoControlsPipErrorsEn.internal(_root);
|
||||||
|
|
||||||
/// en: 'Chapters'
|
/// en: 'Chapters'
|
||||||
@@ -3060,6 +3063,9 @@ class TranslationsVideoSettingsEn {
|
|||||||
/// en: 'Playback Speed'
|
/// en: 'Playback Speed'
|
||||||
String get playbackSpeed => 'Playback Speed';
|
String get playbackSpeed => 'Playback Speed';
|
||||||
|
|
||||||
|
/// en: 'Zoom'
|
||||||
|
String get zoom => 'Zoom';
|
||||||
|
|
||||||
/// en: 'Sleep Timer'
|
/// en: 'Sleep Timer'
|
||||||
String get sleepTimer => 'Sleep Timer';
|
String get sleepTimer => 'Sleep Timer';
|
||||||
|
|
||||||
@@ -3680,6 +3686,15 @@ class TranslationsHotkeysActionsEn {
|
|||||||
/// en: 'Reset Speed'
|
/// en: 'Reset Speed'
|
||||||
String get speedReset => 'Reset Speed';
|
String get speedReset => 'Reset Speed';
|
||||||
|
|
||||||
|
/// en: 'Zoom In'
|
||||||
|
String get zoomIn => 'Zoom In';
|
||||||
|
|
||||||
|
/// en: 'Zoom Out'
|
||||||
|
String get zoomOut => 'Zoom Out';
|
||||||
|
|
||||||
|
/// en: 'Reset Zoom'
|
||||||
|
String get zoomReset => 'Reset Zoom';
|
||||||
|
|
||||||
/// en: 'Seek to Next Subtitle'
|
/// en: 'Seek to Next Subtitle'
|
||||||
String get subSeekNext => 'Seek to Next Subtitle';
|
String get subSeekNext => 'Seek to Next Subtitle';
|
||||||
|
|
||||||
@@ -4463,6 +4478,9 @@ extension on Translations {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Increase Speed',
|
'hotkeys.actions.speedIncrease' => 'Increase Speed',
|
||||||
'hotkeys.actions.speedDecrease' => 'Decrease Speed',
|
'hotkeys.actions.speedDecrease' => 'Decrease Speed',
|
||||||
'hotkeys.actions.speedReset' => 'Reset Speed',
|
'hotkeys.actions.speedReset' => 'Reset Speed',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Zoom In',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Zoom Out',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Reset Zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Seek to Next Subtitle',
|
'hotkeys.actions.subSeekNext' => 'Seek to Next Subtitle',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Seek to Previous Subtitle',
|
'hotkeys.actions.subSeekPrev' => 'Seek to Previous Subtitle',
|
||||||
'hotkeys.actions.shaderToggle' => 'Toggle Shaders',
|
'hotkeys.actions.shaderToggle' => 'Toggle Shaders',
|
||||||
@@ -4588,6 +4606,7 @@ extension on Translations {
|
|||||||
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
|
'videoControls.pipActive' => 'Playing in Picture-in-Picture',
|
||||||
'videoControls.pipFailed' => 'Picture-in-picture failed to start',
|
'videoControls.pipFailed' => 'Picture-in-picture failed to start',
|
||||||
'videoControls.screenshotSaved' => 'Screenshot saved',
|
'videoControls.screenshotSaved' => 'Screenshot saved',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Requires Android 8.0 or newer',
|
'videoControls.pipErrors.androidVersion' => 'Requires Android 8.0 or newer',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Requires iOS 15.0 or newer',
|
'videoControls.pipErrors.iosVersion' => 'Requires iOS 15.0 or newer',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture is disabled. Enable it in system settings.',
|
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture is disabled. Enable it in system settings.',
|
||||||
@@ -4680,12 +4699,12 @@ extension on Translations {
|
|||||||
'profiles.addPlezyProfile' => 'Add Plezy profile',
|
'profiles.addPlezyProfile' => 'Add Plezy profile',
|
||||||
'profiles.switchingProfile' => 'Switching profile…',
|
'profiles.switchingProfile' => 'Switching profile…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Delete this profile?',
|
'profiles.deleteThisProfileTitle' => 'Delete this profile?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Remove ${displayName}. Connections aren\'t affected.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Remove ${displayName}. Connections aren\'t affected.',
|
||||||
'profiles.active' => 'Active',
|
'profiles.active' => 'Active',
|
||||||
'profiles.manage' => 'Manage',
|
'profiles.manage' => 'Manage',
|
||||||
'profiles.delete' => 'Delete',
|
'profiles.delete' => 'Delete',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Sign out',
|
'profiles.signOut' => 'Sign out',
|
||||||
'profiles.signOutPlexTitle' => 'Sign out of Plex?',
|
'profiles.signOutPlexTitle' => 'Sign out of Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Remove ${displayName} and all Plex Home users? Sign back in anytime.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Remove ${displayName} and all Plex Home users? Sign back in anytime.',
|
||||||
@@ -5165,6 +5184,7 @@ extension on Translations {
|
|||||||
'companionRemote.remote.audio' => 'Audio',
|
'companionRemote.remote.audio' => 'Audio',
|
||||||
'companionRemote.remote.searchHint' => 'Search on desktop...',
|
'companionRemote.remote.searchHint' => 'Search on desktop...',
|
||||||
'videoSettings.playbackSpeed' => 'Playback Speed',
|
'videoSettings.playbackSpeed' => 'Playback Speed',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Sleep Timer',
|
'videoSettings.sleepTimer' => 'Sleep Timer',
|
||||||
'videoSettings.audioSync' => 'Audio Sync',
|
'videoSettings.audioSync' => 'Audio Sync',
|
||||||
'videoSettings.subtitleSync' => 'Subtitle Sync',
|
'videoSettings.subtitleSync' => 'Subtitle Sync',
|
||||||
@@ -5193,13 +5213,13 @@ extension on Translations {
|
|||||||
'metadataEdit.basicInfo' => 'Basic Info',
|
'metadataEdit.basicInfo' => 'Basic Info',
|
||||||
'metadataEdit.artwork' => 'Artwork',
|
'metadataEdit.artwork' => 'Artwork',
|
||||||
'metadataEdit.advancedSettings' => 'Advanced Settings',
|
'metadataEdit.advancedSettings' => 'Advanced Settings',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Title',
|
'metadataEdit.title' => 'Title',
|
||||||
'metadataEdit.sortTitle' => 'Sort Title',
|
'metadataEdit.sortTitle' => 'Sort Title',
|
||||||
'metadataEdit.originalTitle' => 'Original Title',
|
'metadataEdit.originalTitle' => 'Original Title',
|
||||||
'metadataEdit.releaseDate' => 'Release Date',
|
'metadataEdit.releaseDate' => 'Release Date',
|
||||||
'metadataEdit.contentRating' => 'Content Rating',
|
'metadataEdit.contentRating' => 'Content Rating',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Summary',
|
'metadataEdit.summary' => 'Summary',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsEs extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Reproduciendo en Imagen en Imagen';
|
@override String get pipActive => 'Reproduciendo en Imagen en Imagen';
|
||||||
@override String get pipFailed => 'Error al iniciar Imagen en Imagen';
|
@override String get pipFailed => 'Error al iniciar Imagen en Imagen';
|
||||||
@override String get screenshotSaved => 'Captura de pantalla guardada';
|
@override String get screenshotSaved => 'Captura de pantalla guardada';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsEs pipErrors = _TranslationsVideoControlsPipErrorsEs._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsEs pipErrors = _TranslationsVideoControlsPipErrorsEs._(_root);
|
||||||
@override String get chapters => 'Capítulos';
|
@override String get chapters => 'Capítulos';
|
||||||
@override String get noChaptersAvailable => 'No hay capítulos disponibles';
|
@override String get noChaptersAvailable => 'No hay capítulos disponibles';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsEs extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Velocidad de reproducción';
|
@override String get playbackSpeed => 'Velocidad de reproducción';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Temporizador de apagado';
|
@override String get sleepTimer => 'Temporizador de apagado';
|
||||||
@override String get audioSync => 'Sincronización de audio';
|
@override String get audioSync => 'Sincronización de audio';
|
||||||
@override String get subtitleSync => 'Sincronización de subtítulos';
|
@override String get subtitleSync => 'Sincronización de subtítulos';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsEs extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Aumentar Velocidad';
|
@override String get speedIncrease => 'Aumentar Velocidad';
|
||||||
@override String get speedDecrease => 'Disminuir Velocidad';
|
@override String get speedDecrease => 'Disminuir Velocidad';
|
||||||
@override String get speedReset => 'Restablecer Velocidad';
|
@override String get speedReset => 'Restablecer Velocidad';
|
||||||
|
@override String get zoomIn => 'Acercar';
|
||||||
|
@override String get zoomOut => 'Alejar';
|
||||||
|
@override String get zoomReset => 'Restablecer zoom';
|
||||||
@override String get subSeekNext => 'Ir al Siguiente Subtítulo';
|
@override String get subSeekNext => 'Ir al Siguiente Subtítulo';
|
||||||
@override String get subSeekPrev => 'Ir al Anterior Subtítulo';
|
@override String get subSeekPrev => 'Ir al Anterior Subtítulo';
|
||||||
@override String get shaderToggle => 'Alternar Shaders';
|
@override String get shaderToggle => 'Alternar Shaders';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsEs {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Aumentar Velocidad',
|
'hotkeys.actions.speedIncrease' => 'Aumentar Velocidad',
|
||||||
'hotkeys.actions.speedDecrease' => 'Disminuir Velocidad',
|
'hotkeys.actions.speedDecrease' => 'Disminuir Velocidad',
|
||||||
'hotkeys.actions.speedReset' => 'Restablecer Velocidad',
|
'hotkeys.actions.speedReset' => 'Restablecer Velocidad',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Acercar',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Alejar',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Restablecer zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Ir al Siguiente Subtítulo',
|
'hotkeys.actions.subSeekNext' => 'Ir al Siguiente Subtítulo',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Ir al Anterior Subtítulo',
|
'hotkeys.actions.subSeekPrev' => 'Ir al Anterior Subtítulo',
|
||||||
'hotkeys.actions.shaderToggle' => 'Alternar Shaders',
|
'hotkeys.actions.shaderToggle' => 'Alternar Shaders',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsEs {
|
|||||||
'videoControls.pipActive' => 'Reproduciendo en Imagen en Imagen',
|
'videoControls.pipActive' => 'Reproduciendo en Imagen en Imagen',
|
||||||
'videoControls.pipFailed' => 'Error al iniciar Imagen en Imagen',
|
'videoControls.pipFailed' => 'Error al iniciar Imagen en Imagen',
|
||||||
'videoControls.screenshotSaved' => 'Captura de pantalla guardada',
|
'videoControls.screenshotSaved' => 'Captura de pantalla guardada',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Requiere Android 8.0 o más reciente',
|
'videoControls.pipErrors.androidVersion' => 'Requiere Android 8.0 o más reciente',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Requiere iOS 15.0 o más reciente',
|
'videoControls.pipErrors.iosVersion' => 'Requiere iOS 15.0 o más reciente',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture está desactivado. Actívalo en los ajustes del sistema.',
|
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture está desactivado. Actívalo en los ajustes del sistema.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsEs {
|
|||||||
'profiles.addPlezyProfile' => 'Añadir perfil de Plezy',
|
'profiles.addPlezyProfile' => 'Añadir perfil de Plezy',
|
||||||
'profiles.switchingProfile' => 'Cambiando de perfil…',
|
'profiles.switchingProfile' => 'Cambiando de perfil…',
|
||||||
'profiles.deleteThisProfileTitle' => '¿Eliminar este perfil?',
|
'profiles.deleteThisProfileTitle' => '¿Eliminar este perfil?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Eliminar ${displayName}. Las conexiones no se verán afectadas.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Eliminar ${displayName}. Las conexiones no se verán afectadas.',
|
||||||
'profiles.active' => 'Activo',
|
'profiles.active' => 'Activo',
|
||||||
'profiles.manage' => 'Administrar',
|
'profiles.manage' => 'Administrar',
|
||||||
'profiles.delete' => 'Eliminar',
|
'profiles.delete' => 'Eliminar',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Cerrar sesión',
|
'profiles.signOut' => 'Cerrar sesión',
|
||||||
'profiles.signOutPlexTitle' => '¿Cerrar sesión de Plex?',
|
'profiles.signOutPlexTitle' => '¿Cerrar sesión de Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => '¿Eliminar ${displayName} y todos los usuarios de Plex Home? Puedes iniciar sesión de nuevo cuando quieras.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => '¿Eliminar ${displayName} y todos los usuarios de Plex Home? Puedes iniciar sesión de nuevo cuando quieras.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsEs {
|
|||||||
'companionRemote.remote.audio' => 'Audio',
|
'companionRemote.remote.audio' => 'Audio',
|
||||||
'companionRemote.remote.searchHint' => 'Buscar en escritorio...',
|
'companionRemote.remote.searchHint' => 'Buscar en escritorio...',
|
||||||
'videoSettings.playbackSpeed' => 'Velocidad de reproducción',
|
'videoSettings.playbackSpeed' => 'Velocidad de reproducción',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Temporizador de apagado',
|
'videoSettings.sleepTimer' => 'Temporizador de apagado',
|
||||||
'videoSettings.audioSync' => 'Sincronización de audio',
|
'videoSettings.audioSync' => 'Sincronización de audio',
|
||||||
'videoSettings.subtitleSync' => 'Sincronización de subtítulos',
|
'videoSettings.subtitleSync' => 'Sincronización de subtítulos',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsEs {
|
|||||||
'metadataEdit.basicInfo' => 'Información básica',
|
'metadataEdit.basicInfo' => 'Información básica',
|
||||||
'metadataEdit.artwork' => 'Artwork',
|
'metadataEdit.artwork' => 'Artwork',
|
||||||
'metadataEdit.advancedSettings' => 'Ajustes avanzados',
|
'metadataEdit.advancedSettings' => 'Ajustes avanzados',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Título',
|
'metadataEdit.title' => 'Título',
|
||||||
'metadataEdit.sortTitle' => 'Título de ordenación',
|
'metadataEdit.sortTitle' => 'Título de ordenación',
|
||||||
'metadataEdit.originalTitle' => 'Título original',
|
'metadataEdit.originalTitle' => 'Título original',
|
||||||
'metadataEdit.releaseDate' => 'Fecha de estreno',
|
'metadataEdit.releaseDate' => 'Fecha de estreno',
|
||||||
'metadataEdit.contentRating' => 'Clasificación de contenido',
|
'metadataEdit.contentRating' => 'Clasificación de contenido',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Estudio',
|
'metadataEdit.studio' => 'Estudio',
|
||||||
'metadataEdit.tagline' => 'Eslogan',
|
'metadataEdit.tagline' => 'Eslogan',
|
||||||
'metadataEdit.summary' => 'Resumen',
|
'metadataEdit.summary' => 'Resumen',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsFr extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Lecture en mode image dans l\'image';
|
@override String get pipActive => 'Lecture en mode image dans l\'image';
|
||||||
@override String get pipFailed => 'Échec du démarrage du mode image dans l\'image';
|
@override String get pipFailed => 'Échec du démarrage du mode image dans l\'image';
|
||||||
@override String get screenshotSaved => 'Capture d\'écran enregistrée';
|
@override String get screenshotSaved => 'Capture d\'écran enregistrée';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent} %';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsFr pipErrors = _TranslationsVideoControlsPipErrorsFr._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsFr pipErrors = _TranslationsVideoControlsPipErrorsFr._(_root);
|
||||||
@override String get chapters => 'Chapitres';
|
@override String get chapters => 'Chapitres';
|
||||||
@override String get noChaptersAvailable => 'Aucun chapitre disponible';
|
@override String get noChaptersAvailable => 'Aucun chapitre disponible';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsFr extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Vitesse de lecture';
|
@override String get playbackSpeed => 'Vitesse de lecture';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Minuterie de mise en veille';
|
@override String get sleepTimer => 'Minuterie de mise en veille';
|
||||||
@override String get audioSync => 'Synchronisation audio';
|
@override String get audioSync => 'Synchronisation audio';
|
||||||
@override String get subtitleSync => 'Synchronisation des sous-titres';
|
@override String get subtitleSync => 'Synchronisation des sous-titres';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsFr extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Augmenter la vitesse';
|
@override String get speedIncrease => 'Augmenter la vitesse';
|
||||||
@override String get speedDecrease => 'Réduire la vitesse';
|
@override String get speedDecrease => 'Réduire la vitesse';
|
||||||
@override String get speedReset => 'Réinitialiser la vitesse';
|
@override String get speedReset => 'Réinitialiser la vitesse';
|
||||||
|
@override String get zoomIn => 'Zoom avant';
|
||||||
|
@override String get zoomOut => 'Zoom arrière';
|
||||||
|
@override String get zoomReset => 'Réinitialiser le zoom';
|
||||||
@override String get subSeekNext => 'Rechercher le sous-titre suivant';
|
@override String get subSeekNext => 'Rechercher le sous-titre suivant';
|
||||||
@override String get subSeekPrev => 'Rechercher le sous-titre précédent';
|
@override String get subSeekPrev => 'Rechercher le sous-titre précédent';
|
||||||
@override String get shaderToggle => 'Activer/désactiver les shaders';
|
@override String get shaderToggle => 'Activer/désactiver les shaders';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsFr {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Augmenter la vitesse',
|
'hotkeys.actions.speedIncrease' => 'Augmenter la vitesse',
|
||||||
'hotkeys.actions.speedDecrease' => 'Réduire la vitesse',
|
'hotkeys.actions.speedDecrease' => 'Réduire la vitesse',
|
||||||
'hotkeys.actions.speedReset' => 'Réinitialiser la vitesse',
|
'hotkeys.actions.speedReset' => 'Réinitialiser la vitesse',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Zoom avant',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Zoom arrière',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Réinitialiser le zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Rechercher le sous-titre suivant',
|
'hotkeys.actions.subSeekNext' => 'Rechercher le sous-titre suivant',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Rechercher le sous-titre précédent',
|
'hotkeys.actions.subSeekPrev' => 'Rechercher le sous-titre précédent',
|
||||||
'hotkeys.actions.shaderToggle' => 'Activer/désactiver les shaders',
|
'hotkeys.actions.shaderToggle' => 'Activer/désactiver les shaders',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsFr {
|
|||||||
'videoControls.pipActive' => 'Lecture en mode image dans l\'image',
|
'videoControls.pipActive' => 'Lecture en mode image dans l\'image',
|
||||||
'videoControls.pipFailed' => 'Échec du démarrage du mode image dans l\'image',
|
'videoControls.pipFailed' => 'Échec du démarrage du mode image dans l\'image',
|
||||||
'videoControls.screenshotSaved' => 'Capture d\'écran enregistrée',
|
'videoControls.screenshotSaved' => 'Capture d\'écran enregistrée',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent} %',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Nécessite Android 8.0 ou plus récent',
|
'videoControls.pipErrors.androidVersion' => 'Nécessite Android 8.0 ou plus récent',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Nécessite iOS 15.0 ou plus récent',
|
'videoControls.pipErrors.iosVersion' => 'Nécessite iOS 15.0 ou plus récent',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture est désactivé. Activez-le dans les paramètres système.',
|
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture est désactivé. Activez-le dans les paramètres système.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsFr {
|
|||||||
'profiles.addPlezyProfile' => 'Ajouter un profil Plezy',
|
'profiles.addPlezyProfile' => 'Ajouter un profil Plezy',
|
||||||
'profiles.switchingProfile' => 'Changement de profil…',
|
'profiles.switchingProfile' => 'Changement de profil…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Supprimer ce profil ?',
|
'profiles.deleteThisProfileTitle' => 'Supprimer ce profil ?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Supprimer ${displayName}. Les connexions ne sont pas affectées.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Supprimer ${displayName}. Les connexions ne sont pas affectées.',
|
||||||
'profiles.active' => 'Actif',
|
'profiles.active' => 'Actif',
|
||||||
'profiles.manage' => 'Gérer',
|
'profiles.manage' => 'Gérer',
|
||||||
'profiles.delete' => 'Supprimer',
|
'profiles.delete' => 'Supprimer',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Se déconnecter',
|
'profiles.signOut' => 'Se déconnecter',
|
||||||
'profiles.signOutPlexTitle' => 'Se déconnecter de Plex ?',
|
'profiles.signOutPlexTitle' => 'Se déconnecter de Plex ?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Supprimer ${displayName} et tous les utilisateurs Plex Home ? Reconnexion possible à tout moment.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Supprimer ${displayName} et tous les utilisateurs Plex Home ? Reconnexion possible à tout moment.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsFr {
|
|||||||
'companionRemote.remote.audio' => 'Audio',
|
'companionRemote.remote.audio' => 'Audio',
|
||||||
'companionRemote.remote.searchHint' => 'Rechercher sur le bureau...',
|
'companionRemote.remote.searchHint' => 'Rechercher sur le bureau...',
|
||||||
'videoSettings.playbackSpeed' => 'Vitesse de lecture',
|
'videoSettings.playbackSpeed' => 'Vitesse de lecture',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Minuterie de mise en veille',
|
'videoSettings.sleepTimer' => 'Minuterie de mise en veille',
|
||||||
'videoSettings.audioSync' => 'Synchronisation audio',
|
'videoSettings.audioSync' => 'Synchronisation audio',
|
||||||
'videoSettings.subtitleSync' => 'Synchronisation des sous-titres',
|
'videoSettings.subtitleSync' => 'Synchronisation des sous-titres',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsFr {
|
|||||||
'metadataEdit.basicInfo' => 'Informations de base',
|
'metadataEdit.basicInfo' => 'Informations de base',
|
||||||
'metadataEdit.artwork' => 'Artwork',
|
'metadataEdit.artwork' => 'Artwork',
|
||||||
'metadataEdit.advancedSettings' => 'Paramètres avancés',
|
'metadataEdit.advancedSettings' => 'Paramètres avancés',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Titre',
|
'metadataEdit.title' => 'Titre',
|
||||||
'metadataEdit.sortTitle' => 'Titre de tri',
|
'metadataEdit.sortTitle' => 'Titre de tri',
|
||||||
'metadataEdit.originalTitle' => 'Titre original',
|
'metadataEdit.originalTitle' => 'Titre original',
|
||||||
'metadataEdit.releaseDate' => 'Date de sortie',
|
'metadataEdit.releaseDate' => 'Date de sortie',
|
||||||
'metadataEdit.contentRating' => 'Classification',
|
'metadataEdit.contentRating' => 'Classification',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Slogan',
|
'metadataEdit.tagline' => 'Slogan',
|
||||||
'metadataEdit.summary' => 'Résumé',
|
'metadataEdit.summary' => 'Résumé',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsIt extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Riproduzione in Picture-in-Picture';
|
@override String get pipActive => 'Riproduzione in Picture-in-Picture';
|
||||||
@override String get pipFailed => 'Impossibile avviare la modalità Picture-in-Picture';
|
@override String get pipFailed => 'Impossibile avviare la modalità Picture-in-Picture';
|
||||||
@override String get screenshotSaved => 'Schermata salvata';
|
@override String get screenshotSaved => 'Schermata salvata';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsIt pipErrors = _TranslationsVideoControlsPipErrorsIt._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsIt pipErrors = _TranslationsVideoControlsPipErrorsIt._(_root);
|
||||||
@override String get chapters => 'Capitoli';
|
@override String get chapters => 'Capitoli';
|
||||||
@override String get noChaptersAvailable => 'Nessun capitolo disponibile';
|
@override String get noChaptersAvailable => 'Nessun capitolo disponibile';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsIt extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Velocità di riproduzione';
|
@override String get playbackSpeed => 'Velocità di riproduzione';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Timer di spegnimento';
|
@override String get sleepTimer => 'Timer di spegnimento';
|
||||||
@override String get audioSync => 'Sincronizzazione audio';
|
@override String get audioSync => 'Sincronizzazione audio';
|
||||||
@override String get subtitleSync => 'Sincronizzazione sottotitoli';
|
@override String get subtitleSync => 'Sincronizzazione sottotitoli';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsIt extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Aumenta velocità';
|
@override String get speedIncrease => 'Aumenta velocità';
|
||||||
@override String get speedDecrease => 'Diminuisci velocità';
|
@override String get speedDecrease => 'Diminuisci velocità';
|
||||||
@override String get speedReset => 'Ripristina velocità';
|
@override String get speedReset => 'Ripristina velocità';
|
||||||
|
@override String get zoomIn => 'Aumenta zoom';
|
||||||
|
@override String get zoomOut => 'Riduci zoom';
|
||||||
|
@override String get zoomReset => 'Ripristina zoom';
|
||||||
@override String get subSeekNext => 'Vai al sottotitolo successivo';
|
@override String get subSeekNext => 'Vai al sottotitolo successivo';
|
||||||
@override String get subSeekPrev => 'Vai al sottotitolo precedente';
|
@override String get subSeekPrev => 'Vai al sottotitolo precedente';
|
||||||
@override String get shaderToggle => 'Attiva/disattiva shader';
|
@override String get shaderToggle => 'Attiva/disattiva shader';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsIt {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Aumenta velocità',
|
'hotkeys.actions.speedIncrease' => 'Aumenta velocità',
|
||||||
'hotkeys.actions.speedDecrease' => 'Diminuisci velocità',
|
'hotkeys.actions.speedDecrease' => 'Diminuisci velocità',
|
||||||
'hotkeys.actions.speedReset' => 'Ripristina velocità',
|
'hotkeys.actions.speedReset' => 'Ripristina velocità',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Aumenta zoom',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Riduci zoom',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Ripristina zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Vai al sottotitolo successivo',
|
'hotkeys.actions.subSeekNext' => 'Vai al sottotitolo successivo',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Vai al sottotitolo precedente',
|
'hotkeys.actions.subSeekPrev' => 'Vai al sottotitolo precedente',
|
||||||
'hotkeys.actions.shaderToggle' => 'Attiva/disattiva shader',
|
'hotkeys.actions.shaderToggle' => 'Attiva/disattiva shader',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsIt {
|
|||||||
'videoControls.pipActive' => 'Riproduzione in Picture-in-Picture',
|
'videoControls.pipActive' => 'Riproduzione in Picture-in-Picture',
|
||||||
'videoControls.pipFailed' => 'Impossibile avviare la modalità Picture-in-Picture',
|
'videoControls.pipFailed' => 'Impossibile avviare la modalità Picture-in-Picture',
|
||||||
'videoControls.screenshotSaved' => 'Schermata salvata',
|
'videoControls.screenshotSaved' => 'Schermata salvata',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Richiede Android 8.0 o versioni successive',
|
'videoControls.pipErrors.androidVersion' => 'Richiede Android 8.0 o versioni successive',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Richiede iOS 15.0 o versioni successive',
|
'videoControls.pipErrors.iosVersion' => 'Richiede iOS 15.0 o versioni successive',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture è disattivato. Attivalo nelle impostazioni di sistema.',
|
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture è disattivato. Attivalo nelle impostazioni di sistema.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsIt {
|
|||||||
'profiles.addPlezyProfile' => 'Aggiungi profilo Plezy',
|
'profiles.addPlezyProfile' => 'Aggiungi profilo Plezy',
|
||||||
'profiles.switchingProfile' => 'Cambio profilo…',
|
'profiles.switchingProfile' => 'Cambio profilo…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Eliminare questo profilo?',
|
'profiles.deleteThisProfileTitle' => 'Eliminare questo profilo?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Rimuovi ${displayName}. Le connessioni non sono interessate.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Rimuovi ${displayName}. Le connessioni non sono interessate.',
|
||||||
'profiles.active' => 'Attivo',
|
'profiles.active' => 'Attivo',
|
||||||
'profiles.manage' => 'Gestisci',
|
'profiles.manage' => 'Gestisci',
|
||||||
'profiles.delete' => 'Elimina',
|
'profiles.delete' => 'Elimina',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Esci',
|
'profiles.signOut' => 'Esci',
|
||||||
'profiles.signOutPlexTitle' => 'Uscire da Plex?',
|
'profiles.signOutPlexTitle' => 'Uscire da Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Rimuovere ${displayName} e tutti gli utenti Plex Home? Puoi accedere di nuovo quando vuoi.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Rimuovere ${displayName} e tutti gli utenti Plex Home? Puoi accedere di nuovo quando vuoi.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsIt {
|
|||||||
'companionRemote.remote.audio' => 'Audio',
|
'companionRemote.remote.audio' => 'Audio',
|
||||||
'companionRemote.remote.searchHint' => 'Cerca sul desktop...',
|
'companionRemote.remote.searchHint' => 'Cerca sul desktop...',
|
||||||
'videoSettings.playbackSpeed' => 'Velocità di riproduzione',
|
'videoSettings.playbackSpeed' => 'Velocità di riproduzione',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Timer di spegnimento',
|
'videoSettings.sleepTimer' => 'Timer di spegnimento',
|
||||||
'videoSettings.audioSync' => 'Sincronizzazione audio',
|
'videoSettings.audioSync' => 'Sincronizzazione audio',
|
||||||
'videoSettings.subtitleSync' => 'Sincronizzazione sottotitoli',
|
'videoSettings.subtitleSync' => 'Sincronizzazione sottotitoli',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsIt {
|
|||||||
'metadataEdit.basicInfo' => 'Informazioni di base',
|
'metadataEdit.basicInfo' => 'Informazioni di base',
|
||||||
'metadataEdit.artwork' => 'Artwork',
|
'metadataEdit.artwork' => 'Artwork',
|
||||||
'metadataEdit.advancedSettings' => 'Impostazioni avanzate',
|
'metadataEdit.advancedSettings' => 'Impostazioni avanzate',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Titolo',
|
'metadataEdit.title' => 'Titolo',
|
||||||
'metadataEdit.sortTitle' => 'Titolo di ordinamento',
|
'metadataEdit.sortTitle' => 'Titolo di ordinamento',
|
||||||
'metadataEdit.originalTitle' => 'Titolo originale',
|
'metadataEdit.originalTitle' => 'Titolo originale',
|
||||||
'metadataEdit.releaseDate' => 'Data di uscita',
|
'metadataEdit.releaseDate' => 'Data di uscita',
|
||||||
'metadataEdit.contentRating' => 'Classificazione',
|
'metadataEdit.contentRating' => 'Classificazione',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Trama',
|
'metadataEdit.summary' => 'Trama',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsJa extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'ピクチャーインピクチャーで再生中';
|
@override String get pipActive => 'ピクチャーインピクチャーで再生中';
|
||||||
@override String get pipFailed => 'ピクチャーインピクチャーの開始に失敗しました';
|
@override String get pipFailed => 'ピクチャーインピクチャーの開始に失敗しました';
|
||||||
@override String get screenshotSaved => 'スクリーンショットを保存しました';
|
@override String get screenshotSaved => 'スクリーンショットを保存しました';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'ズーム ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsJa pipErrors = _TranslationsVideoControlsPipErrorsJa._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsJa pipErrors = _TranslationsVideoControlsPipErrorsJa._(_root);
|
||||||
@override String get chapters => 'チャプター';
|
@override String get chapters => 'チャプター';
|
||||||
@override String get noChaptersAvailable => 'チャプターがありません';
|
@override String get noChaptersAvailable => 'チャプターがありません';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsJa extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => '再生速度';
|
@override String get playbackSpeed => '再生速度';
|
||||||
|
@override String get zoom => 'ズーム';
|
||||||
@override String get sleepTimer => 'スリープタイマー';
|
@override String get sleepTimer => 'スリープタイマー';
|
||||||
@override String get audioSync => '音声同期';
|
@override String get audioSync => '音声同期';
|
||||||
@override String get subtitleSync => '字幕同期';
|
@override String get subtitleSync => '字幕同期';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsJa extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => '速度を上げる';
|
@override String get speedIncrease => '速度を上げる';
|
||||||
@override String get speedDecrease => '速度を下げる';
|
@override String get speedDecrease => '速度を下げる';
|
||||||
@override String get speedReset => '速度をリセット';
|
@override String get speedReset => '速度をリセット';
|
||||||
|
@override String get zoomIn => 'ズームイン';
|
||||||
|
@override String get zoomOut => 'ズームアウト';
|
||||||
|
@override String get zoomReset => 'ズームをリセット';
|
||||||
@override String get subSeekNext => '次の字幕にシーク';
|
@override String get subSeekNext => '次の字幕にシーク';
|
||||||
@override String get subSeekPrev => '前の字幕にシーク';
|
@override String get subSeekPrev => '前の字幕にシーク';
|
||||||
@override String get shaderToggle => 'シェーダー切替';
|
@override String get shaderToggle => 'シェーダー切替';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsJa {
|
|||||||
'hotkeys.actions.speedIncrease' => '速度を上げる',
|
'hotkeys.actions.speedIncrease' => '速度を上げる',
|
||||||
'hotkeys.actions.speedDecrease' => '速度を下げる',
|
'hotkeys.actions.speedDecrease' => '速度を下げる',
|
||||||
'hotkeys.actions.speedReset' => '速度をリセット',
|
'hotkeys.actions.speedReset' => '速度をリセット',
|
||||||
|
'hotkeys.actions.zoomIn' => 'ズームイン',
|
||||||
|
'hotkeys.actions.zoomOut' => 'ズームアウト',
|
||||||
|
'hotkeys.actions.zoomReset' => 'ズームをリセット',
|
||||||
'hotkeys.actions.subSeekNext' => '次の字幕にシーク',
|
'hotkeys.actions.subSeekNext' => '次の字幕にシーク',
|
||||||
'hotkeys.actions.subSeekPrev' => '前の字幕にシーク',
|
'hotkeys.actions.subSeekPrev' => '前の字幕にシーク',
|
||||||
'hotkeys.actions.shaderToggle' => 'シェーダー切替',
|
'hotkeys.actions.shaderToggle' => 'シェーダー切替',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsJa {
|
|||||||
'videoControls.pipActive' => 'ピクチャーインピクチャーで再生中',
|
'videoControls.pipActive' => 'ピクチャーインピクチャーで再生中',
|
||||||
'videoControls.pipFailed' => 'ピクチャーインピクチャーの開始に失敗しました',
|
'videoControls.pipFailed' => 'ピクチャーインピクチャーの開始に失敗しました',
|
||||||
'videoControls.screenshotSaved' => 'スクリーンショットを保存しました',
|
'videoControls.screenshotSaved' => 'スクリーンショットを保存しました',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'ズーム ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Android 8.0以降が必要です',
|
'videoControls.pipErrors.androidVersion' => 'Android 8.0以降が必要です',
|
||||||
'videoControls.pipErrors.iosVersion' => 'iOS 15.0以降が必要です',
|
'videoControls.pipErrors.iosVersion' => 'iOS 15.0以降が必要です',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'ピクチャーインピクチャーが無効です。システム設定で有効にしてください。',
|
'videoControls.pipErrors.permissionDisabled' => 'ピクチャーインピクチャーが無効です。システム設定で有効にしてください。',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsJa {
|
|||||||
'profiles.addPlezyProfile' => 'Plezyプロファイルを追加',
|
'profiles.addPlezyProfile' => 'Plezyプロファイルを追加',
|
||||||
'profiles.switchingProfile' => 'プロファイルを切り替え中…',
|
'profiles.switchingProfile' => 'プロファイルを切り替え中…',
|
||||||
'profiles.deleteThisProfileTitle' => 'このプロファイルを削除しますか?',
|
'profiles.deleteThisProfileTitle' => 'このプロファイルを削除しますか?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '${displayName}を削除します。接続には影響しません。',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '${displayName}を削除します。接続には影響しません。',
|
||||||
'profiles.active' => 'アクティブ',
|
'profiles.active' => 'アクティブ',
|
||||||
'profiles.manage' => '管理',
|
'profiles.manage' => '管理',
|
||||||
'profiles.delete' => '削除',
|
'profiles.delete' => '削除',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'サインアウト',
|
'profiles.signOut' => 'サインアウト',
|
||||||
'profiles.signOutPlexTitle' => 'Plex からサインアウトしますか?',
|
'profiles.signOutPlexTitle' => 'Plex からサインアウトしますか?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName}とすべてのPlex Homeユーザーを削除しますか?いつでも再サインインできます。',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName}とすべてのPlex Homeユーザーを削除しますか?いつでも再サインインできます。',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsJa {
|
|||||||
'companionRemote.remote.audio' => '音声',
|
'companionRemote.remote.audio' => '音声',
|
||||||
'companionRemote.remote.searchHint' => 'デスクトップで検索...',
|
'companionRemote.remote.searchHint' => 'デスクトップで検索...',
|
||||||
'videoSettings.playbackSpeed' => '再生速度',
|
'videoSettings.playbackSpeed' => '再生速度',
|
||||||
|
'videoSettings.zoom' => 'ズーム',
|
||||||
'videoSettings.sleepTimer' => 'スリープタイマー',
|
'videoSettings.sleepTimer' => 'スリープタイマー',
|
||||||
'videoSettings.audioSync' => '音声同期',
|
'videoSettings.audioSync' => '音声同期',
|
||||||
'videoSettings.subtitleSync' => '字幕同期',
|
'videoSettings.subtitleSync' => '字幕同期',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsJa {
|
|||||||
'metadataEdit.basicInfo' => '基本情報',
|
'metadataEdit.basicInfo' => '基本情報',
|
||||||
'metadataEdit.artwork' => 'アートワーク',
|
'metadataEdit.artwork' => 'アートワーク',
|
||||||
'metadataEdit.advancedSettings' => '詳細設定',
|
'metadataEdit.advancedSettings' => '詳細設定',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'タイトル',
|
'metadataEdit.title' => 'タイトル',
|
||||||
'metadataEdit.sortTitle' => 'ソートタイトル',
|
'metadataEdit.sortTitle' => 'ソートタイトル',
|
||||||
'metadataEdit.originalTitle' => '原題',
|
'metadataEdit.originalTitle' => '原題',
|
||||||
'metadataEdit.releaseDate' => '公開日',
|
'metadataEdit.releaseDate' => '公開日',
|
||||||
'metadataEdit.contentRating' => 'コンテンツレーティング',
|
'metadataEdit.contentRating' => 'コンテンツレーティング',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'スタジオ',
|
'metadataEdit.studio' => 'スタジオ',
|
||||||
'metadataEdit.tagline' => 'タグライン',
|
'metadataEdit.tagline' => 'タグライン',
|
||||||
'metadataEdit.summary' => 'あらすじ',
|
'metadataEdit.summary' => 'あらすじ',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsKo extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => '화면 속 화면으로 재생 중';
|
@override String get pipActive => '화면 속 화면으로 재생 중';
|
||||||
@override String get pipFailed => '화면 속 화면 모드를 시작할 수 없습니다';
|
@override String get pipFailed => '화면 속 화면 모드를 시작할 수 없습니다';
|
||||||
@override String get screenshotSaved => '스크린샷 저장됨';
|
@override String get screenshotSaved => '스크린샷 저장됨';
|
||||||
|
@override String zoomPercent({required Object percent}) => '확대/축소 ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsKo pipErrors = _TranslationsVideoControlsPipErrorsKo._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsKo pipErrors = _TranslationsVideoControlsPipErrorsKo._(_root);
|
||||||
@override String get chapters => '챕터';
|
@override String get chapters => '챕터';
|
||||||
@override String get noChaptersAvailable => '사용 가능한 챕터가 없습니다';
|
@override String get noChaptersAvailable => '사용 가능한 챕터가 없습니다';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsKo extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => '재생 속도';
|
@override String get playbackSpeed => '재생 속도';
|
||||||
|
@override String get zoom => '확대/축소';
|
||||||
@override String get sleepTimer => '취침 타이머';
|
@override String get sleepTimer => '취침 타이머';
|
||||||
@override String get audioSync => '오디오 동기화';
|
@override String get audioSync => '오디오 동기화';
|
||||||
@override String get subtitleSync => '자막 동기화';
|
@override String get subtitleSync => '자막 동기화';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsKo extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => '속도 높이기';
|
@override String get speedIncrease => '속도 높이기';
|
||||||
@override String get speedDecrease => '속도 낮추기';
|
@override String get speedDecrease => '속도 낮추기';
|
||||||
@override String get speedReset => '속도 초기화';
|
@override String get speedReset => '속도 초기화';
|
||||||
|
@override String get zoomIn => '확대';
|
||||||
|
@override String get zoomOut => '축소';
|
||||||
|
@override String get zoomReset => '확대/축소 초기화';
|
||||||
@override String get subSeekNext => '다음 자막으로 이동';
|
@override String get subSeekNext => '다음 자막으로 이동';
|
||||||
@override String get subSeekPrev => '이전 자막으로 이동';
|
@override String get subSeekPrev => '이전 자막으로 이동';
|
||||||
@override String get shaderToggle => '셰이더 전환';
|
@override String get shaderToggle => '셰이더 전환';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsKo {
|
|||||||
'hotkeys.actions.speedIncrease' => '속도 높이기',
|
'hotkeys.actions.speedIncrease' => '속도 높이기',
|
||||||
'hotkeys.actions.speedDecrease' => '속도 낮추기',
|
'hotkeys.actions.speedDecrease' => '속도 낮추기',
|
||||||
'hotkeys.actions.speedReset' => '속도 초기화',
|
'hotkeys.actions.speedReset' => '속도 초기화',
|
||||||
|
'hotkeys.actions.zoomIn' => '확대',
|
||||||
|
'hotkeys.actions.zoomOut' => '축소',
|
||||||
|
'hotkeys.actions.zoomReset' => '확대/축소 초기화',
|
||||||
'hotkeys.actions.subSeekNext' => '다음 자막으로 이동',
|
'hotkeys.actions.subSeekNext' => '다음 자막으로 이동',
|
||||||
'hotkeys.actions.subSeekPrev' => '이전 자막으로 이동',
|
'hotkeys.actions.subSeekPrev' => '이전 자막으로 이동',
|
||||||
'hotkeys.actions.shaderToggle' => '셰이더 전환',
|
'hotkeys.actions.shaderToggle' => '셰이더 전환',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsKo {
|
|||||||
'videoControls.pipActive' => '화면 속 화면으로 재생 중',
|
'videoControls.pipActive' => '화면 속 화면으로 재생 중',
|
||||||
'videoControls.pipFailed' => '화면 속 화면 모드를 시작할 수 없습니다',
|
'videoControls.pipFailed' => '화면 속 화면 모드를 시작할 수 없습니다',
|
||||||
'videoControls.screenshotSaved' => '스크린샷 저장됨',
|
'videoControls.screenshotSaved' => '스크린샷 저장됨',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => '확대/축소 ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Android 8.0 이상이 필요합니다',
|
'videoControls.pipErrors.androidVersion' => 'Android 8.0 이상이 필요합니다',
|
||||||
'videoControls.pipErrors.iosVersion' => 'iOS 15.0 이상이 필요합니다',
|
'videoControls.pipErrors.iosVersion' => 'iOS 15.0 이상이 필요합니다',
|
||||||
'videoControls.pipErrors.permissionDisabled' => '화면 속 화면이 비활성화되어 있습니다. 시스템 설정에서 활성화하세요.',
|
'videoControls.pipErrors.permissionDisabled' => '화면 속 화면이 비활성화되어 있습니다. 시스템 설정에서 활성화하세요.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsKo {
|
|||||||
'profiles.addPlezyProfile' => 'Plezy 프로필 추가',
|
'profiles.addPlezyProfile' => 'Plezy 프로필 추가',
|
||||||
'profiles.switchingProfile' => '프로필 전환 중…',
|
'profiles.switchingProfile' => '프로필 전환 중…',
|
||||||
'profiles.deleteThisProfileTitle' => '이 프로필을 삭제하시겠습니까?',
|
'profiles.deleteThisProfileTitle' => '이 프로필을 삭제하시겠습니까?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '${displayName}을(를) 제거합니다. 연결에는 영향이 없습니다.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '${displayName}을(를) 제거합니다. 연결에는 영향이 없습니다.',
|
||||||
'profiles.active' => '활성',
|
'profiles.active' => '활성',
|
||||||
'profiles.manage' => '관리',
|
'profiles.manage' => '관리',
|
||||||
'profiles.delete' => '삭제',
|
'profiles.delete' => '삭제',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => '로그아웃',
|
'profiles.signOut' => '로그아웃',
|
||||||
'profiles.signOutPlexTitle' => 'Plex에서 로그아웃하시겠습니까?',
|
'profiles.signOutPlexTitle' => 'Plex에서 로그아웃하시겠습니까?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName} 및 모든 Plex Home 사용자를 제거할까요? 언제든 다시 로그인할 수 있습니다.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName} 및 모든 Plex Home 사용자를 제거할까요? 언제든 다시 로그인할 수 있습니다.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsKo {
|
|||||||
'companionRemote.remote.audio' => '오디오',
|
'companionRemote.remote.audio' => '오디오',
|
||||||
'companionRemote.remote.searchHint' => '데스크톱에서 검색...',
|
'companionRemote.remote.searchHint' => '데스크톱에서 검색...',
|
||||||
'videoSettings.playbackSpeed' => '재생 속도',
|
'videoSettings.playbackSpeed' => '재생 속도',
|
||||||
|
'videoSettings.zoom' => '확대/축소',
|
||||||
'videoSettings.sleepTimer' => '취침 타이머',
|
'videoSettings.sleepTimer' => '취침 타이머',
|
||||||
'videoSettings.audioSync' => '오디오 동기화',
|
'videoSettings.audioSync' => '오디오 동기화',
|
||||||
'videoSettings.subtitleSync' => '자막 동기화',
|
'videoSettings.subtitleSync' => '자막 동기화',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsKo {
|
|||||||
'metadataEdit.basicInfo' => '기본 정보',
|
'metadataEdit.basicInfo' => '기본 정보',
|
||||||
'metadataEdit.artwork' => '아트워크',
|
'metadataEdit.artwork' => '아트워크',
|
||||||
'metadataEdit.advancedSettings' => '고급 설정',
|
'metadataEdit.advancedSettings' => '고급 설정',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => '제목',
|
'metadataEdit.title' => '제목',
|
||||||
'metadataEdit.sortTitle' => '정렬 제목',
|
'metadataEdit.sortTitle' => '정렬 제목',
|
||||||
'metadataEdit.originalTitle' => '원제',
|
'metadataEdit.originalTitle' => '원제',
|
||||||
'metadataEdit.releaseDate' => '출시일',
|
'metadataEdit.releaseDate' => '출시일',
|
||||||
'metadataEdit.contentRating' => '콘텐츠 등급',
|
'metadataEdit.contentRating' => '콘텐츠 등급',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => '스튜디오',
|
'metadataEdit.studio' => '스튜디오',
|
||||||
'metadataEdit.tagline' => '태그라인',
|
'metadataEdit.tagline' => '태그라인',
|
||||||
'metadataEdit.summary' => '줄거리',
|
'metadataEdit.summary' => '줄거리',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsNb extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Spiller i bilde-i-bilde';
|
@override String get pipActive => 'Spiller i bilde-i-bilde';
|
||||||
@override String get pipFailed => 'Bilde-i-bilde kunne ikke starte';
|
@override String get pipFailed => 'Bilde-i-bilde kunne ikke starte';
|
||||||
@override String get screenshotSaved => 'Skjermbilde lagret';
|
@override String get screenshotSaved => 'Skjermbilde lagret';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsNb pipErrors = _TranslationsVideoControlsPipErrorsNb._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsNb pipErrors = _TranslationsVideoControlsPipErrorsNb._(_root);
|
||||||
@override String get chapters => 'Kapitler';
|
@override String get chapters => 'Kapitler';
|
||||||
@override String get noChaptersAvailable => 'Ingen kapitler tilgjengelig';
|
@override String get noChaptersAvailable => 'Ingen kapitler tilgjengelig';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsNb extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Avspillingshastighet';
|
@override String get playbackSpeed => 'Avspillingshastighet';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Søvntimer';
|
@override String get sleepTimer => 'Søvntimer';
|
||||||
@override String get audioSync => 'Lydsynkronisering';
|
@override String get audioSync => 'Lydsynkronisering';
|
||||||
@override String get subtitleSync => 'Undertekstsynkronisering';
|
@override String get subtitleSync => 'Undertekstsynkronisering';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsNb extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Øk hastighet';
|
@override String get speedIncrease => 'Øk hastighet';
|
||||||
@override String get speedDecrease => 'Reduser hastighet';
|
@override String get speedDecrease => 'Reduser hastighet';
|
||||||
@override String get speedReset => 'Tilbakestill hastighet';
|
@override String get speedReset => 'Tilbakestill hastighet';
|
||||||
|
@override String get zoomIn => 'Zoom inn';
|
||||||
|
@override String get zoomOut => 'Zoom ut';
|
||||||
|
@override String get zoomReset => 'Tilbakestill zoom';
|
||||||
@override String get subSeekNext => 'Spol til neste undertekst';
|
@override String get subSeekNext => 'Spol til neste undertekst';
|
||||||
@override String get subSeekPrev => 'Spol til forrige undertekst';
|
@override String get subSeekPrev => 'Spol til forrige undertekst';
|
||||||
@override String get shaderToggle => 'Veksle shadere';
|
@override String get shaderToggle => 'Veksle shadere';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsNb {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Øk hastighet',
|
'hotkeys.actions.speedIncrease' => 'Øk hastighet',
|
||||||
'hotkeys.actions.speedDecrease' => 'Reduser hastighet',
|
'hotkeys.actions.speedDecrease' => 'Reduser hastighet',
|
||||||
'hotkeys.actions.speedReset' => 'Tilbakestill hastighet',
|
'hotkeys.actions.speedReset' => 'Tilbakestill hastighet',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Zoom inn',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Zoom ut',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Tilbakestill zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Spol til neste undertekst',
|
'hotkeys.actions.subSeekNext' => 'Spol til neste undertekst',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Spol til forrige undertekst',
|
'hotkeys.actions.subSeekPrev' => 'Spol til forrige undertekst',
|
||||||
'hotkeys.actions.shaderToggle' => 'Veksle shadere',
|
'hotkeys.actions.shaderToggle' => 'Veksle shadere',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsNb {
|
|||||||
'videoControls.pipActive' => 'Spiller i bilde-i-bilde',
|
'videoControls.pipActive' => 'Spiller i bilde-i-bilde',
|
||||||
'videoControls.pipFailed' => 'Bilde-i-bilde kunne ikke starte',
|
'videoControls.pipFailed' => 'Bilde-i-bilde kunne ikke starte',
|
||||||
'videoControls.screenshotSaved' => 'Skjermbilde lagret',
|
'videoControls.screenshotSaved' => 'Skjermbilde lagret',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Krever Android 8.0 eller nyere',
|
'videoControls.pipErrors.androidVersion' => 'Krever Android 8.0 eller nyere',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Krever iOS 15.0 eller nyere',
|
'videoControls.pipErrors.iosVersion' => 'Krever iOS 15.0 eller nyere',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Bilde-i-bilde er deaktivert. Slå det på i systeminnstillinger.',
|
'videoControls.pipErrors.permissionDisabled' => 'Bilde-i-bilde er deaktivert. Slå det på i systeminnstillinger.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsNb {
|
|||||||
'profiles.addPlezyProfile' => 'Legg til Plezy-profil',
|
'profiles.addPlezyProfile' => 'Legg til Plezy-profil',
|
||||||
'profiles.switchingProfile' => 'Bytter profil…',
|
'profiles.switchingProfile' => 'Bytter profil…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Slett denne profilen?',
|
'profiles.deleteThisProfileTitle' => 'Slett denne profilen?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Fjern ${displayName}. Tilkoblinger påvirkes ikke.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Fjern ${displayName}. Tilkoblinger påvirkes ikke.',
|
||||||
'profiles.active' => 'Aktiv',
|
'profiles.active' => 'Aktiv',
|
||||||
'profiles.manage' => 'Administrer',
|
'profiles.manage' => 'Administrer',
|
||||||
'profiles.delete' => 'Slett',
|
'profiles.delete' => 'Slett',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Logg ut',
|
'profiles.signOut' => 'Logg ut',
|
||||||
'profiles.signOutPlexTitle' => 'Logge ut av Plex?',
|
'profiles.signOutPlexTitle' => 'Logge ut av Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Fjerne ${displayName} og alle Plex Home-brukere? Du kan logge inn igjen når som helst.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Fjerne ${displayName} og alle Plex Home-brukere? Du kan logge inn igjen når som helst.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsNb {
|
|||||||
'companionRemote.remote.audio' => 'Lyd',
|
'companionRemote.remote.audio' => 'Lyd',
|
||||||
'companionRemote.remote.searchHint' => 'Søk på stasjonær...',
|
'companionRemote.remote.searchHint' => 'Søk på stasjonær...',
|
||||||
'videoSettings.playbackSpeed' => 'Avspillingshastighet',
|
'videoSettings.playbackSpeed' => 'Avspillingshastighet',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Søvntimer',
|
'videoSettings.sleepTimer' => 'Søvntimer',
|
||||||
'videoSettings.audioSync' => 'Lydsynkronisering',
|
'videoSettings.audioSync' => 'Lydsynkronisering',
|
||||||
'videoSettings.subtitleSync' => 'Undertekstsynkronisering',
|
'videoSettings.subtitleSync' => 'Undertekstsynkronisering',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsNb {
|
|||||||
'metadataEdit.basicInfo' => 'Grunnleggende info',
|
'metadataEdit.basicInfo' => 'Grunnleggende info',
|
||||||
'metadataEdit.artwork' => 'Kunstverk',
|
'metadataEdit.artwork' => 'Kunstverk',
|
||||||
'metadataEdit.advancedSettings' => 'Avanserte innstillinger',
|
'metadataEdit.advancedSettings' => 'Avanserte innstillinger',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Tittel',
|
'metadataEdit.title' => 'Tittel',
|
||||||
'metadataEdit.sortTitle' => 'Sorteringsstittel',
|
'metadataEdit.sortTitle' => 'Sorteringsstittel',
|
||||||
'metadataEdit.originalTitle' => 'Originaltittel',
|
'metadataEdit.originalTitle' => 'Originaltittel',
|
||||||
'metadataEdit.releaseDate' => 'Utgivelsesdato',
|
'metadataEdit.releaseDate' => 'Utgivelsesdato',
|
||||||
'metadataEdit.contentRating' => 'Innholdsvurdering',
|
'metadataEdit.contentRating' => 'Innholdsvurdering',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Slagord',
|
'metadataEdit.tagline' => 'Slagord',
|
||||||
'metadataEdit.summary' => 'Sammendrag',
|
'metadataEdit.summary' => 'Sammendrag',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsNl extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Afspelen in beeld-in-beeld';
|
@override String get pipActive => 'Afspelen in beeld-in-beeld';
|
||||||
@override String get pipFailed => 'Beeld-in-beeld kon niet worden gestart';
|
@override String get pipFailed => 'Beeld-in-beeld kon niet worden gestart';
|
||||||
@override String get screenshotSaved => 'Schermafbeelding opgeslagen';
|
@override String get screenshotSaved => 'Schermafbeelding opgeslagen';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsNl pipErrors = _TranslationsVideoControlsPipErrorsNl._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsNl pipErrors = _TranslationsVideoControlsPipErrorsNl._(_root);
|
||||||
@override String get chapters => 'Hoofdstukken';
|
@override String get chapters => 'Hoofdstukken';
|
||||||
@override String get noChaptersAvailable => 'Geen hoofdstukken beschikbaar';
|
@override String get noChaptersAvailable => 'Geen hoofdstukken beschikbaar';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsNl extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Afspeelsnelheid';
|
@override String get playbackSpeed => 'Afspeelsnelheid';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Slaaptimer';
|
@override String get sleepTimer => 'Slaaptimer';
|
||||||
@override String get audioSync => 'Audio synchronisatie';
|
@override String get audioSync => 'Audio synchronisatie';
|
||||||
@override String get subtitleSync => 'Ondertitel synchronisatie';
|
@override String get subtitleSync => 'Ondertitel synchronisatie';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsNl extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Snelheid verhogen';
|
@override String get speedIncrease => 'Snelheid verhogen';
|
||||||
@override String get speedDecrease => 'Snelheid verlagen';
|
@override String get speedDecrease => 'Snelheid verlagen';
|
||||||
@override String get speedReset => 'Snelheid resetten';
|
@override String get speedReset => 'Snelheid resetten';
|
||||||
|
@override String get zoomIn => 'Inzoomen';
|
||||||
|
@override String get zoomOut => 'Uitzoomen';
|
||||||
|
@override String get zoomReset => 'Zoom resetten';
|
||||||
@override String get subSeekNext => 'Naar volgende ondertitel';
|
@override String get subSeekNext => 'Naar volgende ondertitel';
|
||||||
@override String get subSeekPrev => 'Naar vorige ondertitel';
|
@override String get subSeekPrev => 'Naar vorige ondertitel';
|
||||||
@override String get shaderToggle => 'Shaders aan/uit';
|
@override String get shaderToggle => 'Shaders aan/uit';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsNl {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Snelheid verhogen',
|
'hotkeys.actions.speedIncrease' => 'Snelheid verhogen',
|
||||||
'hotkeys.actions.speedDecrease' => 'Snelheid verlagen',
|
'hotkeys.actions.speedDecrease' => 'Snelheid verlagen',
|
||||||
'hotkeys.actions.speedReset' => 'Snelheid resetten',
|
'hotkeys.actions.speedReset' => 'Snelheid resetten',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Inzoomen',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Uitzoomen',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Zoom resetten',
|
||||||
'hotkeys.actions.subSeekNext' => 'Naar volgende ondertitel',
|
'hotkeys.actions.subSeekNext' => 'Naar volgende ondertitel',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Naar vorige ondertitel',
|
'hotkeys.actions.subSeekPrev' => 'Naar vorige ondertitel',
|
||||||
'hotkeys.actions.shaderToggle' => 'Shaders aan/uit',
|
'hotkeys.actions.shaderToggle' => 'Shaders aan/uit',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsNl {
|
|||||||
'videoControls.pipActive' => 'Afspelen in beeld-in-beeld',
|
'videoControls.pipActive' => 'Afspelen in beeld-in-beeld',
|
||||||
'videoControls.pipFailed' => 'Beeld-in-beeld kon niet worden gestart',
|
'videoControls.pipFailed' => 'Beeld-in-beeld kon niet worden gestart',
|
||||||
'videoControls.screenshotSaved' => 'Schermafbeelding opgeslagen',
|
'videoControls.screenshotSaved' => 'Schermafbeelding opgeslagen',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Vereist Android 8.0 of nieuwer',
|
'videoControls.pipErrors.androidVersion' => 'Vereist Android 8.0 of nieuwer',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Vereist iOS 15.0 of nieuwer',
|
'videoControls.pipErrors.iosVersion' => 'Vereist iOS 15.0 of nieuwer',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture is uitgeschakeld. Schakel het in via systeeminstellingen.',
|
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture is uitgeschakeld. Schakel het in via systeeminstellingen.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsNl {
|
|||||||
'profiles.addPlezyProfile' => 'Plezy-profiel toevoegen',
|
'profiles.addPlezyProfile' => 'Plezy-profiel toevoegen',
|
||||||
'profiles.switchingProfile' => 'Profiel wisselen…',
|
'profiles.switchingProfile' => 'Profiel wisselen…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Dit profiel verwijderen?',
|
'profiles.deleteThisProfileTitle' => 'Dit profiel verwijderen?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Verwijder ${displayName}. Verbindingen blijven ongewijzigd.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Verwijder ${displayName}. Verbindingen blijven ongewijzigd.',
|
||||||
'profiles.active' => 'Actief',
|
'profiles.active' => 'Actief',
|
||||||
'profiles.manage' => 'Beheren',
|
'profiles.manage' => 'Beheren',
|
||||||
'profiles.delete' => 'Verwijderen',
|
'profiles.delete' => 'Verwijderen',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Afmelden',
|
'profiles.signOut' => 'Afmelden',
|
||||||
'profiles.signOutPlexTitle' => 'Afmelden bij Plex?',
|
'profiles.signOutPlexTitle' => 'Afmelden bij Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName} en alle Plex Home-gebruikers verwijderen? Je kunt altijd opnieuw inloggen.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => '${displayName} en alle Plex Home-gebruikers verwijderen? Je kunt altijd opnieuw inloggen.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsNl {
|
|||||||
'companionRemote.remote.audio' => 'Audio',
|
'companionRemote.remote.audio' => 'Audio',
|
||||||
'companionRemote.remote.searchHint' => 'Zoeken op desktop...',
|
'companionRemote.remote.searchHint' => 'Zoeken op desktop...',
|
||||||
'videoSettings.playbackSpeed' => 'Afspeelsnelheid',
|
'videoSettings.playbackSpeed' => 'Afspeelsnelheid',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Slaaptimer',
|
'videoSettings.sleepTimer' => 'Slaaptimer',
|
||||||
'videoSettings.audioSync' => 'Audio synchronisatie',
|
'videoSettings.audioSync' => 'Audio synchronisatie',
|
||||||
'videoSettings.subtitleSync' => 'Ondertitel synchronisatie',
|
'videoSettings.subtitleSync' => 'Ondertitel synchronisatie',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsNl {
|
|||||||
'metadataEdit.basicInfo' => 'Basisinformatie',
|
'metadataEdit.basicInfo' => 'Basisinformatie',
|
||||||
'metadataEdit.artwork' => 'Artwork',
|
'metadataEdit.artwork' => 'Artwork',
|
||||||
'metadataEdit.advancedSettings' => 'Geavanceerde instellingen',
|
'metadataEdit.advancedSettings' => 'Geavanceerde instellingen',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Titel',
|
'metadataEdit.title' => 'Titel',
|
||||||
'metadataEdit.sortTitle' => 'Sorteertitel',
|
'metadataEdit.sortTitle' => 'Sorteertitel',
|
||||||
'metadataEdit.originalTitle' => 'Oorspronkelijke titel',
|
'metadataEdit.originalTitle' => 'Oorspronkelijke titel',
|
||||||
'metadataEdit.releaseDate' => 'Releasedatum',
|
'metadataEdit.releaseDate' => 'Releasedatum',
|
||||||
'metadataEdit.contentRating' => 'Leeftijdsclassificatie',
|
'metadataEdit.contentRating' => 'Leeftijdsclassificatie',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Samenvatting',
|
'metadataEdit.summary' => 'Samenvatting',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsPl extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Odtwarzanie w trybie obraz w obrazie';
|
@override String get pipActive => 'Odtwarzanie w trybie obraz w obrazie';
|
||||||
@override String get pipFailed => 'Nie udało się uruchomić trybu obraz w obrazie';
|
@override String get pipFailed => 'Nie udało się uruchomić trybu obraz w obrazie';
|
||||||
@override String get screenshotSaved => 'Zrzut ekranu zapisany';
|
@override String get screenshotSaved => 'Zrzut ekranu zapisany';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsPl pipErrors = _TranslationsVideoControlsPipErrorsPl._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsPl pipErrors = _TranslationsVideoControlsPipErrorsPl._(_root);
|
||||||
@override String get chapters => 'Rozdziały';
|
@override String get chapters => 'Rozdziały';
|
||||||
@override String get noChaptersAvailable => 'Brak dostępnych rozdziałów';
|
@override String get noChaptersAvailable => 'Brak dostępnych rozdziałów';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsPl extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Prędkość odtwarzania';
|
@override String get playbackSpeed => 'Prędkość odtwarzania';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Wyłącznik czasowy';
|
@override String get sleepTimer => 'Wyłącznik czasowy';
|
||||||
@override String get audioSync => 'Synchronizacja audio';
|
@override String get audioSync => 'Synchronizacja audio';
|
||||||
@override String get subtitleSync => 'Synchronizacja napisów';
|
@override String get subtitleSync => 'Synchronizacja napisów';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsPl extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Zwiększ prędkość';
|
@override String get speedIncrease => 'Zwiększ prędkość';
|
||||||
@override String get speedDecrease => 'Zmniejsz prędkość';
|
@override String get speedDecrease => 'Zmniejsz prędkość';
|
||||||
@override String get speedReset => 'Zresetuj prędkość';
|
@override String get speedReset => 'Zresetuj prędkość';
|
||||||
|
@override String get zoomIn => 'Powiększ';
|
||||||
|
@override String get zoomOut => 'Pomniejsz';
|
||||||
|
@override String get zoomReset => 'Zresetuj zoom';
|
||||||
@override String get subSeekNext => 'Przewiń do następnego napisu';
|
@override String get subSeekNext => 'Przewiń do następnego napisu';
|
||||||
@override String get subSeekPrev => 'Przewiń do poprzedniego napisu';
|
@override String get subSeekPrev => 'Przewiń do poprzedniego napisu';
|
||||||
@override String get shaderToggle => 'Przełącz shadery';
|
@override String get shaderToggle => 'Przełącz shadery';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsPl {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Zwiększ prędkość',
|
'hotkeys.actions.speedIncrease' => 'Zwiększ prędkość',
|
||||||
'hotkeys.actions.speedDecrease' => 'Zmniejsz prędkość',
|
'hotkeys.actions.speedDecrease' => 'Zmniejsz prędkość',
|
||||||
'hotkeys.actions.speedReset' => 'Zresetuj prędkość',
|
'hotkeys.actions.speedReset' => 'Zresetuj prędkość',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Powiększ',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Pomniejsz',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Zresetuj zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Przewiń do następnego napisu',
|
'hotkeys.actions.subSeekNext' => 'Przewiń do następnego napisu',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Przewiń do poprzedniego napisu',
|
'hotkeys.actions.subSeekPrev' => 'Przewiń do poprzedniego napisu',
|
||||||
'hotkeys.actions.shaderToggle' => 'Przełącz shadery',
|
'hotkeys.actions.shaderToggle' => 'Przełącz shadery',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsPl {
|
|||||||
'videoControls.pipActive' => 'Odtwarzanie w trybie obraz w obrazie',
|
'videoControls.pipActive' => 'Odtwarzanie w trybie obraz w obrazie',
|
||||||
'videoControls.pipFailed' => 'Nie udało się uruchomić trybu obraz w obrazie',
|
'videoControls.pipFailed' => 'Nie udało się uruchomić trybu obraz w obrazie',
|
||||||
'videoControls.screenshotSaved' => 'Zrzut ekranu zapisany',
|
'videoControls.screenshotSaved' => 'Zrzut ekranu zapisany',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Wymaga Androida 8.0 lub nowszego',
|
'videoControls.pipErrors.androidVersion' => 'Wymaga Androida 8.0 lub nowszego',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Wymaga iOS 15.0 lub nowszego',
|
'videoControls.pipErrors.iosVersion' => 'Wymaga iOS 15.0 lub nowszego',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Obraz w obrazie jest wyłączony. Włącz go w ustawieniach systemu.',
|
'videoControls.pipErrors.permissionDisabled' => 'Obraz w obrazie jest wyłączony. Włącz go w ustawieniach systemu.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsPl {
|
|||||||
'profiles.addPlezyProfile' => 'Dodaj profil Plezy',
|
'profiles.addPlezyProfile' => 'Dodaj profil Plezy',
|
||||||
'profiles.switchingProfile' => 'Przełączanie profilu…',
|
'profiles.switchingProfile' => 'Przełączanie profilu…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Usunąć ten profil?',
|
'profiles.deleteThisProfileTitle' => 'Usunąć ten profil?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Usuń ${displayName}. Połączenia nie zostaną zmienione.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Usuń ${displayName}. Połączenia nie zostaną zmienione.',
|
||||||
'profiles.active' => 'Aktywny',
|
'profiles.active' => 'Aktywny',
|
||||||
'profiles.manage' => 'Zarządzaj',
|
'profiles.manage' => 'Zarządzaj',
|
||||||
'profiles.delete' => 'Usuń',
|
'profiles.delete' => 'Usuń',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Wyloguj się',
|
'profiles.signOut' => 'Wyloguj się',
|
||||||
'profiles.signOutPlexTitle' => 'Wylogować się z Plex?',
|
'profiles.signOutPlexTitle' => 'Wylogować się z Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Usunąć ${displayName} i wszystkich użytkowników Plex Home? Możesz zalogować się ponownie w każdej chwili.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Usunąć ${displayName} i wszystkich użytkowników Plex Home? Możesz zalogować się ponownie w każdej chwili.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsPl {
|
|||||||
'companionRemote.remote.audio' => 'Audio',
|
'companionRemote.remote.audio' => 'Audio',
|
||||||
'companionRemote.remote.searchHint' => 'Szukaj na komputerze...',
|
'companionRemote.remote.searchHint' => 'Szukaj na komputerze...',
|
||||||
'videoSettings.playbackSpeed' => 'Prędkość odtwarzania',
|
'videoSettings.playbackSpeed' => 'Prędkość odtwarzania',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Wyłącznik czasowy',
|
'videoSettings.sleepTimer' => 'Wyłącznik czasowy',
|
||||||
'videoSettings.audioSync' => 'Synchronizacja audio',
|
'videoSettings.audioSync' => 'Synchronizacja audio',
|
||||||
'videoSettings.subtitleSync' => 'Synchronizacja napisów',
|
'videoSettings.subtitleSync' => 'Synchronizacja napisów',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsPl {
|
|||||||
'metadataEdit.basicInfo' => 'Podstawowe informacje',
|
'metadataEdit.basicInfo' => 'Podstawowe informacje',
|
||||||
'metadataEdit.artwork' => 'Grafika',
|
'metadataEdit.artwork' => 'Grafika',
|
||||||
'metadataEdit.advancedSettings' => 'Ustawienia zaawansowane',
|
'metadataEdit.advancedSettings' => 'Ustawienia zaawansowane',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Tytuł',
|
'metadataEdit.title' => 'Tytuł',
|
||||||
'metadataEdit.sortTitle' => 'Tytuł do sortowania',
|
'metadataEdit.sortTitle' => 'Tytuł do sortowania',
|
||||||
'metadataEdit.originalTitle' => 'Tytuł oryginalny',
|
'metadataEdit.originalTitle' => 'Tytuł oryginalny',
|
||||||
'metadataEdit.releaseDate' => 'Data premiery',
|
'metadataEdit.releaseDate' => 'Data premiery',
|
||||||
'metadataEdit.contentRating' => 'Klasyfikacja wiekowa',
|
'metadataEdit.contentRating' => 'Klasyfikacja wiekowa',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Opis',
|
'metadataEdit.summary' => 'Opis',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsPt extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Reproduzindo em Picture-in-Picture';
|
@override String get pipActive => 'Reproduzindo em Picture-in-Picture';
|
||||||
@override String get pipFailed => 'Falha ao iniciar picture-in-picture';
|
@override String get pipFailed => 'Falha ao iniciar picture-in-picture';
|
||||||
@override String get screenshotSaved => 'Captura de tela salva';
|
@override String get screenshotSaved => 'Captura de tela salva';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsPt pipErrors = _TranslationsVideoControlsPipErrorsPt._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsPt pipErrors = _TranslationsVideoControlsPipErrorsPt._(_root);
|
||||||
@override String get chapters => 'Capítulos';
|
@override String get chapters => 'Capítulos';
|
||||||
@override String get noChaptersAvailable => 'Nenhum capítulo disponível';
|
@override String get noChaptersAvailable => 'Nenhum capítulo disponível';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsPt extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Velocidade de Reprodução';
|
@override String get playbackSpeed => 'Velocidade de Reprodução';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Timer de Sono';
|
@override String get sleepTimer => 'Timer de Sono';
|
||||||
@override String get audioSync => 'Sincronia de Áudio';
|
@override String get audioSync => 'Sincronia de Áudio';
|
||||||
@override String get subtitleSync => 'Sincronia de Legendas';
|
@override String get subtitleSync => 'Sincronia de Legendas';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsPt extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Aumentar Velocidade';
|
@override String get speedIncrease => 'Aumentar Velocidade';
|
||||||
@override String get speedDecrease => 'Diminuir Velocidade';
|
@override String get speedDecrease => 'Diminuir Velocidade';
|
||||||
@override String get speedReset => 'Redefinir Velocidade';
|
@override String get speedReset => 'Redefinir Velocidade';
|
||||||
|
@override String get zoomIn => 'Aumentar zoom';
|
||||||
|
@override String get zoomOut => 'Diminuir zoom';
|
||||||
|
@override String get zoomReset => 'Redefinir zoom';
|
||||||
@override String get subSeekNext => 'Ir para Próxima Legenda';
|
@override String get subSeekNext => 'Ir para Próxima Legenda';
|
||||||
@override String get subSeekPrev => 'Ir para Legenda Anterior';
|
@override String get subSeekPrev => 'Ir para Legenda Anterior';
|
||||||
@override String get shaderToggle => 'Alternar Shaders';
|
@override String get shaderToggle => 'Alternar Shaders';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsPt {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Aumentar Velocidade',
|
'hotkeys.actions.speedIncrease' => 'Aumentar Velocidade',
|
||||||
'hotkeys.actions.speedDecrease' => 'Diminuir Velocidade',
|
'hotkeys.actions.speedDecrease' => 'Diminuir Velocidade',
|
||||||
'hotkeys.actions.speedReset' => 'Redefinir Velocidade',
|
'hotkeys.actions.speedReset' => 'Redefinir Velocidade',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Aumentar zoom',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Diminuir zoom',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Redefinir zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Ir para Próxima Legenda',
|
'hotkeys.actions.subSeekNext' => 'Ir para Próxima Legenda',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Ir para Legenda Anterior',
|
'hotkeys.actions.subSeekPrev' => 'Ir para Legenda Anterior',
|
||||||
'hotkeys.actions.shaderToggle' => 'Alternar Shaders',
|
'hotkeys.actions.shaderToggle' => 'Alternar Shaders',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsPt {
|
|||||||
'videoControls.pipActive' => 'Reproduzindo em Picture-in-Picture',
|
'videoControls.pipActive' => 'Reproduzindo em Picture-in-Picture',
|
||||||
'videoControls.pipFailed' => 'Falha ao iniciar picture-in-picture',
|
'videoControls.pipFailed' => 'Falha ao iniciar picture-in-picture',
|
||||||
'videoControls.screenshotSaved' => 'Captura de tela salva',
|
'videoControls.screenshotSaved' => 'Captura de tela salva',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Requer Android 8.0 ou superior',
|
'videoControls.pipErrors.androidVersion' => 'Requer Android 8.0 ou superior',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Requer iOS 15.0 ou superior',
|
'videoControls.pipErrors.iosVersion' => 'Requer iOS 15.0 ou superior',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture está desativado. Ative nas configurações do sistema.',
|
'videoControls.pipErrors.permissionDisabled' => 'Picture-in-picture está desativado. Ative nas configurações do sistema.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsPt {
|
|||||||
'profiles.addPlezyProfile' => 'Adicionar perfil Plezy',
|
'profiles.addPlezyProfile' => 'Adicionar perfil Plezy',
|
||||||
'profiles.switchingProfile' => 'Mudando perfil…',
|
'profiles.switchingProfile' => 'Mudando perfil…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Excluir este perfil?',
|
'profiles.deleteThisProfileTitle' => 'Excluir este perfil?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Remover ${displayName}. As conexões não serão afetadas.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Remover ${displayName}. As conexões não serão afetadas.',
|
||||||
'profiles.active' => 'Ativo',
|
'profiles.active' => 'Ativo',
|
||||||
'profiles.manage' => 'Gerenciar',
|
'profiles.manage' => 'Gerenciar',
|
||||||
'profiles.delete' => 'Excluir',
|
'profiles.delete' => 'Excluir',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Sair',
|
'profiles.signOut' => 'Sair',
|
||||||
'profiles.signOutPlexTitle' => 'Sair do Plex?',
|
'profiles.signOutPlexTitle' => 'Sair do Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Remover ${displayName} e todos os usuários Plex Home? Você pode entrar novamente quando quiser.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Remover ${displayName} e todos os usuários Plex Home? Você pode entrar novamente quando quiser.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsPt {
|
|||||||
'companionRemote.remote.audio' => 'Áudio',
|
'companionRemote.remote.audio' => 'Áudio',
|
||||||
'companionRemote.remote.searchHint' => 'Buscar no desktop...',
|
'companionRemote.remote.searchHint' => 'Buscar no desktop...',
|
||||||
'videoSettings.playbackSpeed' => 'Velocidade de Reprodução',
|
'videoSettings.playbackSpeed' => 'Velocidade de Reprodução',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Timer de Sono',
|
'videoSettings.sleepTimer' => 'Timer de Sono',
|
||||||
'videoSettings.audioSync' => 'Sincronia de Áudio',
|
'videoSettings.audioSync' => 'Sincronia de Áudio',
|
||||||
'videoSettings.subtitleSync' => 'Sincronia de Legendas',
|
'videoSettings.subtitleSync' => 'Sincronia de Legendas',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsPt {
|
|||||||
'metadataEdit.basicInfo' => 'Informações Básicas',
|
'metadataEdit.basicInfo' => 'Informações Básicas',
|
||||||
'metadataEdit.artwork' => 'Arte',
|
'metadataEdit.artwork' => 'Arte',
|
||||||
'metadataEdit.advancedSettings' => 'Configurações Avançadas',
|
'metadataEdit.advancedSettings' => 'Configurações Avançadas',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Título',
|
'metadataEdit.title' => 'Título',
|
||||||
'metadataEdit.sortTitle' => 'Título para Ordenação',
|
'metadataEdit.sortTitle' => 'Título para Ordenação',
|
||||||
'metadataEdit.originalTitle' => 'Título Original',
|
'metadataEdit.originalTitle' => 'Título Original',
|
||||||
'metadataEdit.releaseDate' => 'Data de Lançamento',
|
'metadataEdit.releaseDate' => 'Data de Lançamento',
|
||||||
'metadataEdit.contentRating' => 'Classificação Indicativa',
|
'metadataEdit.contentRating' => 'Classificação Indicativa',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Estúdio',
|
'metadataEdit.studio' => 'Estúdio',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Sinopse',
|
'metadataEdit.summary' => 'Sinopse',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsRu extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Воспроизводится в режиме «картинка в картинке»';
|
@override String get pipActive => 'Воспроизводится в режиме «картинка в картинке»';
|
||||||
@override String get pipFailed => 'Не удалось запустить режим «картинка в картинке»';
|
@override String get pipFailed => 'Не удалось запустить режим «картинка в картинке»';
|
||||||
@override String get screenshotSaved => 'Снимок экрана сохранён';
|
@override String get screenshotSaved => 'Снимок экрана сохранён';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Масштаб ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsRu pipErrors = _TranslationsVideoControlsPipErrorsRu._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsRu pipErrors = _TranslationsVideoControlsPipErrorsRu._(_root);
|
||||||
@override String get chapters => 'Главы';
|
@override String get chapters => 'Главы';
|
||||||
@override String get noChaptersAvailable => 'Главы недоступны';
|
@override String get noChaptersAvailable => 'Главы недоступны';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsRu extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Скорость воспроизведения';
|
@override String get playbackSpeed => 'Скорость воспроизведения';
|
||||||
|
@override String get zoom => 'Масштаб';
|
||||||
@override String get sleepTimer => 'Таймер сна';
|
@override String get sleepTimer => 'Таймер сна';
|
||||||
@override String get audioSync => 'Синхронизация аудио';
|
@override String get audioSync => 'Синхронизация аудио';
|
||||||
@override String get subtitleSync => 'Синхронизация субтитров';
|
@override String get subtitleSync => 'Синхронизация субтитров';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsRu extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Увеличить скорость';
|
@override String get speedIncrease => 'Увеличить скорость';
|
||||||
@override String get speedDecrease => 'Уменьшить скорость';
|
@override String get speedDecrease => 'Уменьшить скорость';
|
||||||
@override String get speedReset => 'Сбросить скорость';
|
@override String get speedReset => 'Сбросить скорость';
|
||||||
|
@override String get zoomIn => 'Увеличить масштаб';
|
||||||
|
@override String get zoomOut => 'Уменьшить масштаб';
|
||||||
|
@override String get zoomReset => 'Сбросить масштаб';
|
||||||
@override String get subSeekNext => 'К следующему субтитру';
|
@override String get subSeekNext => 'К следующему субтитру';
|
||||||
@override String get subSeekPrev => 'К предыдущему субтитру';
|
@override String get subSeekPrev => 'К предыдущему субтитру';
|
||||||
@override String get shaderToggle => 'Вкл./выкл. шейдеры';
|
@override String get shaderToggle => 'Вкл./выкл. шейдеры';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsRu {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Увеличить скорость',
|
'hotkeys.actions.speedIncrease' => 'Увеличить скорость',
|
||||||
'hotkeys.actions.speedDecrease' => 'Уменьшить скорость',
|
'hotkeys.actions.speedDecrease' => 'Уменьшить скорость',
|
||||||
'hotkeys.actions.speedReset' => 'Сбросить скорость',
|
'hotkeys.actions.speedReset' => 'Сбросить скорость',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Увеличить масштаб',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Уменьшить масштаб',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Сбросить масштаб',
|
||||||
'hotkeys.actions.subSeekNext' => 'К следующему субтитру',
|
'hotkeys.actions.subSeekNext' => 'К следующему субтитру',
|
||||||
'hotkeys.actions.subSeekPrev' => 'К предыдущему субтитру',
|
'hotkeys.actions.subSeekPrev' => 'К предыдущему субтитру',
|
||||||
'hotkeys.actions.shaderToggle' => 'Вкл./выкл. шейдеры',
|
'hotkeys.actions.shaderToggle' => 'Вкл./выкл. шейдеры',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsRu {
|
|||||||
'videoControls.pipActive' => 'Воспроизводится в режиме «картинка в картинке»',
|
'videoControls.pipActive' => 'Воспроизводится в режиме «картинка в картинке»',
|
||||||
'videoControls.pipFailed' => 'Не удалось запустить режим «картинка в картинке»',
|
'videoControls.pipFailed' => 'Не удалось запустить режим «картинка в картинке»',
|
||||||
'videoControls.screenshotSaved' => 'Снимок экрана сохранён',
|
'videoControls.screenshotSaved' => 'Снимок экрана сохранён',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Масштаб ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Требуется Android 8.0 или новее',
|
'videoControls.pipErrors.androidVersion' => 'Требуется Android 8.0 или новее',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Требуется iOS 15.0 или новее',
|
'videoControls.pipErrors.iosVersion' => 'Требуется iOS 15.0 или новее',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Картинка-в-картинке отключена. Включите ее в системных настройках.',
|
'videoControls.pipErrors.permissionDisabled' => 'Картинка-в-картинке отключена. Включите ее в системных настройках.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsRu {
|
|||||||
'profiles.addPlezyProfile' => 'Добавить профиль Plezy',
|
'profiles.addPlezyProfile' => 'Добавить профиль Plezy',
|
||||||
'profiles.switchingProfile' => 'Переключение профиля…',
|
'profiles.switchingProfile' => 'Переключение профиля…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Удалить этот профиль?',
|
'profiles.deleteThisProfileTitle' => 'Удалить этот профиль?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Удалить ${displayName}. Подключения не изменятся.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Удалить ${displayName}. Подключения не изменятся.',
|
||||||
'profiles.active' => 'Активный',
|
'profiles.active' => 'Активный',
|
||||||
'profiles.manage' => 'Управление',
|
'profiles.manage' => 'Управление',
|
||||||
'profiles.delete' => 'Удалить',
|
'profiles.delete' => 'Удалить',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Выйти',
|
'profiles.signOut' => 'Выйти',
|
||||||
'profiles.signOutPlexTitle' => 'Выйти из Plex?',
|
'profiles.signOutPlexTitle' => 'Выйти из Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Удалить ${displayName} и всех пользователей Plex Home? Вы сможете войти снова в любое время.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Удалить ${displayName} и всех пользователей Plex Home? Вы сможете войти снова в любое время.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsRu {
|
|||||||
'companionRemote.remote.audio' => 'Аудио',
|
'companionRemote.remote.audio' => 'Аудио',
|
||||||
'companionRemote.remote.searchHint' => 'Поиск на десктопе...',
|
'companionRemote.remote.searchHint' => 'Поиск на десктопе...',
|
||||||
'videoSettings.playbackSpeed' => 'Скорость воспроизведения',
|
'videoSettings.playbackSpeed' => 'Скорость воспроизведения',
|
||||||
|
'videoSettings.zoom' => 'Масштаб',
|
||||||
'videoSettings.sleepTimer' => 'Таймер сна',
|
'videoSettings.sleepTimer' => 'Таймер сна',
|
||||||
'videoSettings.audioSync' => 'Синхронизация аудио',
|
'videoSettings.audioSync' => 'Синхронизация аудио',
|
||||||
'videoSettings.subtitleSync' => 'Синхронизация субтитров',
|
'videoSettings.subtitleSync' => 'Синхронизация субтитров',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsRu {
|
|||||||
'metadataEdit.basicInfo' => 'Основная информация',
|
'metadataEdit.basicInfo' => 'Основная информация',
|
||||||
'metadataEdit.artwork' => 'Обложка',
|
'metadataEdit.artwork' => 'Обложка',
|
||||||
'metadataEdit.advancedSettings' => 'Дополнительные настройки',
|
'metadataEdit.advancedSettings' => 'Дополнительные настройки',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Название',
|
'metadataEdit.title' => 'Название',
|
||||||
'metadataEdit.sortTitle' => 'Название для сортировки',
|
'metadataEdit.sortTitle' => 'Название для сортировки',
|
||||||
'metadataEdit.originalTitle' => 'Оригинальное название',
|
'metadataEdit.originalTitle' => 'Оригинальное название',
|
||||||
'metadataEdit.releaseDate' => 'Дата выпуска',
|
'metadataEdit.releaseDate' => 'Дата выпуска',
|
||||||
'metadataEdit.contentRating' => 'Возрастной рейтинг',
|
'metadataEdit.contentRating' => 'Возрастной рейтинг',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Студия',
|
'metadataEdit.studio' => 'Студия',
|
||||||
'metadataEdit.tagline' => 'Слоган',
|
'metadataEdit.tagline' => 'Слоган',
|
||||||
'metadataEdit.summary' => 'Описание',
|
'metadataEdit.summary' => 'Описание',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsSv extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => 'Spelar i bild-i-bild';
|
@override String get pipActive => 'Spelar i bild-i-bild';
|
||||||
@override String get pipFailed => 'Bild-i-bild kunde inte starta';
|
@override String get pipFailed => 'Bild-i-bild kunde inte starta';
|
||||||
@override String get screenshotSaved => 'Skärmbild sparad';
|
@override String get screenshotSaved => 'Skärmbild sparad';
|
||||||
|
@override String zoomPercent({required Object percent}) => 'Zoom ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsSv pipErrors = _TranslationsVideoControlsPipErrorsSv._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsSv pipErrors = _TranslationsVideoControlsPipErrorsSv._(_root);
|
||||||
@override String get chapters => 'Kapitel';
|
@override String get chapters => 'Kapitel';
|
||||||
@override String get noChaptersAvailable => 'Inga kapitel tillgängliga';
|
@override String get noChaptersAvailable => 'Inga kapitel tillgängliga';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsSv extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => 'Uppspelningshastighet';
|
@override String get playbackSpeed => 'Uppspelningshastighet';
|
||||||
|
@override String get zoom => 'Zoom';
|
||||||
@override String get sleepTimer => 'Sovtimer';
|
@override String get sleepTimer => 'Sovtimer';
|
||||||
@override String get audioSync => 'Ljudsynkronisering';
|
@override String get audioSync => 'Ljudsynkronisering';
|
||||||
@override String get subtitleSync => 'Undertextsynkronisering';
|
@override String get subtitleSync => 'Undertextsynkronisering';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsSv extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => 'Öka hastighet';
|
@override String get speedIncrease => 'Öka hastighet';
|
||||||
@override String get speedDecrease => 'Minska hastighet';
|
@override String get speedDecrease => 'Minska hastighet';
|
||||||
@override String get speedReset => 'Återställ hastighet';
|
@override String get speedReset => 'Återställ hastighet';
|
||||||
|
@override String get zoomIn => 'Zooma in';
|
||||||
|
@override String get zoomOut => 'Zooma ut';
|
||||||
|
@override String get zoomReset => 'Återställ zoom';
|
||||||
@override String get subSeekNext => 'Hoppa till nästa undertext';
|
@override String get subSeekNext => 'Hoppa till nästa undertext';
|
||||||
@override String get subSeekPrev => 'Hoppa till föregående undertext';
|
@override String get subSeekPrev => 'Hoppa till föregående undertext';
|
||||||
@override String get shaderToggle => 'Växla shaders';
|
@override String get shaderToggle => 'Växla shaders';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsSv {
|
|||||||
'hotkeys.actions.speedIncrease' => 'Öka hastighet',
|
'hotkeys.actions.speedIncrease' => 'Öka hastighet',
|
||||||
'hotkeys.actions.speedDecrease' => 'Minska hastighet',
|
'hotkeys.actions.speedDecrease' => 'Minska hastighet',
|
||||||
'hotkeys.actions.speedReset' => 'Återställ hastighet',
|
'hotkeys.actions.speedReset' => 'Återställ hastighet',
|
||||||
|
'hotkeys.actions.zoomIn' => 'Zooma in',
|
||||||
|
'hotkeys.actions.zoomOut' => 'Zooma ut',
|
||||||
|
'hotkeys.actions.zoomReset' => 'Återställ zoom',
|
||||||
'hotkeys.actions.subSeekNext' => 'Hoppa till nästa undertext',
|
'hotkeys.actions.subSeekNext' => 'Hoppa till nästa undertext',
|
||||||
'hotkeys.actions.subSeekPrev' => 'Hoppa till föregående undertext',
|
'hotkeys.actions.subSeekPrev' => 'Hoppa till föregående undertext',
|
||||||
'hotkeys.actions.shaderToggle' => 'Växla shaders',
|
'hotkeys.actions.shaderToggle' => 'Växla shaders',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsSv {
|
|||||||
'videoControls.pipActive' => 'Spelar i bild-i-bild',
|
'videoControls.pipActive' => 'Spelar i bild-i-bild',
|
||||||
'videoControls.pipFailed' => 'Bild-i-bild kunde inte starta',
|
'videoControls.pipFailed' => 'Bild-i-bild kunde inte starta',
|
||||||
'videoControls.screenshotSaved' => 'Skärmbild sparad',
|
'videoControls.screenshotSaved' => 'Skärmbild sparad',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => 'Zoom ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => 'Kräver Android 8.0 eller nyare',
|
'videoControls.pipErrors.androidVersion' => 'Kräver Android 8.0 eller nyare',
|
||||||
'videoControls.pipErrors.iosVersion' => 'Kräver iOS 15.0 eller nyare',
|
'videoControls.pipErrors.iosVersion' => 'Kräver iOS 15.0 eller nyare',
|
||||||
'videoControls.pipErrors.permissionDisabled' => 'Bild-i-bild är inaktiverat. Aktivera det i systeminställningarna.',
|
'videoControls.pipErrors.permissionDisabled' => 'Bild-i-bild är inaktiverat. Aktivera det i systeminställningarna.',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsSv {
|
|||||||
'profiles.addPlezyProfile' => 'Lägg till Plezy-profil',
|
'profiles.addPlezyProfile' => 'Lägg till Plezy-profil',
|
||||||
'profiles.switchingProfile' => 'Byter profil…',
|
'profiles.switchingProfile' => 'Byter profil…',
|
||||||
'profiles.deleteThisProfileTitle' => 'Ta bort denna profil?',
|
'profiles.deleteThisProfileTitle' => 'Ta bort denna profil?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Ta bort ${displayName}. Anslutningar påverkas inte.',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => 'Ta bort ${displayName}. Anslutningar påverkas inte.',
|
||||||
'profiles.active' => 'Aktiv',
|
'profiles.active' => 'Aktiv',
|
||||||
'profiles.manage' => 'Hantera',
|
'profiles.manage' => 'Hantera',
|
||||||
'profiles.delete' => 'Ta bort',
|
'profiles.delete' => 'Ta bort',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => 'Logga ut',
|
'profiles.signOut' => 'Logga ut',
|
||||||
'profiles.signOutPlexTitle' => 'Logga ut från Plex?',
|
'profiles.signOutPlexTitle' => 'Logga ut från Plex?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Ta bort ${displayName} och alla Plex Home-användare? Du kan logga in igen när som helst.',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => 'Ta bort ${displayName} och alla Plex Home-användare? Du kan logga in igen när som helst.',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsSv {
|
|||||||
'companionRemote.remote.audio' => 'Ljud',
|
'companionRemote.remote.audio' => 'Ljud',
|
||||||
'companionRemote.remote.searchHint' => 'Sök på datorn...',
|
'companionRemote.remote.searchHint' => 'Sök på datorn...',
|
||||||
'videoSettings.playbackSpeed' => 'Uppspelningshastighet',
|
'videoSettings.playbackSpeed' => 'Uppspelningshastighet',
|
||||||
|
'videoSettings.zoom' => 'Zoom',
|
||||||
'videoSettings.sleepTimer' => 'Sovtimer',
|
'videoSettings.sleepTimer' => 'Sovtimer',
|
||||||
'videoSettings.audioSync' => 'Ljudsynkronisering',
|
'videoSettings.audioSync' => 'Ljudsynkronisering',
|
||||||
'videoSettings.subtitleSync' => 'Undertextsynkronisering',
|
'videoSettings.subtitleSync' => 'Undertextsynkronisering',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsSv {
|
|||||||
'metadataEdit.basicInfo' => 'Grundläggande info',
|
'metadataEdit.basicInfo' => 'Grundläggande info',
|
||||||
'metadataEdit.artwork' => 'Artwork',
|
'metadataEdit.artwork' => 'Artwork',
|
||||||
'metadataEdit.advancedSettings' => 'Avancerade inställningar',
|
'metadataEdit.advancedSettings' => 'Avancerade inställningar',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => 'Titel',
|
'metadataEdit.title' => 'Titel',
|
||||||
'metadataEdit.sortTitle' => 'Sorteringstitel',
|
'metadataEdit.sortTitle' => 'Sorteringstitel',
|
||||||
'metadataEdit.originalTitle' => 'Originaltitel',
|
'metadataEdit.originalTitle' => 'Originaltitel',
|
||||||
'metadataEdit.releaseDate' => 'Utgivningsdatum',
|
'metadataEdit.releaseDate' => 'Utgivningsdatum',
|
||||||
'metadataEdit.contentRating' => 'Åldersgräns',
|
'metadataEdit.contentRating' => 'Åldersgräns',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => 'Studio',
|
'metadataEdit.studio' => 'Studio',
|
||||||
'metadataEdit.tagline' => 'Tagline',
|
'metadataEdit.tagline' => 'Tagline',
|
||||||
'metadataEdit.summary' => 'Sammanfattning',
|
'metadataEdit.summary' => 'Sammanfattning',
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ class _TranslationsVideoControlsZh extends TranslationsVideoControlsEn {
|
|||||||
@override String get pipActive => '正在画中画模式中播放';
|
@override String get pipActive => '正在画中画模式中播放';
|
||||||
@override String get pipFailed => '画中画启动失败';
|
@override String get pipFailed => '画中画启动失败';
|
||||||
@override String get screenshotSaved => '截图已保存';
|
@override String get screenshotSaved => '截图已保存';
|
||||||
|
@override String zoomPercent({required Object percent}) => '缩放 ${percent}%';
|
||||||
@override late final _TranslationsVideoControlsPipErrorsZh pipErrors = _TranslationsVideoControlsPipErrorsZh._(_root);
|
@override late final _TranslationsVideoControlsPipErrorsZh pipErrors = _TranslationsVideoControlsPipErrorsZh._(_root);
|
||||||
@override String get chapters => '章节';
|
@override String get chapters => '章节';
|
||||||
@override String get noChaptersAvailable => '没有可用的章节';
|
@override String get noChaptersAvailable => '没有可用的章节';
|
||||||
@@ -1308,6 +1309,7 @@ class _TranslationsVideoSettingsZh extends TranslationsVideoSettingsEn {
|
|||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
@override String get playbackSpeed => '播放速度';
|
@override String get playbackSpeed => '播放速度';
|
||||||
|
@override String get zoom => '缩放';
|
||||||
@override String get sleepTimer => '睡眠定时器';
|
@override String get sleepTimer => '睡眠定时器';
|
||||||
@override String get audioSync => '音频同步';
|
@override String get audioSync => '音频同步';
|
||||||
@override String get subtitleSync => '字幕同步';
|
@override String get subtitleSync => '字幕同步';
|
||||||
@@ -1565,6 +1567,9 @@ class _TranslationsHotkeysActionsZh extends TranslationsHotkeysActionsEn {
|
|||||||
@override String get speedIncrease => '加速';
|
@override String get speedIncrease => '加速';
|
||||||
@override String get speedDecrease => '减速';
|
@override String get speedDecrease => '减速';
|
||||||
@override String get speedReset => '重置速度';
|
@override String get speedReset => '重置速度';
|
||||||
|
@override String get zoomIn => '放大';
|
||||||
|
@override String get zoomOut => '缩小';
|
||||||
|
@override String get zoomReset => '重置缩放';
|
||||||
@override String get subSeekNext => '跳转到下一字幕';
|
@override String get subSeekNext => '跳转到下一字幕';
|
||||||
@override String get subSeekPrev => '跳转到上一字幕';
|
@override String get subSeekPrev => '跳转到上一字幕';
|
||||||
@override String get shaderToggle => '切换着色器';
|
@override String get shaderToggle => '切换着色器';
|
||||||
@@ -2099,6 +2104,9 @@ extension on TranslationsZh {
|
|||||||
'hotkeys.actions.speedIncrease' => '加速',
|
'hotkeys.actions.speedIncrease' => '加速',
|
||||||
'hotkeys.actions.speedDecrease' => '减速',
|
'hotkeys.actions.speedDecrease' => '减速',
|
||||||
'hotkeys.actions.speedReset' => '重置速度',
|
'hotkeys.actions.speedReset' => '重置速度',
|
||||||
|
'hotkeys.actions.zoomIn' => '放大',
|
||||||
|
'hotkeys.actions.zoomOut' => '缩小',
|
||||||
|
'hotkeys.actions.zoomReset' => '重置缩放',
|
||||||
'hotkeys.actions.subSeekNext' => '跳转到下一字幕',
|
'hotkeys.actions.subSeekNext' => '跳转到下一字幕',
|
||||||
'hotkeys.actions.subSeekPrev' => '跳转到上一字幕',
|
'hotkeys.actions.subSeekPrev' => '跳转到上一字幕',
|
||||||
'hotkeys.actions.shaderToggle' => '切换着色器',
|
'hotkeys.actions.shaderToggle' => '切换着色器',
|
||||||
@@ -2224,6 +2232,7 @@ extension on TranslationsZh {
|
|||||||
'videoControls.pipActive' => '正在画中画模式中播放',
|
'videoControls.pipActive' => '正在画中画模式中播放',
|
||||||
'videoControls.pipFailed' => '画中画启动失败',
|
'videoControls.pipFailed' => '画中画启动失败',
|
||||||
'videoControls.screenshotSaved' => '截图已保存',
|
'videoControls.screenshotSaved' => '截图已保存',
|
||||||
|
'videoControls.zoomPercent' => ({required Object percent}) => '缩放 ${percent}%',
|
||||||
'videoControls.pipErrors.androidVersion' => '需要 Android 8.0 或更高版本',
|
'videoControls.pipErrors.androidVersion' => '需要 Android 8.0 或更高版本',
|
||||||
'videoControls.pipErrors.iosVersion' => '需要 iOS 15.0 或更高版本',
|
'videoControls.pipErrors.iosVersion' => '需要 iOS 15.0 或更高版本',
|
||||||
'videoControls.pipErrors.permissionDisabled' => '画中画已禁用。请在系统设置中启用。',
|
'videoControls.pipErrors.permissionDisabled' => '画中画已禁用。请在系统设置中启用。',
|
||||||
@@ -2316,12 +2325,12 @@ extension on TranslationsZh {
|
|||||||
'profiles.addPlezyProfile' => '添加 Plezy 配置文件',
|
'profiles.addPlezyProfile' => '添加 Plezy 配置文件',
|
||||||
'profiles.switchingProfile' => '切换配置文件中…',
|
'profiles.switchingProfile' => '切换配置文件中…',
|
||||||
'profiles.deleteThisProfileTitle' => '删除此配置文件?',
|
'profiles.deleteThisProfileTitle' => '删除此配置文件?',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '移除 ${displayName}。连接不会受影响。',
|
'profiles.deleteThisProfileMessage' => ({required Object displayName}) => '移除 ${displayName}。连接不会受影响。',
|
||||||
'profiles.active' => '活跃',
|
'profiles.active' => '活跃',
|
||||||
'profiles.manage' => '管理',
|
'profiles.manage' => '管理',
|
||||||
'profiles.delete' => '删除',
|
'profiles.delete' => '删除',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'profiles.signOut' => '退出登录',
|
'profiles.signOut' => '退出登录',
|
||||||
'profiles.signOutPlexTitle' => '退出 Plex 登录?',
|
'profiles.signOutPlexTitle' => '退出 Plex 登录?',
|
||||||
'profiles.signOutPlexMessage' => ({required Object displayName}) => '要移除 ${displayName} 和所有 Plex Home 用户吗?可随时重新登录。',
|
'profiles.signOutPlexMessage' => ({required Object displayName}) => '要移除 ${displayName} 和所有 Plex Home 用户吗?可随时重新登录。',
|
||||||
@@ -2801,6 +2810,7 @@ extension on TranslationsZh {
|
|||||||
'companionRemote.remote.audio' => '音频',
|
'companionRemote.remote.audio' => '音频',
|
||||||
'companionRemote.remote.searchHint' => '在桌面上搜索...',
|
'companionRemote.remote.searchHint' => '在桌面上搜索...',
|
||||||
'videoSettings.playbackSpeed' => '播放速度',
|
'videoSettings.playbackSpeed' => '播放速度',
|
||||||
|
'videoSettings.zoom' => '缩放',
|
||||||
'videoSettings.sleepTimer' => '睡眠定时器',
|
'videoSettings.sleepTimer' => '睡眠定时器',
|
||||||
'videoSettings.audioSync' => '音频同步',
|
'videoSettings.audioSync' => '音频同步',
|
||||||
'videoSettings.subtitleSync' => '字幕同步',
|
'videoSettings.subtitleSync' => '字幕同步',
|
||||||
@@ -2829,13 +2839,13 @@ extension on TranslationsZh {
|
|||||||
'metadataEdit.basicInfo' => '基本信息',
|
'metadataEdit.basicInfo' => '基本信息',
|
||||||
'metadataEdit.artwork' => '封面图片',
|
'metadataEdit.artwork' => '封面图片',
|
||||||
'metadataEdit.advancedSettings' => '高级设置',
|
'metadataEdit.advancedSettings' => '高级设置',
|
||||||
|
_ => null,
|
||||||
|
} ?? switch (path) {
|
||||||
'metadataEdit.title' => '标题',
|
'metadataEdit.title' => '标题',
|
||||||
'metadataEdit.sortTitle' => '排序标题',
|
'metadataEdit.sortTitle' => '排序标题',
|
||||||
'metadataEdit.originalTitle' => '原始标题',
|
'metadataEdit.originalTitle' => '原始标题',
|
||||||
'metadataEdit.releaseDate' => '上映日期',
|
'metadataEdit.releaseDate' => '上映日期',
|
||||||
'metadataEdit.contentRating' => '内容分级',
|
'metadataEdit.contentRating' => '内容分级',
|
||||||
_ => null,
|
|
||||||
} ?? switch (path) {
|
|
||||||
'metadataEdit.studio' => '制片厂',
|
'metadataEdit.studio' => '制片厂',
|
||||||
'metadataEdit.tagline' => '标语',
|
'metadataEdit.tagline' => '标语',
|
||||||
'metadataEdit.summary' => '简介',
|
'metadataEdit.summary' => '简介',
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "Öka hastighet",
|
"speedIncrease": "Öka hastighet",
|
||||||
"speedDecrease": "Minska hastighet",
|
"speedDecrease": "Minska hastighet",
|
||||||
"speedReset": "Återställ hastighet",
|
"speedReset": "Återställ hastighet",
|
||||||
|
"zoomIn": "Zooma in",
|
||||||
|
"zoomOut": "Zooma ut",
|
||||||
|
"zoomReset": "Återställ zoom",
|
||||||
"subSeekNext": "Hoppa till nästa undertext",
|
"subSeekNext": "Hoppa till nästa undertext",
|
||||||
"subSeekPrev": "Hoppa till föregående undertext",
|
"subSeekPrev": "Hoppa till föregående undertext",
|
||||||
"shaderToggle": "Växla shaders",
|
"shaderToggle": "Växla shaders",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "Spelar i bild-i-bild",
|
"pipActive": "Spelar i bild-i-bild",
|
||||||
"pipFailed": "Bild-i-bild kunde inte starta",
|
"pipFailed": "Bild-i-bild kunde inte starta",
|
||||||
"screenshotSaved": "Skärmbild sparad",
|
"screenshotSaved": "Skärmbild sparad",
|
||||||
|
"zoomPercent": "Zoom ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "Kräver Android 8.0 eller nyare",
|
"androidVersion": "Kräver Android 8.0 eller nyare",
|
||||||
"iosVersion": "Kräver iOS 15.0 eller nyare",
|
"iosVersion": "Kräver iOS 15.0 eller nyare",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "Uppspelningshastighet",
|
"playbackSpeed": "Uppspelningshastighet",
|
||||||
|
"zoom": "Zoom",
|
||||||
"sleepTimer": "Sovtimer",
|
"sleepTimer": "Sovtimer",
|
||||||
"audioSync": "Ljudsynkronisering",
|
"audioSync": "Ljudsynkronisering",
|
||||||
"subtitleSync": "Undertextsynkronisering",
|
"subtitleSync": "Undertextsynkronisering",
|
||||||
|
|||||||
@@ -306,6 +306,9 @@
|
|||||||
"speedIncrease": "加速",
|
"speedIncrease": "加速",
|
||||||
"speedDecrease": "减速",
|
"speedDecrease": "减速",
|
||||||
"speedReset": "重置速度",
|
"speedReset": "重置速度",
|
||||||
|
"zoomIn": "放大",
|
||||||
|
"zoomOut": "缩小",
|
||||||
|
"zoomReset": "重置缩放",
|
||||||
"subSeekNext": "跳转到下一字幕",
|
"subSeekNext": "跳转到下一字幕",
|
||||||
"subSeekPrev": "跳转到上一字幕",
|
"subSeekPrev": "跳转到上一字幕",
|
||||||
"shaderToggle": "切换着色器",
|
"shaderToggle": "切换着色器",
|
||||||
@@ -444,6 +447,7 @@
|
|||||||
"pipActive": "正在画中画模式中播放",
|
"pipActive": "正在画中画模式中播放",
|
||||||
"pipFailed": "画中画启动失败",
|
"pipFailed": "画中画启动失败",
|
||||||
"screenshotSaved": "截图已保存",
|
"screenshotSaved": "截图已保存",
|
||||||
|
"zoomPercent": "缩放 ${percent}%",
|
||||||
"pipErrors": {
|
"pipErrors": {
|
||||||
"androidVersion": "需要 Android 8.0 或更高版本",
|
"androidVersion": "需要 Android 8.0 或更高版本",
|
||||||
"iosVersion": "需要 iOS 15.0 或更高版本",
|
"iosVersion": "需要 iOS 15.0 或更高版本",
|
||||||
@@ -1083,6 +1087,7 @@
|
|||||||
},
|
},
|
||||||
"videoSettings": {
|
"videoSettings": {
|
||||||
"playbackSpeed": "播放速度",
|
"playbackSpeed": "播放速度",
|
||||||
|
"zoom": "缩放",
|
||||||
"sleepTimer": "睡眠定时器",
|
"sleepTimer": "睡眠定时器",
|
||||||
"audioSync": "音频同步",
|
"audioSync": "音频同步",
|
||||||
"subtitleSync": "字幕同步",
|
"subtitleSync": "字幕同步",
|
||||||
|
|||||||
@@ -372,6 +372,12 @@ class PlayerAndroid extends PlayerBase {
|
|||||||
await invoke('setBoxFitMode', {'mode': mode});
|
await invoke('setBoxFitMode', {'mode': mode});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply custom zoom to the native ExoPlayer layer.
|
||||||
|
Future<void> setVideoZoom(double scale) async {
|
||||||
|
if (disposed || !initialized) return;
|
||||||
|
await invoke('setVideoZoom', {'scale': scale});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> setVideoFrameRate(double fps, int durationMs, {int extraDelayMs = 0}) async {
|
Future<bool> setVideoFrameRate(double fps, int durationMs, {int extraDelayMs = 0}) async {
|
||||||
if (disposed || !initialized) return false;
|
if (disposed || !initialized) return false;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ part of '../../video_player_screen.dart';
|
|||||||
|
|
||||||
extension _VideoPlayerBuildMethods on VideoPlayerScreenState {
|
extension _VideoPlayerBuildMethods on VideoPlayerScreenState {
|
||||||
static const double _videoLayoutSizeTolerance = 0.1;
|
static const double _videoLayoutSizeTolerance = 0.1;
|
||||||
|
static const double _pinchZoomActivationThreshold = 0.015;
|
||||||
|
|
||||||
bool _isSameVideoLayoutSize(Size a, Size b) {
|
bool _isSameVideoLayoutSize(Size a, Size b) {
|
||||||
return (a.width - b.width).abs() <= _videoLayoutSizeTolerance &&
|
return (a.width - b.width).abs() <= _videoLayoutSizeTolerance &&
|
||||||
@@ -138,6 +139,22 @@ extension _VideoPlayerBuildMethods on VideoPlayerScreenState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _startMobileZoomGesture() {
|
||||||
|
final filterManager = _videoFilterManager;
|
||||||
|
if (filterManager == null || _isPinchZooming) return;
|
||||||
|
|
||||||
|
_ambientLightingService?.disable();
|
||||||
|
_isPinchZooming = true;
|
||||||
|
_pinchZoomChanged = false;
|
||||||
|
_pinchStartZoomScale = filterManager.zoomScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _clearMobileZoomGesture() {
|
||||||
|
_isPinchZooming = false;
|
||||||
|
_pinchZoomChanged = false;
|
||||||
|
_pinchStartZoomScale = null;
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildVideoPlayer(BuildContext context) {
|
Widget _buildVideoPlayer(BuildContext context) {
|
||||||
// Cache platform detection to avoid multiple calls
|
// Cache platform detection to avoid multiple calls
|
||||||
final isMobile = PlatformDetector.isMobile(context);
|
final isMobile = PlatformDetector.isMobile(context);
|
||||||
@@ -167,22 +184,33 @@ extension _VideoPlayerBuildMethods on VideoPlayerScreenState {
|
|||||||
behavior: HitTestBehavior.translucent, // Allow taps to pass through to controls
|
behavior: HitTestBehavior.translucent, // Allow taps to pass through to controls
|
||||||
onScaleStart: (details) {
|
onScaleStart: (details) {
|
||||||
if (!isMobile) return;
|
if (!isMobile) return;
|
||||||
if (_videoFilterManager != null) {
|
if (details.pointerCount >= 2) _startMobileZoomGesture();
|
||||||
_videoFilterManager!.isPinching = false;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onScaleUpdate: (details) {
|
onScaleUpdate: (details) {
|
||||||
if (!isMobile) return;
|
if (!isMobile) return;
|
||||||
if (details.pointerCount >= 2 && _videoFilterManager != null) {
|
if (details.pointerCount < 2) return;
|
||||||
_videoFilterManager!.isPinching = true;
|
if (!_isPinchZooming) _startMobileZoomGesture();
|
||||||
}
|
|
||||||
|
final startZoom = _pinchStartZoomScale;
|
||||||
|
final filterManager = _videoFilterManager;
|
||||||
|
if (!_isPinchZooming || startZoom == null || filterManager == null) return;
|
||||||
|
if ((details.scale - 1.0).abs() <= _pinchZoomActivationThreshold && !_pinchZoomChanged) return;
|
||||||
|
|
||||||
|
_pinchZoomChanged = true;
|
||||||
|
filterManager.setZoomScale(startZoom * details.scale);
|
||||||
},
|
},
|
||||||
onScaleEnd: (details) {
|
onScaleEnd: (details) {
|
||||||
if (!isMobile) return;
|
if (!isMobile) return;
|
||||||
if (_videoFilterManager != null && _videoFilterManager!.isPinching) {
|
if (!_isPinchZooming) return;
|
||||||
_toggleContainCover();
|
if (!_pinchZoomChanged) {
|
||||||
_videoFilterManager!.isPinching = false;
|
_clearMobileZoomGesture();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final zoomScale = _videoFilterManager?.zoomScale ?? 1.0;
|
||||||
|
_showZoomToast(zoomScale);
|
||||||
|
_clearMobileZoomGesture();
|
||||||
|
_setPlayerState(() {});
|
||||||
},
|
},
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
@@ -244,7 +272,12 @@ extension _VideoPlayerBuildMethods on VideoPlayerScreenState {
|
|||||||
sourcePartId: _currentMediaInfo?.partId,
|
sourcePartId: _currentMediaInfo?.partId,
|
||||||
onTogglePIPMode: _togglePIPMode,
|
onTogglePIPMode: _togglePIPMode,
|
||||||
boxFitMode: _videoFilterManager?.boxFitMode ?? 0,
|
boxFitMode: _videoFilterManager?.boxFitMode ?? 0,
|
||||||
|
videoZoomScale: _videoFilterManager?.zoomScale ?? 1.0,
|
||||||
onCycleBoxFitMode: _cycleBoxFitMode,
|
onCycleBoxFitMode: _cycleBoxFitMode,
|
||||||
|
onVideoZoomChanged: _setVideoZoom,
|
||||||
|
onZoomIn: _zoomVideoIn,
|
||||||
|
onZoomOut: _zoomVideoOut,
|
||||||
|
onResetVideoZoom: _resetVideoZoom,
|
||||||
onCycleAudioTrack: _cycleAudioTrack,
|
onCycleAudioTrack: _cycleAudioTrack,
|
||||||
onCycleSubtitleTrack: _cycleSubtitleTrack,
|
onCycleSubtitleTrack: _cycleSubtitleTrack,
|
||||||
onAudioTrackChanged: _onAudioTrackChanged,
|
onAudioTrackChanged: _onAudioTrackChanged,
|
||||||
|
|||||||
@@ -68,6 +68,35 @@ extension _VideoPlayerShaderMethods on VideoPlayerScreenState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showZoomToast(double zoomScale) {
|
||||||
|
_toastController.show(Symbols.zoom_in_rounded, t.videoControls.zoomPercent(percent: (zoomScale * 100).round()));
|
||||||
|
}
|
||||||
|
|
||||||
|
double _setVideoZoom(double zoomScale, {bool showToast = true}) {
|
||||||
|
final filterManager = _videoFilterManager;
|
||||||
|
if (filterManager == null) return 1.0;
|
||||||
|
|
||||||
|
_ambientLightingService?.disable();
|
||||||
|
final next = filterManager.setZoomScale(zoomScale);
|
||||||
|
if (showToast) _showZoomToast(next);
|
||||||
|
if (mounted) _setPlayerState(() {});
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _zoomVideoIn() {
|
||||||
|
final current = _videoFilterManager?.zoomScale ?? 1.0;
|
||||||
|
_setVideoZoom(current + VideoFilterManager.zoomStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _zoomVideoOut() {
|
||||||
|
final current = _videoFilterManager?.zoomScale ?? 1.0;
|
||||||
|
_setVideoZoom(current - VideoFilterManager.zoomStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetVideoZoom() {
|
||||||
|
_setVideoZoom(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
/// Update video-aspect-override when player size changes.
|
/// Update video-aspect-override when player size changes.
|
||||||
/// The shader adapts automatically via built-in target_size uniform.
|
/// The shader adapts automatically via built-in target_size uniform.
|
||||||
void _updateAmbientLightingOnResize(Size newSize) {
|
void _updateAmbientLightingOnResize(Size newSize) {
|
||||||
@@ -120,11 +149,4 @@ extension _VideoPlayerShaderMethods on VideoPlayerScreenState {
|
|||||||
|
|
||||||
if (mounted) _setPlayerState(() {});
|
if (mounted) _setPlayerState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle between contain and cover modes only (for pinch gesture)
|
|
||||||
void _toggleContainCover() {
|
|
||||||
_setPlayerState(() {
|
|
||||||
_videoFilterManager?.toggleContainCover();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -400,6 +400,9 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
|
|||||||
Size? _pendingVideoLayoutSize;
|
Size? _pendingVideoLayoutSize;
|
||||||
Player? _lastVideoLayoutPlayer;
|
Player? _lastVideoLayoutPlayer;
|
||||||
bool _videoLayoutUpdateScheduled = false;
|
bool _videoLayoutUpdateScheduled = false;
|
||||||
|
double? _pinchStartZoomScale;
|
||||||
|
bool _isPinchZooming = false;
|
||||||
|
bool _pinchZoomChanged = false;
|
||||||
final EpisodeNavigationService _episodeNavigation = EpisodeNavigationService();
|
final EpisodeNavigationService _episodeNavigation = EpisodeNavigationService();
|
||||||
|
|
||||||
WatchTogetherProvider? _watchTogetherProvider;
|
WatchTogetherProvider? _watchTogetherProvider;
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import '../utils/platform_detector.dart';
|
|||||||
import '../utils/player_utils.dart';
|
import '../utils/player_utils.dart';
|
||||||
|
|
||||||
class KeyboardShortcutsService extends ChangeNotifier {
|
class KeyboardShortcutsService extends ChangeNotifier {
|
||||||
|
static const Set<String> _repeatableVideoActions = {'zoom_in', 'zoom_out'};
|
||||||
|
|
||||||
static KeyboardShortcutsService? _instance;
|
static KeyboardShortcutsService? _instance;
|
||||||
late SettingsService _settingsService;
|
late SettingsService _settingsService;
|
||||||
final List<VoidCallback> _settingsDisposers = [];
|
final List<VoidCallback> _settingsDisposers = [];
|
||||||
@@ -183,13 +185,17 @@ class KeyboardShortcutsService extends ChangeNotifier {
|
|||||||
VoidCallback? onNextEpisode,
|
VoidCallback? onNextEpisode,
|
||||||
VoidCallback? onPreviousEpisode,
|
VoidCallback? onPreviousEpisode,
|
||||||
VoidCallback? onScreenshot,
|
VoidCallback? onScreenshot,
|
||||||
|
VoidCallback? onZoomIn,
|
||||||
|
VoidCallback? onZoomOut,
|
||||||
|
VoidCallback? onZoomReset,
|
||||||
int? currentPositionEpoch,
|
int? currentPositionEpoch,
|
||||||
ValueChanged<int>? onLiveSeek,
|
ValueChanged<int>? onLiveSeek,
|
||||||
Future<void> Function(Duration position)? onSeekRequested,
|
Future<void> Function(Duration position)? onSeekRequested,
|
||||||
}) {
|
}) {
|
||||||
if (event is! KeyDownEvent) return KeyEventResult.ignored;
|
final isRepeat = event is KeyRepeatEvent;
|
||||||
|
if (event is! KeyDownEvent && !isRepeat) return KeyEventResult.ignored;
|
||||||
|
|
||||||
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.escape) {
|
||||||
onBack?.call();
|
onBack?.call();
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
@@ -247,6 +253,10 @@ class KeyboardShortcutsService extends ChangeNotifier {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isRepeat && !_repeatableVideoActions.contains(action)) {
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
|
||||||
_executeAction(
|
_executeAction(
|
||||||
action,
|
action,
|
||||||
player,
|
player,
|
||||||
@@ -261,6 +271,9 @@ class KeyboardShortcutsService extends ChangeNotifier {
|
|||||||
onNextEpisode: onNextEpisode,
|
onNextEpisode: onNextEpisode,
|
||||||
onPreviousEpisode: onPreviousEpisode,
|
onPreviousEpisode: onPreviousEpisode,
|
||||||
onScreenshot: onScreenshot,
|
onScreenshot: onScreenshot,
|
||||||
|
onZoomIn: onZoomIn,
|
||||||
|
onZoomOut: onZoomOut,
|
||||||
|
onZoomReset: onZoomReset,
|
||||||
currentPositionEpoch: currentPositionEpoch,
|
currentPositionEpoch: currentPositionEpoch,
|
||||||
onLiveSeek: onLiveSeek,
|
onLiveSeek: onLiveSeek,
|
||||||
onSeekRequested: onSeekRequested,
|
onSeekRequested: onSeekRequested,
|
||||||
@@ -286,6 +299,9 @@ class KeyboardShortcutsService extends ChangeNotifier {
|
|||||||
VoidCallback? onNextEpisode,
|
VoidCallback? onNextEpisode,
|
||||||
VoidCallback? onPreviousEpisode,
|
VoidCallback? onPreviousEpisode,
|
||||||
VoidCallback? onScreenshot,
|
VoidCallback? onScreenshot,
|
||||||
|
VoidCallback? onZoomIn,
|
||||||
|
VoidCallback? onZoomOut,
|
||||||
|
VoidCallback? onZoomReset,
|
||||||
int? currentPositionEpoch,
|
int? currentPositionEpoch,
|
||||||
ValueChanged<int>? onLiveSeek,
|
ValueChanged<int>? onLiveSeek,
|
||||||
Future<void> Function(Duration position)? onSeekRequested,
|
Future<void> Function(Duration position)? onSeekRequested,
|
||||||
@@ -383,6 +399,15 @@ class KeyboardShortcutsService extends ChangeNotifier {
|
|||||||
case 'screenshot':
|
case 'screenshot':
|
||||||
unawaited(player.command(['screenshot', 'subtitles']).then((_) => onScreenshot?.call()));
|
unawaited(player.command(['screenshot', 'subtitles']).then((_) => onScreenshot?.call()));
|
||||||
break;
|
break;
|
||||||
|
case 'zoom_in':
|
||||||
|
onZoomIn?.call();
|
||||||
|
break;
|
||||||
|
case 'zoom_out':
|
||||||
|
onZoomOut?.call();
|
||||||
|
break;
|
||||||
|
case 'zoom_reset':
|
||||||
|
onZoomReset?.call();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +461,12 @@ class KeyboardShortcutsService extends ChangeNotifier {
|
|||||||
return t.hotkeys.actions.skipMarker;
|
return t.hotkeys.actions.skipMarker;
|
||||||
case 'screenshot':
|
case 'screenshot':
|
||||||
return t.hotkeys.actions.screenshot;
|
return t.hotkeys.actions.screenshot;
|
||||||
|
case 'zoom_in':
|
||||||
|
return t.hotkeys.actions.zoomIn;
|
||||||
|
case 'zoom_out':
|
||||||
|
return t.hotkeys.actions.zoomOut;
|
||||||
|
case 'zoom_reset':
|
||||||
|
return t.hotkeys.actions.zoomReset;
|
||||||
default:
|
default:
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,6 +216,9 @@ Map<String, String> _defaultKeyboardShortcuts() => {
|
|||||||
'speed_increase': 'Plus',
|
'speed_increase': 'Plus',
|
||||||
'speed_decrease': 'Minus',
|
'speed_decrease': 'Minus',
|
||||||
'speed_reset': 'R',
|
'speed_reset': 'R',
|
||||||
|
'zoom_in': 'Alt+Plus',
|
||||||
|
'zoom_out': 'Alt+Minus',
|
||||||
|
'zoom_reset': 'Alt+Backspace',
|
||||||
'sub_seek_next': 'Ctrl+Right',
|
'sub_seek_next': 'Ctrl+Right',
|
||||||
'sub_seek_prev': 'Ctrl+Left',
|
'sub_seek_prev': 'Ctrl+Left',
|
||||||
'screenshot': 'Ctrl+S',
|
'screenshot': 'Ctrl+S',
|
||||||
@@ -241,6 +244,9 @@ Map<String, HotKey> _defaultKeyboardHotkeys() => {
|
|||||||
'speed_increase': const HotKey(key: PhysicalKeyboardKey.equal),
|
'speed_increase': const HotKey(key: PhysicalKeyboardKey.equal),
|
||||||
'speed_decrease': const HotKey(key: PhysicalKeyboardKey.minus),
|
'speed_decrease': const HotKey(key: PhysicalKeyboardKey.minus),
|
||||||
'speed_reset': const HotKey(key: PhysicalKeyboardKey.keyR),
|
'speed_reset': const HotKey(key: PhysicalKeyboardKey.keyR),
|
||||||
|
'zoom_in': const HotKey(key: PhysicalKeyboardKey.equal, modifiers: [HotKeyModifier.alt]),
|
||||||
|
'zoom_out': const HotKey(key: PhysicalKeyboardKey.minus, modifiers: [HotKeyModifier.alt]),
|
||||||
|
'zoom_reset': const HotKey(key: PhysicalKeyboardKey.backspace, modifiers: [HotKeyModifier.alt]),
|
||||||
'sub_seek_next': const HotKey(key: PhysicalKeyboardKey.arrowRight, modifiers: [HotKeyModifier.control]),
|
'sub_seek_next': const HotKey(key: PhysicalKeyboardKey.arrowRight, modifiers: [HotKeyModifier.control]),
|
||||||
'sub_seek_prev': const HotKey(key: PhysicalKeyboardKey.arrowLeft, modifiers: [HotKeyModifier.control]),
|
'sub_seek_prev': const HotKey(key: PhysicalKeyboardKey.arrowLeft, modifiers: [HotKeyModifier.control]),
|
||||||
'shader_toggle': const HotKey(key: PhysicalKeyboardKey.keyG),
|
'shader_toggle': const HotKey(key: PhysicalKeyboardKey.keyG),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rate_limiter/rate_limiter.dart';
|
import 'package:rate_limiter/rate_limiter.dart';
|
||||||
|
|
||||||
@@ -17,6 +19,10 @@ import 'ambient_lighting_service.dart';
|
|||||||
/// - Debounced video filter updates on resize events
|
/// - Debounced video filter updates on resize events
|
||||||
/// - Ambient-lighting-friendly reset to contain mode
|
/// - Ambient-lighting-friendly reset to contain mode
|
||||||
class VideoFilterManager {
|
class VideoFilterManager {
|
||||||
|
static const double minZoomScale = 0.5;
|
||||||
|
static const double maxZoomScale = 2.0;
|
||||||
|
static const double zoomStep = 0.01;
|
||||||
|
|
||||||
final Player player;
|
final Player player;
|
||||||
final List<MediaVersion> availableVersions;
|
final List<MediaVersion> availableVersions;
|
||||||
final int selectedMediaIndex;
|
final int selectedMediaIndex;
|
||||||
@@ -27,14 +33,17 @@ class VideoFilterManager {
|
|||||||
/// Store the boxFitMode before entering PiP so it can be restored
|
/// Store the boxFitMode before entering PiP so it can be restored
|
||||||
int? _prePipBoxFitMode;
|
int? _prePipBoxFitMode;
|
||||||
|
|
||||||
|
/// Store the zoom level before entering PiP so it can be restored
|
||||||
|
double? _prePipZoomScale;
|
||||||
|
|
||||||
/// Store whether ambient lighting was active before entering PiP
|
/// Store whether ambient lighting was active before entering PiP
|
||||||
bool? _prePipAmbientLighting;
|
bool? _prePipAmbientLighting;
|
||||||
|
|
||||||
/// Ambient lighting service reference - when active, video-aspect-override is managed by ambient lighting
|
/// Ambient lighting service reference - when active, video-aspect-override is managed by ambient lighting
|
||||||
AmbientLightingService? ambientLightingService;
|
AmbientLightingService? ambientLightingService;
|
||||||
|
|
||||||
/// Track if a pinch gesture is occurring (public for gesture tracking)
|
/// Custom video zoom layered on top of the selected fit mode.
|
||||||
bool isPinching = false;
|
double _zoomScale = 1.0;
|
||||||
|
|
||||||
/// Current player viewport size
|
/// Current player viewport size
|
||||||
Size? _playerSize;
|
Size? _playerSize;
|
||||||
@@ -63,8 +72,27 @@ class VideoFilterManager {
|
|||||||
/// Current BoxFit mode (0=contain, 1=cover, 2=fill)
|
/// Current BoxFit mode (0=contain, 1=cover, 2=fill)
|
||||||
int get boxFitMode => _boxFitMode;
|
int get boxFitMode => _boxFitMode;
|
||||||
|
|
||||||
|
double get zoomScale => _zoomScale;
|
||||||
|
|
||||||
Size? get playerSize => _playerSize;
|
Size? get playerSize => _playerSize;
|
||||||
|
|
||||||
|
static double videoZoomPropertyForScale(double scale) {
|
||||||
|
final clamped = scale.clamp(minZoomScale, maxZoomScale).toDouble();
|
||||||
|
return math.log(clamped) / math.ln2;
|
||||||
|
}
|
||||||
|
|
||||||
|
double setZoomScale(double scale) {
|
||||||
|
final next = scale.clamp(minZoomScale, maxZoomScale).toDouble();
|
||||||
|
if ((_zoomScale - next).abs() < 0.0001) return _zoomScale;
|
||||||
|
_zoomScale = next;
|
||||||
|
updateVideoFilter();
|
||||||
|
return _zoomScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
double adjustZoom(double delta) => setZoomScale(_zoomScale + delta);
|
||||||
|
|
||||||
|
double resetZoom() => setZoomScale(1.0);
|
||||||
|
|
||||||
/// Cycle through BoxFit modes: contain → cover → fill → contain (for button)
|
/// Cycle through BoxFit modes: contain → cover → fill → contain (for button)
|
||||||
void cycleBoxFitMode() {
|
void cycleBoxFitMode() {
|
||||||
_boxFitMode = (_boxFitMode + 1) % 3;
|
_boxFitMode = (_boxFitMode + 1) % 3;
|
||||||
@@ -74,19 +102,13 @@ class VideoFilterManager {
|
|||||||
|
|
||||||
/// Reset to contain mode (mode 0). Used when enabling ambient lighting.
|
/// Reset to contain mode (mode 0). Used when enabling ambient lighting.
|
||||||
void resetToContain() {
|
void resetToContain() {
|
||||||
if (_boxFitMode != 0) {
|
if (_boxFitMode != 0 || (_zoomScale - 1.0).abs() > 0.0001) {
|
||||||
_boxFitMode = 0;
|
_boxFitMode = 0;
|
||||||
|
_zoomScale = 1.0;
|
||||||
updateVideoFilter();
|
updateVideoFilter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle between contain and cover modes only (for pinch gesture)
|
|
||||||
void toggleContainCover() {
|
|
||||||
_boxFitMode = _boxFitMode == 0 ? 1 : 0;
|
|
||||||
onBoxFitModeChanged?.call(_boxFitMode);
|
|
||||||
updateVideoFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Force contain mode for PiP (no cropping/stretching)
|
/// Force contain mode for PiP (no cropping/stretching)
|
||||||
void enterPipMode() {
|
void enterPipMode() {
|
||||||
// Disable ambient lighting for PiP — it wastes space on blurred borders
|
// Disable ambient lighting for PiP — it wastes space on blurred borders
|
||||||
@@ -97,17 +119,30 @@ class VideoFilterManager {
|
|||||||
if (_boxFitMode != 0) {
|
if (_boxFitMode != 0) {
|
||||||
_prePipBoxFitMode = _boxFitMode;
|
_prePipBoxFitMode = _boxFitMode;
|
||||||
_boxFitMode = 0; // Contain mode
|
_boxFitMode = 0; // Contain mode
|
||||||
|
}
|
||||||
|
if ((_zoomScale - 1.0).abs() > 0.0001) {
|
||||||
|
_prePipZoomScale = _zoomScale;
|
||||||
|
_zoomScale = 1.0;
|
||||||
|
}
|
||||||
|
if (_prePipBoxFitMode != null || _prePipZoomScale != null) {
|
||||||
updateVideoFilter();
|
updateVideoFilter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore previous mode when exiting PiP
|
/// Restore previous mode when exiting PiP
|
||||||
void exitPipMode() {
|
void exitPipMode() {
|
||||||
|
var shouldUpdate = false;
|
||||||
if (_prePipBoxFitMode != null) {
|
if (_prePipBoxFitMode != null) {
|
||||||
_boxFitMode = _prePipBoxFitMode!;
|
_boxFitMode = _prePipBoxFitMode!;
|
||||||
_prePipBoxFitMode = null;
|
_prePipBoxFitMode = null;
|
||||||
updateVideoFilter();
|
shouldUpdate = true;
|
||||||
}
|
}
|
||||||
|
if (_prePipZoomScale != null) {
|
||||||
|
_zoomScale = _prePipZoomScale!;
|
||||||
|
_prePipZoomScale = null;
|
||||||
|
shouldUpdate = true;
|
||||||
|
}
|
||||||
|
if (shouldUpdate) updateVideoFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether ambient lighting was active before entering PiP
|
/// Whether ambient lighting was active before entering PiP
|
||||||
@@ -129,13 +164,14 @@ class VideoFilterManager {
|
|||||||
|
|
||||||
/// Update the video scaling and positioning based on current display mode.
|
/// Update the video scaling and positioning based on current display mode.
|
||||||
/// When ambient lighting is active, video-aspect-override is managed by ambient lighting.
|
/// When ambient lighting is active, video-aspect-override is managed by ambient lighting.
|
||||||
void updateVideoFilter() async {
|
Future<void> updateVideoFilter() async {
|
||||||
try {
|
try {
|
||||||
// ExoPlayer handles scaling via AspectRatioFrameLayout. The MPV properties
|
// ExoPlayer handles scaling via AspectRatioFrameLayout. The MPV properties
|
||||||
// below still run — on PlayerAndroid they forward to setMpvProperty, which
|
// below still run — on PlayerAndroid they forward to setMpvProperty, which
|
||||||
// queues them for any future fallback to MPV.
|
// queues them for any future fallback to MPV.
|
||||||
if (player is PlayerAndroid) {
|
if (player is PlayerAndroid) {
|
||||||
await (player as PlayerAndroid).setBoxFitMode(_boxFitMode);
|
await (player as PlayerAndroid).setBoxFitMode(_boxFitMode);
|
||||||
|
await (player as PlayerAndroid).setVideoZoom(_zoomScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ambientLightingService?.isEnabled != true) {
|
if (ambientLightingService?.isEnabled != true) {
|
||||||
@@ -143,6 +179,7 @@ class VideoFilterManager {
|
|||||||
}
|
}
|
||||||
await player.setProperty('sub-ass-force-margins', 'no');
|
await player.setProperty('sub-ass-force-margins', 'no');
|
||||||
await player.setProperty('panscan', '0');
|
await player.setProperty('panscan', '0');
|
||||||
|
await player.setProperty('video-zoom', videoZoomPropertyForScale(_zoomScale).toString());
|
||||||
|
|
||||||
if (_boxFitMode == 1) {
|
if (_boxFitMode == 1) {
|
||||||
// Cover mode - use panscan to fill screen while maintaining aspect ratio
|
// Cover mode - use panscan to fill screen while maintaining aspect ratio
|
||||||
@@ -156,6 +193,10 @@ class VideoFilterManager {
|
|||||||
appLogger.d('Stretch mode: aspect-override=$playerAspect (player: $_playerSize)');
|
appLogger.d('Stretch mode: aspect-override=$playerAspect (player: $_playerSize)');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_zoomScale > 1.0001) {
|
||||||
|
await player.setProperty('sub-ass-force-margins', 'yes');
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
appLogger.w('Failed to update video filter', error: e);
|
appLogger.w('Failed to update video filter', error: e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import 'dart:ui' show Offset;
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart' show kDoubleTapSlop, kDoubleTapTimeout, kDoubleTapTouchSlop;
|
||||||
|
|
||||||
|
class TwoFingerDoubleTapTracker {
|
||||||
|
TwoFingerDoubleTapTracker({
|
||||||
|
DateTime Function()? now,
|
||||||
|
this.tapTimeout = kDoubleTapTimeout,
|
||||||
|
this.doubleTapTimeout = kDoubleTapTimeout,
|
||||||
|
this.tapSlop = kDoubleTapTouchSlop,
|
||||||
|
this.doubleTapSlop = kDoubleTapSlop,
|
||||||
|
}) : _now = now ?? DateTime.now;
|
||||||
|
|
||||||
|
final DateTime Function() _now;
|
||||||
|
final Duration tapTimeout;
|
||||||
|
final Duration doubleTapTimeout;
|
||||||
|
final double tapSlop;
|
||||||
|
final double doubleTapSlop;
|
||||||
|
|
||||||
|
final Map<int, _TrackedTouch> _activeTouches = {};
|
||||||
|
DateTime? _candidateStartTime;
|
||||||
|
Offset? _candidateFocalPoint;
|
||||||
|
bool _candidateActive = false;
|
||||||
|
bool _candidateInvalid = false;
|
||||||
|
DateTime? _lastTapTime;
|
||||||
|
Offset? _lastTapFocalPoint;
|
||||||
|
|
||||||
|
bool get isChordActive => _activeTouches.length > 1 || _candidateActive;
|
||||||
|
|
||||||
|
void pointerDown(int pointer, Offset position) {
|
||||||
|
_activeTouches[pointer] = _TrackedTouch(start: position, current: position, downTime: _now());
|
||||||
|
|
||||||
|
if (_activeTouches.length == 2) {
|
||||||
|
_candidateActive = true;
|
||||||
|
_candidateInvalid = false;
|
||||||
|
_candidateStartTime = _earliestActiveDownTime();
|
||||||
|
_candidateFocalPoint = _activeFocalPoint();
|
||||||
|
} else if (_activeTouches.length > 2) {
|
||||||
|
_candidateInvalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointerMove(int pointer, Offset position) {
|
||||||
|
final touch = _activeTouches[pointer];
|
||||||
|
if (touch == null) return;
|
||||||
|
|
||||||
|
touch.current = position;
|
||||||
|
if ((position - touch.start).distance > tapSlop) {
|
||||||
|
_candidateInvalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pointerUp(int pointer, Offset position) {
|
||||||
|
final touch = _activeTouches[pointer];
|
||||||
|
if (touch == null) return false;
|
||||||
|
|
||||||
|
touch.current = position;
|
||||||
|
if ((position - touch.start).distance > tapSlop) {
|
||||||
|
_candidateInvalid = true;
|
||||||
|
}
|
||||||
|
_activeTouches.remove(pointer);
|
||||||
|
|
||||||
|
if (_activeTouches.isNotEmpty) return false;
|
||||||
|
|
||||||
|
final startTime = _candidateStartTime;
|
||||||
|
final focalPoint = _candidateFocalPoint;
|
||||||
|
final isTap =
|
||||||
|
_candidateActive &&
|
||||||
|
!_candidateInvalid &&
|
||||||
|
startTime != null &&
|
||||||
|
focalPoint != null &&
|
||||||
|
_now().difference(startTime) <= tapTimeout;
|
||||||
|
|
||||||
|
_clearCandidate();
|
||||||
|
if (!isTap) return false;
|
||||||
|
return _recordTwoFingerTap(focalPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointerCancel(int pointer) {
|
||||||
|
_activeTouches.remove(pointer);
|
||||||
|
_candidateInvalid = true;
|
||||||
|
if (_activeTouches.isEmpty) _clearCandidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetSeries() {
|
||||||
|
_lastTapTime = null;
|
||||||
|
_lastTapFocalPoint = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime _earliestActiveDownTime() {
|
||||||
|
return _activeTouches.values.map((touch) => touch.downTime).reduce((a, b) => a.isBefore(b) ? a : b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Offset _activeFocalPoint() {
|
||||||
|
var sum = Offset.zero;
|
||||||
|
for (final touch in _activeTouches.values) {
|
||||||
|
sum += touch.current;
|
||||||
|
}
|
||||||
|
return sum / _activeTouches.length.toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _recordTwoFingerTap(Offset focalPoint) {
|
||||||
|
final now = _now();
|
||||||
|
final lastTapTime = _lastTapTime;
|
||||||
|
final lastTapFocalPoint = _lastTapFocalPoint;
|
||||||
|
final isDoubleTap =
|
||||||
|
lastTapTime != null &&
|
||||||
|
lastTapFocalPoint != null &&
|
||||||
|
now.difference(lastTapTime) <= doubleTapTimeout &&
|
||||||
|
(focalPoint - lastTapFocalPoint).distance <= doubleTapSlop;
|
||||||
|
|
||||||
|
if (isDoubleTap) {
|
||||||
|
resetSeries();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastTapTime = now;
|
||||||
|
_lastTapFocalPoint = focalPoint;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _clearCandidate() {
|
||||||
|
_candidateStartTime = null;
|
||||||
|
_candidateFocalPoint = null;
|
||||||
|
_candidateActive = false;
|
||||||
|
_candidateInvalid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TrackedTouch {
|
||||||
|
_TrackedTouch({required this.start, required this.current, required this.downTime});
|
||||||
|
|
||||||
|
final Offset start;
|
||||||
|
Offset current;
|
||||||
|
final DateTime downTime;
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ class TrackControlsState {
|
|||||||
/// to show estimated file sizes per preset (bitrate × duration).
|
/// to show estimated file sizes per preset (bitrate × duration).
|
||||||
final int? sourceDurationMs;
|
final int? sourceDurationMs;
|
||||||
final int boxFitMode;
|
final int boxFitMode;
|
||||||
|
final double videoZoomScale;
|
||||||
final int audioSyncOffset;
|
final int audioSyncOffset;
|
||||||
final int subtitleSyncOffset;
|
final int subtitleSyncOffset;
|
||||||
final bool isRotationLocked;
|
final bool isRotationLocked;
|
||||||
@@ -32,6 +33,8 @@ class TrackControlsState {
|
|||||||
final bool isAlwaysOnTop;
|
final bool isAlwaysOnTop;
|
||||||
final VoidCallback? onTogglePIPMode;
|
final VoidCallback? onTogglePIPMode;
|
||||||
final VoidCallback? onCycleBoxFitMode;
|
final VoidCallback? onCycleBoxFitMode;
|
||||||
|
final ValueChanged<double>? onVideoZoomChanged;
|
||||||
|
final VoidCallback? onResetVideoZoom;
|
||||||
final VoidCallback? onToggleRotationLock;
|
final VoidCallback? onToggleRotationLock;
|
||||||
final VoidCallback? onToggleScreenLock;
|
final VoidCallback? onToggleScreenLock;
|
||||||
final VoidCallback? onToggleFullscreen;
|
final VoidCallback? onToggleFullscreen;
|
||||||
@@ -80,6 +83,7 @@ class TrackControlsState {
|
|||||||
this.sourcePartId,
|
this.sourcePartId,
|
||||||
this.sourceDurationMs,
|
this.sourceDurationMs,
|
||||||
this.boxFitMode = 0,
|
this.boxFitMode = 0,
|
||||||
|
this.videoZoomScale = 1.0,
|
||||||
this.audioSyncOffset = 0,
|
this.audioSyncOffset = 0,
|
||||||
this.subtitleSyncOffset = 0,
|
this.subtitleSyncOffset = 0,
|
||||||
this.isRotationLocked = false,
|
this.isRotationLocked = false,
|
||||||
@@ -88,6 +92,8 @@ class TrackControlsState {
|
|||||||
this.isAlwaysOnTop = false,
|
this.isAlwaysOnTop = false,
|
||||||
this.onTogglePIPMode,
|
this.onTogglePIPMode,
|
||||||
this.onCycleBoxFitMode,
|
this.onCycleBoxFitMode,
|
||||||
|
this.onVideoZoomChanged,
|
||||||
|
this.onResetVideoZoom,
|
||||||
this.onToggleRotationLock,
|
this.onToggleRotationLock,
|
||||||
this.onToggleScreenLock,
|
this.onToggleScreenLock,
|
||||||
this.onToggleFullscreen,
|
this.onToggleFullscreen,
|
||||||
|
|||||||
@@ -156,6 +156,9 @@ extension _PlexVideoControlsKeyEventMethods on _PlexVideoControlsState {
|
|||||||
onNextEpisode: widget.onNext,
|
onNextEpisode: widget.onNext,
|
||||||
onPreviousEpisode: widget.onPrevious,
|
onPreviousEpisode: widget.onPrevious,
|
||||||
onScreenshot: _showScreenshotToast,
|
onScreenshot: _showScreenshotToast,
|
||||||
|
onZoomIn: widget.onZoomIn,
|
||||||
|
onZoomOut: widget.onZoomOut,
|
||||||
|
onZoomReset: widget.onResetVideoZoom,
|
||||||
currentPositionEpoch: widget.currentPositionEpoch,
|
currentPositionEpoch: widget.currentPositionEpoch,
|
||||||
onLiveSeek: widget.onLiveSeek,
|
onLiveSeek: widget.onLiveSeek,
|
||||||
);
|
);
|
||||||
@@ -304,12 +307,14 @@ extension _PlexVideoControlsKeyEventMethods on _PlexVideoControlsState {
|
|||||||
onNextEpisode: widget.onNext,
|
onNextEpisode: widget.onNext,
|
||||||
onPreviousEpisode: widget.onPrevious,
|
onPreviousEpisode: widget.onPrevious,
|
||||||
onScreenshot: _showScreenshotToast,
|
onScreenshot: _showScreenshotToast,
|
||||||
|
onZoomIn: widget.onZoomIn,
|
||||||
|
onZoomOut: widget.onZoomOut,
|
||||||
|
onZoomReset: widget.onResetVideoZoom,
|
||||||
currentPositionEpoch: widget.currentPositionEpoch,
|
currentPositionEpoch: widget.currentPositionEpoch,
|
||||||
onLiveSeek: widget.onLiveSeek,
|
onLiveSeek: widget.onLiveSeek,
|
||||||
onSeekRequested: widget.onSeekRequested,
|
onSeekRequested: widget.onSeekRequested,
|
||||||
);
|
);
|
||||||
// Let non-navigation keys (volume, etc.) pass through to the OS.
|
if (!event.logicalKey.isNavigationKey) return result;
|
||||||
if (!event.logicalKey.isNavigationKey) return KeyEventResult.ignored;
|
|
||||||
// Never return .ignored for navigation keys — prevent leaking to previous routes.
|
// Never return .ignored for navigation keys — prevent leaking to previous routes.
|
||||||
return result == KeyEventResult.ignored ? KeyEventResult.handled : result;
|
return result == KeyEventResult.ignored ? KeyEventResult.handled : result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
part of '../video_controls.dart';
|
part of '../video_controls.dart';
|
||||||
|
|
||||||
extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
|
extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
|
||||||
|
static const Duration _touchTapSuppressionPadding = Duration(milliseconds: 80);
|
||||||
|
|
||||||
void _onRateChanged(double newRate) {
|
void _onRateChanged(double newRate) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
if (_isLongPressing) return;
|
if (_isLongPressing) return;
|
||||||
@@ -95,9 +97,52 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
|
|||||||
unawaited(_seekToPosition(position));
|
unawaited(_seekToPosition(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get _isTouchTapSuppressed {
|
||||||
|
final until = _suppressTouchTapUntil;
|
||||||
|
if (until == null) return false;
|
||||||
|
if (DateTime.now().isAfter(until)) {
|
||||||
|
_suppressTouchTapUntil = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _suppressTouchTaps() {
|
||||||
|
_singleTapTimer?.cancel();
|
||||||
|
_singleTapTimer = null;
|
||||||
|
_suppressTouchTapUntil = DateTime.now().add(kDoubleTapTimeout + _touchTapSuppressionPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleTouchPointerDown(PointerDownEvent event) {
|
||||||
|
if (event.kind != PointerDeviceKind.touch) return;
|
||||||
|
_twoFingerDoubleTapTracker.pointerDown(event.pointer, event.position);
|
||||||
|
if (_twoFingerDoubleTapTracker.isChordActive) _suppressTouchTaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleTouchPointerMove(PointerMoveEvent event) {
|
||||||
|
if (event.kind != PointerDeviceKind.touch) return;
|
||||||
|
_twoFingerDoubleTapTracker.pointerMove(event.pointer, event.position);
|
||||||
|
if (_twoFingerDoubleTapTracker.isChordActive) _suppressTouchTaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleTouchPointerUp(PointerUpEvent event) {
|
||||||
|
if (event.kind != PointerDeviceKind.touch) return;
|
||||||
|
final isResetGesture = _twoFingerDoubleTapTracker.pointerUp(event.pointer, event.position);
|
||||||
|
if (_isTouchTapSuppressed || isResetGesture) _suppressTouchTaps();
|
||||||
|
if (isResetGesture) widget.onResetVideoZoom?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleTouchPointerCancel(PointerCancelEvent event) {
|
||||||
|
if (event.kind != PointerDeviceKind.touch) return;
|
||||||
|
_twoFingerDoubleTapTracker.pointerCancel(event.pointer);
|
||||||
|
if (_twoFingerDoubleTapTracker.isChordActive) _suppressTouchTaps();
|
||||||
|
}
|
||||||
|
|
||||||
/// Timing-based double-click detection: avoids `onDoubleTap`'s ~300 ms
|
/// Timing-based double-click detection: avoids `onDoubleTap`'s ~300 ms
|
||||||
/// tap-resolution delay and the arena competition it introduces.
|
/// tap-resolution delay and the arena competition it introduces.
|
||||||
void _handleOuterTap() {
|
void _handleOuterTap() {
|
||||||
|
if (PlatformDetector.isMobile(context) && _isTouchTapSuppressed) return;
|
||||||
|
|
||||||
if (widget.canControl && _clickVideoTogglesPlayback) {
|
if (widget.canControl && _clickVideoTogglesPlayback) {
|
||||||
_playOrPause();
|
_playOrPause();
|
||||||
} else {
|
} else {
|
||||||
@@ -117,6 +162,8 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
|
|||||||
|
|
||||||
/// Handle tap in skip zone with custom double-tap detection
|
/// Handle tap in skip zone with custom double-tap detection
|
||||||
void _handleTapInSkipZone({required bool isForward}) {
|
void _handleTapInSkipZone({required bool isForward}) {
|
||||||
|
if (_isTouchTapSuppressed) return;
|
||||||
|
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
|
||||||
// Cancel any pending single-tap action
|
// Cancel any pending single-tap action
|
||||||
@@ -253,6 +300,8 @@ extension _PlexVideoControlsPlaybackInputMethods on _PlexVideoControlsState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_isTouchTapSuppressed) return;
|
||||||
|
|
||||||
final skipZone = mobileSkipZoneForTap(position: details.localPosition, size: size);
|
final skipZone = mobileSkipZoneForTap(position: details.localPosition, size: size);
|
||||||
if (skipZone != null) {
|
if (skipZone != null) {
|
||||||
_handleTapInSkipZone(isForward: skipZone);
|
_handleTapInSkipZone(isForward: skipZone);
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ extension _PlexVideoControlsTrackMethods on _PlexVideoControlsState {
|
|||||||
sourcePartId: canSwitchSourceSubtitles ? widget.sourcePartId : null,
|
sourcePartId: canSwitchSourceSubtitles ? widget.sourcePartId : null,
|
||||||
sourceDurationMs: widget.metadata.durationMs,
|
sourceDurationMs: widget.metadata.durationMs,
|
||||||
boxFitMode: widget.boxFitMode,
|
boxFitMode: widget.boxFitMode,
|
||||||
|
videoZoomScale: widget.videoZoomScale,
|
||||||
audioSyncOffset: _audioSyncOffset,
|
audioSyncOffset: _audioSyncOffset,
|
||||||
subtitleSyncOffset: _subtitleSyncOffset,
|
subtitleSyncOffset: _subtitleSyncOffset,
|
||||||
isRotationLocked: _isRotationLocked,
|
isRotationLocked: _isRotationLocked,
|
||||||
@@ -109,6 +110,8 @@ extension _PlexVideoControlsTrackMethods on _PlexVideoControlsState {
|
|||||||
isAlwaysOnTop: _isAlwaysOnTop,
|
isAlwaysOnTop: _isAlwaysOnTop,
|
||||||
onTogglePIPMode: (_isPipSupported && !PlatformDetector.isTV()) ? widget.onTogglePIPMode : null,
|
onTogglePIPMode: (_isPipSupported && !PlatformDetector.isTV()) ? widget.onTogglePIPMode : null,
|
||||||
onCycleBoxFitMode: widget.onCycleBoxFitMode,
|
onCycleBoxFitMode: widget.onCycleBoxFitMode,
|
||||||
|
onVideoZoomChanged: widget.onVideoZoomChanged,
|
||||||
|
onResetVideoZoom: widget.onResetVideoZoom,
|
||||||
onToggleRotationLock: _toggleRotationLock,
|
onToggleRotationLock: _toggleRotationLock,
|
||||||
onToggleScreenLock: _toggleScreenLock,
|
onToggleScreenLock: _toggleScreenLock,
|
||||||
onToggleFullscreen: _toggleFullscreen,
|
onToggleFullscreen: _toggleFullscreen,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import '../../../services/file_picker_service.dart';
|
|||||||
import '../../../services/settings_service.dart';
|
import '../../../services/settings_service.dart';
|
||||||
import '../../../services/shader_service.dart';
|
import '../../../services/shader_service.dart';
|
||||||
import '../../../services/sleep_timer_service.dart';
|
import '../../../services/sleep_timer_service.dart';
|
||||||
|
import '../../../services/video_filter_manager.dart';
|
||||||
import '../../../focus/focusable_wrapper.dart';
|
import '../../../focus/focusable_wrapper.dart';
|
||||||
import '../../../utils/dialogs.dart';
|
import '../../../utils/dialogs.dart';
|
||||||
import '../../../utils/formatters.dart';
|
import '../../../utils/formatters.dart';
|
||||||
@@ -35,7 +36,18 @@ import '../../../i18n/strings.g.dart';
|
|||||||
import 'base_video_control_sheet.dart';
|
import 'base_video_control_sheet.dart';
|
||||||
import 'version_quality_sheet.dart';
|
import 'version_quality_sheet.dart';
|
||||||
|
|
||||||
enum _SettingsView { menu, speed, versionQuality, sleep, audioSync, subtitleSync, audioDevice, shader, dvConversion }
|
enum _SettingsView {
|
||||||
|
menu,
|
||||||
|
speed,
|
||||||
|
zoom,
|
||||||
|
versionQuality,
|
||||||
|
sleep,
|
||||||
|
audioSync,
|
||||||
|
subtitleSync,
|
||||||
|
audioDevice,
|
||||||
|
shader,
|
||||||
|
dvConversion,
|
||||||
|
}
|
||||||
|
|
||||||
class _SettingsMenuItem extends StatelessWidget {
|
class _SettingsMenuItem extends StatelessWidget {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
@@ -115,6 +127,9 @@ class VideoSettingsSheet extends StatefulWidget {
|
|||||||
final Player player;
|
final Player player;
|
||||||
final int audioSyncOffset;
|
final int audioSyncOffset;
|
||||||
final int subtitleSyncOffset;
|
final int subtitleSyncOffset;
|
||||||
|
final double videoZoomScale;
|
||||||
|
final ValueChanged<double>? onVideoZoomChanged;
|
||||||
|
final VoidCallback? onResetVideoZoom;
|
||||||
|
|
||||||
/// Whether the user can control playback (false hides speed option in host-only mode).
|
/// Whether the user can control playback (false hides speed option in host-only mode).
|
||||||
final bool canControl;
|
final bool canControl;
|
||||||
@@ -157,6 +172,9 @@ class VideoSettingsSheet extends StatefulWidget {
|
|||||||
required this.player,
|
required this.player,
|
||||||
required this.audioSyncOffset,
|
required this.audioSyncOffset,
|
||||||
required this.subtitleSyncOffset,
|
required this.subtitleSyncOffset,
|
||||||
|
this.videoZoomScale = 1.0,
|
||||||
|
this.onVideoZoomChanged,
|
||||||
|
this.onResetVideoZoom,
|
||||||
this.canControl = true,
|
this.canControl = true,
|
||||||
this.isLive = false,
|
this.isLive = false,
|
||||||
this.availableVersions = const [],
|
this.availableVersions = const [],
|
||||||
@@ -183,6 +201,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
_SettingsView _currentView = _SettingsView.menu;
|
_SettingsView _currentView = _SettingsView.menu;
|
||||||
late int _audioSyncOffset;
|
late int _audioSyncOffset;
|
||||||
late int _subtitleSyncOffset;
|
late int _subtitleSyncOffset;
|
||||||
|
late double _zoomScale;
|
||||||
String _dvConversionMode = 'auto';
|
String _dvConversionMode = 'auto';
|
||||||
|
|
||||||
bool get _showDebugDvConversionMode {
|
bool get _showDebugDvConversionMode {
|
||||||
@@ -196,9 +215,18 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_audioSyncOffset = widget.audioSyncOffset;
|
_audioSyncOffset = widget.audioSyncOffset;
|
||||||
_subtitleSyncOffset = widget.subtitleSyncOffset;
|
_subtitleSyncOffset = widget.subtitleSyncOffset;
|
||||||
|
_zoomScale = widget.videoZoomScale;
|
||||||
_loadDebugDvConversionMode();
|
_loadDebugDvConversionMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant VideoSettingsSheet oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if ((widget.videoZoomScale - oldWidget.videoZoomScale).abs() > 0.0001) {
|
||||||
|
_zoomScale = widget.videoZoomScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadDebugDvConversionMode() async {
|
Future<void> _loadDebugDvConversionMode() async {
|
||||||
if (!_showDebugDvConversionMode) return;
|
if (!_showDebugDvConversionMode) return;
|
||||||
final dvConversionMode = await widget.player.getProperty('dv-conversion-mode');
|
final dvConversionMode = await widget.player.getProperty('dv-conversion-mode');
|
||||||
@@ -292,6 +320,8 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
return t.videoControls.settingsButton;
|
return t.videoControls.settingsButton;
|
||||||
case _SettingsView.speed:
|
case _SettingsView.speed:
|
||||||
return t.videoSettings.playbackSpeed;
|
return t.videoSettings.playbackSpeed;
|
||||||
|
case _SettingsView.zoom:
|
||||||
|
return t.videoSettings.zoom;
|
||||||
case _SettingsView.versionQuality:
|
case _SettingsView.versionQuality:
|
||||||
return _versionQualityTitle();
|
return _versionQualityTitle();
|
||||||
case _SettingsView.sleep:
|
case _SettingsView.sleep:
|
||||||
@@ -315,6 +345,8 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
return Symbols.tune_rounded;
|
return Symbols.tune_rounded;
|
||||||
case _SettingsView.speed:
|
case _SettingsView.speed:
|
||||||
return Symbols.speed_rounded;
|
return Symbols.speed_rounded;
|
||||||
|
case _SettingsView.zoom:
|
||||||
|
return Symbols.zoom_in_rounded;
|
||||||
case _SettingsView.versionQuality:
|
case _SettingsView.versionQuality:
|
||||||
return Symbols.art_track;
|
return Symbols.art_track;
|
||||||
case _SettingsView.sleep:
|
case _SettingsView.sleep:
|
||||||
@@ -357,6 +389,28 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
return 'Active (${formatDurationWithSeconds(remaining)})';
|
return 'Active (${formatDurationWithSeconds(remaining)})';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatZoomScale(double scale) => '${(scale * 100).round()}%';
|
||||||
|
|
||||||
|
void _setZoomScale(double scale) {
|
||||||
|
final next = scale.clamp(VideoFilterManager.minZoomScale, VideoFilterManager.maxZoomScale).toDouble();
|
||||||
|
setState(() {
|
||||||
|
_zoomScale = next;
|
||||||
|
});
|
||||||
|
widget.onVideoZoomChanged?.call(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetZoomScale() {
|
||||||
|
setState(() {
|
||||||
|
_zoomScale = 1.0;
|
||||||
|
});
|
||||||
|
final reset = widget.onResetVideoZoom;
|
||||||
|
if (reset != null) {
|
||||||
|
reset();
|
||||||
|
} else {
|
||||||
|
widget.onVideoZoomChanged?.call(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool get _hasVersionQuality {
|
bool get _hasVersionQuality {
|
||||||
return (widget.availableVersions.length > 1 || widget.serverSupportsTranscoding) &&
|
return (widget.availableVersions.length > 1 || widget.serverSupportsTranscoding) &&
|
||||||
(widget.onVersionSelected != null || widget.onQualitySelected != null);
|
(widget.onVersionSelected != null || widget.onQualitySelected != null);
|
||||||
@@ -406,6 +460,15 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
if (widget.onVideoZoomChanged != null || widget.onResetVideoZoom != null)
|
||||||
|
_SettingsMenuItem(
|
||||||
|
icon: Symbols.zoom_in_rounded,
|
||||||
|
title: t.videoSettings.zoom,
|
||||||
|
valueText: _formatZoomScale(_zoomScale),
|
||||||
|
isHighlighted: (_zoomScale - 1.0).abs() > 0.0001,
|
||||||
|
onTap: () => _navigateTo(_SettingsView.zoom),
|
||||||
|
),
|
||||||
|
|
||||||
if (_hasVersionQuality)
|
if (_hasVersionQuality)
|
||||||
_SettingsMenuItem(
|
_SettingsMenuItem(
|
||||||
icon: Symbols.art_track,
|
icon: Symbols.art_track,
|
||||||
@@ -633,6 +696,32 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildZoomView() {
|
||||||
|
const zoomPresets = [0.5, 0.75, 0.9, 1.0, 1.1, 1.2, 1.3, 1.5, 1.75, 2.0];
|
||||||
|
final primary = Theme.of(context).colorScheme.primary;
|
||||||
|
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
FocusableListTile(
|
||||||
|
leading: AppIcon(Symbols.restart_alt_rounded, fill: 1, color: tokens(context).textMuted),
|
||||||
|
title: Text(t.common.reset),
|
||||||
|
onTap: _resetZoomScale,
|
||||||
|
),
|
||||||
|
for (final scale in zoomPresets)
|
||||||
|
FocusableListTile(
|
||||||
|
title: Text(
|
||||||
|
_formatZoomScale(scale),
|
||||||
|
style: TextStyle(color: (_zoomScale - scale).abs() < 0.005 ? primary : null),
|
||||||
|
),
|
||||||
|
trailing: (_zoomScale - scale).abs() < 0.005
|
||||||
|
? AppIcon(Symbols.check_rounded, fill: 1, color: primary)
|
||||||
|
: null,
|
||||||
|
onTap: () => _setZoomScale(scale),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildSleepView() {
|
Widget _buildSleepView() {
|
||||||
final sleepTimer = SleepTimerService();
|
final sleepTimer = SleepTimerService();
|
||||||
|
|
||||||
@@ -894,9 +983,10 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final sleepTimer = SleepTimerService();
|
final sleepTimer = SleepTimerService();
|
||||||
final isShaderActive = widget.shaderService != null && widget.shaderService!.currentPreset.isEnabled;
|
final isShaderActive = widget.shaderService != null && widget.shaderService!.currentPreset.isEnabled;
|
||||||
|
final isZoomActive = (_zoomScale - 1.0).abs() > 0.0001;
|
||||||
final isIconActive =
|
final isIconActive =
|
||||||
_currentView == _SettingsView.menu &&
|
_currentView == _SettingsView.menu &&
|
||||||
(sleepTimer.isActive || _audioSyncOffset != 0 || _subtitleSyncOffset != 0 || isShaderActive);
|
(sleepTimer.isActive || _audioSyncOffset != 0 || _subtitleSyncOffset != 0 || isShaderActive || isZoomActive);
|
||||||
|
|
||||||
return BaseVideoControlSheet(
|
return BaseVideoControlSheet(
|
||||||
title: _getTitle(),
|
title: _getTitle(),
|
||||||
@@ -913,6 +1003,8 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
|||||||
return _buildMenuView();
|
return _buildMenuView();
|
||||||
case _SettingsView.speed:
|
case _SettingsView.speed:
|
||||||
return _buildSpeedView();
|
return _buildSpeedView();
|
||||||
|
case _SettingsView.zoom:
|
||||||
|
return _buildZoomView();
|
||||||
case _SettingsView.versionQuality:
|
case _SettingsView.versionQuality:
|
||||||
return _buildVersionQualityView();
|
return _buildVersionQualityView();
|
||||||
case _SettingsView.sleep:
|
case _SettingsView.sleep:
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
import 'dart:async' show StreamSubscription, Timer, unawaited;
|
import 'dart:async' show StreamSubscription, Timer, unawaited;
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart' show PointerSignalEvent, PointerScrollEvent;
|
import 'package:flutter/gestures.dart'
|
||||||
|
show
|
||||||
|
PointerCancelEvent,
|
||||||
|
PointerDeviceKind,
|
||||||
|
PointerDownEvent,
|
||||||
|
PointerMoveEvent,
|
||||||
|
PointerScrollEvent,
|
||||||
|
PointerSignalEvent,
|
||||||
|
PointerUpEvent,
|
||||||
|
kDoubleTapTimeout;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:plezy/widgets/app_icon.dart';
|
import 'package:plezy/widgets/app_icon.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
@@ -54,6 +63,7 @@ import '../../i18n/strings.g.dart';
|
|||||||
import '../../focus/input_mode_tracker.dart';
|
import '../../focus/input_mode_tracker.dart';
|
||||||
import 'models/track_controls_state.dart';
|
import 'models/track_controls_state.dart';
|
||||||
import 'widgets/double_tap_feedback.dart';
|
import 'widgets/double_tap_feedback.dart';
|
||||||
|
import 'helpers/two_finger_double_tap_tracker.dart';
|
||||||
import 'widgets/linux_keep_alive.dart';
|
import 'widgets/linux_keep_alive.dart';
|
||||||
import 'widgets/mobile_skip_zones.dart';
|
import 'widgets/mobile_skip_zones.dart';
|
||||||
import 'widgets/skip_marker_button.dart';
|
import 'widgets/skip_marker_button.dart';
|
||||||
@@ -150,8 +160,13 @@ class PlexVideoControls extends StatefulWidget {
|
|||||||
final int? selectedSubtitleStreamId;
|
final int? selectedSubtitleStreamId;
|
||||||
final int? sourcePartId;
|
final int? sourcePartId;
|
||||||
final int boxFitMode;
|
final int boxFitMode;
|
||||||
|
final double videoZoomScale;
|
||||||
final VoidCallback? onTogglePIPMode;
|
final VoidCallback? onTogglePIPMode;
|
||||||
final VoidCallback? onCycleBoxFitMode;
|
final VoidCallback? onCycleBoxFitMode;
|
||||||
|
final ValueChanged<double>? onVideoZoomChanged;
|
||||||
|
final VoidCallback? onZoomIn;
|
||||||
|
final VoidCallback? onZoomOut;
|
||||||
|
final VoidCallback? onResetVideoZoom;
|
||||||
final VoidCallback? onCycleAudioTrack;
|
final VoidCallback? onCycleAudioTrack;
|
||||||
final VoidCallback? onCycleSubtitleTrack;
|
final VoidCallback? onCycleSubtitleTrack;
|
||||||
final Function(AudioTrack)? onAudioTrackChanged;
|
final Function(AudioTrack)? onAudioTrackChanged;
|
||||||
@@ -247,8 +262,13 @@ class PlexVideoControls extends StatefulWidget {
|
|||||||
this.selectedSubtitleStreamId,
|
this.selectedSubtitleStreamId,
|
||||||
this.sourcePartId,
|
this.sourcePartId,
|
||||||
this.boxFitMode = 0,
|
this.boxFitMode = 0,
|
||||||
|
this.videoZoomScale = 1.0,
|
||||||
this.onTogglePIPMode,
|
this.onTogglePIPMode,
|
||||||
this.onCycleBoxFitMode,
|
this.onCycleBoxFitMode,
|
||||||
|
this.onVideoZoomChanged,
|
||||||
|
this.onZoomIn,
|
||||||
|
this.onZoomOut,
|
||||||
|
this.onResetVideoZoom,
|
||||||
this.onCycleAudioTrack,
|
this.onCycleAudioTrack,
|
||||||
this.onCycleSubtitleTrack,
|
this.onCycleSubtitleTrack,
|
||||||
this.onAudioTrackChanged,
|
this.onAudioTrackChanged,
|
||||||
@@ -324,6 +344,8 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
|
|||||||
bool _lastSkipTapWasForward = true;
|
bool _lastSkipTapWasForward = true;
|
||||||
DateTime? _lastSkipActionTime; // Debounce: prevents double-tap counting as 2 skips
|
DateTime? _lastSkipActionTime; // Debounce: prevents double-tap counting as 2 skips
|
||||||
Timer? _singleTapTimer; // Timer for delayed single-tap action (toggle controls)
|
Timer? _singleTapTimer; // Timer for delayed single-tap action (toggle controls)
|
||||||
|
final TwoFingerDoubleTapTracker _twoFingerDoubleTapTracker = TwoFingerDoubleTapTracker();
|
||||||
|
DateTime? _suppressTouchTapUntil;
|
||||||
// Seek throttle
|
// Seek throttle
|
||||||
late final Throttle _seekThrottle;
|
late final Throttle _seekThrottle;
|
||||||
// Current marker state
|
// Current marker state
|
||||||
@@ -553,6 +575,10 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
|
|||||||
onKeyEvent: (node, event) => _handleControlsKeyEvent(event, isMobile),
|
onKeyEvent: (node, event) => _handleControlsKeyEvent(event, isMobile),
|
||||||
child: Listener(
|
child: Listener(
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onPointerDown: isMobile ? _handleTouchPointerDown : null,
|
||||||
|
onPointerMove: isMobile ? _handleTouchPointerMove : null,
|
||||||
|
onPointerUp: isMobile ? _handleTouchPointerUp : null,
|
||||||
|
onPointerCancel: isMobile ? _handleTouchPointerCancel : null,
|
||||||
onPointerSignal: _handlePointerSignal,
|
onPointerSignal: _handlePointerSignal,
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: (_showControls || _forceShowControls) ? SystemMouseCursors.basic : SystemMouseCursors.none,
|
cursor: (_showControls || _forceShowControls) ? SystemMouseCursors.basic : SystemMouseCursors.none,
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class TrackChapterControls extends StatelessWidget {
|
|||||||
bool get serverSupportsTranscoding => trackControlsState.serverSupportsTranscoding;
|
bool get serverSupportsTranscoding => trackControlsState.serverSupportsTranscoding;
|
||||||
ValueChanged<TranscodeQualityPreset>? get onSwitchQualityPreset => trackControlsState.onSwitchQualityPreset;
|
ValueChanged<TranscodeQualityPreset>? get onSwitchQualityPreset => trackControlsState.onSwitchQualityPreset;
|
||||||
int get boxFitMode => trackControlsState.boxFitMode;
|
int get boxFitMode => trackControlsState.boxFitMode;
|
||||||
|
double get videoZoomScale => trackControlsState.videoZoomScale;
|
||||||
int get audioSyncOffset => trackControlsState.audioSyncOffset;
|
int get audioSyncOffset => trackControlsState.audioSyncOffset;
|
||||||
int get subtitleSyncOffset => trackControlsState.subtitleSyncOffset;
|
int get subtitleSyncOffset => trackControlsState.subtitleSyncOffset;
|
||||||
bool get isRotationLocked => trackControlsState.isRotationLocked;
|
bool get isRotationLocked => trackControlsState.isRotationLocked;
|
||||||
@@ -78,6 +79,8 @@ class TrackChapterControls extends StatelessWidget {
|
|||||||
bool get isAlwaysOnTop => trackControlsState.isAlwaysOnTop;
|
bool get isAlwaysOnTop => trackControlsState.isAlwaysOnTop;
|
||||||
VoidCallback? get onTogglePIPMode => trackControlsState.onTogglePIPMode;
|
VoidCallback? get onTogglePIPMode => trackControlsState.onTogglePIPMode;
|
||||||
VoidCallback? get onCycleBoxFitMode => trackControlsState.onCycleBoxFitMode;
|
VoidCallback? get onCycleBoxFitMode => trackControlsState.onCycleBoxFitMode;
|
||||||
|
ValueChanged<double>? get onVideoZoomChanged => trackControlsState.onVideoZoomChanged;
|
||||||
|
VoidCallback? get onResetVideoZoom => trackControlsState.onResetVideoZoom;
|
||||||
VoidCallback? get onToggleRotationLock => trackControlsState.onToggleRotationLock;
|
VoidCallback? get onToggleRotationLock => trackControlsState.onToggleRotationLock;
|
||||||
VoidCallback? get onToggleScreenLock => trackControlsState.onToggleScreenLock;
|
VoidCallback? get onToggleScreenLock => trackControlsState.onToggleScreenLock;
|
||||||
VoidCallback? get onToggleFullscreen => trackControlsState.onToggleFullscreen;
|
VoidCallback? get onToggleFullscreen => trackControlsState.onToggleFullscreen;
|
||||||
@@ -196,7 +199,13 @@ class TrackChapterControls extends StatelessWidget {
|
|||||||
final sleepTimer = SleepTimerService();
|
final sleepTimer = SleepTimerService();
|
||||||
final isShaderActive =
|
final isShaderActive =
|
||||||
shaderService != null && shaderService!.isSupported && shaderService!.currentPreset.isEnabled;
|
shaderService != null && shaderService!.isSupported && shaderService!.currentPreset.isEnabled;
|
||||||
final isActive = sleepTimer.isActive || audioSyncOffset != 0 || subtitleSyncOffset != 0 || isShaderActive;
|
final isZoomActive = (videoZoomScale - 1.0).abs() > 0.0001;
|
||||||
|
final isActive =
|
||||||
|
sleepTimer.isActive ||
|
||||||
|
audioSyncOffset != 0 ||
|
||||||
|
subtitleSyncOffset != 0 ||
|
||||||
|
isShaderActive ||
|
||||||
|
isZoomActive;
|
||||||
return _buildTrackButton(
|
return _buildTrackButton(
|
||||||
buttonIndex: 0,
|
buttonIndex: 0,
|
||||||
icon: Symbols.tune_rounded,
|
icon: Symbols.tune_rounded,
|
||||||
@@ -213,6 +222,9 @@ class TrackChapterControls extends StatelessWidget {
|
|||||||
player: player,
|
player: player,
|
||||||
audioSyncOffset: audioSyncOffset,
|
audioSyncOffset: audioSyncOffset,
|
||||||
subtitleSyncOffset: subtitleSyncOffset,
|
subtitleSyncOffset: subtitleSyncOffset,
|
||||||
|
videoZoomScale: videoZoomScale,
|
||||||
|
onVideoZoomChanged: onVideoZoomChanged,
|
||||||
|
onResetVideoZoom: onResetVideoZoom,
|
||||||
canControl: canControl,
|
canControl: canControl,
|
||||||
isLive: isLive,
|
isLive: isLive,
|
||||||
availableVersions: availableVersions,
|
availableVersions: availableVersions,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
import 'package:plezy/mpv/mpv.dart';
|
import 'package:plezy/mpv/mpv.dart';
|
||||||
import 'package:plezy/services/keyboard_shortcuts_service.dart';
|
import 'package:plezy/services/keyboard_shortcuts_service.dart';
|
||||||
import 'package:plezy/services/settings_service.dart';
|
import 'package:plezy/services/settings_service.dart';
|
||||||
|
import 'package:plezy/services/video_filter_manager.dart';
|
||||||
|
|
||||||
import '../test_helpers/prefs.dart';
|
import '../test_helpers/prefs.dart';
|
||||||
|
|
||||||
@@ -13,7 +14,7 @@ void main() {
|
|||||||
SettingsService.resetForTesting();
|
SettingsService.resetForTesting();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Ctrl+S takes a screenshot and shows feedback', (tester) async {
|
testWidgets('Ctrl+S takes a screenshot once while held', (tester) async {
|
||||||
final service = await KeyboardShortcutsService.getInstance();
|
final service = await KeyboardShortcutsService.getInstance();
|
||||||
addTearDown(service.dispose);
|
addTearDown(service.dispose);
|
||||||
final player = _FakePlayer();
|
final player = _FakePlayer();
|
||||||
@@ -35,15 +36,197 @@ void main() {
|
|||||||
null,
|
null,
|
||||||
onScreenshot: () => feedbackCount++,
|
onScreenshot: () => feedbackCount++,
|
||||||
);
|
);
|
||||||
|
final repeatResult = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyRepeatEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.keyS,
|
||||||
|
logicalKey: LogicalKeyboardKey.keyS,
|
||||||
|
timeStamp: Duration(milliseconds: 30),
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onScreenshot: () => feedbackCount++,
|
||||||
|
);
|
||||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft);
|
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
expect(result, KeyEventResult.handled);
|
expect(result, KeyEventResult.handled);
|
||||||
|
expect(repeatResult, KeyEventResult.handled);
|
||||||
expect(player.commands, [
|
expect(player.commands, [
|
||||||
['screenshot', 'subtitles'],
|
['screenshot', 'subtitles'],
|
||||||
]);
|
]);
|
||||||
expect(feedbackCount, 1);
|
expect(feedbackCount, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Alt+Plus triggers zoom in callback', (tester) async {
|
||||||
|
final service = await KeyboardShortcutsService.getInstance();
|
||||||
|
addTearDown(service.dispose);
|
||||||
|
final player = _FakePlayer();
|
||||||
|
var zoomInCount = 0;
|
||||||
|
|
||||||
|
await tester.sendKeyDownEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
final result = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyDownEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.equal,
|
||||||
|
logicalKey: LogicalKeyboardKey.equal,
|
||||||
|
timeStamp: Duration.zero,
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onZoomIn: () => zoomInCount++,
|
||||||
|
);
|
||||||
|
await tester.sendKeyUpEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
|
||||||
|
expect(result, KeyEventResult.handled);
|
||||||
|
expect(zoomInCount, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Alt+Plus repeats zoom in callback while held', (tester) async {
|
||||||
|
final service = await KeyboardShortcutsService.getInstance();
|
||||||
|
addTearDown(service.dispose);
|
||||||
|
final player = _FakePlayer();
|
||||||
|
var zoomInCount = 0;
|
||||||
|
|
||||||
|
await tester.sendKeyDownEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
final downResult = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyDownEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.equal,
|
||||||
|
logicalKey: LogicalKeyboardKey.equal,
|
||||||
|
timeStamp: Duration.zero,
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onZoomIn: () => zoomInCount++,
|
||||||
|
);
|
||||||
|
final repeatResult = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyRepeatEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.equal,
|
||||||
|
logicalKey: LogicalKeyboardKey.equal,
|
||||||
|
timeStamp: Duration(milliseconds: 30),
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onZoomIn: () => zoomInCount++,
|
||||||
|
);
|
||||||
|
await tester.sendKeyUpEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
|
||||||
|
expect(downResult, KeyEventResult.handled);
|
||||||
|
expect(repeatResult, KeyEventResult.handled);
|
||||||
|
expect(zoomInCount, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Alt+Minus repeats zoom out callback while held', (tester) async {
|
||||||
|
final service = await KeyboardShortcutsService.getInstance();
|
||||||
|
addTearDown(service.dispose);
|
||||||
|
final player = _FakePlayer();
|
||||||
|
var zoomOutCount = 0;
|
||||||
|
|
||||||
|
await tester.sendKeyDownEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
final downResult = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyDownEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.minus,
|
||||||
|
logicalKey: LogicalKeyboardKey.minus,
|
||||||
|
timeStamp: Duration.zero,
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onZoomOut: () => zoomOutCount++,
|
||||||
|
);
|
||||||
|
final repeatResult = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyRepeatEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.minus,
|
||||||
|
logicalKey: LogicalKeyboardKey.minus,
|
||||||
|
timeStamp: Duration(milliseconds: 30),
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onZoomOut: () => zoomOutCount++,
|
||||||
|
);
|
||||||
|
await tester.sendKeyUpEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
|
||||||
|
expect(downResult, KeyEventResult.handled);
|
||||||
|
expect(repeatResult, KeyEventResult.handled);
|
||||||
|
expect(zoomOutCount, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Alt+Backspace reset does not repeat while held', (tester) async {
|
||||||
|
final service = await KeyboardShortcutsService.getInstance();
|
||||||
|
addTearDown(service.dispose);
|
||||||
|
final player = _FakePlayer();
|
||||||
|
var resetCount = 0;
|
||||||
|
|
||||||
|
await tester.sendKeyDownEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
final downResult = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyDownEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.backspace,
|
||||||
|
logicalKey: LogicalKeyboardKey.backspace,
|
||||||
|
timeStamp: Duration.zero,
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onZoomReset: () => resetCount++,
|
||||||
|
);
|
||||||
|
final repeatResult = service.handleVideoPlayerKeyEvent(
|
||||||
|
const KeyRepeatEvent(
|
||||||
|
physicalKey: PhysicalKeyboardKey.backspace,
|
||||||
|
logicalKey: LogicalKeyboardKey.backspace,
|
||||||
|
timeStamp: Duration(milliseconds: 30),
|
||||||
|
),
|
||||||
|
player,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
onZoomReset: () => resetCount++,
|
||||||
|
);
|
||||||
|
await tester.sendKeyUpEvent(LogicalKeyboardKey.altLeft);
|
||||||
|
|
||||||
|
expect(downResult, KeyEventResult.handled);
|
||||||
|
expect(repeatResult, KeyEventResult.handled);
|
||||||
|
expect(resetCount, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('video zoom scale maps to mpv logarithmic property', () {
|
||||||
|
expect(VideoFilterManager.videoZoomPropertyForScale(1.0), closeTo(0.0, 0.0001));
|
||||||
|
expect(VideoFilterManager.videoZoomPropertyForScale(2.0), closeTo(1.0, 0.0001));
|
||||||
|
expect(VideoFilterManager.videoZoomPropertyForScale(0.5), closeTo(-1.0, 0.0001));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FakePlayer implements Player {
|
class _FakePlayer implements Player {
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:plezy/widgets/video_controls/helpers/two_finger_double_tap_tracker.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late DateTime now;
|
||||||
|
late TwoFingerDoubleTapTracker tracker;
|
||||||
|
|
||||||
|
void advance(Duration duration) {
|
||||||
|
now = now.add(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
now = DateTime(2026);
|
||||||
|
tracker = TwoFingerDoubleTapTracker(now: () => now);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('detects two two-finger taps as double tap', () {
|
||||||
|
tracker.pointerDown(1, const Offset(100, 100));
|
||||||
|
tracker.pointerDown(2, const Offset(140, 100));
|
||||||
|
expect(tracker.pointerUp(1, const Offset(100, 100)), isFalse);
|
||||||
|
expect(tracker.pointerUp(2, const Offset(140, 100)), isFalse);
|
||||||
|
|
||||||
|
advance(const Duration(milliseconds: 120));
|
||||||
|
|
||||||
|
tracker.pointerDown(3, const Offset(102, 102));
|
||||||
|
tracker.pointerDown(4, const Offset(142, 102));
|
||||||
|
expect(tracker.pointerUp(3, const Offset(102, 102)), isFalse);
|
||||||
|
expect(tracker.pointerUp(4, const Offset(142, 102)), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not detect one-finger double tap', () {
|
||||||
|
tracker.pointerDown(1, const Offset(100, 100));
|
||||||
|
expect(tracker.pointerUp(1, const Offset(100, 100)), isFalse);
|
||||||
|
|
||||||
|
advance(const Duration(milliseconds: 120));
|
||||||
|
|
||||||
|
tracker.pointerDown(2, const Offset(100, 100));
|
||||||
|
expect(tracker.pointerUp(2, const Offset(100, 100)), isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('movement invalidates a candidate two-finger tap', () {
|
||||||
|
tracker.pointerDown(1, const Offset(100, 100));
|
||||||
|
tracker.pointerDown(2, const Offset(140, 100));
|
||||||
|
tracker.pointerMove(1, const Offset(140, 140));
|
||||||
|
expect(tracker.pointerUp(1, const Offset(140, 140)), isFalse);
|
||||||
|
expect(tracker.pointerUp(2, const Offset(140, 100)), isFalse);
|
||||||
|
|
||||||
|
advance(const Duration(milliseconds: 120));
|
||||||
|
|
||||||
|
tracker.pointerDown(3, const Offset(100, 100));
|
||||||
|
tracker.pointerDown(4, const Offset(140, 100));
|
||||||
|
expect(tracker.pointerUp(3, const Offset(100, 100)), isFalse);
|
||||||
|
expect(tracker.pointerUp(4, const Offset(140, 100)), isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('third finger invalidates a candidate two-finger tap', () {
|
||||||
|
tracker.pointerDown(1, const Offset(100, 100));
|
||||||
|
tracker.pointerDown(2, const Offset(140, 100));
|
||||||
|
tracker.pointerDown(3, const Offset(180, 100));
|
||||||
|
expect(tracker.pointerUp(1, const Offset(100, 100)), isFalse);
|
||||||
|
expect(tracker.pointerUp(2, const Offset(140, 100)), isFalse);
|
||||||
|
expect(tracker.pointerUp(3, const Offset(180, 100)), isFalse);
|
||||||
|
|
||||||
|
advance(const Duration(milliseconds: 120));
|
||||||
|
|
||||||
|
tracker.pointerDown(4, const Offset(100, 100));
|
||||||
|
tracker.pointerDown(5, const Offset(140, 100));
|
||||||
|
expect(tracker.pointerUp(4, const Offset(100, 100)), isFalse);
|
||||||
|
expect(tracker.pointerUp(5, const Offset(140, 100)), isFalse);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user