From 0bb200a785bc0a2b391b50c0752f7c7719b471b9 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:43:00 +0100 Subject: [PATCH] feat: play from beginning context menu option close #761 --- lib/i18n/da.i18n.json | 3 ++- lib/i18n/de.i18n.json | 3 ++- lib/i18n/en.i18n.json | 3 ++- lib/i18n/es.i18n.json | 3 ++- lib/i18n/fr.i18n.json | 3 ++- lib/i18n/it.i18n.json | 3 ++- lib/i18n/ja.i18n.json | 3 ++- lib/i18n/ko.i18n.json | 3 ++- lib/i18n/nb.i18n.json | 3 ++- lib/i18n/nl.i18n.json | 3 ++- lib/i18n/pl.i18n.json | 3 ++- lib/i18n/pt.i18n.json | 3 ++- lib/i18n/ru.i18n.json | 3 ++- lib/i18n/strings.g.dart | 4 ++-- lib/i18n/strings_da.g.dart | 4 +++- lib/i18n/strings_de.g.dart | 4 +++- lib/i18n/strings_en.g.dart | 6 +++++- lib/i18n/strings_es.g.dart | 4 +++- lib/i18n/strings_fr.g.dart | 4 +++- lib/i18n/strings_it.g.dart | 4 +++- lib/i18n/strings_ja.g.dart | 4 +++- lib/i18n/strings_ko.g.dart | 4 +++- lib/i18n/strings_nb.g.dart | 4 +++- lib/i18n/strings_nl.g.dart | 4 +++- lib/i18n/strings_pl.g.dart | 4 +++- lib/i18n/strings_pt.g.dart | 4 +++- lib/i18n/strings_ru.g.dart | 4 +++- lib/i18n/strings_sv.g.dart | 4 +++- lib/i18n/strings_zh.g.dart | 4 +++- lib/i18n/sv.i18n.json | 3 ++- lib/i18n/zh.i18n.json | 3 ++- lib/widgets/media_context_menu.dart | 15 +++++++++++++++ 32 files changed, 94 insertions(+), 32 deletions(-) diff --git a/lib/i18n/da.i18n.json b/lib/i18n/da.i18n.json index 720299e1..9db3662f 100644 --- a/lib/i18n/da.i18n.json +++ b/lib/i18n/da.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Dette inkluderer alle episoder og deres filer.", "mediaDeletedSuccessfully": "Medieelement slettet", "mediaFailedToDelete": "Kunne ikke slette medieelement", - "rate": "Bedøm" + "rate": "Bedøm", + "playFromBeginning": "Afspil fra begyndelsen" }, "accessibility": { "mediaCardMovie": "${title}, film", diff --git a/lib/i18n/de.i18n.json b/lib/i18n/de.i18n.json index 960383f7..fdeffdb4 100644 --- a/lib/i18n/de.i18n.json +++ b/lib/i18n/de.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Dies umfasst alle Episoden und deren Dateien.", "mediaDeletedSuccessfully": "Medienelement erfolgreich gelöscht", "mediaFailedToDelete": "Löschen des Medienelements fehlgeschlagen", - "rate": "Bewerten" + "rate": "Bewerten", + "playFromBeginning": "Von Anfang an abspielen" }, "accessibility": { "mediaCardMovie": "${title}, Film", diff --git a/lib/i18n/en.i18n.json b/lib/i18n/en.i18n.json index 36d54bb4..38cedb34 100644 --- a/lib/i18n/en.i18n.json +++ b/lib/i18n/en.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "This includes all episodes and their files.", "mediaDeletedSuccessfully": "Media item deleted successfully", "mediaFailedToDelete": "Failed to delete media item", - "rate": "Rate" + "rate": "Rate", + "playFromBeginning": "Play from Beginning" }, "accessibility": { "mediaCardMovie": "${title}, movie", diff --git a/lib/i18n/es.i18n.json b/lib/i18n/es.i18n.json index 539c8de5..3302c07a 100644 --- a/lib/i18n/es.i18n.json +++ b/lib/i18n/es.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Esto incluye todos los episodios y sus archivos.", "mediaDeletedSuccessfully": "Elemento multimedia eliminado con éxito", "mediaFailedToDelete": "Error al eliminar el elemento multimedia", - "rate": "Calificar" + "rate": "Calificar", + "playFromBeginning": "Reproducir desde el inicio" }, "accessibility": { "mediaCardMovie": "${title}, película", diff --git a/lib/i18n/fr.i18n.json b/lib/i18n/fr.i18n.json index 3a81a7b1..ef75b83c 100644 --- a/lib/i18n/fr.i18n.json +++ b/lib/i18n/fr.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Cela inclut tous les épisodes et leurs fichiers.", "mediaDeletedSuccessfully": "Élément média supprimé avec succès", "mediaFailedToDelete": "Échec de la suppression de l'élément média", - "rate": "Noter" + "rate": "Noter", + "playFromBeginning": "Lire depuis le début" }, "accessibility": { "mediaCardMovie": "${title}, film", diff --git a/lib/i18n/it.i18n.json b/lib/i18n/it.i18n.json index 4007f629..2ea90778 100644 --- a/lib/i18n/it.i18n.json +++ b/lib/i18n/it.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Questo include tutti gli episodi e i loro file.", "mediaDeletedSuccessfully": "Elemento multimediale eliminato con successo", "mediaFailedToDelete": "Impossibile eliminare l'elemento multimediale", - "rate": "Valuta" + "rate": "Valuta", + "playFromBeginning": "Riproduci dall'inizio" }, "accessibility": { "mediaCardMovie": "${title}, film", diff --git a/lib/i18n/ja.i18n.json b/lib/i18n/ja.i18n.json index 5b369a35..bfb41a2c 100644 --- a/lib/i18n/ja.i18n.json +++ b/lib/i18n/ja.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "すべてのエピソードとそのファイルが含まれます。", "mediaDeletedSuccessfully": "メディアアイテムを正常に削除しました", "mediaFailedToDelete": "メディアアイテムの削除に失敗しました", - "rate": "評価" + "rate": "評価", + "playFromBeginning": "最初から再生" }, "accessibility": { "mediaCardMovie": "${title}、映画", diff --git a/lib/i18n/ko.i18n.json b/lib/i18n/ko.i18n.json index 3a4417b6..f164547a 100644 --- a/lib/i18n/ko.i18n.json +++ b/lib/i18n/ko.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "모든 에피소드와 파일이 포함됩니다.", "mediaDeletedSuccessfully": "미디어 항목이 성공적으로 삭제되었습니다", "mediaFailedToDelete": "미디어 항목 삭제 실패", - "rate": "평가" + "rate": "평가", + "playFromBeginning": "처음부터 재생" }, "accessibility": { "mediaCardMovie": "${title}, 영화", diff --git a/lib/i18n/nb.i18n.json b/lib/i18n/nb.i18n.json index de80c7b6..4a15b16c 100644 --- a/lib/i18n/nb.i18n.json +++ b/lib/i18n/nb.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Dette inkluderer alle episoder og deres filer.", "mediaDeletedSuccessfully": "Medieelement slettet", "mediaFailedToDelete": "Kunne ikke slette medieelement", - "rate": "Vurder" + "rate": "Vurder", + "playFromBeginning": "Spill fra begynnelsen" }, "accessibility": { "mediaCardMovie": "${title}, film", diff --git a/lib/i18n/nl.i18n.json b/lib/i18n/nl.i18n.json index 1a57bf0f..5b10ea3e 100644 --- a/lib/i18n/nl.i18n.json +++ b/lib/i18n/nl.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Dit omvat alle afleveringen en hun bestanden.", "mediaDeletedSuccessfully": "Media-item succesvol verwijderd", "mediaFailedToDelete": "Verwijderen van media-item mislukt", - "rate": "Beoordelen" + "rate": "Beoordelen", + "playFromBeginning": "Afspelen vanaf het begin" }, "accessibility": { "mediaCardMovie": "${title}, film", diff --git a/lib/i18n/pl.i18n.json b/lib/i18n/pl.i18n.json index a273e99e..bb431634 100644 --- a/lib/i18n/pl.i18n.json +++ b/lib/i18n/pl.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Obejmuje to wszystkie odcinki i ich pliki.", "mediaDeletedSuccessfully": "Element multimedialny usunięty pomyślnie", "mediaFailedToDelete": "Nie udało się usunąć elementu multimedialnego", - "rate": "Oceń" + "rate": "Oceń", + "playFromBeginning": "Odtwórz od początku" }, "accessibility": { "mediaCardMovie": "${title}, film", diff --git a/lib/i18n/pt.i18n.json b/lib/i18n/pt.i18n.json index c8d738f3..03229dc8 100644 --- a/lib/i18n/pt.i18n.json +++ b/lib/i18n/pt.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Isso inclui todos os episódios e seus arquivos.", "mediaDeletedSuccessfully": "Item de mídia excluído com sucesso", "mediaFailedToDelete": "Falha ao excluir item de mídia", - "rate": "Avaliar" + "rate": "Avaliar", + "playFromBeginning": "Reproduzir do início" }, "accessibility": { "mediaCardMovie": "${title}, filme", diff --git a/lib/i18n/ru.i18n.json b/lib/i18n/ru.i18n.json index 38b84bdf..95d704b5 100644 --- a/lib/i18n/ru.i18n.json +++ b/lib/i18n/ru.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Это включает все эпизоды и их файлы.", "mediaDeletedSuccessfully": "Медиаэлемент успешно удалён", "mediaFailedToDelete": "Не удалось удалить медиаэлемент", - "rate": "Оценить" + "rate": "Оценить", + "playFromBeginning": "Воспроизвести сначала" }, "accessibility": { "mediaCardMovie": "${title}, фильм", diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 0d3c193b..85cf7280 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: 12030 (802 per locale) +/// Strings: 12045 (803 per locale) /// -/// Built on 2026-03-27 at 07:32 UTC +/// Built on 2026-03-27 at 16:34 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 ab360e8c..698220ef 100644 --- a/lib/i18n/strings_da.g.dart +++ b/lib/i18n/strings_da.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuDa implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Medieelement slettet'; @override String get mediaFailedToDelete => 'Kunne ikke slette medieelement'; @override String get rate => 'Bedøm'; + @override String get playFromBeginning => 'Afspil fra begyndelsen'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsDa { 'mediaMenu.mediaDeletedSuccessfully' => 'Medieelement slettet', 'mediaMenu.mediaFailedToDelete' => 'Kunne ikke slette medieelement', 'mediaMenu.rate' => 'Bedøm', + 'mediaMenu.playFromBeginning' => 'Afspil fra begyndelsen', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, TV-serie', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsDa { 'liveTv.daytime' => 'Dagtid', 'liveTv.evening' => 'Aften', 'liveTv.lateNight' => 'Sen aften', - 'liveTv.whatsOn' => 'Hvad der kører', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'Hvad der kører', 'liveTv.watchChannel' => 'Se kanal', 'liveTv.favorites' => 'Favoritter', 'collections.title' => 'Samlinger', diff --git a/lib/i18n/strings_de.g.dart b/lib/i18n/strings_de.g.dart index 87b2c7c0..fe4e07b2 100644 --- a/lib/i18n/strings_de.g.dart +++ b/lib/i18n/strings_de.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuDe implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Medienelement erfolgreich gelöscht'; @override String get mediaFailedToDelete => 'Löschen des Medienelements fehlgeschlagen'; @override String get rate => 'Bewerten'; + @override String get playFromBeginning => 'Von Anfang an abspielen'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsDe { 'mediaMenu.mediaDeletedSuccessfully' => 'Medienelement erfolgreich gelöscht', 'mediaMenu.mediaFailedToDelete' => 'Löschen des Medienelements fehlgeschlagen', 'mediaMenu.rate' => 'Bewerten', + 'mediaMenu.playFromBeginning' => 'Von Anfang an abspielen', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, Film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, Serie', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsDe { 'liveTv.daytime' => 'Tagsüber', 'liveTv.evening' => 'Abend', 'liveTv.lateNight' => 'Spätnacht', - 'liveTv.whatsOn' => 'Jetzt im TV', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'Jetzt im TV', 'liveTv.watchChannel' => 'Kanal ansehen', 'liveTv.favorites' => 'Favoriten', 'downloads.title' => 'Downloads', diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index 7c8b2871..c65423b8 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -917,6 +917,9 @@ class TranslationsMediaMenuEn { /// en: 'Rate' String get rate => 'Rate'; + + /// en: 'Play from Beginning' + String get playFromBeginning => 'Play from Beginning'; } // Path: accessibility @@ -3178,6 +3181,7 @@ extension on Translations { 'mediaMenu.mediaDeletedSuccessfully' => 'Media item deleted successfully', 'mediaMenu.mediaFailedToDelete' => 'Failed to delete media item', 'mediaMenu.rate' => 'Rate', + 'mediaMenu.playFromBeginning' => 'Play from Beginning', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, movie', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, TV show', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -3421,9 +3425,9 @@ extension on Translations { 'liveTv.daytime' => 'Daytime', 'liveTv.evening' => 'Evening', 'liveTv.lateNight' => 'Late Night', - 'liveTv.whatsOn' => 'What\'s On', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'What\'s On', 'liveTv.watchChannel' => 'Watch Channel', 'liveTv.favorites' => 'Favorites', 'collections.title' => 'Collections', diff --git a/lib/i18n/strings_es.g.dart b/lib/i18n/strings_es.g.dart index ef056d22..623b11ac 100644 --- a/lib/i18n/strings_es.g.dart +++ b/lib/i18n/strings_es.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuEs implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Elemento multimedia eliminado con éxito'; @override String get mediaFailedToDelete => 'Error al eliminar el elemento multimedia'; @override String get rate => 'Calificar'; + @override String get playFromBeginning => 'Reproducir desde el inicio'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsEs { 'mediaMenu.mediaDeletedSuccessfully' => 'Elemento multimedia eliminado con éxito', 'mediaMenu.mediaFailedToDelete' => 'Error al eliminar el elemento multimedia', 'mediaMenu.rate' => 'Calificar', + 'mediaMenu.playFromBeginning' => 'Reproducir desde el inicio', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, película', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, serie de TV', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsEs { 'liveTv.daytime' => 'Día', 'liveTv.evening' => 'Noche', 'liveTv.lateNight' => 'Trasnoche', - 'liveTv.whatsOn' => 'En emisión', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'En emisión', 'liveTv.watchChannel' => 'Ver canal', 'liveTv.favorites' => 'Favoritos', 'collections.title' => 'Colecciones', diff --git a/lib/i18n/strings_fr.g.dart b/lib/i18n/strings_fr.g.dart index 2ef261e5..a3e26017 100644 --- a/lib/i18n/strings_fr.g.dart +++ b/lib/i18n/strings_fr.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuFr implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Élément média supprimé avec succès'; @override String get mediaFailedToDelete => 'Échec de la suppression de l\'élément média'; @override String get rate => 'Noter'; + @override String get playFromBeginning => 'Lire depuis le début'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsFr { 'mediaMenu.mediaDeletedSuccessfully' => 'Élément média supprimé avec succès', 'mediaMenu.mediaFailedToDelete' => 'Échec de la suppression de l\'élément média', 'mediaMenu.rate' => 'Noter', + 'mediaMenu.playFromBeginning' => 'Lire depuis le début', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, show TV', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsFr { 'liveTv.daytime' => 'Journée', 'liveTv.evening' => 'Soirée', 'liveTv.lateNight' => 'Nuit tardive', - 'liveTv.whatsOn' => 'En ce moment', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'En ce moment', 'liveTv.watchChannel' => 'Regarder la chaîne', 'liveTv.favorites' => 'Favoris', 'collections.title' => 'Collections', diff --git a/lib/i18n/strings_it.g.dart b/lib/i18n/strings_it.g.dart index 37b73b1e..a5fb368f 100644 --- a/lib/i18n/strings_it.g.dart +++ b/lib/i18n/strings_it.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuIt implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Elemento multimediale eliminato con successo'; @override String get mediaFailedToDelete => 'Impossibile eliminare l\'elemento multimediale'; @override String get rate => 'Valuta'; + @override String get playFromBeginning => 'Riproduci dall\'inizio'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsIt { 'mediaMenu.mediaDeletedSuccessfully' => 'Elemento multimediale eliminato con successo', 'mediaMenu.mediaFailedToDelete' => 'Impossibile eliminare l\'elemento multimediale', 'mediaMenu.rate' => 'Valuta', + 'mediaMenu.playFromBeginning' => 'Riproduci dall\'inizio', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, serie TV', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsIt { 'liveTv.daytime' => 'Giorno', 'liveTv.evening' => 'Sera', 'liveTv.lateNight' => 'Notte tarda', - 'liveTv.whatsOn' => 'In onda ora', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'In onda ora', 'liveTv.watchChannel' => 'Guarda canale', 'liveTv.favorites' => 'Preferiti', 'downloads.title' => 'Download', diff --git a/lib/i18n/strings_ja.g.dart b/lib/i18n/strings_ja.g.dart index a41ba657..7a26e285 100644 --- a/lib/i18n/strings_ja.g.dart +++ b/lib/i18n/strings_ja.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuJa implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'メディアアイテムを正常に削除しました'; @override String get mediaFailedToDelete => 'メディアアイテムの削除に失敗しました'; @override String get rate => '評価'; + @override String get playFromBeginning => '最初から再生'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsJa { 'mediaMenu.mediaDeletedSuccessfully' => 'メディアアイテムを正常に削除しました', 'mediaMenu.mediaFailedToDelete' => 'メディアアイテムの削除に失敗しました', 'mediaMenu.rate' => '評価', + 'mediaMenu.playFromBeginning' => '最初から再生', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}、映画', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}、テレビ番組', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}、${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsJa { 'liveTv.daytime' => '昼', 'liveTv.evening' => '夕方', 'liveTv.lateNight' => '深夜', - 'liveTv.whatsOn' => '放送中', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => '放送中', 'liveTv.watchChannel' => 'チャンネルを視聴', 'liveTv.favorites' => 'お気に入り', 'collections.title' => 'コレクション', diff --git a/lib/i18n/strings_ko.g.dart b/lib/i18n/strings_ko.g.dart index e3a8ed6d..055cf0f5 100644 --- a/lib/i18n/strings_ko.g.dart +++ b/lib/i18n/strings_ko.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuKo implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => '미디어 항목이 성공적으로 삭제되었습니다'; @override String get mediaFailedToDelete => '미디어 항목 삭제 실패'; @override String get rate => '평가'; + @override String get playFromBeginning => '처음부터 재생'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsKo { 'mediaMenu.mediaDeletedSuccessfully' => '미디어 항목이 성공적으로 삭제되었습니다', 'mediaMenu.mediaFailedToDelete' => '미디어 항목 삭제 실패', 'mediaMenu.rate' => '평가', + 'mediaMenu.playFromBeginning' => '처음부터 재생', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, 영화', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, TV 프로그램', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsKo { 'liveTv.daytime' => '낮', 'liveTv.evening' => '저녁', 'liveTv.lateNight' => '심야 방송', - 'liveTv.whatsOn' => '지금 방송 중', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => '지금 방송 중', 'liveTv.watchChannel' => '채널 시청', 'liveTv.favorites' => '즐겨찾기', 'collections.title' => '컬렉션', diff --git a/lib/i18n/strings_nb.g.dart b/lib/i18n/strings_nb.g.dart index d4dd2d6c..bb675a20 100644 --- a/lib/i18n/strings_nb.g.dart +++ b/lib/i18n/strings_nb.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuNb implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Medieelement slettet'; @override String get mediaFailedToDelete => 'Kunne ikke slette medieelement'; @override String get rate => 'Vurder'; + @override String get playFromBeginning => 'Spill fra begynnelsen'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsNb { 'mediaMenu.mediaDeletedSuccessfully' => 'Medieelement slettet', 'mediaMenu.mediaFailedToDelete' => 'Kunne ikke slette medieelement', 'mediaMenu.rate' => 'Vurder', + 'mediaMenu.playFromBeginning' => 'Spill fra begynnelsen', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, TV-serie', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsNb { 'liveTv.daytime' => 'Dagtid', 'liveTv.evening' => 'Kveld', 'liveTv.lateNight' => 'Sen kveld', - 'liveTv.whatsOn' => 'Hva går nå', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'Hva går nå', 'liveTv.watchChannel' => 'Se kanal', 'liveTv.favorites' => 'Favoritter', 'collections.title' => 'Samlinger', diff --git a/lib/i18n/strings_nl.g.dart b/lib/i18n/strings_nl.g.dart index 22e5ed9b..ddffb49e 100644 --- a/lib/i18n/strings_nl.g.dart +++ b/lib/i18n/strings_nl.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuNl implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Media-item succesvol verwijderd'; @override String get mediaFailedToDelete => 'Verwijderen van media-item mislukt'; @override String get rate => 'Beoordelen'; + @override String get playFromBeginning => 'Afspelen vanaf het begin'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsNl { 'mediaMenu.mediaDeletedSuccessfully' => 'Media-item succesvol verwijderd', 'mediaMenu.mediaFailedToDelete' => 'Verwijderen van media-item mislukt', 'mediaMenu.rate' => 'Beoordelen', + 'mediaMenu.playFromBeginning' => 'Afspelen vanaf het begin', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, TV-serie', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsNl { 'liveTv.daytime' => 'Overdag', 'liveTv.evening' => 'Avond', 'liveTv.lateNight' => 'Late avond', - 'liveTv.whatsOn' => 'Nu op TV', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'Nu op TV', 'liveTv.watchChannel' => 'Kanaal bekijken', 'liveTv.favorites' => 'Favorieten', 'downloads.title' => 'Downloads', diff --git a/lib/i18n/strings_pl.g.dart b/lib/i18n/strings_pl.g.dart index 2c505daf..df9a1703 100644 --- a/lib/i18n/strings_pl.g.dart +++ b/lib/i18n/strings_pl.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuPl implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Element multimedialny usunięty pomyślnie'; @override String get mediaFailedToDelete => 'Nie udało się usunąć elementu multimedialnego'; @override String get rate => 'Oceń'; + @override String get playFromBeginning => 'Odtwórz od początku'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsPl { 'mediaMenu.mediaDeletedSuccessfully' => 'Element multimedialny usunięty pomyślnie', 'mediaMenu.mediaFailedToDelete' => 'Nie udało się usunąć elementu multimedialnego', 'mediaMenu.rate' => 'Oceń', + 'mediaMenu.playFromBeginning' => 'Odtwórz od początku', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, serial TV', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsPl { 'liveTv.daytime' => 'W ciągu dnia', 'liveTv.evening' => 'Wieczorem', 'liveTv.lateNight' => 'Późna noc', - 'liveTv.whatsOn' => 'Co leci', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'Co leci', 'liveTv.watchChannel' => 'Oglądaj kanał', 'liveTv.favorites' => 'Ulubione', 'collections.title' => 'Kolekcje', diff --git a/lib/i18n/strings_pt.g.dart b/lib/i18n/strings_pt.g.dart index c1b43d36..a1bb8a36 100644 --- a/lib/i18n/strings_pt.g.dart +++ b/lib/i18n/strings_pt.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuPt implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Item de mídia excluído com sucesso'; @override String get mediaFailedToDelete => 'Falha ao excluir item de mídia'; @override String get rate => 'Avaliar'; + @override String get playFromBeginning => 'Reproduzir do início'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsPt { 'mediaMenu.mediaDeletedSuccessfully' => 'Item de mídia excluído com sucesso', 'mediaMenu.mediaFailedToDelete' => 'Falha ao excluir item de mídia', 'mediaMenu.rate' => 'Avaliar', + 'mediaMenu.playFromBeginning' => 'Reproduzir do início', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, filme', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, série de TV', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsPt { 'liveTv.daytime' => 'Dia', 'liveTv.evening' => 'Noite', 'liveTv.lateNight' => 'Madrugada', - 'liveTv.whatsOn' => 'O que Está Passando', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'O que Está Passando', 'liveTv.watchChannel' => 'Assistir Canal', 'liveTv.favorites' => 'Favoritos', 'collections.title' => 'Coleções', diff --git a/lib/i18n/strings_ru.g.dart b/lib/i18n/strings_ru.g.dart index a6d206c1..84d3fb30 100644 --- a/lib/i18n/strings_ru.g.dart +++ b/lib/i18n/strings_ru.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuRu implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Медиаэлемент успешно удалён'; @override String get mediaFailedToDelete => 'Не удалось удалить медиаэлемент'; @override String get rate => 'Оценить'; + @override String get playFromBeginning => 'Воспроизвести сначала'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsRu { 'mediaMenu.mediaDeletedSuccessfully' => 'Медиаэлемент успешно удалён', 'mediaMenu.mediaFailedToDelete' => 'Не удалось удалить медиаэлемент', 'mediaMenu.rate' => 'Оценить', + 'mediaMenu.playFromBeginning' => 'Воспроизвести сначала', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, фильм', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, сериал', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsRu { 'liveTv.daytime' => 'День', 'liveTv.evening' => 'Вечер', 'liveTv.lateNight' => 'Поздний вечер', - 'liveTv.whatsOn' => 'Что идёт', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'Что идёт', 'liveTv.watchChannel' => 'Смотреть канал', 'liveTv.favorites' => 'Избранное', 'collections.title' => 'Коллекции', diff --git a/lib/i18n/strings_sv.g.dart b/lib/i18n/strings_sv.g.dart index 20102852..85d8bfc8 100644 --- a/lib/i18n/strings_sv.g.dart +++ b/lib/i18n/strings_sv.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuSv implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => 'Mediaobjekt borttaget'; @override String get mediaFailedToDelete => 'Kunde inte ta bort mediaobjekt'; @override String get rate => 'Betygsätt'; + @override String get playFromBeginning => 'Spela från början'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsSv { 'mediaMenu.mediaDeletedSuccessfully' => 'Mediaobjekt borttaget', 'mediaMenu.mediaFailedToDelete' => 'Kunde inte ta bort mediaobjekt', 'mediaMenu.rate' => 'Betygsätt', + 'mediaMenu.playFromBeginning' => 'Spela från början', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, film', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, TV-serie', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsSv { 'liveTv.daytime' => 'Dagtid', 'liveTv.evening' => 'Kväll', 'liveTv.lateNight' => 'Sen kväll', - 'liveTv.whatsOn' => 'På TV nu', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => 'På TV nu', 'liveTv.watchChannel' => 'Titta på kanal', 'liveTv.favorites' => 'Favoriter', 'downloads.title' => 'Nedladdningar', diff --git a/lib/i18n/strings_zh.g.dart b/lib/i18n/strings_zh.g.dart index c7d3494e..cc13574e 100644 --- a/lib/i18n/strings_zh.g.dart +++ b/lib/i18n/strings_zh.g.dart @@ -415,6 +415,7 @@ class _TranslationsMediaMenuZh implements TranslationsMediaMenuEn { @override String get mediaDeletedSuccessfully => '媒体项已成功删除'; @override String get mediaFailedToDelete => '删除媒体项失败'; @override String get rate => '评分'; + @override String get playFromBeginning => '从头播放'; } // Path: accessibility @@ -1567,6 +1568,7 @@ extension on TranslationsZh { 'mediaMenu.mediaDeletedSuccessfully' => '媒体项已成功删除', 'mediaMenu.mediaFailedToDelete' => '删除媒体项失败', 'mediaMenu.rate' => '评分', + 'mediaMenu.playFromBeginning' => '从头播放', 'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, 电影', 'accessibility.mediaCardShow' => ({required Object title}) => '${title}, 电视剧', 'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}', @@ -1810,9 +1812,9 @@ extension on TranslationsZh { 'liveTv.daytime' => '白天', 'liveTv.evening' => '晚上', 'liveTv.lateNight' => '深夜', - 'liveTv.whatsOn' => '正在播出', _ => null, } ?? switch (path) { + 'liveTv.whatsOn' => '正在播出', 'liveTv.watchChannel' => '观看频道', 'liveTv.favorites' => '收藏', 'downloads.title' => '下载', diff --git a/lib/i18n/sv.i18n.json b/lib/i18n/sv.i18n.json index cee04d6e..0fe7e76a 100644 --- a/lib/i18n/sv.i18n.json +++ b/lib/i18n/sv.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "Detta inkluderar alla avsnitt och deras filer.", "mediaDeletedSuccessfully": "Mediaobjekt borttaget", "mediaFailedToDelete": "Kunde inte ta bort mediaobjekt", - "rate": "Betygsätt" + "rate": "Betygsätt", + "playFromBeginning": "Spela från början" }, "accessibility": { "mediaCardMovie": "${title}, film", diff --git a/lib/i18n/zh.i18n.json b/lib/i18n/zh.i18n.json index 8ee819fd..957c70d0 100644 --- a/lib/i18n/zh.i18n.json +++ b/lib/i18n/zh.i18n.json @@ -287,7 +287,8 @@ "deleteMultipleWarning": "这包括所有剧集及其文件。", "mediaDeletedSuccessfully": "媒体项已成功删除", "mediaFailedToDelete": "删除媒体项失败", - "rate": "评分" + "rate": "评分", + "playFromBeginning": "从头播放" }, "accessibility": { "mediaCardMovie": "${title}, 电影", diff --git a/lib/widgets/media_context_menu.dart b/lib/widgets/media_context_menu.dart index eee4f8e5..4b8aa634 100644 --- a/lib/widgets/media_context_menu.dart +++ b/lib/widgets/media_context_menu.dart @@ -25,6 +25,7 @@ import '../focus/dpad_navigator.dart'; import '../screens/media_detail_screen.dart'; import '../screens/metadata_edit_screen.dart'; import '../utils/smart_deletion_handler.dart'; +import '../utils/video_player_navigation.dart'; import '../utils/deletion_notifier.dart'; import '../theme/mono_tokens.dart'; import '../widgets/file_info_bottom_sheet.dart'; @@ -161,6 +162,13 @@ class MediaContextMenuState extends State { } else { // Regular menu items for other types + // Play from Beginning (for movies and episodes with active progress) + if (hasActiveProgress) { + menuActions.add( + _MenuAction(value: 'play_from_beginning', icon: Symbols.replay_rounded, label: t.mediaMenu.playFromBeginning), + ); + } + // Mark as Watched if (!metadata!.isWatched || isPartiallyWatched || hasActiveProgress) { menuActions.add( @@ -357,6 +365,13 @@ class MediaContextMenuState extends State { if (!context.mounted) return; switch (selected) { + case 'play_from_beginning': + didNavigate = true; + if (context.mounted) { + await navigateToVideoPlayer(context, metadata: metadata!.copyWith(viewOffset: 0)); + } + break; + case 'watch': final isOffline = context.read().isOffline; if (isOffline && metadata?.serverId != null) {