diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 000000000..fa0b357c4 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 4edf7a607..72e05c2ea 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -7230,6 +7230,8 @@ "alwaysOn": "Always on.", "replaceSkipOutroWithNextUpDisplay": "Replace Skip Outro with Next Up Display", "replaceSkipOutroWithNextUpDisplaySubtitle": "Show the Next Up overlay instead of the Skip Outro button.", + "nextUpIgnoreRemainingContentDisplay": "Next Up Forced (Ignore Remaining Content After Outro)", + "nextUpIgnoreRemainingContentDisplaySubtitle": "Trigger Next Up pop-up even if there's remaining content after outro.", "playerRouting": "Player Routing", "preferSoftwareDecoders": "Prefer software decoders", "preferSoftwareDecodersSubtitle": "Use FFmpeg (audio) and libgav1 (AV1) before hardware decoders. Disable if HDMI audio passthrough breaks.", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index b2deb72ad..033f9bfc4 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -13678,6 +13678,18 @@ abstract class AppLocalizations { /// **'Show the Next Up overlay instead of the Skip Outro button.'** String get replaceSkipOutroWithNextUpDisplaySubtitle; + /// No description provided for @nextUpIgnoreRemainingContentDisplay. + /// + /// In en, this message translates to: + /// **'Next Up Forced (Ignore Remaining Content After Outro)'** + String get nextUpIgnoreRemainingContentDisplay; + + /// No description provided for @nextUpIgnoreRemainingContentDisplaySubtitle. + /// + /// In en, this message translates to: + /// **'Trigger Next Up pop-up even if there\'s remaining content after outro.'** + String get nextUpIgnoreRemainingContentDisplaySubtitle; + /// No description provided for @playerRouting. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_af.dart b/lib/l10n/app_localizations_af.dart index 9ae5e1f57..fa9e24d88 100644 --- a/lib/l10n/app_localizations_af.dart +++ b/lib/l10n/app_localizations_af.dart @@ -7692,6 +7692,14 @@ class AppLocalizationsAf extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Wys die Next Up-oorleg in plaas van die Skip Outro-knoppie.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Spelerroetering'; diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart index 7346c3480..270dc50b2 100644 --- a/lib/l10n/app_localizations_ar.dart +++ b/lib/l10n/app_localizations_ar.dart @@ -7640,6 +7640,14 @@ class AppLocalizationsAr extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'قم بإظهار تراكب Next Up بدلاً من زر Skip Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'توجيه اللاعب'; diff --git a/lib/l10n/app_localizations_be.dart b/lib/l10n/app_localizations_be.dart index ddd160783..5f912a88b 100644 --- a/lib/l10n/app_localizations_be.dart +++ b/lib/l10n/app_localizations_be.dart @@ -7715,6 +7715,14 @@ class AppLocalizationsBe extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Паказваць накладанне Next Up замест кнопкі Skip Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Маршрутызацыя гульцоў'; diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index 1a41accbf..a796ddb25 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -7757,6 +7757,14 @@ class AppLocalizationsBg extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Показване на наслагването Next Up вместо бутона Skip Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Насочване на играчи'; diff --git a/lib/l10n/app_localizations_bn.dart b/lib/l10n/app_localizations_bn.dart index bbe3cd8bf..f44360a9e 100644 --- a/lib/l10n/app_localizations_bn.dart +++ b/lib/l10n/app_localizations_bn.dart @@ -7678,6 +7678,14 @@ class AppLocalizationsBn extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Skip Outro বোতামের পরিবর্তে Next Up ওভারলে দেখান।'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'প্লেয়ার রাউটিং'; diff --git a/lib/l10n/app_localizations_ca.dart b/lib/l10n/app_localizations_ca.dart index a6d2b56f5..6c57b7b56 100644 --- a/lib/l10n/app_localizations_ca.dart +++ b/lib/l10n/app_localizations_ca.dart @@ -7798,6 +7798,14 @@ class AppLocalizationsCa extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Mostra la superposició Next Up en lloc del botó Salta Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Encaminament del jugador'; diff --git a/lib/l10n/app_localizations_cs.dart b/lib/l10n/app_localizations_cs.dart index c25ff046f..246d8c93d 100644 --- a/lib/l10n/app_localizations_cs.dart +++ b/lib/l10n/app_localizations_cs.dart @@ -7696,6 +7696,14 @@ class AppLocalizationsCs extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Zobrazte překryvnou vrstvu Next Up namísto tlačítka Skip Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Směrování hráčů'; diff --git a/lib/l10n/app_localizations_cy.dart b/lib/l10n/app_localizations_cy.dart index 09d83b92a..a7a27c6b1 100644 --- a/lib/l10n/app_localizations_cy.dart +++ b/lib/l10n/app_localizations_cy.dart @@ -7710,6 +7710,14 @@ class AppLocalizationsCy extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Dangoswch y troshaen Next Up yn lle\'r botwm Skip Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Llwybro Chwaraewr'; diff --git a/lib/l10n/app_localizations_da.dart b/lib/l10n/app_localizations_da.dart index 4b4be6804..f5af5dd8d 100644 --- a/lib/l10n/app_localizations_da.dart +++ b/lib/l10n/app_localizations_da.dart @@ -7682,6 +7682,14 @@ class AppLocalizationsDa extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Vis Next Up-overlejringen i stedet for knappen Spring Outro over.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Spillerruting'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index a29a1454a..377d0ea49 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -7751,6 +7751,14 @@ class AppLocalizationsDe extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Zeigen Sie das Next Up-Overlay anstelle der Schaltfläche „Outro überspringen“ an.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Spielerrouting'; diff --git a/lib/l10n/app_localizations_el.dart b/lib/l10n/app_localizations_el.dart index 940cb10d0..e93ba3040 100644 --- a/lib/l10n/app_localizations_el.dart +++ b/lib/l10n/app_localizations_el.dart @@ -7801,6 +7801,14 @@ class AppLocalizationsEl extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Εμφάνιση της επικάλυψης Next Up αντί για το κουμπί Skip Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Δρομολόγηση παίκτη'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index b381be77f..1c4f89c40 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -7628,6 +7628,14 @@ class AppLocalizationsEn extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_eo.dart b/lib/l10n/app_localizations_eo.dart index 824fa730a..d0eb26187 100644 --- a/lib/l10n/app_localizations_eo.dart +++ b/lib/l10n/app_localizations_eo.dart @@ -7674,6 +7674,14 @@ class AppLocalizationsEo extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Montru la kovraĵon Sekva Supre anstataŭ la butonon Saltu Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Ludanta Vokado'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index a66dc02f4..7c7008403 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -7760,6 +7760,14 @@ class AppLocalizationsEs extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Muestra la superposición Siguiente en lugar del botón Omitir salida.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Enrutamiento del jugador'; diff --git a/lib/l10n/app_localizations_et.dart b/lib/l10n/app_localizations_et.dart index 6e09931e4..b2b59b1a7 100644 --- a/lib/l10n/app_localizations_et.dart +++ b/lib/l10n/app_localizations_et.dart @@ -7698,6 +7698,14 @@ class AppLocalizationsEt extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Kuvage ülekate Next Up (Järgmine üles) nupu Skip Outro asemel.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Mängija marsruutimine'; diff --git a/lib/l10n/app_localizations_fa.dart b/lib/l10n/app_localizations_fa.dart index b5c1ef812..ec44d7aa8 100644 --- a/lib/l10n/app_localizations_fa.dart +++ b/lib/l10n/app_localizations_fa.dart @@ -7647,6 +7647,14 @@ class AppLocalizationsFa extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'به جای دکمه Skip Outro، روکش Next Up را نشان دهید.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'مسیریابی بازیکن'; diff --git a/lib/l10n/app_localizations_fi.dart b/lib/l10n/app_localizations_fi.dart index 8f146f656..be8325be5 100644 --- a/lib/l10n/app_localizations_fi.dart +++ b/lib/l10n/app_localizations_fi.dart @@ -7710,6 +7710,14 @@ class AppLocalizationsFi extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Näytä Next Up -peittokuva Ohita Outro -painikkeen sijaan.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Pelaajien reititys'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 9996083ad..85c69ff0d 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -7782,6 +7782,14 @@ class AppLocalizationsFr extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Afficher la superposition Next Up au lieu du bouton Ignorer Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Routage des joueurs'; diff --git a/lib/l10n/app_localizations_gl.dart b/lib/l10n/app_localizations_gl.dart index 3b79596ee..bc942407e 100644 --- a/lib/l10n/app_localizations_gl.dart +++ b/lib/l10n/app_localizations_gl.dart @@ -7783,6 +7783,14 @@ class AppLocalizationsGl extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_he.dart b/lib/l10n/app_localizations_he.dart index ef6f93f5e..5188cfb18 100644 --- a/lib/l10n/app_localizations_he.dart +++ b/lib/l10n/app_localizations_he.dart @@ -7582,6 +7582,14 @@ class AppLocalizationsHe extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index d664bca81..9ac76e65d 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -7664,6 +7664,14 @@ class AppLocalizationsHi extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_hr.dart b/lib/l10n/app_localizations_hr.dart index dd6897720..f6094e74c 100644 --- a/lib/l10n/app_localizations_hr.dart +++ b/lib/l10n/app_localizations_hr.dart @@ -7708,6 +7708,14 @@ class AppLocalizationsHr extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 4c309bc33..0341704ea 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -7751,6 +7751,14 @@ class AppLocalizationsHu extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index d025cb19d..582320f0a 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -7696,6 +7696,14 @@ class AppLocalizationsId extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 74892d689..486388e32 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -7727,6 +7727,14 @@ class AppLocalizationsIt extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index e72a345a7..1a3e0ec68 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -7490,6 +7490,14 @@ class AppLocalizationsJa extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_kk.dart b/lib/l10n/app_localizations_kk.dart index 6466ca792..72b6040ed 100644 --- a/lib/l10n/app_localizations_kk.dart +++ b/lib/l10n/app_localizations_kk.dart @@ -7719,6 +7719,14 @@ class AppLocalizationsKk extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_kn.dart b/lib/l10n/app_localizations_kn.dart index c750aa64f..e12c3822c 100644 --- a/lib/l10n/app_localizations_kn.dart +++ b/lib/l10n/app_localizations_kn.dart @@ -7735,6 +7735,14 @@ class AppLocalizationsKn extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index cd99bf04f..6b328eec4 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -7485,6 +7485,14 @@ class AppLocalizationsKo extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_lt.dart b/lib/l10n/app_localizations_lt.dart index a6795076f..490214934 100644 --- a/lib/l10n/app_localizations_lt.dart +++ b/lib/l10n/app_localizations_lt.dart @@ -7714,6 +7714,14 @@ class AppLocalizationsLt extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Vietoj mygtuko Skip Outro rodyti perdangą Next Up.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Žaidėjų maršrutas'; diff --git a/lib/l10n/app_localizations_lv.dart b/lib/l10n/app_localizations_lv.dart index 7d908a7ce..702c3c8a3 100644 --- a/lib/l10n/app_localizations_lv.dart +++ b/lib/l10n/app_localizations_lv.dart @@ -7723,6 +7723,14 @@ class AppLocalizationsLv extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Rādīt pārklājumu Next Up, nevis pogu Izlaist Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Spēlētāja maršrutēšana'; diff --git a/lib/l10n/app_localizations_mk.dart b/lib/l10n/app_localizations_mk.dart index 02754d302..42bf7e989 100644 --- a/lib/l10n/app_localizations_mk.dart +++ b/lib/l10n/app_localizations_mk.dart @@ -7737,6 +7737,14 @@ class AppLocalizationsMk extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_ml.dart b/lib/l10n/app_localizations_ml.dart index 0d18da370..d46ddb2be 100644 --- a/lib/l10n/app_localizations_ml.dart +++ b/lib/l10n/app_localizations_ml.dart @@ -7781,6 +7781,14 @@ class AppLocalizationsMl extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'സ്കിപ്പ് ഔട്ട്‌റോ ബട്ടണിന് പകരം അടുത്ത അപ്പ് ഓവർലേ കാണിക്കുക.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'പ്ലെയർ റൂട്ടിംഗ്'; diff --git a/lib/l10n/app_localizations_mn.dart b/lib/l10n/app_localizations_mn.dart index 2eb63775d..3e6943ef6 100644 --- a/lib/l10n/app_localizations_mn.dart +++ b/lib/l10n/app_localizations_mn.dart @@ -7715,6 +7715,14 @@ class AppLocalizationsMn extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Skip Outro товчлуурын оронд Next Up давхаргыг харуул.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Тоглогчийн чиглүүлэлт'; diff --git a/lib/l10n/app_localizations_nb.dart b/lib/l10n/app_localizations_nb.dart index 54318cde1..77e6e8535 100644 --- a/lib/l10n/app_localizations_nb.dart +++ b/lib/l10n/app_localizations_nb.dart @@ -7687,6 +7687,14 @@ class AppLocalizationsNb extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 81baa6f78..ffb9fcc1e 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -7718,6 +7718,14 @@ class AppLocalizationsNl extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_pa.dart b/lib/l10n/app_localizations_pa.dart index 6dea79784..d3736fa12 100644 --- a/lib/l10n/app_localizations_pa.dart +++ b/lib/l10n/app_localizations_pa.dart @@ -7667,6 +7667,14 @@ class AppLocalizationsPa extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 3b9a5fee2..28e36a444 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -7729,6 +7729,14 @@ class AppLocalizationsPl extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 53612e3df..d063ec30c 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -7741,6 +7741,14 @@ class AppLocalizationsPt extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Mostre a sobreposição Next Up em vez do botão Pular Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Roteamento de Jogador'; diff --git a/lib/l10n/app_localizations_ro.dart b/lib/l10n/app_localizations_ro.dart index 0424fa775..6d1fe1458 100644 --- a/lib/l10n/app_localizations_ro.dart +++ b/lib/l10n/app_localizations_ro.dart @@ -7732,6 +7732,14 @@ class AppLocalizationsRo extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index 195259e04..e8b010fc7 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -7738,6 +7738,14 @@ class AppLocalizationsRu extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_si.dart b/lib/l10n/app_localizations_si.dart index de960df92..c94a9f960 100644 --- a/lib/l10n/app_localizations_si.dart +++ b/lib/l10n/app_localizations_si.dart @@ -7675,6 +7675,14 @@ class AppLocalizationsSi extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index c8d1fe58b..e5246c09d 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -7719,6 +7719,14 @@ class AppLocalizationsSk extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index eb8b57ad6..d18f6f686 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -7716,6 +7716,14 @@ class AppLocalizationsSl extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_sq.dart b/lib/l10n/app_localizations_sq.dart index fc06179fe..79ecd59c4 100644 --- a/lib/l10n/app_localizations_sq.dart +++ b/lib/l10n/app_localizations_sq.dart @@ -7747,6 +7747,14 @@ class AppLocalizationsSq extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_sr.dart b/lib/l10n/app_localizations_sr.dart index b98978c31..993f77746 100644 --- a/lib/l10n/app_localizations_sr.dart +++ b/lib/l10n/app_localizations_sr.dart @@ -7712,6 +7712,14 @@ class AppLocalizationsSr extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index d5b2c1157..80e0e7e26 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -7698,6 +7698,14 @@ class AppLocalizationsSv extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_sw.dart b/lib/l10n/app_localizations_sw.dart index 6a07eea43..dbd8266ea 100644 --- a/lib/l10n/app_localizations_sw.dart +++ b/lib/l10n/app_localizations_sw.dart @@ -7739,6 +7739,14 @@ class AppLocalizationsSw extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_ta.dart b/lib/l10n/app_localizations_ta.dart index b9d83ed51..46bc2544f 100644 --- a/lib/l10n/app_localizations_ta.dart +++ b/lib/l10n/app_localizations_ta.dart @@ -7742,6 +7742,14 @@ class AppLocalizationsTa extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_te.dart b/lib/l10n/app_localizations_te.dart index bc7e37974..62394df0e 100644 --- a/lib/l10n/app_localizations_te.dart +++ b/lib/l10n/app_localizations_te.dart @@ -7736,6 +7736,14 @@ class AppLocalizationsTe extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'స్కిప్ అవుట్‌రో బటన్‌కు బదులుగా తదుపరి పైకి అతివ్యాప్తిని చూపండి.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'ప్లేయర్ రూటింగ్'; diff --git a/lib/l10n/app_localizations_th.dart b/lib/l10n/app_localizations_th.dart index 5081f48b9..84d840f8a 100644 --- a/lib/l10n/app_localizations_th.dart +++ b/lib/l10n/app_localizations_th.dart @@ -7633,6 +7633,14 @@ class AppLocalizationsTh extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'แสดงโอเวอร์เลย์ถัดไปแทนปุ่มข้าม'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'เส้นทางผู้เล่น'; diff --git a/lib/l10n/app_localizations_tl.dart b/lib/l10n/app_localizations_tl.dart index 472abeb5d..c48c9f902 100644 --- a/lib/l10n/app_localizations_tl.dart +++ b/lib/l10n/app_localizations_tl.dart @@ -7767,6 +7767,14 @@ class AppLocalizationsTl extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart index 40395a48f..51c433619 100644 --- a/lib/l10n/app_localizations_tr.dart +++ b/lib/l10n/app_localizations_tr.dart @@ -7702,6 +7702,14 @@ class AppLocalizationsTr extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => '\"Jeneriği Atla\" butonu yerine \"Sıradaki Bölüm\" ekranını göster.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Oynatıcı Yönlendirme'; diff --git a/lib/l10n/app_localizations_ug.dart b/lib/l10n/app_localizations_ug.dart index 3f2d002d0..13c4307b8 100644 --- a/lib/l10n/app_localizations_ug.dart +++ b/lib/l10n/app_localizations_ug.dart @@ -7694,6 +7694,14 @@ class AppLocalizationsUg extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 424dfddfa..38ca2cef2 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -7732,6 +7732,14 @@ class AppLocalizationsUk extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Показати накладення Next Up замість кнопки Skip Outro.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Маршрутизація гравців'; diff --git a/lib/l10n/app_localizations_vi.dart b/lib/l10n/app_localizations_vi.dart index 7414a6b92..48d1ba4f2 100644 --- a/lib/l10n/app_localizations_vi.dart +++ b/lib/l10n/app_localizations_vi.dart @@ -7694,6 +7694,14 @@ class AppLocalizationsVi extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Hiển thị lớp phủ Tiếp theo thay vì nút Bỏ qua.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Định tuyến người chơi'; diff --git a/lib/l10n/app_localizations_yue.dart b/lib/l10n/app_localizations_yue.dart index ac2d4ecdb..1ef167aad 100644 --- a/lib/l10n/app_localizations_yue.dart +++ b/lib/l10n/app_localizations_yue.dart @@ -7442,6 +7442,14 @@ class AppLocalizationsYue extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => 'Show the Next Up overlay instead of the Skip Outro button.'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => 'Player Routing'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index bbeb69190..11f63e588 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -7420,6 +7420,14 @@ class AppLocalizationsZh extends AppLocalizations { String get replaceSkipOutroWithNextUpDisplaySubtitle => '显示“下一个”叠加层而不是“跳过尾奏”按钮。'; + @override + String get nextUpIgnoreRemainingContentDisplay => + 'Next Up Forced (Ignore Remaining Content After Outro)'; + + @override + String get nextUpIgnoreRemainingContentDisplaySubtitle => + 'Trigger Next Up pop-up even if there\'s remaining content after outro.'; + @override String get playerRouting => '玩家路线'; diff --git a/lib/preference/user_preferences.dart b/lib/preference/user_preferences.dart index 8cd3949f2..06e1c0860 100644 --- a/lib/preference/user_preferences.dart +++ b/lib/preference/user_preferences.dart @@ -1130,6 +1130,11 @@ class UserPreferences extends ChangeNotifier { defaultValue: false, ); + static final nextUpIgnoreRemainingContent = Preference( + key: 'next_up_ignore_remaining_content', + defaultValue: false, + ); + static final skipBackLength = Preference( key: 'skipBackLength', defaultValue: 10000, diff --git a/lib/ui/screens/playback/video_player_screen.dart b/lib/ui/screens/playback/video_player_screen.dart index 20d09699b..1a5b7144d 100644 --- a/lib/ui/screens/playback/video_player_screen.dart +++ b/lib/ui/screens/playback/video_player_screen.dart @@ -171,8 +171,12 @@ class _VideoPlayerScreenState extends State MediaSegment? _skipSegment; Duration? _skipTo; + Timer? _skipSegmentHideTimer; + bool _skipSegmentAutoHidden = false; bool _showNextUp = false; AggregatedItem? _nextUpItem; + Timer? _nextUpHideTimer; + bool _nextUpAutoHidden = false; bool _nextUpDismissed = false; bool _isNextUpAdvancing = false; int _consecutiveEpisodes = 0; @@ -940,6 +944,10 @@ class _VideoPlayerScreenState extends State _removeZoomModeToastOverlay(); _skipForwardTimer?.cancel(); _skipBackwardTimer?.cancel(); + _skipSegmentHideTimer?.cancel(); + _skipSegmentHideTimer = null; + _nextUpHideTimer?.cancel(); + _nextUpHideTimer = null; if (_useSystemVolume) { _volumeListenerSub?.cancel(); VolumeController.instance.removeListener(); @@ -2118,22 +2126,43 @@ class _VideoPlayerScreenState extends State } } + /// Return true if there is content after the outro segment + /// (segment.end significantly before the end of the video). + bool _hasContentAfterOutro(MediaSegment? outroSegment) { + if (outroSegment == null) return false; + final duration = _state.duration; + if (duration <= Duration.zero) return false; + + // Less than 10 seconds of content after the outro is considered "not enough" to block a Next Up prompt. + final remaining = duration - outroSegment.end; + return remaining > const Duration(seconds: 10); + } + void _checkSegments(Duration position) { final result = _segmentService.checkPosition(position); final replaceSkipOutroWithNextUp = _prefs.get( UserPreferences.replaceSkipOutroWithNextUp, ); + final nextUpIgnoreRemainingContent = _prefs.get( + UserPreferences.nextUpIgnoreRemainingContent, + ); + + final effectiveIgnoreContentAfterOutro = replaceSkipOutroWithNextUp && nextUpIgnoreRemainingContent; + if (result.shouldSkip && result.skipTo != null) { final isOutro = result.segment?.type == MediaSegmentType.outro; - if (replaceSkipOutroWithNextUp && isOutro && _showNextUp) { + final hasContentAfterOutro = isOutro && _hasContentAfterOutro(result.segment); + final protectContentAfterOutro = hasContentAfterOutro && !effectiveIgnoreContentAfterOutro; + + if (replaceSkipOutroWithNextUp && isOutro && _showNextUp && !protectContentAfterOutro) { return; } - if (replaceSkipOutroWithNextUp && isOutro && _shouldShowNextUpOverlay()) { + if (replaceSkipOutroWithNextUp && isOutro && _shouldShowNextUpOverlay() && !protectContentAfterOutro) { unawaited(_manager.seekTo(result.skipTo!)); _presentNextUpOverlay(); return; } - if (isOutro && !_hasDistinctQueueNextItem()) { + if (isOutro && !_hasDistinctQueueNextItem() && !protectContentAfterOutro) { unawaited(_exitPlayback()); return; } @@ -2149,7 +2178,10 @@ class _VideoPlayerScreenState extends State } if (result.shouldAsk && result.isNew && result.segment != null) { final isOutro = result.segment!.type == MediaSegmentType.outro; - if (replaceSkipOutroWithNextUp && isOutro && _shouldShowNextUpOverlay()) { + final effectiveIgnoreContentAfterOutro = replaceSkipOutroWithNextUp && nextUpIgnoreRemainingContent; + final hasContentAfterOutro = isOutro && _hasContentAfterOutro(result.segment); + final protectContentAfterOutro = hasContentAfterOutro && !effectiveIgnoreContentAfterOutro; + if (replaceSkipOutroWithNextUp && isOutro && _shouldShowNextUpOverlay() && !protectContentAfterOutro) { _presentNextUpOverlay(); return; } @@ -2157,8 +2189,10 @@ class _VideoPlayerScreenState extends State _skipSegment = result.segment; _skipTo = result.skipTo; _controlsVisible = false; + _skipSegmentAutoHidden = false; }); _hideTimer?.cancel(); + _startSkipSegmentAutoHide(); _focusTvSkipSegment(); } else if (result.isNone && _skipSegment != null) { _clearSkipSegment(); @@ -2268,8 +2302,12 @@ class _VideoPlayerScreenState extends State _controlsVisible = false; _skipSegment = null; _skipTo = null; + _skipSegmentAutoHidden = false; }); + _skipSegmentHideTimer?.cancel(); + _skipSegmentHideTimer = null; _hideTimer?.cancel(); + _startNextUpAutoHide(); _focusTvNextUpPlay(); } @@ -2280,11 +2318,17 @@ class _VideoPlayerScreenState extends State return; } _suppressBackNavigation(duration: const Duration(milliseconds: 500)); + _nextUpHideTimer?.cancel(); + _nextUpHideTimer = null; _isNextUpAdvancing = true; + + final previousItem = _queue.currentItem; + setState(() => _showNextUp = false); try { await _checkStillWatching(); await _manager.next(); + unawaited(_markItemAsPlayed(previousItem)); } finally { _isNextUpAdvancing = false; } @@ -2292,18 +2336,24 @@ class _VideoPlayerScreenState extends State void _handleNextUpDismiss() { _suppressBackNavigation(duration: const Duration(milliseconds: 500)); + _nextUpHideTimer?.cancel(); + _nextUpHideTimer = null; _manager.suppressAutoNext = false; setState(() { _showNextUp = false; _nextUpDismissed = true; + _nextUpAutoHidden = false; }); } void _handleNextUpCancel() { _suppressBackNavigation(duration: const Duration(milliseconds: 500)); + _nextUpHideTimer?.cancel(); + _nextUpHideTimer = null; setState(() { _showNextUp = false; _nextUpDismissed = true; + _nextUpAutoHidden = false; }); unawaited(_exitPlayback()); } @@ -2338,37 +2388,65 @@ class _VideoPlayerScreenState extends State } } + // Mark item as "played", used when skipping an outro segment with replaceSkipOutroWithNextUp enabled to ensure the item is marked as played in the server's library. + Future _markItemAsPlayed(dynamic item) async { + if (item == null) return; + final itemId = _itemIdForQueueItem(item); + if (itemId != null && itemId.isNotEmpty) { + try { + final raw = _rawDataForQueueItem(item); + if (raw != null) { + final existingUserData = raw['UserData']; + final userData = existingUserData is Map + ? existingUserData + : (existingUserData is Map + ? existingUserData.cast() + : {}); + userData['Played'] = true; + userData.remove('PlaybackPositionTicks'); + raw['UserData'] = userData; + } + + final mutations = ItemMutationRepository(_clientForQueueItem(item)); + await mutations.setPlayed(itemId, isPlayed: true); + } catch (_) {} + } + } + void _skipCurrentSegment() { final replaceSkipOutroWithNextUp = _prefs.get( UserPreferences.replaceSkipOutroWithNextUp, ); - final isOutro = - _skipSegment?.type == MediaSegmentType.outro || - _segmentService.activeSegment?.type == MediaSegmentType.outro; + final nextUpIgnoreRemainingContent = _prefs.get( + UserPreferences.nextUpIgnoreRemainingContent, + ); + + final activeOutroSegment = _skipSegment?.type == MediaSegmentType.outro + ? _skipSegment + : (_segmentService.activeSegment?.type == MediaSegmentType.outro + ? _segmentService.activeSegment + : null); + final isOutro = activeOutroSegment != null; + + final effectiveIgnoreContentAfterOutro = replaceSkipOutroWithNextUp && nextUpIgnoreRemainingContent; + final hasContentAfterOutro = _hasContentAfterOutro(activeOutroSegment); + final protectContentAfterOutro = hasContentAfterOutro && !effectiveIgnoreContentAfterOutro; + bool shouldMarkPlayed = false; if (isOutro) { - final item = _queue.currentItem; - final itemId = _itemIdForQueueItem(item); - if (itemId != null && itemId.isNotEmpty) { - try { - final mutations = ItemMutationRepository(_clientForQueueItem(item)); - unawaited( - mutations.setPlayed(itemId, isPlayed: true).catchError((_) {}), - ); - final raw = _rawDataForQueueItem(item); - if (raw != null) { - final existingUserData = raw['UserData']; - final userData = existingUserData is Map - ? existingUserData - : (existingUserData is Map - ? existingUserData.cast() - : {}); - userData['Played'] = true; - raw['UserData'] = userData; - } - } catch (_) {} + + if (_skipTo != null && _state.duration > Duration.zero) { + final percentage = _skipTo!.inMilliseconds / _state.duration.inMilliseconds; + if (percentage >= 0.90) { + shouldMarkPlayed = true; + } + } else if (_skipTo == null) { + shouldMarkPlayed = true; } } - if (replaceSkipOutroWithNextUp && isOutro && _shouldShowNextUpOverlay()) { + + final currentItem = _queue.currentItem; + + if (replaceSkipOutroWithNextUp && isOutro && _shouldShowNextUpOverlay() && !protectContentAfterOutro) { final skipTo = _skipTo; if (skipTo != null) { unawaited(_manager.seekTo(skipTo)); @@ -2376,7 +2454,10 @@ class _VideoPlayerScreenState extends State _presentNextUpOverlay(); return; } - if (isOutro && !_hasDistinctQueueNextItem()) { + if (isOutro && !_hasDistinctQueueNextItem() && !protectContentAfterOutro) { + if (shouldMarkPlayed) { + unawaited(_manager.stop().then((_) => _markItemAsPlayed(currentItem))); + } unawaited(_exitPlayback()); return; } @@ -2384,6 +2465,9 @@ class _VideoPlayerScreenState extends State if (_skipTo != null) { _manager.seekTo(_skipTo!); } + if (shouldMarkPlayed) { + unawaited(_markItemAsPlayed(currentItem)); + } setState(() { _skipSegment = null; _skipTo = null; @@ -2397,9 +2481,12 @@ class _VideoPlayerScreenState extends State } void _clearSkipSegment() { + _skipSegmentHideTimer?.cancel(); + _skipSegmentHideTimer = null; setState(() { _skipSegment = null; _skipTo = null; + _skipSegmentAutoHidden = false; }); } @@ -2538,13 +2625,47 @@ class _VideoPlayerScreenState extends State _syncPrerollOsdState(); return; } - setState(() => _controlsVisible = true); + setState(() { + _controlsVisible = true; + }); _scheduleHide(); if (focusSeekbar) { _focusPreferredTvOverlayTarget(); } } + // Launch auto-hide timer (10s) for SkipSegment. + void _startSkipSegmentAutoHide() { + _skipSegmentHideTimer?.cancel(); + if (_skipSegment == null) return; + _skipSegmentHideTimer = Timer(const Duration(seconds: 10), () { + if (mounted) { + setState(() => _skipSegmentAutoHidden = true); + } + }); + } + + // Launch auto-hide timer (10s) for NextUp. + void _startNextUpAutoHide() { + _nextUpHideTimer?.cancel(); + if (_showNextUp == false || _nextUpItem == null) return; + + final duration = _state.duration; + final position = _state.position; + + if (duration > Duration.zero) { + final remaining = duration - position; + if (remaining <= const Duration(seconds: 2)) { + return; + } + } + _nextUpHideTimer = Timer(const Duration(seconds: 10), () { + if (mounted) { + setState(() => _nextUpAutoHidden = true); + } + }); + } + void _toggleControls() { if (_isCurrentPreroll) { _syncPrerollOsdState(); @@ -3316,9 +3437,9 @@ class _VideoPlayerScreenState extends State : PlatformDetection.useMobileUi && !_isOsdLocked ? _onVerticalDragCancel : null, - onPanDown: PlatformDetection.useDesktopUi + /* onPanDown: PlatformDetection.useDesktopUi ? (_) => _showControls() - : null, + : null, */ // Disabled because it inteferes with onTap on desktop, controls overlay would show on mouse down and instantly hide on mouse up. behavior: HitTestBehavior.opaque, child: Listener( onPointerSignal: PlatformDetection.useDesktopUi @@ -3341,6 +3462,10 @@ class _VideoPlayerScreenState extends State fit: StackFit.expand, children: [ _buildVideoSurface(), + if (PlatformDetection.isWeb) + const Positioned.fill( + child: ColoredBox(color: Colors.transparent), + ), // Workaround for a Flutter web issue where the video surface can block pointer events. _buildBringupOverlay(context), if (_isRestoringPosition) const Positioned.fill( @@ -3368,6 +3493,7 @@ class _VideoPlayerScreenState extends State if (_skipSegment != null) SkipSegmentOverlay( segment: _skipSegment!, + isHidden: _skipSegmentAutoHidden && !_controlsVisible, onSkip: _skipCurrentSegment, focusNode: PlatformDetection.isTV ? _tvSkipSegmentFocus @@ -3378,6 +3504,7 @@ class _VideoPlayerScreenState extends State if (_showNextUp && _nextUpItem != null) NextUpOverlay( nextItem: _nextUpItem!, + isHidden: _nextUpAutoHidden && !_controlsVisible, imageUrl: _nextUpItem!.primaryImageTag != null ? _clientForItem( _nextUpItem!, @@ -4435,6 +4562,7 @@ class _VideoPlayerScreenState extends State final buttonIconSize = isLandscape ? 28.0 : 24.0; final hasPrevious = _queue.hasPrevious; final hasNext = _queue.hasNext; + final hasAnyNavigation = hasPrevious || hasNext; return FocusTraversalGroup( policy: ReadingOrderTraversalPolicy(), @@ -4444,14 +4572,17 @@ class _VideoPlayerScreenState extends State mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: [ - if (_queue.hasPrevious) + if (hasAnyNavigation) _controlButton( Icons.skip_previous_rounded, - onPressed: _manager.previous, + onPressed: hasPrevious ? _manager.previous : null, size: buttonIconSize, extent: buttonExtent, focusNode: _tvTransportFirstFocus, tooltip: l10n.playerTooltipPrevious, + iconColor: hasPrevious + ? Colors.white + : Colors.white38, ), const SizedBox(width: 4), _controlButton( @@ -4460,7 +4591,7 @@ class _VideoPlayerScreenState extends State _seekRelative(-_prefs.get(UserPreferences.skipBackLength)), size: buttonIconSize, extent: buttonExtent, - focusNode: hasPrevious ? null : _tvTransportFirstFocus, + focusNode: hasAnyNavigation ? null : _tvTransportFirstFocus, tooltip: _tooltipMessage( l10n.playerTooltipSeekBack, shortcut: 'Left', @@ -4494,21 +4625,24 @@ class _VideoPlayerScreenState extends State _seekRelative(_prefs.get(UserPreferences.skipForwardLength)), size: buttonIconSize, extent: buttonExtent, - focusNode: hasNext ? null : _tvTransportLastFocus, + focusNode: hasAnyNavigation ? null : _tvTransportLastFocus, tooltip: _tooltipMessage( l10n.playerTooltipSeekForward, shortcut: 'Right', ), ), const SizedBox(width: 4), - if (_queue.hasNext) + if (hasAnyNavigation) _controlButton( Icons.skip_next_rounded, - onPressed: _manager.next, + onPressed: hasNext ? _manager.next : null, size: buttonIconSize, extent: buttonExtent, focusNode: _tvTransportLastFocus, tooltip: l10n.next, + iconColor: hasNext + ? Colors.white + : Colors.white38, ), ], ), @@ -5475,18 +5609,22 @@ class _VideoPlayerScreenState extends State builder: (context, snap) { final l10n = AppLocalizations.of(context); final isPlaying = _displayPlaying; + final hasAnyNavigation = _queue.hasPrevious || _queue.hasNext; return Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ - if (_queue.hasPrevious) + if (hasAnyNavigation) _controlButton( Icons.skip_previous_rounded, - onPressed: _manager.previous, + onPressed: _queue.hasPrevious ? _manager.previous : null, size: 40, extent: 72, tooltip: l10n.playerTooltipPrevious, + iconColor: _queue.hasPrevious + ? Colors.white + : Colors.white38, ), _controlButton( seekBackIcon(_prefs.get(UserPreferences.skipBackLength)), @@ -5521,13 +5659,16 @@ class _VideoPlayerScreenState extends State shortcut: 'Right', ), ), - if (_queue.hasNext) + if (hasAnyNavigation) _controlButton( Icons.skip_next_rounded, - onPressed: _manager.next, + onPressed: _queue.hasNext ? _manager.next : null, size: 40, extent: 72, tooltip: l10n.next, + iconColor: _queue.hasNext + ? Colors.white + : Colors.white38, ), ], ); @@ -5537,7 +5678,7 @@ class _VideoPlayerScreenState extends State Widget _controlButton( IconData icon, { - required VoidCallback onPressed, + VoidCallback? onPressed, double size = 24, double extent = 48, String? tooltip, @@ -5550,7 +5691,9 @@ class _VideoPlayerScreenState extends State focusNode: focusNode, extent: extent, tooltip: tooltip, - onPressed: () => _handleControlButtonPress(onPressed), + onPressed: onPressed != null + ? () => _handleControlButtonPress(onPressed) + : null, onRightBoundary: onRightBoundary, child: Icon(icon, color: iconColor, size: size), ); @@ -5560,7 +5703,9 @@ class _VideoPlayerScreenState extends State height: extent, child: IconButton( focusNode: focusNode, - onPressed: () => _handleControlButtonPress(onPressed), + onPressed: onPressed != null + ? () => _handleControlButtonPress(onPressed) + : null, tooltip: PlatformDetection.useDesktopUi ? tooltip : null, icon: Icon(icon, color: iconColor, size: size), padding: EdgeInsets.zero, diff --git a/lib/ui/screens/settings/settings_side_panel.dart b/lib/ui/screens/settings/settings_side_panel.dart index 242e270a3..1b81eb156 100644 --- a/lib/ui/screens/settings/settings_side_panel.dart +++ b/lib/ui/screens/settings/settings_side_panel.dart @@ -2880,6 +2880,7 @@ class _AutomationQueueScreenState extends State<_AutomationQueueScreen> { final showNextUpOptions = nextUpBehavior != NextUpBehavior.disabled; final showReplaceSkipOutroWithNextUp = showNextUpOptions && mediaSegmentActions == _promptSkipSegments; + final isReplaceSkipOutroEnabled = _prefs.get(UserPreferences.replaceSkipOutroWithNextUp); return Scaffold( appBar: buildSettingsAppBar( @@ -2958,6 +2959,13 @@ class _AutomationQueueScreenState extends State<_AutomationQueueScreen> { subtitle: l10n.replaceSkipOutroWithNextUpDisplaySubtitle, icon: Icons.skip_next, ), + if (showReplaceSkipOutroWithNextUp && isReplaceSkipOutroEnabled) + SwitchPreferenceTile( + preference: UserPreferences.nextUpIgnoreRemainingContent, + title: l10n.nextUpIgnoreRemainingContentDisplay, + subtitle: l10n.nextUpIgnoreRemainingContentDisplaySubtitle, + icon: Icons.play_arrow, + ), EnumPreferenceTile( preference: UserPreferences.stillWatchingBehavior, title: l10n.stillWatchingPrompt, diff --git a/lib/ui/widgets/playback/next_up_overlay.dart b/lib/ui/widgets/playback/next_up_overlay.dart index 2dd7d70d5..1c107ea32 100644 --- a/lib/ui/widgets/playback/next_up_overlay.dart +++ b/lib/ui/widgets/playback/next_up_overlay.dart @@ -10,6 +10,7 @@ import '../../../data/models/aggregated_item.dart'; import '../../../l10n/app_localizations.dart'; import '../../../preference/preference_constants.dart'; import '../../../preference/user_preferences.dart'; +import '../../../util/platform_detection.dart'; class NextUpOverlay extends StatefulWidget { final AggregatedItem nextItem; @@ -20,6 +21,7 @@ class NextUpOverlay extends StatefulWidget { final VoidCallback? onTimeout; final FocusNode? focusNode; final FocusNode? dismissFocusNode; + final bool isHidden; const NextUpOverlay({ super.key, @@ -31,6 +33,7 @@ class NextUpOverlay extends StatefulWidget { this.onTimeout, this.focusNode, this.dismissFocusNode, + this.isHidden = false, }); @override @@ -83,174 +86,181 @@ class _NextUpOverlayState extends State final showTimer = mediaSegmentCountdown == MediaSegmentCountdown.timer || mediaSegmentCountdown == MediaSegmentCountdown.both; + final isTV = PlatformDetection.isTV; + final isDesktop = PlatformDetection.useDesktopUi; + final bottomOffset = isTV ? 190.0 : (isDesktop ? 140.0 : 100.0); + return Positioned( right: 24, - bottom: 120, - child: Container( - width: 340, - decoration: BoxDecoration( - color: AppColorScheme.surface.withValues(alpha: 0.95), - borderRadius: BorderRadius.circular(12), - boxShadow: const [ - BoxShadow( - color: Colors.black54, - blurRadius: 20, - offset: Offset(0, 4), - ), - ], - ), - clipBehavior: Clip.antiAlias, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (widget.imageUrl != null) - SizedBox( - height: 120, - child: CachedNetworkImage( - imageUrl: widget.imageUrl!, - fit: BoxFit.cover, - errorWidget: (_, _, _) => - Container(color: AppColorScheme.surfaceVariant), - ), + bottom: bottomOffset, + child: Offstage( + offstage: widget.isHidden, + child: Container( + width: 340, + decoration: BoxDecoration( + color: AppColorScheme.surface.withValues(alpha: 0.95), + borderRadius: BorderRadius.circular(12), + boxShadow: const [ + BoxShadow( + color: Colors.black54, + blurRadius: 20, + offset: Offset(0, 4), ), - Padding( - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - l10n.upNext, - style: const TextStyle( - color: Colors.white54, - fontSize: 12, - fontWeight: FontWeight.w600, + ], + ), + clipBehavior: Clip.antiAlias, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (widget.imageUrl != null) + SizedBox( + height: 120, + child: CachedNetworkImage( + imageUrl: widget.imageUrl!, + fit: BoxFit.cover, + errorWidget: (_, _, _) => + Container(color: AppColorScheme.surfaceVariant), + ), + ), + Padding( + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + l10n.upNext, + style: const TextStyle( + color: Colors.white54, + fontSize: 12, + fontWeight: FontWeight.w600, + ), ), + if (showTimer && widget.timeoutMs > 0) + AnimatedBuilder( + animation: _countdownController, + builder: (context, _) { + final remainingMs = widget.timeoutMs * (1.0 - _countdownController.value); + final remainingSeconds = (remainingMs / 1000).ceil(); + final int minutes = remainingSeconds ~/ 60; + final int secs = remainingSeconds % 60; + final timerText = remainingSeconds >= 60 + ? '$minutes:${secs.toString().padLeft(2, '0')}' + : ':${secs.toString().padLeft(2, '0')}'; + return Text( + l10n.endsIn(timerText), + style: const TextStyle( + color: Colors.white54, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ); + }, + ), + ], + ), + const SizedBox(height: 4), + Text( + [epInfo, item.name] + .where((s) => s != null && s.isNotEmpty) + .join(' — '), + style: const TextStyle( + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w600, ), - if (showTimer && widget.timeoutMs > 0) - AnimatedBuilder( - animation: _countdownController, - builder: (context, _) { - final remainingMs = widget.timeoutMs * (1.0 - _countdownController.value); - final remainingSeconds = (remainingMs / 1000).ceil(); - final int minutes = remainingSeconds ~/ 60; - final int secs = remainingSeconds % 60; - final timerText = remainingSeconds >= 60 - ? '$minutes:${secs.toString().padLeft(2, '0')}' - : ':${secs.toString().padLeft(2, '0')}'; - return Text( - l10n.endsIn(timerText), - style: const TextStyle( - color: Colors.white54, - fontSize: 12, - fontWeight: FontWeight.w600, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: Focus( + focusNode: widget.focusNode, + onFocusChange: (focused) { + if (_playFocused != focused) { + setState(() => _playFocused = focused); + } + }, + onKeyEvent: (_, event) { + if (event is KeyDownEvent && + (event.logicalKey == LogicalKeyboardKey.select || + event.logicalKey == LogicalKeyboardKey.enter)) { + widget.onPlayNext(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + }, + child: ElevatedButton( + autofocus: widget.focusNode == null, + onPressed: widget.onPlayNext, + style: ElevatedButton.styleFrom( + backgroundColor: tvFocusMode + ? (_playFocused + ? AppColorScheme.accent + : AppColorScheme.surfaceVariant.withValues(alpha: 0.9)) + : AppColorScheme.accent, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 10), ), - ); - }, + child: Text(l10n.playNext), + ), + ), ), - ], - ), - const SizedBox(height: 4), - Text( - [epInfo, item.name] - .where((s) => s != null && s.isNotEmpty) - .join(' — '), - style: const TextStyle( - color: Colors.white, - fontSize: 15, - fontWeight: FontWeight.w600, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 12), - Row( - children: [ - Expanded( - child: Focus( - focusNode: widget.focusNode, + const SizedBox(width: 8), + Focus( + focusNode: widget.dismissFocusNode, onFocusChange: (focused) { - if (_playFocused != focused) { - setState(() => _playFocused = focused); + if (_dismissFocused != focused) { + setState(() => _dismissFocused = focused); } }, onKeyEvent: (_, event) { if (event is KeyDownEvent && (event.logicalKey == LogicalKeyboardKey.select || event.logicalKey == LogicalKeyboardKey.enter)) { - widget.onPlayNext(); + widget.onDismiss(); return KeyEventResult.handled; } return KeyEventResult.ignored; }, - child: ElevatedButton( - autofocus: widget.focusNode == null, - onPressed: widget.onPlayNext, - style: ElevatedButton.styleFrom( - backgroundColor: tvFocusMode - ? (_playFocused - ? AppColorScheme.accent - : AppColorScheme.surfaceVariant.withValues(alpha: 0.9)) - : AppColorScheme.accent, + child: OutlinedButton( + onPressed: widget.onDismiss, + style: OutlinedButton.styleFrom( foregroundColor: Colors.white, + backgroundColor: _dismissFocused + ? AppColorScheme.accent.withValues(alpha: 0.24) + : Colors.transparent, + side: _dismissFocused + ? ThemeRegistry.active.borders.focusBorder + : ThemeRegistry.active.borders.chipBorder, padding: const EdgeInsets.symmetric(vertical: 10), ), - child: Text(l10n.playNext), - ), - ), - ), - const SizedBox(width: 8), - Focus( - focusNode: widget.dismissFocusNode, - onFocusChange: (focused) { - if (_dismissFocused != focused) { - setState(() => _dismissFocused = focused); - } - }, - onKeyEvent: (_, event) { - if (event is KeyDownEvent && - (event.logicalKey == LogicalKeyboardKey.select || - event.logicalKey == LogicalKeyboardKey.enter)) { - widget.onDismiss(); - return KeyEventResult.handled; - } - return KeyEventResult.ignored; - }, - child: OutlinedButton( - onPressed: widget.onDismiss, - style: OutlinedButton.styleFrom( - foregroundColor: Colors.white, - backgroundColor: _dismissFocused - ? AppColorScheme.accent.withValues(alpha: 0.24) - : Colors.transparent, - side: _dismissFocused - ? ThemeRegistry.active.borders.focusBorder - : ThemeRegistry.active.borders.chipBorder, - padding: const EdgeInsets.symmetric(vertical: 10), + child: Text(l10n.close), ), - child: Text(l10n.close), ), - ), - ], - ), - ], - ), - ), - if (showProgressBar && widget.timeoutMs > 0) - AnimatedBuilder( - animation: _countdownController, - builder: (context, _) => LinearProgressIndicator( - value: 1.0 - _countdownController.value, - backgroundColor: Colors.transparent, - color: AppColorScheme.accent, - minHeight: 6, + ], + ), + ], ), ), - ], + if (showProgressBar && widget.timeoutMs > 0) + AnimatedBuilder( + animation: _countdownController, + builder: (context, _) => LinearProgressIndicator( + value: 1.0 - _countdownController.value, + backgroundColor: Colors.transparent, + color: AppColorScheme.accent, + minHeight: 6, + ), + ), + ], + ), ), ), ); diff --git a/lib/ui/widgets/playback/skip_segment_overlay.dart b/lib/ui/widgets/playback/skip_segment_overlay.dart index c4110c1e4..7faa11d08 100644 --- a/lib/ui/widgets/playback/skip_segment_overlay.dart +++ b/lib/ui/widgets/playback/skip_segment_overlay.dart @@ -18,6 +18,7 @@ class SkipSegmentOverlay extends StatefulWidget { final VoidCallback onDismiss; final FocusNode? focusNode; final Stream? positionStream; + final bool isHidden; const SkipSegmentOverlay({ super.key, @@ -26,6 +27,7 @@ class SkipSegmentOverlay extends StatefulWidget { required this.onDismiss, this.focusNode, this.positionStream, + this.isHidden = false, }); @override @@ -82,7 +84,9 @@ class _SkipSegmentOverlayState extends State { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); + final isTV = PlatformDetection.isTV; final isDesktop = PlatformDetection.useDesktopUi; + final bottomOffset = isTV ? 190.0 : (isDesktop ? 140.0 : 100.0); final prefs = GetIt.instance(); final mediaSegmentCountdown = prefs.get(UserPreferences.mediaSegmentCountdown); @@ -114,97 +118,100 @@ class _SkipSegmentOverlayState extends State { return Positioned( right: 24, - bottom: 120, - child: Material( - color: Colors.transparent, - child: Focus( - focusNode: widget.focusNode, - onKeyEvent: (_, event) { - if (widget.focusNode == null) { + bottom: bottomOffset, + child: Offstage( + offstage: widget.isHidden, + child: Material( + color: Colors.transparent, + child: Focus( + focusNode: widget.focusNode, + onKeyEvent: (_, event) { + if (widget.focusNode == null) { + return KeyEventResult.ignored; + } + if (event is KeyDownEvent && + (event.logicalKey == LogicalKeyboardKey.select || + event.logicalKey == LogicalKeyboardKey.enter)) { + widget.onSkip(); + return KeyEventResult.handled; + } return KeyEventResult.ignored; - } - if (event is KeyDownEvent && - (event.logicalKey == LogicalKeyboardKey.select || - event.logicalKey == LogicalKeyboardKey.enter)) { - widget.onSkip(); - return KeyEventResult.handled; - } - return KeyEventResult.ignored; - }, - child: Stack( - children: [ - InkWell( - onTap: widget.onSkip, - borderRadius: BorderRadius.circular(8), - child: Container( - width: isDesktop ? 320.0 : 280.0, - clipBehavior: Clip.antiAlias, - decoration: FocusTheme.focusDecoration( - isFocused: true, - radius: 8, - color: AppColorScheme.accent, - backgroundColor: AppColorScheme.surface.withValues( - alpha: 0.9, + }, + child: Stack( + children: [ + InkWell( + onTap: widget.onSkip, + borderRadius: BorderRadius.circular(8), + child: Container( + width: isDesktop ? 320.0 : 280.0, + clipBehavior: Clip.antiAlias, + decoration: FocusTheme.focusDecoration( + isFocused: true, + radius: 8, + color: AppColorScheme.accent, + backgroundColor: AppColorScheme.surface.withValues( + alpha: 0.9, + ), ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: EdgeInsets.fromLTRB(20, 12, isDesktop ? 44 : 20, 12), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - '${l10n.skipSegment(widget.segment.type.displayName)}$timerSuffix', - style: TextStyle( - color: AppColorScheme.onSurface, - fontSize: 15, - fontWeight: FontWeight.w600, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: EdgeInsets.fromLTRB(20, 12, isDesktop ? 44 : 20, 12), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '${l10n.skipSegment(widget.segment.type.displayName)}$timerSuffix', + style: TextStyle( + color: AppColorScheme.onSurface, + fontSize: 15, + fontWeight: FontWeight.w600, + ), ), - ), - const SizedBox(width: 8), - Icon( - Icons.skip_next_rounded, - color: AppColorScheme.accent, - size: 22, - ), - ], - ), - ), - if (showProgressBar) - LinearProgressIndicator( - value: progress, - backgroundColor: Colors.transparent, - color: AppColorScheme.accent, - minHeight: 6, + const SizedBox(width: 8), + Icon( + Icons.skip_next_rounded, + color: AppColorScheme.accent, + size: 22, + ), + ], + ), ), - ], + if (showProgressBar) + LinearProgressIndicator( + value: progress, + backgroundColor: Colors.transparent, + color: AppColorScheme.accent, + minHeight: 6, + ), + ], + ), ), ), - ), - if (isDesktop) - Positioned( - top: 4, - right: 4, - child: IconButton( - onPressed: widget.onDismiss, - tooltip: l10n.close, - padding: EdgeInsets.zero, - visualDensity: VisualDensity.compact, - constraints: const BoxConstraints.tightFor( - width: 24, - height: 24, - ), - icon: Icon( - Icons.close_rounded, - size: 16, - color: AppColorScheme.onSurface, + if (isDesktop) + Positioned( + top: 4, + right: 4, + child: IconButton( + onPressed: widget.onDismiss, + tooltip: l10n.close, + padding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + constraints: const BoxConstraints.tightFor( + width: 24, + height: 24, + ), + icon: Icon( + Icons.close_rounded, + size: 16, + color: AppColorScheme.onSurface, + ), ), ), - ), - ], + ], + ), ), ), ),