diff --git a/lib/i18n/da.i18n.json b/lib/i18n/da.i18n.json index dc3aa721..65f15111 100644 --- a/lib/i18n/da.i18n.json +++ b/lib/i18n/da.i18n.json @@ -374,7 +374,13 @@ "chapters": "Kapitler", "noChaptersAvailable": "Ingen kapitler tilgængelige", "queue": "Kø", - "noQueueItems": "Ingen elementer i køen" + "noQueueItems": "Ingen elementer i køen", + "searchSubtitles": "Søg undertekster", + "language": "Sprog", + "noSubtitlesFound": "Ingen undertekster fundet", + "subtitleDownloaded": "Undertekst downloadet", + "subtitleDownloadFailed": "Kunne ikke downloade undertekst", + "searchLanguages": "Søg sprog..." }, "userStatus": { "admin": "Administrator", diff --git a/lib/i18n/de.i18n.json b/lib/i18n/de.i18n.json index 19aecaca..f534e071 100644 --- a/lib/i18n/de.i18n.json +++ b/lib/i18n/de.i18n.json @@ -374,7 +374,13 @@ "chapters": "Kapitel", "noChaptersAvailable": "Keine Kapitel verfügbar", "queue": "Warteschlange", - "noQueueItems": "Keine Elemente in der Warteschlange" + "noQueueItems": "Keine Elemente in der Warteschlange", + "searchSubtitles": "Untertitel suchen", + "language": "Sprache", + "noSubtitlesFound": "Keine Untertitel gefunden", + "subtitleDownloaded": "Untertitel heruntergeladen", + "subtitleDownloadFailed": "Untertitel konnte nicht heruntergeladen werden", + "searchLanguages": "Sprachen suchen..." }, "userStatus": { "admin": "Eigentümer", diff --git a/lib/i18n/en.i18n.json b/lib/i18n/en.i18n.json index 19839e49..0a0cbb00 100644 --- a/lib/i18n/en.i18n.json +++ b/lib/i18n/en.i18n.json @@ -374,7 +374,13 @@ "chapters": "Chapters", "noChaptersAvailable": "No chapters available", "queue": "Queue", - "noQueueItems": "No items in queue" + "noQueueItems": "No items in queue", + "searchSubtitles": "Search Subtitles", + "language": "Language", + "noSubtitlesFound": "No subtitles found", + "subtitleDownloaded": "Subtitle downloaded", + "subtitleDownloadFailed": "Failed to download subtitle", + "searchLanguages": "Search languages..." }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/es.i18n.json b/lib/i18n/es.i18n.json index 1bde66ed..8d584c19 100644 --- a/lib/i18n/es.i18n.json +++ b/lib/i18n/es.i18n.json @@ -374,7 +374,13 @@ "chapters": "Capítulos", "noChaptersAvailable": "No hay capítulos disponibles", "queue": "Cola", - "noQueueItems": "No hay elementos en la cola" + "noQueueItems": "No hay elementos en la cola", + "searchSubtitles": "Buscar subtítulos", + "language": "Idioma", + "noSubtitlesFound": "No se encontraron subtítulos", + "subtitleDownloaded": "Subtítulo descargado", + "subtitleDownloadFailed": "Error al descargar subtítulo", + "searchLanguages": "Buscar idiomas..." }, "userStatus": { "admin": "Administrador", diff --git a/lib/i18n/fr.i18n.json b/lib/i18n/fr.i18n.json index 5e1a8e83..7ff7453b 100644 --- a/lib/i18n/fr.i18n.json +++ b/lib/i18n/fr.i18n.json @@ -374,7 +374,13 @@ "chapters": "Chapitres", "noChaptersAvailable": "Aucun chapitre disponible", "queue": "File d'attente", - "noQueueItems": "Aucun élément dans la file d'attente" + "noQueueItems": "Aucun élément dans la file d'attente", + "searchSubtitles": "Rechercher des sous-titres", + "language": "Langue", + "noSubtitlesFound": "Aucun sous-titre trouvé", + "subtitleDownloaded": "Sous-titre téléchargé", + "subtitleDownloadFailed": "Échec du téléchargement du sous-titre", + "searchLanguages": "Rechercher des langues..." }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/it.i18n.json b/lib/i18n/it.i18n.json index 193a4804..f4c6ba7d 100644 --- a/lib/i18n/it.i18n.json +++ b/lib/i18n/it.i18n.json @@ -374,7 +374,13 @@ "chapters": "Capitoli", "noChaptersAvailable": "Nessun capitolo disponibile", "queue": "Coda", - "noQueueItems": "Nessun elemento in coda" + "noQueueItems": "Nessun elemento in coda", + "searchSubtitles": "Cerca sottotitoli", + "language": "Lingua", + "noSubtitlesFound": "Nessun sottotitolo trovato", + "subtitleDownloaded": "Sottotitolo scaricato", + "subtitleDownloadFailed": "Impossibile scaricare il sottotitolo", + "searchLanguages": "Cerca lingue..." }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/ja.i18n.json b/lib/i18n/ja.i18n.json index f1674ad7..b280be95 100644 --- a/lib/i18n/ja.i18n.json +++ b/lib/i18n/ja.i18n.json @@ -374,7 +374,13 @@ "chapters": "チャプター", "noChaptersAvailable": "チャプターがありません", "queue": "キュー", - "noQueueItems": "キューにアイテムがありません" + "noQueueItems": "キューにアイテムがありません", + "searchSubtitles": "字幕を検索", + "language": "言語", + "noSubtitlesFound": "字幕が見つかりません", + "subtitleDownloaded": "字幕をダウンロードしました", + "subtitleDownloadFailed": "字幕のダウンロードに失敗しました", + "searchLanguages": "言語を検索..." }, "userStatus": { "admin": "管理者", diff --git a/lib/i18n/ko.i18n.json b/lib/i18n/ko.i18n.json index bc446387..f14b8a28 100644 --- a/lib/i18n/ko.i18n.json +++ b/lib/i18n/ko.i18n.json @@ -374,7 +374,13 @@ "chapters": "챕터", "noChaptersAvailable": "사용 가능한 챕터가 없습니다", "queue": "재생 대기열", - "noQueueItems": "대기열에 항목이 없습니다" + "noQueueItems": "대기열에 항목이 없습니다", + "searchSubtitles": "자막 검색", + "language": "언어", + "noSubtitlesFound": "자막을 찾을 수 없습니다", + "subtitleDownloaded": "자막이 다운로드되었습니다", + "subtitleDownloadFailed": "자막 다운로드에 실패했습니다", + "searchLanguages": "언어 검색..." }, "userStatus": { "admin": "관리자", diff --git a/lib/i18n/nb.i18n.json b/lib/i18n/nb.i18n.json index 78432219..a790ad52 100644 --- a/lib/i18n/nb.i18n.json +++ b/lib/i18n/nb.i18n.json @@ -374,7 +374,13 @@ "chapters": "Kapitler", "noChaptersAvailable": "Ingen kapitler tilgjengelig", "queue": "Kø", - "noQueueItems": "Ingen elementer i kø" + "noQueueItems": "Ingen elementer i kø", + "searchSubtitles": "Søk etter undertekster", + "language": "Språk", + "noSubtitlesFound": "Ingen undertekster funnet", + "subtitleDownloaded": "Undertekst lastet ned", + "subtitleDownloadFailed": "Kunne ikke laste ned undertekst", + "searchLanguages": "Søk etter språk..." }, "userStatus": { "admin": "Administrator", diff --git a/lib/i18n/nl.i18n.json b/lib/i18n/nl.i18n.json index 47bdc65a..54624080 100644 --- a/lib/i18n/nl.i18n.json +++ b/lib/i18n/nl.i18n.json @@ -374,7 +374,13 @@ "chapters": "Hoofdstukken", "noChaptersAvailable": "Geen hoofdstukken beschikbaar", "queue": "Wachtrij", - "noQueueItems": "Geen items in de wachtrij" + "noQueueItems": "Geen items in de wachtrij", + "searchSubtitles": "Ondertitels zoeken", + "language": "Taal", + "noSubtitlesFound": "Geen ondertitels gevonden", + "subtitleDownloaded": "Ondertitel gedownload", + "subtitleDownloadFailed": "Ondertitel downloaden mislukt", + "searchLanguages": "Talen zoeken..." }, "userStatus": { "admin": "Beheerder", diff --git a/lib/i18n/pl.i18n.json b/lib/i18n/pl.i18n.json index d8af82f3..a6e5bce9 100644 --- a/lib/i18n/pl.i18n.json +++ b/lib/i18n/pl.i18n.json @@ -374,7 +374,13 @@ "chapters": "Rozdziały", "noChaptersAvailable": "Brak dostępnych rozdziałów", "queue": "Kolejka", - "noQueueItems": "Brak elementów w kolejce" + "noQueueItems": "Brak elementów w kolejce", + "searchSubtitles": "Szukaj napisów", + "language": "Język", + "noSubtitlesFound": "Nie znaleziono napisów", + "subtitleDownloaded": "Napisy pobrane", + "subtitleDownloadFailed": "Nie udało się pobrać napisów", + "searchLanguages": "Szukaj języków..." }, "userStatus": { "admin": "Administrator", diff --git a/lib/i18n/pt.i18n.json b/lib/i18n/pt.i18n.json index cbf47fdc..30c6d553 100644 --- a/lib/i18n/pt.i18n.json +++ b/lib/i18n/pt.i18n.json @@ -374,7 +374,13 @@ "chapters": "Capítulos", "noChaptersAvailable": "Nenhum capítulo disponível", "queue": "Fila", - "noQueueItems": "Nenhum item na fila" + "noQueueItems": "Nenhum item na fila", + "searchSubtitles": "Pesquisar legendas", + "language": "Idioma", + "noSubtitlesFound": "Nenhuma legenda encontrada", + "subtitleDownloaded": "Legenda baixada", + "subtitleDownloadFailed": "Falha ao baixar legenda", + "searchLanguages": "Pesquisar idiomas..." }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/ru.i18n.json b/lib/i18n/ru.i18n.json index 089ef962..c89896fe 100644 --- a/lib/i18n/ru.i18n.json +++ b/lib/i18n/ru.i18n.json @@ -374,7 +374,13 @@ "chapters": "Главы", "noChaptersAvailable": "Главы недоступны", "queue": "Очередь", - "noQueueItems": "В очереди нет элементов" + "noQueueItems": "В очереди нет элементов", + "searchSubtitles": "Поиск субтитров", + "language": "Язык", + "noSubtitlesFound": "Субтитры не найдены", + "subtitleDownloaded": "Субтитры загружены", + "subtitleDownloadFailed": "Не удалось загрузить субтитры", + "searchLanguages": "Поиск языков..." }, "userStatus": { "admin": "Администратор", diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 740a01d8..30fc0d33 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 15 -/// Strings: 12375 (825 per locale) +/// Strings: 12465 (831 per locale) /// -/// Built on 2026-04-01 at 09:43 UTC +/// Built on 2026-04-01 at 15:18 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/lib/i18n/strings_da.g.dart b/lib/i18n/strings_da.g.dart index 951e9e62..e847496b 100644 --- a/lib/i18n/strings_da.g.dart +++ b/lib/i18n/strings_da.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsDa implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Ingen kapitler tilgængelige'; @override String get queue => 'Kø'; @override String get noQueueItems => 'Ingen elementer i køen'; + @override String get searchSubtitles => 'Søg undertekster'; + @override String get language => 'Sprog'; + @override String get noSubtitlesFound => 'Ingen undertekster fundet'; + @override String get subtitleDownloaded => 'Undertekst downloadet'; + @override String get subtitleDownloadFailed => 'Kunne ikke downloade undertekst'; + @override String get searchLanguages => 'Søg sprog...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsDa { 'videoControls.noChaptersAvailable' => 'Ingen kapitler tilgængelige', 'videoControls.queue' => 'Kø', 'videoControls.noQueueItems' => 'Ingen elementer i køen', + 'videoControls.searchSubtitles' => 'Søg undertekster', + 'videoControls.language' => 'Sprog', + 'videoControls.noSubtitlesFound' => 'Ingen undertekster fundet', + 'videoControls.subtitleDownloaded' => 'Undertekst downloadet', + 'videoControls.subtitleDownloadFailed' => 'Kunne ikke downloade undertekst', + 'videoControls.searchLanguages' => 'Søg sprog...', 'userStatus.admin' => 'Administrator', 'userStatus.restricted' => 'Begrænset', 'userStatus.protected' => 'Beskyttet', @@ -1828,14 +1840,14 @@ extension on TranslationsDa { 'liveTv.guide' => 'Guide', 'liveTv.noChannels' => 'Ingen kanaler tilgængelige', 'liveTv.noDvr' => 'Ingen DVR konfigureret på nogen server', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Ingen programdata tilgængelig', 'liveTv.live' => 'LIVE', 'liveTv.reloadGuide' => 'Genindlæs guide', 'liveTv.now' => 'Nu', 'liveTv.today' => 'I dag', 'liveTv.midnight' => 'Midnat', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Nat', 'liveTv.morning' => 'Morgen', 'liveTv.daytime' => 'Dagtid', diff --git a/lib/i18n/strings_de.g.dart b/lib/i18n/strings_de.g.dart index fa025b78..84cc5912 100644 --- a/lib/i18n/strings_de.g.dart +++ b/lib/i18n/strings_de.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsDe implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Keine Kapitel verfügbar'; @override String get queue => 'Warteschlange'; @override String get noQueueItems => 'Keine Elemente in der Warteschlange'; + @override String get searchSubtitles => 'Untertitel suchen'; + @override String get language => 'Sprache'; + @override String get noSubtitlesFound => 'Keine Untertitel gefunden'; + @override String get subtitleDownloaded => 'Untertitel heruntergeladen'; + @override String get subtitleDownloadFailed => 'Untertitel konnte nicht heruntergeladen werden'; + @override String get searchLanguages => 'Sprachen suchen...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsDe { 'videoControls.noChaptersAvailable' => 'Keine Kapitel verfügbar', 'videoControls.queue' => 'Warteschlange', 'videoControls.noQueueItems' => 'Keine Elemente in der Warteschlange', + 'videoControls.searchSubtitles' => 'Untertitel suchen', + 'videoControls.language' => 'Sprache', + 'videoControls.noSubtitlesFound' => 'Keine Untertitel gefunden', + 'videoControls.subtitleDownloaded' => 'Untertitel heruntergeladen', + 'videoControls.subtitleDownloadFailed' => 'Untertitel konnte nicht heruntergeladen werden', + 'videoControls.searchLanguages' => 'Sprachen suchen...', 'userStatus.admin' => 'Eigentümer', 'userStatus.restricted' => 'Eingeschränkt', 'userStatus.protected' => 'Geschützt', @@ -1828,14 +1840,14 @@ extension on TranslationsDe { 'liveTv.guide' => 'Programmführer', 'liveTv.noChannels' => 'Keine Kanäle verfügbar', 'liveTv.noDvr' => 'Kein DVR auf einem Server konfiguriert', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Keine Programmdaten verfügbar', 'liveTv.live' => 'LIVE', 'liveTv.reloadGuide' => 'Programmführer neu laden', 'liveTv.now' => 'Jetzt', 'liveTv.today' => 'Heute', 'liveTv.midnight' => 'Mitternacht', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Nacht', 'liveTv.morning' => 'Morgen', 'liveTv.daytime' => 'Tagsüber', diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index 9266e565..66792997 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -1162,6 +1162,24 @@ class TranslationsVideoControlsEn { /// en: 'No items in queue' String get noQueueItems => 'No items in queue'; + + /// en: 'Search Subtitles' + String get searchSubtitles => 'Search Subtitles'; + + /// en: 'Language' + String get language => 'Language'; + + /// en: 'No subtitles found' + String get noSubtitlesFound => 'No subtitles found'; + + /// en: 'Subtitle downloaded' + String get subtitleDownloaded => 'Subtitle downloaded'; + + /// en: 'Failed to download subtitle' + String get subtitleDownloadFailed => 'Failed to download subtitle'; + + /// en: 'Search languages...' + String get searchLanguages => 'Search languages...'; } // Path: userStatus @@ -3326,6 +3344,12 @@ extension on Translations { 'videoControls.noChaptersAvailable' => 'No chapters available', 'videoControls.queue' => 'Queue', 'videoControls.noQueueItems' => 'No items in queue', + 'videoControls.searchSubtitles' => 'Search Subtitles', + 'videoControls.language' => 'Language', + 'videoControls.noSubtitlesFound' => 'No subtitles found', + 'videoControls.subtitleDownloaded' => 'Subtitle downloaded', + 'videoControls.subtitleDownloadFailed' => 'Failed to download subtitle', + 'videoControls.searchLanguages' => 'Search languages...', 'userStatus.admin' => 'Admin', 'userStatus.restricted' => 'Restricted', 'userStatus.protected' => 'Protected', @@ -3485,14 +3509,14 @@ extension on Translations { 'liveTv.guide' => 'Guide', 'liveTv.noChannels' => 'No channels available', 'liveTv.noDvr' => 'No DVR configured on any server', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'No program data available', 'liveTv.live' => 'LIVE', 'liveTv.reloadGuide' => 'Reload Guide', 'liveTv.now' => 'Now', 'liveTv.today' => 'Today', 'liveTv.midnight' => 'Midnight', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Overnight', 'liveTv.morning' => 'Morning', 'liveTv.daytime' => 'Daytime', diff --git a/lib/i18n/strings_es.g.dart b/lib/i18n/strings_es.g.dart index b5230110..a287ea8b 100644 --- a/lib/i18n/strings_es.g.dart +++ b/lib/i18n/strings_es.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsEs implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'No hay capítulos disponibles'; @override String get queue => 'Cola'; @override String get noQueueItems => 'No hay elementos en la cola'; + @override String get searchSubtitles => 'Buscar subtítulos'; + @override String get language => 'Idioma'; + @override String get noSubtitlesFound => 'No se encontraron subtítulos'; + @override String get subtitleDownloaded => 'Subtítulo descargado'; + @override String get subtitleDownloadFailed => 'Error al descargar subtítulo'; + @override String get searchLanguages => 'Buscar idiomas...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsEs { 'videoControls.noChaptersAvailable' => 'No hay capítulos disponibles', 'videoControls.queue' => 'Cola', 'videoControls.noQueueItems' => 'No hay elementos en la cola', + 'videoControls.searchSubtitles' => 'Buscar subtítulos', + 'videoControls.language' => 'Idioma', + 'videoControls.noSubtitlesFound' => 'No se encontraron subtítulos', + 'videoControls.subtitleDownloaded' => 'Subtítulo descargado', + 'videoControls.subtitleDownloadFailed' => 'Error al descargar subtítulo', + 'videoControls.searchLanguages' => 'Buscar idiomas...', 'userStatus.admin' => 'Administrador', 'userStatus.restricted' => 'Restringido', 'userStatus.protected' => 'Protegido', @@ -1828,14 +1840,14 @@ extension on TranslationsEs { 'liveTv.guide' => 'Guía', 'liveTv.noChannels' => 'No hay canales disponibles', 'liveTv.noDvr' => 'No hay DVR configurado en ningún servidor', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'No hay datos de programación disponibles', 'liveTv.live' => 'EN VIVO', 'liveTv.reloadGuide' => 'Recargar guía', 'liveTv.now' => 'Ahora', 'liveTv.today' => 'Hoy', 'liveTv.midnight' => 'Medianoche', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Madrugada', 'liveTv.morning' => 'Mañana', 'liveTv.daytime' => 'Día', diff --git a/lib/i18n/strings_fr.g.dart b/lib/i18n/strings_fr.g.dart index 248ec2dd..b79554c8 100644 --- a/lib/i18n/strings_fr.g.dart +++ b/lib/i18n/strings_fr.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsFr implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Aucun chapitre disponible'; @override String get queue => 'File d\'attente'; @override String get noQueueItems => 'Aucun élément dans la file d\'attente'; + @override String get searchSubtitles => 'Rechercher des sous-titres'; + @override String get language => 'Langue'; + @override String get noSubtitlesFound => 'Aucun sous-titre trouvé'; + @override String get subtitleDownloaded => 'Sous-titre téléchargé'; + @override String get subtitleDownloadFailed => 'Échec du téléchargement du sous-titre'; + @override String get searchLanguages => 'Rechercher des langues...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsFr { 'videoControls.noChaptersAvailable' => 'Aucun chapitre disponible', 'videoControls.queue' => 'File d\'attente', 'videoControls.noQueueItems' => 'Aucun élément dans la file d\'attente', + 'videoControls.searchSubtitles' => 'Rechercher des sous-titres', + 'videoControls.language' => 'Langue', + 'videoControls.noSubtitlesFound' => 'Aucun sous-titre trouvé', + 'videoControls.subtitleDownloaded' => 'Sous-titre téléchargé', + 'videoControls.subtitleDownloadFailed' => 'Échec du téléchargement du sous-titre', + 'videoControls.searchLanguages' => 'Rechercher des langues...', 'userStatus.admin' => 'Admin', 'userStatus.restricted' => 'Restreint', 'userStatus.protected' => 'Protégé', @@ -1828,14 +1840,14 @@ extension on TranslationsFr { 'liveTv.guide' => 'Guide', 'liveTv.noChannels' => 'Aucune chaîne disponible', 'liveTv.noDvr' => 'Aucun DVR configuré sur les serveurs', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Aucune donnée de programme disponible', 'liveTv.live' => 'EN DIRECT', 'liveTv.reloadGuide' => 'Recharger le guide', 'liveTv.now' => 'Maintenant', 'liveTv.today' => 'Aujourd\'hui', 'liveTv.midnight' => 'Minuit', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Nuit', 'liveTv.morning' => 'Matin', 'liveTv.daytime' => 'Journée', diff --git a/lib/i18n/strings_it.g.dart b/lib/i18n/strings_it.g.dart index db40ee81..8a8c74dd 100644 --- a/lib/i18n/strings_it.g.dart +++ b/lib/i18n/strings_it.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsIt implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Nessun capitolo disponibile'; @override String get queue => 'Coda'; @override String get noQueueItems => 'Nessun elemento in coda'; + @override String get searchSubtitles => 'Cerca sottotitoli'; + @override String get language => 'Lingua'; + @override String get noSubtitlesFound => 'Nessun sottotitolo trovato'; + @override String get subtitleDownloaded => 'Sottotitolo scaricato'; + @override String get subtitleDownloadFailed => 'Impossibile scaricare il sottotitolo'; + @override String get searchLanguages => 'Cerca lingue...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsIt { 'videoControls.noChaptersAvailable' => 'Nessun capitolo disponibile', 'videoControls.queue' => 'Coda', 'videoControls.noQueueItems' => 'Nessun elemento in coda', + 'videoControls.searchSubtitles' => 'Cerca sottotitoli', + 'videoControls.language' => 'Lingua', + 'videoControls.noSubtitlesFound' => 'Nessun sottotitolo trovato', + 'videoControls.subtitleDownloaded' => 'Sottotitolo scaricato', + 'videoControls.subtitleDownloadFailed' => 'Impossibile scaricare il sottotitolo', + 'videoControls.searchLanguages' => 'Cerca lingue...', 'userStatus.admin' => 'Admin', 'userStatus.restricted' => 'Limitato', 'userStatus.protected' => 'Protetto', @@ -1828,14 +1840,14 @@ extension on TranslationsIt { 'liveTv.guide' => 'Guida', 'liveTv.noChannels' => 'Nessun canale disponibile', 'liveTv.noDvr' => 'Nessun DVR configurato su nessun server', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Nessun dato di programma disponibile', 'liveTv.live' => 'IN DIRETTA', 'liveTv.reloadGuide' => 'Ricarica guida', 'liveTv.now' => 'Ora', 'liveTv.today' => 'Oggi', 'liveTv.midnight' => 'Mezzanotte', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Notte', 'liveTv.morning' => 'Mattina', 'liveTv.daytime' => 'Giorno', diff --git a/lib/i18n/strings_ja.g.dart b/lib/i18n/strings_ja.g.dart index ef571522..aed3a549 100644 --- a/lib/i18n/strings_ja.g.dart +++ b/lib/i18n/strings_ja.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsJa implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'チャプターがありません'; @override String get queue => 'キュー'; @override String get noQueueItems => 'キューにアイテムがありません'; + @override String get searchSubtitles => '字幕を検索'; + @override String get language => '言語'; + @override String get noSubtitlesFound => '字幕が見つかりません'; + @override String get subtitleDownloaded => '字幕をダウンロードしました'; + @override String get subtitleDownloadFailed => '字幕のダウンロードに失敗しました'; + @override String get searchLanguages => '言語を検索...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsJa { 'videoControls.noChaptersAvailable' => 'チャプターがありません', 'videoControls.queue' => 'キュー', 'videoControls.noQueueItems' => 'キューにアイテムがありません', + 'videoControls.searchSubtitles' => '字幕を検索', + 'videoControls.language' => '言語', + 'videoControls.noSubtitlesFound' => '字幕が見つかりません', + 'videoControls.subtitleDownloaded' => '字幕をダウンロードしました', + 'videoControls.subtitleDownloadFailed' => '字幕のダウンロードに失敗しました', + 'videoControls.searchLanguages' => '言語を検索...', 'userStatus.admin' => '管理者', 'userStatus.restricted' => '制限付き', 'userStatus.protected' => '保護済み', @@ -1828,14 +1840,14 @@ extension on TranslationsJa { 'liveTv.guide' => '番組表', 'liveTv.noChannels' => 'チャンネルがありません', 'liveTv.noDvr' => 'どのサーバーにもDVRが設定されていません', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => '番組データがありません', 'liveTv.live' => 'ライブ', 'liveTv.reloadGuide' => '番組表を再読込', 'liveTv.now' => '現在', 'liveTv.today' => '今日', 'liveTv.midnight' => '深夜', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => '深夜', 'liveTv.morning' => '朝', 'liveTv.daytime' => '昼', diff --git a/lib/i18n/strings_ko.g.dart b/lib/i18n/strings_ko.g.dart index 2e4e7bed..812e0d11 100644 --- a/lib/i18n/strings_ko.g.dart +++ b/lib/i18n/strings_ko.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsKo implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => '사용 가능한 챕터가 없습니다'; @override String get queue => '재생 대기열'; @override String get noQueueItems => '대기열에 항목이 없습니다'; + @override String get searchSubtitles => '자막 검색'; + @override String get language => '언어'; + @override String get noSubtitlesFound => '자막을 찾을 수 없습니다'; + @override String get subtitleDownloaded => '자막이 다운로드되었습니다'; + @override String get subtitleDownloadFailed => '자막 다운로드에 실패했습니다'; + @override String get searchLanguages => '언어 검색...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsKo { 'videoControls.noChaptersAvailable' => '사용 가능한 챕터가 없습니다', 'videoControls.queue' => '재생 대기열', 'videoControls.noQueueItems' => '대기열에 항목이 없습니다', + 'videoControls.searchSubtitles' => '자막 검색', + 'videoControls.language' => '언어', + 'videoControls.noSubtitlesFound' => '자막을 찾을 수 없습니다', + 'videoControls.subtitleDownloaded' => '자막이 다운로드되었습니다', + 'videoControls.subtitleDownloadFailed' => '자막 다운로드에 실패했습니다', + 'videoControls.searchLanguages' => '언어 검색...', 'userStatus.admin' => '관리자', 'userStatus.restricted' => '제한됨', 'userStatus.protected' => '보호됨', @@ -1828,14 +1840,14 @@ extension on TranslationsKo { 'liveTv.guide' => '편성표', 'liveTv.noChannels' => '사용 가능한 채널이 없습니다', 'liveTv.noDvr' => '서버에 DVR이 구성되어 있지 않습니다', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => '프로그램 데이터가 없습니다', 'liveTv.live' => '실시간', 'liveTv.reloadGuide' => '편성표 새로고침', 'liveTv.now' => '지금', 'liveTv.today' => '오늘', 'liveTv.midnight' => '자정', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => '심야', 'liveTv.morning' => '아침', 'liveTv.daytime' => '낮', diff --git a/lib/i18n/strings_nb.g.dart b/lib/i18n/strings_nb.g.dart index 8ec02718..412e49be 100644 --- a/lib/i18n/strings_nb.g.dart +++ b/lib/i18n/strings_nb.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsNb implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Ingen kapitler tilgjengelig'; @override String get queue => 'Kø'; @override String get noQueueItems => 'Ingen elementer i kø'; + @override String get searchSubtitles => 'Søk etter undertekster'; + @override String get language => 'Språk'; + @override String get noSubtitlesFound => 'Ingen undertekster funnet'; + @override String get subtitleDownloaded => 'Undertekst lastet ned'; + @override String get subtitleDownloadFailed => 'Kunne ikke laste ned undertekst'; + @override String get searchLanguages => 'Søk etter språk...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsNb { 'videoControls.noChaptersAvailable' => 'Ingen kapitler tilgjengelig', 'videoControls.queue' => 'Kø', 'videoControls.noQueueItems' => 'Ingen elementer i kø', + 'videoControls.searchSubtitles' => 'Søk etter undertekster', + 'videoControls.language' => 'Språk', + 'videoControls.noSubtitlesFound' => 'Ingen undertekster funnet', + 'videoControls.subtitleDownloaded' => 'Undertekst lastet ned', + 'videoControls.subtitleDownloadFailed' => 'Kunne ikke laste ned undertekst', + 'videoControls.searchLanguages' => 'Søk etter språk...', 'userStatus.admin' => 'Administrator', 'userStatus.restricted' => 'Begrenset', 'userStatus.protected' => 'Beskyttet', @@ -1828,14 +1840,14 @@ extension on TranslationsNb { 'liveTv.guide' => 'Programguide', 'liveTv.noChannels' => 'Ingen kanaler tilgjengelig', 'liveTv.noDvr' => 'Ingen DVR konfigurert på noen server', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Ingen programdata tilgjengelig', 'liveTv.live' => 'DIREKTE', 'liveTv.reloadGuide' => 'Last inn programguide på nytt', 'liveTv.now' => 'Nå', 'liveTv.today' => 'I dag', 'liveTv.midnight' => 'Midnatt', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Natt', 'liveTv.morning' => 'Morgen', 'liveTv.daytime' => 'Dagtid', diff --git a/lib/i18n/strings_nl.g.dart b/lib/i18n/strings_nl.g.dart index 67355bb6..a0ba79c2 100644 --- a/lib/i18n/strings_nl.g.dart +++ b/lib/i18n/strings_nl.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsNl implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Geen hoofdstukken beschikbaar'; @override String get queue => 'Wachtrij'; @override String get noQueueItems => 'Geen items in de wachtrij'; + @override String get searchSubtitles => 'Ondertitels zoeken'; + @override String get language => 'Taal'; + @override String get noSubtitlesFound => 'Geen ondertitels gevonden'; + @override String get subtitleDownloaded => 'Ondertitel gedownload'; + @override String get subtitleDownloadFailed => 'Ondertitel downloaden mislukt'; + @override String get searchLanguages => 'Talen zoeken...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsNl { 'videoControls.noChaptersAvailable' => 'Geen hoofdstukken beschikbaar', 'videoControls.queue' => 'Wachtrij', 'videoControls.noQueueItems' => 'Geen items in de wachtrij', + 'videoControls.searchSubtitles' => 'Ondertitels zoeken', + 'videoControls.language' => 'Taal', + 'videoControls.noSubtitlesFound' => 'Geen ondertitels gevonden', + 'videoControls.subtitleDownloaded' => 'Ondertitel gedownload', + 'videoControls.subtitleDownloadFailed' => 'Ondertitel downloaden mislukt', + 'videoControls.searchLanguages' => 'Talen zoeken...', 'userStatus.admin' => 'Beheerder', 'userStatus.restricted' => 'Beperkt', 'userStatus.protected' => 'Beschermd', @@ -1828,14 +1840,14 @@ extension on TranslationsNl { 'liveTv.guide' => 'Gids', 'liveTv.noChannels' => 'Geen zenders beschikbaar', 'liveTv.noDvr' => 'Geen DVR geconfigureerd op een server', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Geen programmagegevens beschikbaar', 'liveTv.live' => 'LIVE', 'liveTv.reloadGuide' => 'Gids herladen', 'liveTv.now' => 'Nu', 'liveTv.today' => 'Vandaag', 'liveTv.midnight' => 'Middernacht', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Nacht', 'liveTv.morning' => 'Ochtend', 'liveTv.daytime' => 'Overdag', diff --git a/lib/i18n/strings_pl.g.dart b/lib/i18n/strings_pl.g.dart index 6acc3b17..126c68f1 100644 --- a/lib/i18n/strings_pl.g.dart +++ b/lib/i18n/strings_pl.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsPl implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Brak dostępnych rozdziałów'; @override String get queue => 'Kolejka'; @override String get noQueueItems => 'Brak elementów w kolejce'; + @override String get searchSubtitles => 'Szukaj napisów'; + @override String get language => 'Język'; + @override String get noSubtitlesFound => 'Nie znaleziono napisów'; + @override String get subtitleDownloaded => 'Napisy pobrane'; + @override String get subtitleDownloadFailed => 'Nie udało się pobrać napisów'; + @override String get searchLanguages => 'Szukaj języków...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsPl { 'videoControls.noChaptersAvailable' => 'Brak dostępnych rozdziałów', 'videoControls.queue' => 'Kolejka', 'videoControls.noQueueItems' => 'Brak elementów w kolejce', + 'videoControls.searchSubtitles' => 'Szukaj napisów', + 'videoControls.language' => 'Język', + 'videoControls.noSubtitlesFound' => 'Nie znaleziono napisów', + 'videoControls.subtitleDownloaded' => 'Napisy pobrane', + 'videoControls.subtitleDownloadFailed' => 'Nie udało się pobrać napisów', + 'videoControls.searchLanguages' => 'Szukaj języków...', 'userStatus.admin' => 'Administrator', 'userStatus.restricted' => 'Ograniczony', 'userStatus.protected' => 'Chroniony', @@ -1828,14 +1840,14 @@ extension on TranslationsPl { 'liveTv.guide' => 'Przewodnik', 'liveTv.noChannels' => 'Brak dostępnych kanałów', 'liveTv.noDvr' => 'Brak skonfigurowanego DVR na żadnym serwerze', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Brak danych o programach', 'liveTv.live' => 'NA ŻYWO', 'liveTv.reloadGuide' => 'Odśwież przewodnik', 'liveTv.now' => 'Teraz', 'liveTv.today' => 'Dzisiaj', 'liveTv.midnight' => 'Północ', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Nocą', 'liveTv.morning' => 'Rano', 'liveTv.daytime' => 'W ciągu dnia', diff --git a/lib/i18n/strings_pt.g.dart b/lib/i18n/strings_pt.g.dart index 4282b871..9ffeabbc 100644 --- a/lib/i18n/strings_pt.g.dart +++ b/lib/i18n/strings_pt.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsPt implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Nenhum capítulo disponível'; @override String get queue => 'Fila'; @override String get noQueueItems => 'Nenhum item na fila'; + @override String get searchSubtitles => 'Pesquisar legendas'; + @override String get language => 'Idioma'; + @override String get noSubtitlesFound => 'Nenhuma legenda encontrada'; + @override String get subtitleDownloaded => 'Legenda baixada'; + @override String get subtitleDownloadFailed => 'Falha ao baixar legenda'; + @override String get searchLanguages => 'Pesquisar idiomas...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsPt { 'videoControls.noChaptersAvailable' => 'Nenhum capítulo disponível', 'videoControls.queue' => 'Fila', 'videoControls.noQueueItems' => 'Nenhum item na fila', + 'videoControls.searchSubtitles' => 'Pesquisar legendas', + 'videoControls.language' => 'Idioma', + 'videoControls.noSubtitlesFound' => 'Nenhuma legenda encontrada', + 'videoControls.subtitleDownloaded' => 'Legenda baixada', + 'videoControls.subtitleDownloadFailed' => 'Falha ao baixar legenda', + 'videoControls.searchLanguages' => 'Pesquisar idiomas...', 'userStatus.admin' => 'Admin', 'userStatus.restricted' => 'Restrito', 'userStatus.protected' => 'Protegido', @@ -1828,14 +1840,14 @@ extension on TranslationsPt { 'liveTv.guide' => 'Guia', 'liveTv.noChannels' => 'Nenhum canal disponível', 'liveTv.noDvr' => 'Nenhum DVR configurado em nenhum servidor', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Nenhum dado de programação disponível', 'liveTv.live' => 'AO VIVO', 'liveTv.reloadGuide' => 'Recarregar Guia', 'liveTv.now' => 'Agora', 'liveTv.today' => 'Hoje', 'liveTv.midnight' => 'Meia-noite', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Madrugada', 'liveTv.morning' => 'Manhã', 'liveTv.daytime' => 'Dia', diff --git a/lib/i18n/strings_ru.g.dart b/lib/i18n/strings_ru.g.dart index 1ffe69e8..df2b3699 100644 --- a/lib/i18n/strings_ru.g.dart +++ b/lib/i18n/strings_ru.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsRu implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Главы недоступны'; @override String get queue => 'Очередь'; @override String get noQueueItems => 'В очереди нет элементов'; + @override String get searchSubtitles => 'Поиск субтитров'; + @override String get language => 'Язык'; + @override String get noSubtitlesFound => 'Субтитры не найдены'; + @override String get subtitleDownloaded => 'Субтитры загружены'; + @override String get subtitleDownloadFailed => 'Не удалось загрузить субтитры'; + @override String get searchLanguages => 'Поиск языков...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsRu { 'videoControls.noChaptersAvailable' => 'Главы недоступны', 'videoControls.queue' => 'Очередь', 'videoControls.noQueueItems' => 'В очереди нет элементов', + 'videoControls.searchSubtitles' => 'Поиск субтитров', + 'videoControls.language' => 'Язык', + 'videoControls.noSubtitlesFound' => 'Субтитры не найдены', + 'videoControls.subtitleDownloaded' => 'Субтитры загружены', + 'videoControls.subtitleDownloadFailed' => 'Не удалось загрузить субтитры', + 'videoControls.searchLanguages' => 'Поиск языков...', 'userStatus.admin' => 'Администратор', 'userStatus.restricted' => 'Ограниченный', 'userStatus.protected' => 'Защищённый', @@ -1828,14 +1840,14 @@ extension on TranslationsRu { 'liveTv.guide' => 'Программа', 'liveTv.noChannels' => 'Нет доступных каналов', 'liveTv.noDvr' => 'DVR не настроен ни на одном сервере', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Нет данных о программах', 'liveTv.live' => 'ЭФИР', 'liveTv.reloadGuide' => 'Перезагрузить программу', 'liveTv.now' => 'Сейчас', 'liveTv.today' => 'Сегодня', 'liveTv.midnight' => 'Полночь', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Ночь', 'liveTv.morning' => 'Утро', 'liveTv.daytime' => 'День', diff --git a/lib/i18n/strings_sv.g.dart b/lib/i18n/strings_sv.g.dart index d47d7fb5..7ca6452b 100644 --- a/lib/i18n/strings_sv.g.dart +++ b/lib/i18n/strings_sv.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsSv implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => 'Inga kapitel tillgängliga'; @override String get queue => 'Kö'; @override String get noQueueItems => 'Inga objekt i kön'; + @override String get searchSubtitles => 'Sök undertexter'; + @override String get language => 'Språk'; + @override String get noSubtitlesFound => 'Inga undertexter hittades'; + @override String get subtitleDownloaded => 'Undertext nedladdad'; + @override String get subtitleDownloadFailed => 'Kunde inte ladda ner undertext'; + @override String get searchLanguages => 'Sök språk...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsSv { 'videoControls.noChaptersAvailable' => 'Inga kapitel tillgängliga', 'videoControls.queue' => 'Kö', 'videoControls.noQueueItems' => 'Inga objekt i kön', + 'videoControls.searchSubtitles' => 'Sök undertexter', + 'videoControls.language' => 'Språk', + 'videoControls.noSubtitlesFound' => 'Inga undertexter hittades', + 'videoControls.subtitleDownloaded' => 'Undertext nedladdad', + 'videoControls.subtitleDownloadFailed' => 'Kunde inte ladda ner undertext', + 'videoControls.searchLanguages' => 'Sök språk...', 'userStatus.admin' => 'Admin', 'userStatus.restricted' => 'Begränsad', 'userStatus.protected' => 'Skyddad', @@ -1828,14 +1840,14 @@ extension on TranslationsSv { 'liveTv.guide' => 'Programguide', 'liveTv.noChannels' => 'Inga kanaler tillgängliga', 'liveTv.noDvr' => 'Ingen DVR konfigurerad på någon server', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => 'Ingen programdata tillgänglig', 'liveTv.live' => 'LIVE', 'liveTv.reloadGuide' => 'Ladda om programguide', 'liveTv.now' => 'Nu', 'liveTv.today' => 'Idag', 'liveTv.midnight' => 'Midnatt', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => 'Natt', 'liveTv.morning' => 'Morgon', 'liveTv.daytime' => 'Dagtid', diff --git a/lib/i18n/strings_zh.g.dart b/lib/i18n/strings_zh.g.dart index a62e9cc8..f52d25a0 100644 --- a/lib/i18n/strings_zh.g.dart +++ b/lib/i18n/strings_zh.g.dart @@ -515,6 +515,12 @@ class _TranslationsVideoControlsZh implements TranslationsVideoControlsEn { @override String get noChaptersAvailable => '没有可用的章节'; @override String get queue => '播放队列'; @override String get noQueueItems => '队列中没有项目'; + @override String get searchSubtitles => '搜索字幕'; + @override String get language => '语言'; + @override String get noSubtitlesFound => '未找到字幕'; + @override String get subtitleDownloaded => '字幕已下载'; + @override String get subtitleDownloadFailed => '字幕下载失败'; + @override String get searchLanguages => '搜索语言...'; } // Path: userStatus @@ -1669,6 +1675,12 @@ extension on TranslationsZh { 'videoControls.noChaptersAvailable' => '没有可用的章节', 'videoControls.queue' => '播放队列', 'videoControls.noQueueItems' => '队列中没有项目', + 'videoControls.searchSubtitles' => '搜索字幕', + 'videoControls.language' => '语言', + 'videoControls.noSubtitlesFound' => '未找到字幕', + 'videoControls.subtitleDownloaded' => '字幕已下载', + 'videoControls.subtitleDownloadFailed' => '字幕下载失败', + 'videoControls.searchLanguages' => '搜索语言...', 'userStatus.admin' => '管理员', 'userStatus.restricted' => '受限', 'userStatus.protected' => '受保护', @@ -1828,14 +1840,14 @@ extension on TranslationsZh { 'liveTv.guide' => '节目指南', 'liveTv.noChannels' => '没有可用的频道', 'liveTv.noDvr' => '没有服务器配置了DVR', + _ => null, + } ?? switch (path) { 'liveTv.noPrograms' => '没有可用的节目数据', 'liveTv.live' => '直播', 'liveTv.reloadGuide' => '重新加载节目指南', 'liveTv.now' => '现在', 'liveTv.today' => '今天', 'liveTv.midnight' => '午夜', - _ => null, - } ?? switch (path) { 'liveTv.overnight' => '凌晨', 'liveTv.morning' => '上午', 'liveTv.daytime' => '白天', diff --git a/lib/i18n/sv.i18n.json b/lib/i18n/sv.i18n.json index 4461433b..f1e5bdb7 100644 --- a/lib/i18n/sv.i18n.json +++ b/lib/i18n/sv.i18n.json @@ -374,7 +374,13 @@ "chapters": "Kapitel", "noChaptersAvailable": "Inga kapitel tillgängliga", "queue": "Kö", - "noQueueItems": "Inga objekt i kön" + "noQueueItems": "Inga objekt i kön", + "searchSubtitles": "Sök undertexter", + "language": "Språk", + "noSubtitlesFound": "Inga undertexter hittades", + "subtitleDownloaded": "Undertext nedladdad", + "subtitleDownloadFailed": "Kunde inte ladda ner undertext", + "searchLanguages": "Sök språk..." }, "userStatus": { "admin": "Admin", diff --git a/lib/i18n/zh.i18n.json b/lib/i18n/zh.i18n.json index d3194ee1..893e9216 100644 --- a/lib/i18n/zh.i18n.json +++ b/lib/i18n/zh.i18n.json @@ -374,7 +374,13 @@ "chapters": "章节", "noChaptersAvailable": "没有可用的章节", "queue": "播放队列", - "noQueueItems": "队列中没有项目" + "noQueueItems": "队列中没有项目", + "searchSubtitles": "搜索字幕", + "language": "语言", + "noSubtitlesFound": "未找到字幕", + "subtitleDownloaded": "字幕已下载", + "subtitleDownloadFailed": "字幕下载失败", + "searchLanguages": "搜索语言..." }, "userStatus": { "admin": "管理员", diff --git a/lib/models/plex_subtitle_search_result.dart b/lib/models/plex_subtitle_search_result.dart new file mode 100644 index 00000000..c18de92e --- /dev/null +++ b/lib/models/plex_subtitle_search_result.dart @@ -0,0 +1,63 @@ +class PlexSubtitleSearchResult { + final int id; + final String key; + final String? codec; + final String? language; + final String? languageCode; + final double? score; + final String? providerTitle; + final String? title; + final String? displayTitle; + final bool hearingImpaired; + final bool perfectMatch; + final bool downloaded; + final bool forced; + + PlexSubtitleSearchResult({ + required this.id, + required this.key, + this.codec, + this.language, + this.languageCode, + this.score, + this.providerTitle, + this.title, + this.displayTitle, + this.hearingImpaired = false, + this.perfectMatch = false, + this.downloaded = false, + this.forced = false, + }); + + factory PlexSubtitleSearchResult.fromJson(Map json) { + return PlexSubtitleSearchResult( + id: _parseInt(json['id']), + key: json['key']?.toString() ?? '', + codec: json['codec']?.toString(), + language: json['language']?.toString(), + languageCode: json['languageCode']?.toString(), + score: _parseDouble(json['score']), + providerTitle: json['providerTitle']?.toString(), + title: json['title']?.toString(), + displayTitle: json['displayTitle']?.toString(), + hearingImpaired: json['hearingImpaired'] == 1 || json['hearingImpaired'] == true, + perfectMatch: json['perfectMatch'] == 1 || json['perfectMatch'] == true, + downloaded: json['downloaded'] == 1 || json['downloaded'] == true, + forced: json['forced'] == 1 || json['forced'] == true, + ); + } + + static int _parseInt(dynamic v) { + if (v is int) return v; + if (v is num) return v.toInt(); + if (v is String) return int.tryParse(v) ?? 0; + return 0; + } + + static double? _parseDouble(dynamic v) { + if (v is double) return v; + if (v is num) return v.toDouble(); + if (v is String) return double.tryParse(v); + return null; + } +} diff --git a/lib/services/plex_client.dart b/lib/services/plex_client.dart index b480e8db..b3de88c7 100644 --- a/lib/services/plex_client.dart +++ b/lib/services/plex_client.dart @@ -20,6 +20,7 @@ import '../models/plex_first_character.dart'; import '../models/plex_hub.dart'; import '../models/plex_library.dart'; import '../models/plex_media_info.dart'; +import '../models/plex_subtitle_search_result.dart'; import '../models/plex_media_version.dart'; import '../models/plex_metadata.dart'; import '../utils/content_utils.dart'; @@ -931,6 +932,55 @@ class PlexClient { return true; } + /// Search for subtitles from external providers (e.g. OpenSubtitles) via the Plex server. + /// [language] is an ISO 639-1 two-letter code (e.g. "en", "es"). + Future> searchSubtitles( + String ratingKey, { + required String language, + String? title, + int hearingImpaired = 0, + int forced = 0, + }) async { + return _wrapListApiCall( + () => _dio.get('/library/metadata/$ratingKey/subtitles', queryParameters: { + 'language': language, + if (title != null && title.isNotEmpty) 'title': title, + 'hearingImpaired': hearingImpaired, + 'forced': forced, + }), + (response) { + final container = _getMediaContainer(response); + final streams = container?['Stream'] as List? ?? []; + return streams.map((s) => PlexSubtitleSearchResult.fromJson(s as Map)).toList(); + }, + 'Failed to search subtitles', + ); + } + + /// Download a subtitle from an external provider and add it to the media item. + /// The server downloads the file asynchronously; the new stream appears after a short delay. + Future downloadSubtitle( + String ratingKey, { + required String key, + required String codec, + required String language, + required bool hearingImpaired, + required bool forced, + required String providerTitle, + }) async { + return _wrapBoolApiCall( + () => _dio.put('/library/metadata/$ratingKey/subtitles', queryParameters: { + 'key': key, + 'codec': codec, + 'language': language, + 'hearingImpaired': hearingImpaired ? 1 : 0, + 'forced': forced ? 1 : 0, + 'providerTitle': providerTitle, + }), + 'Failed to download subtitle', + ); + } + /// Search across all libraries including individually shared items. /// Uses /library/search (same endpoint as Plex Web) which finds shared content. /// Only returns movies and shows, filtering out other types. diff --git a/lib/utils/language_codes.dart b/lib/utils/language_codes.dart index fb9c019c..90f79b52 100644 --- a/lib/utils/language_codes.dart +++ b/lib/utils/language_codes.dart @@ -102,6 +102,25 @@ class LanguageCodes { 'TW': 'Taiwan', }; + /// Get all available languages as (code, name) pairs, sorted by name. + /// Returns ISO 639-1 two-letter codes with their English names. + static List<({String code, String name})> getAllLanguages() { + if (_codes == null) return []; + final languages = <({String code, String name})>[]; + for (final entry in _codes!.entries) { + final code = entry.key; + // Skip locale variants like "en-GB" — only include base 2-letter codes + if (code.contains('-')) continue; + final map = entry.value as Map; + final name = map['name'] as String?; + if (name != null) { + languages.add((code: code, name: name)); + } + } + languages.sort((a, b) => a.name.compareTo(b.name)); + return languages; + } + /// Get the English name of a language from its code static String? getLanguageName(String languageCode) { if (_codes == null) return null; diff --git a/lib/widgets/video_controls/models/track_controls_state.dart b/lib/widgets/video_controls/models/track_controls_state.dart index 236b25dd..5672d256 100644 --- a/lib/widgets/video_controls/models/track_controls_state.dart +++ b/lib/widgets/video_controls/models/track_controls_state.dart @@ -40,6 +40,9 @@ class TrackControlsState { final bool subtitlesVisible; final bool showQueueButton; final Function(PlexMetadata)? onQueueItemSelected; + final String ratingKey; + final String? mediaTitle; + final Future Function()? onSubtitleDownloaded; const TrackControlsState({ this.availableVersions = const [], @@ -75,5 +78,8 @@ class TrackControlsState { this.subtitlesVisible = true, this.showQueueButton = false, this.onQueueItemSelected, + this.ratingKey = '', + this.mediaTitle, + this.onSubtitleDownloaded, }); } diff --git a/lib/widgets/video_controls/sheets/subtitle_search_sheet.dart b/lib/widgets/video_controls/sheets/subtitle_search_sheet.dart new file mode 100644 index 00000000..7d63f63b --- /dev/null +++ b/lib/widgets/video_controls/sheets/subtitle_search_sheet.dart @@ -0,0 +1,379 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/symbols.dart'; + +import '../../../i18n/strings.g.dart'; +import '../../../models/plex_subtitle_search_result.dart'; +import '../../../utils/language_codes.dart'; +import '../../../utils/provider_extensions.dart'; +import '../../../utils/snackbar_helper.dart'; +import '../../../widgets/app_icon.dart'; +import '../../../widgets/focusable_list_tile.dart'; +import '../../../widgets/overlay_sheet.dart'; +import 'base_video_control_sheet.dart'; + +class SubtitleSearchSheet extends StatefulWidget { + final String ratingKey; + final String serverId; + final String? mediaTitle; + final Future Function()? onSubtitleDownloaded; + + const SubtitleSearchSheet({ + super.key, + required this.ratingKey, + required this.serverId, + this.mediaTitle, + this.onSubtitleDownloaded, + }); + + @override + State createState() => _SubtitleSearchSheetState(); +} + +class _SubtitleSearchSheetState extends State { + String _languageCode = 'en'; + String _languageName = 'English'; + final _titleController = TextEditingController(); + Timer? _debounceTimer; + + List? _results; + bool _isSearching = false; + String? _error; + String? _downloadingKey; + + // Internal view switching instead of push/pop + bool _showLanguagePicker = false; + + @override + void initState() { + super.initState(); + _initDefaultLanguage(); + WidgetsBinding.instance.addPostFrameCallback((_) => _search()); + } + + void _initDefaultLanguage() { + final locale = WidgetsBinding.instance.platformDispatcher.locale; + final code = locale.languageCode; + final name = LanguageCodes.getLanguageName(code); + if (name != null) { + _languageCode = code; + _languageName = name; + } + } + + @override + void dispose() { + _debounceTimer?.cancel(); + _titleController.dispose(); + super.dispose(); + } + + Future _search() async { + if (!mounted) return; + setState(() { + _isSearching = true; + _error = null; + }); + + try { + final client = context.getClientForServer(widget.serverId); + final title = _titleController.text.trim(); + final results = await client.searchSubtitles( + widget.ratingKey, + language: _languageCode, + title: title.isEmpty ? null : title, + ); + if (!mounted) return; + setState(() { + _results = results; + _isSearching = false; + }); + } catch (e) { + if (!mounted) return; + setState(() { + _error = e.toString(); + _isSearching = false; + }); + } + } + + void _onTitleChanged(String _) { + _debounceTimer?.cancel(); + _debounceTimer = Timer(const Duration(milliseconds: 500), _search); + } + + void _onLanguageSelected(String code, String name) { + setState(() { + _languageCode = code; + _languageName = name; + _showLanguagePicker = false; + }); + _search(); + } + + Future _downloadSubtitle(PlexSubtitleSearchResult result) async { + if (_downloadingKey != null) return; + setState(() => _downloadingKey = result.key); + + try { + final client = context.getClientForServer(widget.serverId); + final success = await client.downloadSubtitle( + widget.ratingKey, + key: result.key, + codec: result.codec ?? 'srt', + language: result.languageCode ?? _languageCode, + hearingImpaired: result.hearingImpaired, + forced: result.forced, + providerTitle: result.providerTitle ?? '', + ); + + if (!mounted) return; + + if (success) { + await widget.onSubtitleDownloaded?.call(); + if (!mounted) return; + showSuccessSnackBar(context, t.videoControls.subtitleDownloaded); + OverlaySheetController.of(context).close(); + } else { + showErrorSnackBar(context, t.videoControls.subtitleDownloadFailed); + setState(() => _downloadingKey = null); + } + } catch (e) { + if (!mounted) return; + showErrorSnackBar(context, t.videoControls.subtitleDownloadFailed); + setState(() => _downloadingKey = null); + } + } + + @override + Widget build(BuildContext context) { + if (_showLanguagePicker) { + return _LanguagePickerView( + currentCode: _languageCode, + onSelected: _onLanguageSelected, + onBack: () => setState(() => _showLanguagePicker = false), + ); + } + + return BaseVideoControlSheet( + title: t.videoControls.searchSubtitles, + icon: Symbols.search_rounded, + onBack: () => OverlaySheetController.of(context).pop(), + child: Column( + children: [ + FocusableListTile( + leading: const AppIcon(Symbols.language_rounded), + title: Text(t.videoControls.language), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + _languageName, + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + const SizedBox(width: 4), + const AppIcon(Symbols.arrow_drop_down_rounded), + ], + ), + onTap: () => setState(() => _showLanguagePicker = true), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: TextField( + controller: _titleController, + decoration: InputDecoration( + hintText: widget.mediaTitle ?? 'Title', + isDense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + ), + onChanged: _onTitleChanged, + textInputAction: TextInputAction.search, + onSubmitted: (_) => _search(), + ), + ), + Divider(height: 1, color: Theme.of(context).dividerColor), + Expanded(child: _buildResults()), + ], + ), + ); + } + + Widget _buildResults() { + if (_isSearching) { + return const Center(child: CircularProgressIndicator()); + } + + if (_error != null) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16), + child: Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)), + ), + ); + } + + if (_results == null) { + return const SizedBox.shrink(); + } + + if (_results!.isEmpty) { + return Center( + child: Text( + t.videoControls.noSubtitlesFound, + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + ); + } + + return ListView.builder( + itemCount: _results!.length, + itemBuilder: (context, index) { + final result = _results![index]; + final isDownloading = _downloadingKey == result.key; + final colorScheme = Theme.of(context).colorScheme; + + Widget? trailing; + if (isDownloading) { + trailing = const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ); + } else { + final trailingChildren = []; + if (result.perfectMatch) { + trailingChildren.add( + const AppIcon(Symbols.star_rounded, fill: 1, color: Color(0xFFCC7B19), size: 16), + ); + } + if (result.score != null) { + trailingChildren.add(Text( + result.score!.toInt().toString(), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), + )); + } + if (trailingChildren.isNotEmpty) { + trailing = Row( + mainAxisSize: MainAxisSize.min, + spacing: 4, + children: trailingChildren, + ); + } + } + + return FocusableListTile( + title: Text( + result.title ?? result.displayTitle ?? 'Unknown', + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + result.displayTitle ?? '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: colorScheme.onSurfaceVariant), + ), + trailing: trailing, + onTap: isDownloading ? null : () => _downloadSubtitle(result), + ); + }, + ); + } +} + +/// Language picker rendered as an internal view, not a pushed page. +class _LanguagePickerView extends StatefulWidget { + final String currentCode; + final void Function(String code, String name) onSelected; + final VoidCallback onBack; + + const _LanguagePickerView({ + required this.currentCode, + required this.onSelected, + required this.onBack, + }); + + @override + State<_LanguagePickerView> createState() => _LanguagePickerViewState(); +} + +class _LanguagePickerViewState extends State<_LanguagePickerView> { + final _filterController = TextEditingController(); + late List<({String code, String name})> _allLanguages; + List<({String code, String name})> _filteredLanguages = []; + + @override + void initState() { + super.initState(); + _allLanguages = LanguageCodes.getAllLanguages(); + _filteredLanguages = _allLanguages; + } + + @override + void dispose() { + _filterController.dispose(); + super.dispose(); + } + + void _onFilterChanged(String query) { + final lower = query.toLowerCase(); + setState(() { + if (lower.isEmpty) { + _filteredLanguages = _allLanguages; + } else { + _filteredLanguages = _allLanguages.where((l) { + return l.name.toLowerCase().contains(lower) || l.code.toLowerCase().contains(lower); + }).toList(); + } + }); + } + + @override + Widget build(BuildContext context) { + return BaseVideoControlSheet( + title: t.videoControls.language, + icon: Symbols.language_rounded, + onBack: widget.onBack, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: TextField( + controller: _filterController, + autofocus: true, + decoration: InputDecoration( + hintText: t.videoControls.searchLanguages, + isDense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), + prefixIcon: const Icon(Symbols.search_rounded, size: 20), + ), + onChanged: _onFilterChanged, + ), + ), + Expanded( + child: ListView.builder( + itemCount: _filteredLanguages.length, + itemBuilder: (context, index) { + final lang = _filteredLanguages[index]; + final isSelected = lang.code == widget.currentCode; + return FocusableListTile( + title: Text( + lang.name, + style: TextStyle(color: isSelected ? Theme.of(context).colorScheme.primary : null), + ), + trailing: isSelected + ? AppIcon(Symbols.check_rounded, fill: 1, color: Theme.of(context).colorScheme.primary) + : null, + onTap: () => widget.onSelected(lang.code, lang.name), + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/video_controls/sheets/track_sheet.dart b/lib/widgets/video_controls/sheets/track_sheet.dart index 4e4f45e0..4193dabd 100644 --- a/lib/widgets/video_controls/sheets/track_sheet.dart +++ b/lib/widgets/video_controls/sheets/track_sheet.dart @@ -5,14 +5,21 @@ import '../../../mpv/mpv.dart'; import '../../../i18n/strings.g.dart'; import '../../../utils/scroll_utils.dart'; import '../../../utils/track_label_builder.dart'; +import '../../../widgets/app_icon.dart'; +import '../../../widgets/focusable_list_tile.dart'; import '../../../widgets/overlay_sheet.dart'; import 'base_video_control_sheet.dart'; +import 'subtitle_search_sheet.dart'; import '../helpers/track_filter_helper.dart'; import '../helpers/track_selection_helper.dart'; /// Combined bottom sheet for selecting audio and subtitle tracks side-by-side. class TrackSheet extends StatelessWidget { final Player player; + final String ratingKey; + final String serverId; + final String? mediaTitle; + final Future Function()? onSubtitleDownloaded; final Function(AudioTrack)? onAudioTrackChanged; final Function(SubtitleTrack)? onSubtitleTrackChanged; final Function(SubtitleTrack)? onSecondarySubtitleTrackChanged; @@ -20,6 +27,10 @@ class TrackSheet extends StatelessWidget { const TrackSheet({ super.key, required this.player, + this.ratingKey = '', + this.serverId = '', + this.mediaTitle, + this.onSubtitleDownloaded, this.onAudioTrackChanged, this.onSubtitleTrackChanged, this.onSecondarySubtitleTrackChanged, @@ -88,6 +99,10 @@ class TrackSheet extends StatelessWidget { tracks: subtitleTracks, selection: selection, player: player, + ratingKey: ratingKey, + serverId: serverId, + mediaTitle: mediaTitle, + onSubtitleDownloaded: onSubtitleDownloaded, onTrackChanged: onSubtitleTrackChanged, onSecondaryTrackChanged: onSecondarySubtitleTrackChanged, supportsSecondary: supportsSecondary, @@ -113,6 +128,10 @@ class TrackSheet extends StatelessWidget { tracks: subtitleTracks, selection: selection, player: player, + ratingKey: ratingKey, + serverId: serverId, + mediaTitle: mediaTitle, + onSubtitleDownloaded: onSubtitleDownloaded, onTrackChanged: onSubtitleTrackChanged, onSecondaryTrackChanged: onSecondarySubtitleTrackChanged, supportsSecondary: supportsSecondary, @@ -207,6 +226,10 @@ class _SubtitleColumn extends StatefulWidget { final List tracks; final TrackSelection selection; final Player player; + final String ratingKey; + final String serverId; + final String? mediaTitle; + final Future Function()? onSubtitleDownloaded; final Function(SubtitleTrack)? onTrackChanged; final Function(SubtitleTrack)? onSecondaryTrackChanged; final bool supportsSecondary; @@ -216,6 +239,10 @@ class _SubtitleColumn extends StatefulWidget { required this.tracks, required this.selection, required this.player, + this.ratingKey = '', + this.serverId = '', + this.mediaTitle, + this.onSubtitleDownloaded, this.onTrackChanged, this.onSecondaryTrackChanged, this.supportsSecondary = false, @@ -358,6 +385,23 @@ class _SubtitleColumnState extends State<_SubtitleColumn> { }, ), ), + if (widget.ratingKey.isNotEmpty) ...[ + Divider(height: 1, color: Theme.of(context).dividerColor), + FocusableListTile( + leading: const AppIcon(Symbols.search_rounded), + title: Text(t.videoControls.searchSubtitles), + onTap: () { + OverlaySheetController.of(context).push( + builder: (_) => SubtitleSearchSheet( + ratingKey: widget.ratingKey, + serverId: widget.serverId, + mediaTitle: widget.mediaTitle, + onSubtitleDownloaded: widget.onSubtitleDownloaded, + ), + ); + }, + ), + ], ], ); } diff --git a/lib/widgets/video_controls/video_controls.dart b/lib/widgets/video_controls/video_controls.dart index 4782b33e..e959fe0c 100644 --- a/lib/widgets/video_controls/video_controls.dart +++ b/lib/widgets/video_controls/video_controls.dart @@ -1170,6 +1170,9 @@ class _PlexVideoControlsState extends State with WindowListen subtitlesVisible: _subtitlesVisible, showQueueButton: playbackState.isQueueActive, onQueueItemSelected: playbackState.isQueueActive ? _onQueueItemSelected : null, + ratingKey: widget.metadata.ratingKey, + mediaTitle: widget.metadata.title, + onSubtitleDownloaded: _onSubtitleDownloaded, ); } @@ -1631,6 +1634,12 @@ class _PlexVideoControlsState extends State with WindowListen bool _handleGlobalKeyEvent(KeyEvent event) { if (!mounted) return false; + // When an overlay sheet is open (e.g. subtitle search with text fields), + // don't consume key events — let text input work normally. + if (OverlaySheetController.maybeOf(context)?.isOpen ?? false) { + return false; + } + // Back key fallback when _focusNode lost focus (TV, or desktop with nav on). // Focus.onKeyEvent won't fire if _focusNode lost focus, so handle ESC here. if ((_videoPlayerNavigationEnabled || PlatformDetector.isTV()) && event.logicalKey.isBackKey) { @@ -2428,6 +2437,51 @@ class _PlexVideoControlsState extends State with WindowListen videoPlayerState?.navigateToQueueItem(item); } + Future _onSubtitleDownloaded() async { + if (!mounted) return; + // Wait for the server to finish downloading the subtitle file + await Future.delayed(const Duration(seconds: 2)); + if (!mounted) return; + + try { + final client = _getClientForMetadata(); + final data = await client.getVideoPlaybackData(widget.metadata.ratingKey); + if (!mounted || data.mediaInfo == null) return; + + final token = client.config.token; + if (token == null) return; + + // Find external subtitle tracks from the refreshed metadata + final existingUris = widget.player.state.tracks.subtitle + .where((t) => t.uri != null) + .map((t) => t.uri!) + .toSet(); + + for (final plexTrack in data.mediaInfo!.subtitleTracks) { + if (!plexTrack.isExternal) continue; + final url = plexTrack.getSubtitleUrl(client.config.baseUrl, token); + if (url == null) continue; + // Skip tracks already loaded in the player + if (existingUris.any((uri) => uri.contains(plexTrack.key!))) continue; + + await widget.player.addSubtitleTrack( + uri: url, + title: plexTrack.displayTitle ?? plexTrack.language ?? 'Downloaded', + language: plexTrack.languageCode, + select: true, + ); + + // Save the selection on the server so it persists across sessions + final partId = data.mediaInfo!.partId; + if (partId != null) { + await client.selectStreams(partId, subtitleStreamID: plexTrack.id); + } + } + } catch (e) { + appLogger.w('Failed to refresh subtitles after download', error: e); + } + } + Future _switchMediaVersion(int newMediaIndex) async { if (newMediaIndex == widget.selectedMediaIndex) { return; // Already using this version diff --git a/lib/widgets/video_controls/widgets/track_chapter_controls.dart b/lib/widgets/video_controls/widgets/track_chapter_controls.dart index 192d873f..5dd1c4f6 100644 --- a/lib/widgets/video_controls/widgets/track_chapter_controls.dart +++ b/lib/widgets/video_controls/widgets/track_chapter_controls.dart @@ -97,6 +97,9 @@ class TrackChapterControls extends StatelessWidget { bool get subtitlesVisible => trackControlsState.subtitlesVisible; bool get showQueueButton => trackControlsState.showQueueButton; Function(PlexMetadata)? get onQueueItemSelected => trackControlsState.onQueueItemSelected; + String get ratingKey => trackControlsState.ratingKey; + String? get mediaTitle => trackControlsState.mediaTitle; + Future Function()? get onSubtitleDownloaded => trackControlsState.onSubtitleDownloaded; /// Handle key event for button navigation KeyEventResult _handleButtonKeyEvent(FocusNode _, KeyEvent event, int index, int totalButtons) { @@ -257,6 +260,10 @@ class TrackChapterControls extends StatelessWidget { .show( builder: (_) => TrackSheet( player: player, + ratingKey: ratingKey, + serverId: serverId, + mediaTitle: mediaTitle, + onSubtitleDownloaded: onSubtitleDownloaded, onAudioTrackChanged: onAudioTrackChanged, onSubtitleTrackChanged: onSubtitleTrackChanged, onSecondarySubtitleTrackChanged: onSecondarySubtitleTrackChanged,