feat: play version context menu option

This commit is contained in:
edde746
2026-03-30 08:03:39 +02:00
parent ad8faab9ea
commit ddb157809a
34 changed files with 135 additions and 32 deletions
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Medieelement slettet",
"mediaFailedToDelete": "Kunne ikke slette medieelement",
"rate": "Bedøm",
"playFromBeginning": "Afspil fra begyndelsen"
"playFromBeginning": "Afspil fra begyndelsen",
"playVersion": "Afspil version..."
},
"accessibility": {
"mediaCardMovie": "${title}, film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Medienelement erfolgreich gelöscht",
"mediaFailedToDelete": "Löschen des Medienelements fehlgeschlagen",
"rate": "Bewerten",
"playFromBeginning": "Von Anfang an abspielen"
"playFromBeginning": "Von Anfang an abspielen",
"playVersion": "Version abspielen..."
},
"accessibility": {
"mediaCardMovie": "${title}, Film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Media item deleted successfully",
"mediaFailedToDelete": "Failed to delete media item",
"rate": "Rate",
"playFromBeginning": "Play from Beginning"
"playFromBeginning": "Play from Beginning",
"playVersion": "Play Version..."
},
"accessibility": {
"mediaCardMovie": "${title}, movie",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Elemento multimedia eliminado con éxito",
"mediaFailedToDelete": "Error al eliminar el elemento multimedia",
"rate": "Calificar",
"playFromBeginning": "Reproducir desde el inicio"
"playFromBeginning": "Reproducir desde el inicio",
"playVersion": "Reproducir versión..."
},
"accessibility": {
"mediaCardMovie": "${title}, película",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Élément média supprimé avec succès",
"mediaFailedToDelete": "Échec de la suppression de l'élément média",
"rate": "Noter",
"playFromBeginning": "Lire depuis le début"
"playFromBeginning": "Lire depuis le début",
"playVersion": "Lire la version..."
},
"accessibility": {
"mediaCardMovie": "${title}, film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Elemento multimediale eliminato con successo",
"mediaFailedToDelete": "Impossibile eliminare l'elemento multimediale",
"rate": "Valuta",
"playFromBeginning": "Riproduci dall'inizio"
"playFromBeginning": "Riproduci dall'inizio",
"playVersion": "Riproduci versione..."
},
"accessibility": {
"mediaCardMovie": "${title}, film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "メディアアイテムを正常に削除しました",
"mediaFailedToDelete": "メディアアイテムの削除に失敗しました",
"rate": "評価",
"playFromBeginning": "最初から再生"
"playFromBeginning": "最初から再生",
"playVersion": "バージョンを再生..."
},
"accessibility": {
"mediaCardMovie": "${title}、映画",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "미디어 항목이 성공적으로 삭제되었습니다",
"mediaFailedToDelete": "미디어 항목 삭제 실패",
"rate": "평가",
"playFromBeginning": "처음부터 재생"
"playFromBeginning": "처음부터 재생",
"playVersion": "버전 재생..."
},
"accessibility": {
"mediaCardMovie": "${title}, 영화",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Medieelement slettet",
"mediaFailedToDelete": "Kunne ikke slette medieelement",
"rate": "Vurder",
"playFromBeginning": "Spill fra begynnelsen"
"playFromBeginning": "Spill fra begynnelsen",
"playVersion": "Spill av versjon..."
},
"accessibility": {
"mediaCardMovie": "${title}, film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Media-item succesvol verwijderd",
"mediaFailedToDelete": "Verwijderen van media-item mislukt",
"rate": "Beoordelen",
"playFromBeginning": "Afspelen vanaf het begin"
"playFromBeginning": "Afspelen vanaf het begin",
"playVersion": "Versie afspelen..."
},
"accessibility": {
"mediaCardMovie": "${title}, film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Element multimedialny usunięty pomyślnie",
"mediaFailedToDelete": "Nie udało się usunąć elementu multimedialnego",
"rate": "Oceń",
"playFromBeginning": "Odtwórz od początku"
"playFromBeginning": "Odtwórz od początku",
"playVersion": "Odtwórz wersję..."
},
"accessibility": {
"mediaCardMovie": "${title}, film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Item de mídia excluído com sucesso",
"mediaFailedToDelete": "Falha ao excluir item de mídia",
"rate": "Avaliar",
"playFromBeginning": "Reproduzir do início"
"playFromBeginning": "Reproduzir do início",
"playVersion": "Reproduzir versão..."
},
"accessibility": {
"mediaCardMovie": "${title}, filme",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Медиаэлемент успешно удалён",
"mediaFailedToDelete": "Не удалось удалить медиаэлемент",
"rate": "Оценить",
"playFromBeginning": "Воспроизвести сначала"
"playFromBeginning": "Воспроизвести сначала",
"playVersion": "Воспроизвести версию..."
},
"accessibility": {
"mediaCardMovie": "${title}, фильм",
+2 -2
View File
@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 15
/// Strings: 12105 (807 per locale)
/// Strings: 12120 (808 per locale)
///
/// Built on 2026-03-28 at 13:35 UTC
/// Built on 2026-03-29 at 18:23 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuDa implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Kunne ikke slette medieelement';
@override String get rate => 'Bedøm';
@override String get playFromBeginning => 'Afspil fra begyndelsen';
@override String get playVersion => 'Afspil version...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsDa {
'mediaMenu.mediaFailedToDelete' => 'Kunne ikke slette medieelement',
'mediaMenu.rate' => 'Bedøm',
'mediaMenu.playFromBeginning' => 'Afspil fra begyndelsen',
'mediaMenu.playVersion' => 'Afspil version...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsDa {
'liveTv.now' => 'Nu',
'liveTv.today' => 'I dag',
'liveTv.midnight' => 'Midnat',
'liveTv.overnight' => 'Nat',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Nat',
'liveTv.morning' => 'Morgen',
'liveTv.daytime' => 'Dagtid',
'liveTv.evening' => 'Aften',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuDe implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Löschen des Medienelements fehlgeschlagen';
@override String get rate => 'Bewerten';
@override String get playFromBeginning => 'Von Anfang an abspielen';
@override String get playVersion => 'Version abspielen...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsDe {
'mediaMenu.mediaFailedToDelete' => 'Löschen des Medienelements fehlgeschlagen',
'mediaMenu.rate' => 'Bewerten',
'mediaMenu.playFromBeginning' => 'Von Anfang an abspielen',
'mediaMenu.playVersion' => 'Version 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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsDe {
'liveTv.now' => 'Jetzt',
'liveTv.today' => 'Heute',
'liveTv.midnight' => 'Mitternacht',
'liveTv.overnight' => 'Nacht',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Nacht',
'liveTv.morning' => 'Morgen',
'liveTv.daytime' => 'Tagsüber',
'liveTv.evening' => 'Abend',
+5 -1
View File
@@ -932,6 +932,9 @@ class TranslationsMediaMenuEn {
/// en: 'Play from Beginning'
String get playFromBeginning => 'Play from Beginning';
/// en: 'Play Version...'
String get playVersion => 'Play Version...';
}
// Path: accessibility
@@ -3198,6 +3201,7 @@ extension on Translations {
'mediaMenu.mediaFailedToDelete' => 'Failed to delete media item',
'mediaMenu.rate' => 'Rate',
'mediaMenu.playFromBeginning' => 'Play from Beginning',
'mediaMenu.playVersion' => 'Play Version...',
'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}',
@@ -3436,9 +3440,9 @@ extension on Translations {
'liveTv.now' => 'Now',
'liveTv.today' => 'Today',
'liveTv.midnight' => 'Midnight',
'liveTv.overnight' => 'Overnight',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Overnight',
'liveTv.morning' => 'Morning',
'liveTv.daytime' => 'Daytime',
'liveTv.evening' => 'Evening',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuEs implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Error al eliminar el elemento multimedia';
@override String get rate => 'Calificar';
@override String get playFromBeginning => 'Reproducir desde el inicio';
@override String get playVersion => 'Reproducir versión...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsEs {
'mediaMenu.mediaFailedToDelete' => 'Error al eliminar el elemento multimedia',
'mediaMenu.rate' => 'Calificar',
'mediaMenu.playFromBeginning' => 'Reproducir desde el inicio',
'mediaMenu.playVersion' => 'Reproducir versión...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsEs {
'liveTv.now' => 'Ahora',
'liveTv.today' => 'Hoy',
'liveTv.midnight' => 'Medianoche',
'liveTv.overnight' => 'Madrugada',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Madrugada',
'liveTv.morning' => 'Mañana',
'liveTv.daytime' => 'Día',
'liveTv.evening' => 'Noche',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuFr implements TranslationsMediaMenuEn {
@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';
@override String get playVersion => 'Lire la version...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsFr {
'mediaMenu.mediaFailedToDelete' => 'Échec de la suppression de l\'élément média',
'mediaMenu.rate' => 'Noter',
'mediaMenu.playFromBeginning' => 'Lire depuis le début',
'mediaMenu.playVersion' => 'Lire la version...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsFr {
'liveTv.now' => 'Maintenant',
'liveTv.today' => 'Aujourd\'hui',
'liveTv.midnight' => 'Minuit',
'liveTv.overnight' => 'Nuit',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Nuit',
'liveTv.morning' => 'Matin',
'liveTv.daytime' => 'Journée',
'liveTv.evening' => 'Soirée',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuIt implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Impossibile eliminare l\'elemento multimediale';
@override String get rate => 'Valuta';
@override String get playFromBeginning => 'Riproduci dall\'inizio';
@override String get playVersion => 'Riproduci versione...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsIt {
'mediaMenu.mediaFailedToDelete' => 'Impossibile eliminare l\'elemento multimediale',
'mediaMenu.rate' => 'Valuta',
'mediaMenu.playFromBeginning' => 'Riproduci dall\'inizio',
'mediaMenu.playVersion' => 'Riproduci versione...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsIt {
'liveTv.now' => 'Ora',
'liveTv.today' => 'Oggi',
'liveTv.midnight' => 'Mezzanotte',
'liveTv.overnight' => 'Notte',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Notte',
'liveTv.morning' => 'Mattina',
'liveTv.daytime' => 'Giorno',
'liveTv.evening' => 'Sera',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuJa implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'メディアアイテムの削除に失敗しました';
@override String get rate => '評価';
@override String get playFromBeginning => '最初から再生';
@override String get playVersion => 'バージョンを再生...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsJa {
'mediaMenu.mediaFailedToDelete' => 'メディアアイテムの削除に失敗しました',
'mediaMenu.rate' => '評価',
'mediaMenu.playFromBeginning' => '最初から再生',
'mediaMenu.playVersion' => 'バージョンを再生...',
'accessibility.mediaCardMovie' => ({required Object title}) => '${title}、映画',
'accessibility.mediaCardShow' => ({required Object title}) => '${title}、テレビ番組',
'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}${episodeInfo}',
@@ -1815,9 +1817,9 @@ extension on TranslationsJa {
'liveTv.now' => '現在',
'liveTv.today' => '今日',
'liveTv.midnight' => '深夜',
'liveTv.overnight' => '深夜',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => '深夜',
'liveTv.morning' => '',
'liveTv.daytime' => '',
'liveTv.evening' => '夕方',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuKo implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => '미디어 항목 삭제 실패';
@override String get rate => '평가';
@override String get playFromBeginning => '처음부터 재생';
@override String get playVersion => '버전 재생...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsKo {
'mediaMenu.mediaFailedToDelete' => '미디어 항목 삭제 실패',
'mediaMenu.rate' => '평가',
'mediaMenu.playFromBeginning' => '처음부터 재생',
'mediaMenu.playVersion' => '버전 재생...',
'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, 영화',
'accessibility.mediaCardShow' => ({required Object title}) => '${title}, TV 프로그램',
'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}',
@@ -1815,9 +1817,9 @@ extension on TranslationsKo {
'liveTv.now' => '지금',
'liveTv.today' => '오늘',
'liveTv.midnight' => '자정',
'liveTv.overnight' => '심야',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => '심야',
'liveTv.morning' => '아침',
'liveTv.daytime' => '',
'liveTv.evening' => '저녁',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuNb implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Kunne ikke slette medieelement';
@override String get rate => 'Vurder';
@override String get playFromBeginning => 'Spill fra begynnelsen';
@override String get playVersion => 'Spill av versjon...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsNb {
'mediaMenu.mediaFailedToDelete' => 'Kunne ikke slette medieelement',
'mediaMenu.rate' => 'Vurder',
'mediaMenu.playFromBeginning' => 'Spill fra begynnelsen',
'mediaMenu.playVersion' => 'Spill av versjon...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsNb {
'liveTv.now' => '',
'liveTv.today' => 'I dag',
'liveTv.midnight' => 'Midnatt',
'liveTv.overnight' => 'Natt',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Natt',
'liveTv.morning' => 'Morgen',
'liveTv.daytime' => 'Dagtid',
'liveTv.evening' => 'Kveld',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuNl implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Verwijderen van media-item mislukt';
@override String get rate => 'Beoordelen';
@override String get playFromBeginning => 'Afspelen vanaf het begin';
@override String get playVersion => 'Versie afspelen...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsNl {
'mediaMenu.mediaFailedToDelete' => 'Verwijderen van media-item mislukt',
'mediaMenu.rate' => 'Beoordelen',
'mediaMenu.playFromBeginning' => 'Afspelen vanaf het begin',
'mediaMenu.playVersion' => 'Versie afspelen...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsNl {
'liveTv.now' => 'Nu',
'liveTv.today' => 'Vandaag',
'liveTv.midnight' => 'Middernacht',
'liveTv.overnight' => 'Nacht',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Nacht',
'liveTv.morning' => 'Ochtend',
'liveTv.daytime' => 'Overdag',
'liveTv.evening' => 'Avond',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuPl implements TranslationsMediaMenuEn {
@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';
@override String get playVersion => 'Odtwórz wersję...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsPl {
'mediaMenu.mediaFailedToDelete' => 'Nie udało się usunąć elementu multimedialnego',
'mediaMenu.rate' => 'Oceń',
'mediaMenu.playFromBeginning' => 'Odtwórz od początku',
'mediaMenu.playVersion' => 'Odtwórz wersję...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsPl {
'liveTv.now' => 'Teraz',
'liveTv.today' => 'Dzisiaj',
'liveTv.midnight' => 'Północ',
'liveTv.overnight' => 'Nocą',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Nocą',
'liveTv.morning' => 'Rano',
'liveTv.daytime' => 'W ciągu dnia',
'liveTv.evening' => 'Wieczorem',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuPt implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Falha ao excluir item de mídia';
@override String get rate => 'Avaliar';
@override String get playFromBeginning => 'Reproduzir do início';
@override String get playVersion => 'Reproduzir versão...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsPt {
'mediaMenu.mediaFailedToDelete' => 'Falha ao excluir item de mídia',
'mediaMenu.rate' => 'Avaliar',
'mediaMenu.playFromBeginning' => 'Reproduzir do início',
'mediaMenu.playVersion' => 'Reproduzir versão...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsPt {
'liveTv.now' => 'Agora',
'liveTv.today' => 'Hoje',
'liveTv.midnight' => 'Meia-noite',
'liveTv.overnight' => 'Madrugada',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Madrugada',
'liveTv.morning' => 'Manhã',
'liveTv.daytime' => 'Dia',
'liveTv.evening' => 'Noite',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuRu implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Не удалось удалить медиаэлемент';
@override String get rate => 'Оценить';
@override String get playFromBeginning => 'Воспроизвести сначала';
@override String get playVersion => 'Воспроизвести версию...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsRu {
'mediaMenu.mediaFailedToDelete' => 'Не удалось удалить медиаэлемент',
'mediaMenu.rate' => 'Оценить',
'mediaMenu.playFromBeginning' => 'Воспроизвести сначала',
'mediaMenu.playVersion' => 'Воспроизвести версию...',
'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, фильм',
'accessibility.mediaCardShow' => ({required Object title}) => '${title}, сериал',
'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}',
@@ -1815,9 +1817,9 @@ extension on TranslationsRu {
'liveTv.now' => 'Сейчас',
'liveTv.today' => 'Сегодня',
'liveTv.midnight' => 'Полночь',
'liveTv.overnight' => 'Ночь',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Ночь',
'liveTv.morning' => 'Утро',
'liveTv.daytime' => 'День',
'liveTv.evening' => 'Вечер',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuSv implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => 'Kunde inte ta bort mediaobjekt';
@override String get rate => 'Betygsätt';
@override String get playFromBeginning => 'Spela från början';
@override String get playVersion => 'Spela version...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsSv {
'mediaMenu.mediaFailedToDelete' => 'Kunde inte ta bort mediaobjekt',
'mediaMenu.rate' => 'Betygsätt',
'mediaMenu.playFromBeginning' => 'Spela från början',
'mediaMenu.playVersion' => 'Spela version...',
'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}',
@@ -1815,9 +1817,9 @@ extension on TranslationsSv {
'liveTv.now' => 'Nu',
'liveTv.today' => 'Idag',
'liveTv.midnight' => 'Midnatt',
'liveTv.overnight' => 'Natt',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => 'Natt',
'liveTv.morning' => 'Morgon',
'liveTv.daytime' => 'Dagtid',
'liveTv.evening' => 'Kväll',
+3 -1
View File
@@ -420,6 +420,7 @@ class _TranslationsMediaMenuZh implements TranslationsMediaMenuEn {
@override String get mediaFailedToDelete => '删除媒体项失败';
@override String get rate => '评分';
@override String get playFromBeginning => '从头播放';
@override String get playVersion => '播放版本...';
}
// Path: accessibility
@@ -1577,6 +1578,7 @@ extension on TranslationsZh {
'mediaMenu.mediaFailedToDelete' => '删除媒体项失败',
'mediaMenu.rate' => '评分',
'mediaMenu.playFromBeginning' => '从头播放',
'mediaMenu.playVersion' => '播放版本...',
'accessibility.mediaCardMovie' => ({required Object title}) => '${title}, 电影',
'accessibility.mediaCardShow' => ({required Object title}) => '${title}, 电视剧',
'accessibility.mediaCardEpisode' => ({required Object title, required Object episodeInfo}) => '${title}, ${episodeInfo}',
@@ -1815,9 +1817,9 @@ extension on TranslationsZh {
'liveTv.now' => '现在',
'liveTv.today' => '今天',
'liveTv.midnight' => '午夜',
'liveTv.overnight' => '凌晨',
_ => null,
} ?? switch (path) {
'liveTv.overnight' => '凌晨',
'liveTv.morning' => '上午',
'liveTv.daytime' => '白天',
'liveTv.evening' => '晚上',
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "Mediaobjekt borttaget",
"mediaFailedToDelete": "Kunde inte ta bort mediaobjekt",
"rate": "Betygsätt",
"playFromBeginning": "Spela från början"
"playFromBeginning": "Spela från början",
"playVersion": "Spela version..."
},
"accessibility": {
"mediaCardMovie": "${title}, film",
+2 -1
View File
@@ -292,7 +292,8 @@
"mediaDeletedSuccessfully": "媒体项已成功删除",
"mediaFailedToDelete": "删除媒体项失败",
"rate": "评分",
"playFromBeginning": "从头播放"
"playFromBeginning": "从头播放",
"playVersion": "播放版本..."
},
"accessibility": {
"mediaCardMovie": "${title}, 电影",
+6
View File
@@ -4,6 +4,7 @@ import 'package:sentry_flutter/sentry_flutter.dart';
import '../services/settings_service.dart' show EpisodePosterMode;
import '../widgets/plex_optimized_image.dart' show kBlurArtwork, obfuscateText;
import 'mixins/multi_server_fields.dart';
import 'plex_media_version.dart';
import 'plex_role.dart';
import '../utils/global_key_utils.dart';
@@ -99,6 +100,8 @@ class PlexMetadata with MultiServerFields {
final int? childCount; // Number of items in a collection or playlist
@JsonKey(name: 'Role')
final List<PlexRole>? role; // Cast members
@JsonKey(name: 'Media', includeToJson: false)
final List<PlexMediaVersion>? mediaVersions; // Available media versions/editions
final String? audioLanguage; // Per-media preferred audio language
final String? subtitleLanguage; // Per-media preferred subtitle language
@JsonKey(fromJson: _flexibleInt)
@@ -201,6 +204,7 @@ class PlexMetadata with MultiServerFields {
this.viewedLeafCount,
this.childCount,
this.role,
this.mediaVersions,
this.audioLanguage,
this.subtitleLanguage,
this.subtitleMode,
@@ -260,6 +264,7 @@ class PlexMetadata with MultiServerFields {
int? viewedLeafCount,
int? childCount,
List<PlexRole>? role,
List<PlexMediaVersion>? mediaVersions,
String? audioLanguage,
String? subtitleLanguage,
int? subtitleMode,
@@ -317,6 +322,7 @@ class PlexMetadata with MultiServerFields {
viewedLeafCount: viewedLeafCount ?? this.viewedLeafCount,
childCount: childCount ?? this.childCount,
role: role ?? this.role,
mediaVersions: mediaVersions ?? this.mediaVersions,
audioLanguage: audioLanguage ?? this.audioLanguage,
subtitleLanguage: subtitleLanguage ?? this.subtitleLanguage,
subtitleMode: subtitleMode ?? this.subtitleMode,
+3
View File
@@ -45,6 +45,9 @@ PlexMetadata _$PlexMetadataFromJson(Map<String, dynamic> json) => PlexMetadata(
role: (json['Role'] as List<dynamic>?)
?.map((e) => PlexRole.fromJson(e as Map<String, dynamic>))
.toList(),
mediaVersions: (json['Media'] as List<dynamic>?)
?.map((e) => PlexMediaVersion.fromJson(e as Map<String, dynamic>))
.toList(),
audioLanguage: json['audioLanguage'] as String?,
subtitleLanguage: json['subtitleLanguage'] as String?,
subtitleMode: _flexibleInt(json['subtitleMode']),
+47
View File
@@ -261,6 +261,15 @@ class MediaContextMenuState extends State<MediaContextMenu> {
);
}
// Play Version (for episodes and movies with multiple versions)
if ((mediaType == PlexMediaType.episode || mediaType == PlexMediaType.movie) &&
metadata.mediaVersions != null &&
metadata.mediaVersions!.length > 1) {
menuActions.add(
_MenuAction(value: 'play_version', icon: Symbols.video_file_rounded, label: t.mediaMenu.playVersion),
);
}
// File Info (for episodes and movies)
if (mediaType == PlexMediaType.episode || mediaType == PlexMediaType.movie) {
menuActions.add(_MenuAction(value: 'fileinfo', icon: Symbols.info_rounded, label: t.mediaMenu.fileInfo));
@@ -490,6 +499,10 @@ class MediaContextMenuState extends State<MediaContextMenu> {
);
break;
case 'play_version':
didNavigate = await _handlePlayVersion(context);
break;
case 'fileinfo':
await _showFileInfo(context);
break;
@@ -629,6 +642,40 @@ class MediaContextMenuState extends State<MediaContextMenu> {
}
}
/// Handle play version selection
Future<bool> _handlePlayVersion(BuildContext context) async {
final metadata = widget.item as PlexMetadata;
final versions = metadata.mediaVersions!;
final selectedIndex = await showDialog<int>(
context: context,
builder: (dialogContext) => SimpleDialog(
title: Text(t.mediaMenu.playVersion),
contentPadding: const EdgeInsets.symmetric(vertical: 8),
children: List.generate(versions.length, (index) {
final version = versions[index];
return SimpleDialogOption(
onPressed: () => Navigator.pop(dialogContext, index),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: [
AppIcon(Symbols.video_file_rounded, fill: 1, size: 24),
const SizedBox(width: 16),
Text(version.displayLabel, style: Theme.of(dialogContext).textTheme.bodyLarge),
],
),
);
}),
),
);
if (selectedIndex != null && context.mounted) {
await navigateToVideoPlayer(context, metadata: metadata, selectedMediaIndex: selectedIndex);
return true;
}
return false;
}
/// Handle shuffle play using play queues
Future<void> _handleShufflePlayWithQueue(BuildContext context) async {
final client = _getClientForItem();