feat: watch together buffering indicators

This commit is contained in:
edde746
2026-04-14 22:07:13 +02:00
parent 6eb5c847af
commit 1e1bb9176a
35 changed files with 192 additions and 45 deletions
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} satte på pause",
"participantResumed": "${name} genoptog",
"participantSeeked": "${name} spoled",
"participantBuffering": "${name} bufferer",
"waitingForParticipants": "Venter på at andre indlæser...",
"recentRooms": "Seneste rum",
"renameRoom": "Omdøb rum",
"removeRoom": "Fjern"
+2
View File
@@ -753,6 +753,8 @@
"participantPaused": "${name} hat pausiert",
"participantResumed": "${name} hat fortgesetzt",
"participantSeeked": "${name} hat gespult",
"participantBuffering": "${name} puffert",
"waitingForParticipants": "Warte auf andere zum Laden...",
"recentRooms": "Letzte Räume",
"renameRoom": "Raum umbenennen",
"removeRoom": "Entfernen"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} paused",
"participantResumed": "${name} resumed",
"participantSeeked": "${name} seeked",
"participantBuffering": "${name} is buffering",
"waitingForParticipants": "Waiting for others to load...",
"recentRooms": "Recent Rooms",
"renameRoom": "Rename Room",
"removeRoom": "Remove"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} pausó",
"participantResumed": "${name} reanudó",
"participantSeeked": "${name} avanzó",
"participantBuffering": "${name} está cargando",
"waitingForParticipants": "Esperando a que otros carguen...",
"recentRooms": "Salas recientes",
"renameRoom": "Renombrar sala",
"removeRoom": "Eliminar"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} a mis en pause",
"participantResumed": "${name} a repris",
"participantSeeked": "${name} a avancé",
"participantBuffering": "${name} met en mémoire tampon",
"waitingForParticipants": "En attente du chargement des autres...",
"recentRooms": "Salons récents",
"renameRoom": "Renommer le salon",
"removeRoom": "Supprimer"
+2
View File
@@ -753,6 +753,8 @@
"participantPaused": "${name} ha messo in pausa",
"participantResumed": "${name} ha ripreso",
"participantSeeked": "${name} ha cercato",
"participantBuffering": "${name} sta caricando",
"waitingForParticipants": "In attesa che gli altri carichino...",
"recentRooms": "Stanze recenti",
"renameRoom": "Rinomina stanza",
"removeRoom": "Rimuovi"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name}が一時停止しました",
"participantResumed": "${name}が再開しました",
"participantSeeked": "${name}がシークしました",
"participantBuffering": "${name}がバッファリング中",
"waitingForParticipants": "他の参加者の読み込みを待っています...",
"recentRooms": "最近のルーム",
"renameRoom": "ルーム名を変更",
"removeRoom": "削除"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name}님이 일시정지했습니다",
"participantResumed": "${name}님이 재생했습니다",
"participantSeeked": "${name}님이 탐색했습니다",
"participantBuffering": "${name}님이 버퍼링 중입니다",
"waitingForParticipants": "다른 참가자의 로딩을 기다리는 중...",
"recentRooms": "최근 방",
"renameRoom": "방 이름 변경",
"removeRoom": "제거"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} pauset",
"participantResumed": "${name} gjenopptok",
"participantSeeked": "${name} spolet",
"participantBuffering": "${name} buffrer",
"waitingForParticipants": "Venter på at andre laster inn...",
"recentRooms": "Nylige rom",
"renameRoom": "Gi nytt navn til rom",
"removeRoom": "Fjern"
+2
View File
@@ -753,6 +753,8 @@
"participantPaused": "${name} heeft gepauzeerd",
"participantResumed": "${name} heeft hervat",
"participantSeeked": "${name} heeft gespoeld",
"participantBuffering": "${name} is aan het bufferen",
"waitingForParticipants": "Wachten tot anderen geladen zijn...",
"recentRooms": "Recente kamers",
"renameRoom": "Kamer hernoemen",
"removeRoom": "Verwijderen"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} wstrzymał",
"participantResumed": "${name} wznowił",
"participantSeeked": "${name} przewinął",
"participantBuffering": "${name} buforuje",
"waitingForParticipants": "Oczekiwanie na załadowanie u innych...",
"recentRooms": "Ostatnie pokoje",
"renameRoom": "Zmień nazwę pokoju",
"removeRoom": "Usuń"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} pausou",
"participantResumed": "${name} retomou",
"participantSeeked": "${name} avançou",
"participantBuffering": "${name} está carregando",
"waitingForParticipants": "Aguardando outros carregarem...",
"recentRooms": "Salas recentes",
"renameRoom": "Renomear sala",
"removeRoom": "Remover"
+2
View File
@@ -726,6 +726,8 @@
"participantPaused": "${name} поставил на паузу",
"participantResumed": "${name} возобновил",
"participantSeeked": "${name} перемотал",
"participantBuffering": "${name} буферизует",
"waitingForParticipants": "Ожидание загрузки у других...",
"recentRooms": "Недавние комнаты",
"renameRoom": "Переименовать комнату",
"removeRoom": "Удалить"
+2 -2
View File
@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 15
/// Strings: 12960 (864 per locale)
/// Strings: 12990 (866 per locale)
///
/// Built on 2026-04-14 at 10:29 UTC
/// Built on 2026-04-14 at 19:56 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherDa implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} satte på pause';
@override String participantResumed({required Object name}) => '${name} genoptog';
@override String participantSeeked({required Object name}) => '${name} spoled';
@override String participantBuffering({required Object name}) => '${name} bufferer';
@override String get waitingForParticipants => 'Venter på at andre indlæser...';
@override String get recentRooms => 'Seneste rum';
@override String get renameRoom => 'Omdøb rum';
@override String get removeRoom => 'Fjern';
@@ -2021,6 +2023,8 @@ extension on TranslationsDa {
'watchTogether.participantPaused' => ({required Object name}) => '${name} satte på pause',
'watchTogether.participantResumed' => ({required Object name}) => '${name} genoptog',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} spoled',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} bufferer',
'watchTogether.waitingForParticipants' => 'Venter på at andre indlæser...',
'watchTogether.recentRooms' => 'Seneste rum',
'watchTogether.renameRoom' => 'Omdøb rum',
'watchTogether.removeRoom' => 'Fjern',
+4
View File
@@ -1013,6 +1013,8 @@ class _TranslationsWatchTogetherDe implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} hat pausiert';
@override String participantResumed({required Object name}) => '${name} hat fortgesetzt';
@override String participantSeeked({required Object name}) => '${name} hat gespult';
@override String participantBuffering({required Object name}) => '${name} puffert';
@override String get waitingForParticipants => 'Warte auf andere zum Laden...';
@override String get recentRooms => 'Letzte Räume';
@override String get renameRoom => 'Raum umbenennen';
@override String get removeRoom => 'Entfernen';
@@ -2046,6 +2048,8 @@ extension on TranslationsDe {
'watchTogether.participantPaused' => ({required Object name}) => '${name} hat pausiert',
'watchTogether.participantResumed' => ({required Object name}) => '${name} hat fortgesetzt',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} hat gespult',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} puffert',
'watchTogether.waitingForParticipants' => 'Warte auf andere zum Laden...',
'watchTogether.recentRooms' => 'Letzte Räume',
'watchTogether.renameRoom' => 'Raum umbenennen',
'watchTogether.removeRoom' => 'Entfernen',
+8
View File
@@ -2228,6 +2228,12 @@ class TranslationsWatchTogetherEn {
/// en: '${name} seeked'
String participantSeeked({required Object name}) => '${name} seeked';
/// en: '${name} is buffering'
String participantBuffering({required Object name}) => '${name} is buffering';
/// en: 'Waiting for others to load...'
String get waitingForParticipants => 'Waiting for others to load...';
/// en: 'Recent Rooms'
String get recentRooms => 'Recent Rooms';
@@ -3756,6 +3762,8 @@ extension on Translations {
'watchTogether.participantPaused' => ({required Object name}) => '${name} paused',
'watchTogether.participantResumed' => ({required Object name}) => '${name} resumed',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} seeked',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} is buffering',
'watchTogether.waitingForParticipants' => 'Waiting for others to load...',
'watchTogether.recentRooms' => 'Recent Rooms',
'watchTogether.renameRoom' => 'Rename Room',
'watchTogether.removeRoom' => 'Remove',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherEs implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} pausó';
@override String participantResumed({required Object name}) => '${name} reanudó';
@override String participantSeeked({required Object name}) => '${name} avanzó';
@override String participantBuffering({required Object name}) => '${name} está cargando';
@override String get waitingForParticipants => 'Esperando a que otros carguen...';
@override String get recentRooms => 'Salas recientes';
@override String get renameRoom => 'Renombrar sala';
@override String get removeRoom => 'Eliminar';
@@ -2021,6 +2023,8 @@ extension on TranslationsEs {
'watchTogether.participantPaused' => ({required Object name}) => '${name} pausó',
'watchTogether.participantResumed' => ({required Object name}) => '${name} reanudó',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} avanzó',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} está cargando',
'watchTogether.waitingForParticipants' => 'Esperando a que otros carguen...',
'watchTogether.recentRooms' => 'Salas recientes',
'watchTogether.renameRoom' => 'Renombrar sala',
'watchTogether.removeRoom' => 'Eliminar',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherFr implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} a mis en pause';
@override String participantResumed({required Object name}) => '${name} a repris';
@override String participantSeeked({required Object name}) => '${name} a avancé';
@override String participantBuffering({required Object name}) => '${name} met en mémoire tampon';
@override String get waitingForParticipants => 'En attente du chargement des autres...';
@override String get recentRooms => 'Salons récents';
@override String get renameRoom => 'Renommer le salon';
@override String get removeRoom => 'Supprimer';
@@ -2021,6 +2023,8 @@ extension on TranslationsFr {
'watchTogether.participantPaused' => ({required Object name}) => '${name} a mis en pause',
'watchTogether.participantResumed' => ({required Object name}) => '${name} a repris',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} a avancé',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} met en mémoire tampon',
'watchTogether.waitingForParticipants' => 'En attente du chargement des autres...',
'watchTogether.recentRooms' => 'Salons récents',
'watchTogether.renameRoom' => 'Renommer le salon',
'watchTogether.removeRoom' => 'Supprimer',
+4
View File
@@ -1013,6 +1013,8 @@ class _TranslationsWatchTogetherIt implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} ha messo in pausa';
@override String participantResumed({required Object name}) => '${name} ha ripreso';
@override String participantSeeked({required Object name}) => '${name} ha cercato';
@override String participantBuffering({required Object name}) => '${name} sta caricando';
@override String get waitingForParticipants => 'In attesa che gli altri carichino...';
@override String get recentRooms => 'Stanze recenti';
@override String get renameRoom => 'Rinomina stanza';
@override String get removeRoom => 'Rimuovi';
@@ -2046,6 +2048,8 @@ extension on TranslationsIt {
'watchTogether.participantPaused' => ({required Object name}) => '${name} ha messo in pausa',
'watchTogether.participantResumed' => ({required Object name}) => '${name} ha ripreso',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} ha cercato',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} sta caricando',
'watchTogether.waitingForParticipants' => 'In attesa che gli altri carichino...',
'watchTogether.recentRooms' => 'Stanze recenti',
'watchTogether.renameRoom' => 'Rinomina stanza',
'watchTogether.removeRoom' => 'Rimuovi',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherJa implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name}が一時停止しました';
@override String participantResumed({required Object name}) => '${name}が再開しました';
@override String participantSeeked({required Object name}) => '${name}がシークしました';
@override String participantBuffering({required Object name}) => '${name}がバッファリング中';
@override String get waitingForParticipants => '他の参加者の読み込みを待っています...';
@override String get recentRooms => '最近のルーム';
@override String get renameRoom => 'ルーム名を変更';
@override String get removeRoom => '削除';
@@ -2021,6 +2023,8 @@ extension on TranslationsJa {
'watchTogether.participantPaused' => ({required Object name}) => '${name}が一時停止しました',
'watchTogether.participantResumed' => ({required Object name}) => '${name}が再開しました',
'watchTogether.participantSeeked' => ({required Object name}) => '${name}がシークしました',
'watchTogether.participantBuffering' => ({required Object name}) => '${name}がバッファリング中',
'watchTogether.waitingForParticipants' => '他の参加者の読み込みを待っています...',
'watchTogether.recentRooms' => '最近のルーム',
'watchTogether.renameRoom' => 'ルーム名を変更',
'watchTogether.removeRoom' => '削除',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherKo implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name}님이 일시정지했습니다';
@override String participantResumed({required Object name}) => '${name}님이 재생했습니다';
@override String participantSeeked({required Object name}) => '${name}님이 탐색했습니다';
@override String participantBuffering({required Object name}) => '${name}님이 버퍼링 중입니다';
@override String get waitingForParticipants => '다른 참가자의 로딩을 기다리는 중...';
@override String get recentRooms => '최근 방';
@override String get renameRoom => '방 이름 변경';
@override String get removeRoom => '제거';
@@ -2021,6 +2023,8 @@ extension on TranslationsKo {
'watchTogether.participantPaused' => ({required Object name}) => '${name}님이 일시정지했습니다',
'watchTogether.participantResumed' => ({required Object name}) => '${name}님이 재생했습니다',
'watchTogether.participantSeeked' => ({required Object name}) => '${name}님이 탐색했습니다',
'watchTogether.participantBuffering' => ({required Object name}) => '${name}님이 버퍼링 중입니다',
'watchTogether.waitingForParticipants' => '다른 참가자의 로딩을 기다리는 중...',
'watchTogether.recentRooms' => '최근 방',
'watchTogether.renameRoom' => '방 이름 변경',
'watchTogether.removeRoom' => '제거',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherNb implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} pauset';
@override String participantResumed({required Object name}) => '${name} gjenopptok';
@override String participantSeeked({required Object name}) => '${name} spolet';
@override String participantBuffering({required Object name}) => '${name} buffrer';
@override String get waitingForParticipants => 'Venter på at andre laster inn...';
@override String get recentRooms => 'Nylige rom';
@override String get renameRoom => 'Gi nytt navn til rom';
@override String get removeRoom => 'Fjern';
@@ -2021,6 +2023,8 @@ extension on TranslationsNb {
'watchTogether.participantPaused' => ({required Object name}) => '${name} pauset',
'watchTogether.participantResumed' => ({required Object name}) => '${name} gjenopptok',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} spolet',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} buffrer',
'watchTogether.waitingForParticipants' => 'Venter på at andre laster inn...',
'watchTogether.recentRooms' => 'Nylige rom',
'watchTogether.renameRoom' => 'Gi nytt navn til rom',
'watchTogether.removeRoom' => 'Fjern',
+4
View File
@@ -1013,6 +1013,8 @@ class _TranslationsWatchTogetherNl implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} heeft gepauzeerd';
@override String participantResumed({required Object name}) => '${name} heeft hervat';
@override String participantSeeked({required Object name}) => '${name} heeft gespoeld';
@override String participantBuffering({required Object name}) => '${name} is aan het bufferen';
@override String get waitingForParticipants => 'Wachten tot anderen geladen zijn...';
@override String get recentRooms => 'Recente kamers';
@override String get renameRoom => 'Kamer hernoemen';
@override String get removeRoom => 'Verwijderen';
@@ -2046,6 +2048,8 @@ extension on TranslationsNl {
'watchTogether.participantPaused' => ({required Object name}) => '${name} heeft gepauzeerd',
'watchTogether.participantResumed' => ({required Object name}) => '${name} heeft hervat',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} heeft gespoeld',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} is aan het bufferen',
'watchTogether.waitingForParticipants' => 'Wachten tot anderen geladen zijn...',
'watchTogether.recentRooms' => 'Recente kamers',
'watchTogether.renameRoom' => 'Kamer hernoemen',
'watchTogether.removeRoom' => 'Verwijderen',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherPl implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} wstrzymał';
@override String participantResumed({required Object name}) => '${name} wznowił';
@override String participantSeeked({required Object name}) => '${name} przewinął';
@override String participantBuffering({required Object name}) => '${name} buforuje';
@override String get waitingForParticipants => 'Oczekiwanie na załadowanie u innych...';
@override String get recentRooms => 'Ostatnie pokoje';
@override String get renameRoom => 'Zmień nazwę pokoju';
@override String get removeRoom => 'Usuń';
@@ -2021,6 +2023,8 @@ extension on TranslationsPl {
'watchTogether.participantPaused' => ({required Object name}) => '${name} wstrzymał',
'watchTogether.participantResumed' => ({required Object name}) => '${name} wznowił',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} przewinął',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} buforuje',
'watchTogether.waitingForParticipants' => 'Oczekiwanie na załadowanie u innych...',
'watchTogether.recentRooms' => 'Ostatnie pokoje',
'watchTogether.renameRoom' => 'Zmień nazwę pokoju',
'watchTogether.removeRoom' => 'Usuń',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherPt implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} pausou';
@override String participantResumed({required Object name}) => '${name} retomou';
@override String participantSeeked({required Object name}) => '${name} avançou';
@override String participantBuffering({required Object name}) => '${name} está carregando';
@override String get waitingForParticipants => 'Aguardando outros carregarem...';
@override String get recentRooms => 'Salas recentes';
@override String get renameRoom => 'Renomear sala';
@override String get removeRoom => 'Remover';
@@ -2021,6 +2023,8 @@ extension on TranslationsPt {
'watchTogether.participantPaused' => ({required Object name}) => '${name} pausou',
'watchTogether.participantResumed' => ({required Object name}) => '${name} retomou',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} avançou',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} está carregando',
'watchTogether.waitingForParticipants' => 'Aguardando outros carregarem...',
'watchTogether.recentRooms' => 'Salas recentes',
'watchTogether.renameRoom' => 'Renomear sala',
'watchTogether.removeRoom' => 'Remover',
+4
View File
@@ -979,6 +979,8 @@ class _TranslationsWatchTogetherRu implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} поставил на паузу';
@override String participantResumed({required Object name}) => '${name} возобновил';
@override String participantSeeked({required Object name}) => '${name} перемотал';
@override String participantBuffering({required Object name}) => '${name} буферизует';
@override String get waitingForParticipants => 'Ожидание загрузки у других...';
@override String get recentRooms => 'Недавние комнаты';
@override String get renameRoom => 'Переименовать комнату';
@override String get removeRoom => 'Удалить';
@@ -2021,6 +2023,8 @@ extension on TranslationsRu {
'watchTogether.participantPaused' => ({required Object name}) => '${name} поставил на паузу',
'watchTogether.participantResumed' => ({required Object name}) => '${name} возобновил',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} перемотал',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} буферизует',
'watchTogether.waitingForParticipants' => 'Ожидание загрузки у других...',
'watchTogether.recentRooms' => 'Недавние комнаты',
'watchTogether.renameRoom' => 'Переименовать комнату',
'watchTogether.removeRoom' => 'Удалить',
+4
View File
@@ -1013,6 +1013,8 @@ class _TranslationsWatchTogetherSv implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} pausade';
@override String participantResumed({required Object name}) => '${name} återupptog';
@override String participantSeeked({required Object name}) => '${name} spolade';
@override String participantBuffering({required Object name}) => '${name} buffrar';
@override String get waitingForParticipants => 'Väntar på att andra laddar...';
@override String get recentRooms => 'Senaste rum';
@override String get renameRoom => 'Byt namn på rum';
@override String get removeRoom => 'Ta bort';
@@ -2046,6 +2048,8 @@ extension on TranslationsSv {
'watchTogether.participantPaused' => ({required Object name}) => '${name} pausade',
'watchTogether.participantResumed' => ({required Object name}) => '${name} återupptog',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} spolade',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} buffrar',
'watchTogether.waitingForParticipants' => 'Väntar på att andra laddar...',
'watchTogether.recentRooms' => 'Senaste rum',
'watchTogether.renameRoom' => 'Byt namn på rum',
'watchTogether.removeRoom' => 'Ta bort',
+4
View File
@@ -1013,6 +1013,8 @@ class _TranslationsWatchTogetherZh implements TranslationsWatchTogetherEn {
@override String participantPaused({required Object name}) => '${name} 暂停了';
@override String participantResumed({required Object name}) => '${name} 继续播放了';
@override String participantSeeked({required Object name}) => '${name} 跳转了';
@override String participantBuffering({required Object name}) => '${name} 正在缓冲';
@override String get waitingForParticipants => '等待其他人加载...';
@override String get recentRooms => '最近的房间';
@override String get renameRoom => '重命名房间';
@override String get removeRoom => '移除';
@@ -2046,6 +2048,8 @@ extension on TranslationsZh {
'watchTogether.participantPaused' => ({required Object name}) => '${name} 暂停了',
'watchTogether.participantResumed' => ({required Object name}) => '${name} 继续播放了',
'watchTogether.participantSeeked' => ({required Object name}) => '${name} 跳转了',
'watchTogether.participantBuffering' => ({required Object name}) => '${name} 正在缓冲',
'watchTogether.waitingForParticipants' => '等待其他人加载...',
'watchTogether.recentRooms' => '最近的房间',
'watchTogether.renameRoom' => '重命名房间',
'watchTogether.removeRoom' => '移除',
+2
View File
@@ -753,6 +753,8 @@
"participantPaused": "${name} pausade",
"participantResumed": "${name} återupptog",
"participantSeeked": "${name} spolade",
"participantBuffering": "${name} buffrar",
"waitingForParticipants": "Väntar på att andra laddar...",
"recentRooms": "Senaste rum",
"renameRoom": "Byt namn på rum",
"removeRoom": "Ta bort"
+2
View File
@@ -753,6 +753,8 @@
"participantPaused": "${name} 暂停了",
"participantResumed": "${name} 继续播放了",
"participantSeeked": "${name} 跳转了",
"participantBuffering": "${name} 正在缓冲",
"waitingForParticipants": "等待其他人加载...",
"recentRooms": "最近的房间",
"renameRoom": "重命名房间",
"removeRoom": "移除"
+5 -1
View File
@@ -3413,8 +3413,12 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
);
},
),
// Watch Together: participant join/leave notifications
// Watch Together: participant join/leave/buffering notifications
const ParticipantNotificationOverlay(),
// Watch Together: waiting for participants to load
const WaitingForParticipantsIndicator(),
// Watch Together: syncing indicator during drift correction
const SyncingIndicator(),
],
),
),
@@ -28,6 +28,7 @@ class WatchTogetherProvider with ChangeNotifier {
WatchTogetherSyncManager? _syncManager;
final List<Participant> _participants = [];
bool _isSyncing = false;
bool _isDeferredPlay = false;
String _displayName = 'User';
String? _lastHandledCurrentPlaybackKey;
@@ -83,6 +84,7 @@ class WatchTogetherProvider with ChangeNotifier {
bool get isHost => _session?.isHost ?? false;
bool get isConnected => _session?.isConnected ?? false;
bool get isSyncing => _isSyncing;
bool get isDeferredPlay => _isDeferredPlay;
WatchSession? get session => _session;
List<Participant> get participants => List.unmodifiable(_participants);
int get participantCount => _participants.length;
@@ -178,6 +180,10 @@ class WatchTogetherProvider with ChangeNotifier {
_isSyncing = isSyncing;
notifyListeners();
};
_syncManager!.onDeferredPlayChanged = (isDeferredPlay) {
_isDeferredPlay = isDeferredPlay;
notifyListeners();
};
}
/// Create a new watch together session as host
@@ -348,6 +354,7 @@ class WatchTogetherProvider with ChangeNotifier {
_session = null;
_participants.clear();
_isSyncing = false;
_isDeferredPlay = false;
_lastHandledCurrentPlaybackKey = null;
_lastActionEventMs.clear();
@@ -491,6 +498,9 @@ class WatchTogetherProvider with ChangeNotifier {
final newState = message.bufferingState ?? false;
if (_participants[index].isBuffering != newState) {
_participants[index] = _participants[index].copyWith(isBuffering: newState);
if (newState) {
_emitActionEvent(message.peerId, ParticipantEventType.buffering);
}
notifyListeners();
}
}
@@ -737,7 +747,7 @@ class WatchTogetherProvider with ChangeNotifier {
}
/// Type of participant event
enum ParticipantEventType { joined, left, paused, resumed, seeked }
enum ParticipantEventType { joined, left, paused, resumed, seeked, buffering }
/// Event emitted when a participant joins or leaves
class ParticipantEvent {
@@ -14,6 +14,9 @@ typedef SessionConfigCallback = void Function(ControlMode controlMode);
/// Callback type for when sync state changes
typedef SyncStateCallback = void Function(bool isSyncing);
/// Callback type for when deferred play state changes
typedef DeferredPlayCallback = void Function(bool isDeferredPlay);
/// Manages playback synchronization between peers
///
/// This class:
@@ -79,6 +82,7 @@ class WatchTogetherSyncManager {
// Callbacks
SessionConfigCallback? onSessionConfigReceived;
SyncStateCallback? onSyncStateChanged;
DeferredPlayCallback? onDeferredPlayChanged;
WatchTogetherSyncManager({
required WatchTogetherPeerService peerService,
@@ -169,7 +173,7 @@ class WatchTogetherSyncManager {
_peerReady[_peerService.myPeerId!] = false;
}
_hasAnnouncedReady = false;
_deferredPlay = false;
_setDeferredPlay(false);
_deferredPlayPosition = null;
_firstPlayCompleted = false;
_syncingTimer?.cancel();
@@ -208,7 +212,7 @@ class WatchTogetherSyncManager {
if (isPlaying && !isAllReady && !_firstPlayCompleted) {
// Defer until all peers have loaded video (initial sync only)
_deferredPlay = true;
_setDeferredPlay(true);
_deferredPlayPosition = player.state.position;
_isRemoteAction = true;
try {
@@ -228,7 +232,7 @@ class WatchTogetherSyncManager {
return;
}
if (!isPlaying) _deferredPlay = false;
if (!isPlaying) _setDeferredPlay(false);
_broadcastPlayPause(isPlaying);
}),
);
@@ -572,7 +576,7 @@ class WatchTogetherSyncManager {
case SyncMessageType.pause:
if (!_shouldApplyRemoteControl(message)) break;
_deferredPlay = false;
_setDeferredPlay(false);
await _applyRemotePause(expectedAttachmentGeneration: queuedAttachmentGeneration);
break;
@@ -663,7 +667,7 @@ class WatchTogetherSyncManager {
appLogger.d('WatchTogether: Peer ${message.peerId} player ready: ${message.bufferingState}');
if (_deferredPlay && isAllReady) {
_deferredPlay = false;
_setDeferredPlay(false);
_firstPlayCompleted = true;
await _applyRemotePlay(
position: _deferredPlayPosition,
@@ -903,10 +907,10 @@ class WatchTogetherSyncManager {
final hostIsPlaying = message.isPlaying ?? (message.bufferingState == false);
if (hostIsPlaying) {
// Host was playing — defer until our video is loaded
_deferredPlay = true;
_setDeferredPlay(true);
_deferredPlayPosition = message.position;
if (_hasAnnouncedReady) {
_deferredPlay = false;
_setDeferredPlay(false);
_firstPlayCompleted = true;
final didPlay = await _runGuardedPlayerCommand(
actionName: 'session config play',
@@ -943,6 +947,14 @@ class WatchTogetherSyncManager {
}
}
/// Set deferred play state and notify listeners
void _setDeferredPlay(bool value) {
if (_deferredPlay != value) {
_deferredPlay = value;
onDeferredPlayChanged?.call(value);
}
}
/// Send join announcement to all peers
void announceJoin(String displayName) {
_peerService.broadcast(
@@ -352,6 +352,7 @@ class _ParticipantNotificationOverlayState extends State<ParticipantNotification
ParticipantEventType.paused => t.watchTogether.participantPaused(name: n.event.displayName),
ParticipantEventType.resumed => t.watchTogether.participantResumed(name: n.event.displayName),
ParticipantEventType.seeked => t.watchTogether.participantSeeked(name: n.event.displayName),
ParticipantEventType.buffering => t.watchTogether.participantBuffering(name: n.event.displayName),
};
return Container(
margin: const EdgeInsets.only(bottom: 4),
@@ -381,41 +382,67 @@ class SyncingIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<WatchTogetherProvider>(
builder: (context, provider, child) {
if (!provider.isSyncing) {
return const SizedBox.shrink();
}
return Positioned(
bottom: 80,
left: 0,
right: 0,
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: const BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 14,
height: 14,
child: PlatformDetector.isTV()
? const Icon(Symbols.sync_rounded, size: 14, color: Colors.white)
: const CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
),
const SizedBox(width: 8),
Text(t.watchTogether.syncing, style: const TextStyle(color: Colors.white, fontSize: 12)),
],
),
),
),
);
return Selector<WatchTogetherProvider, bool>(
selector: (_, provider) => provider.isSyncing,
builder: (context, isSyncing, child) {
if (!isSyncing) return const SizedBox.shrink();
return _StatusPill(tvIcon: Symbols.sync_rounded, label: t.watchTogether.syncing);
},
);
}
}
/// Indicator shown when playback is deferred waiting for participants to load
class WaitingForParticipantsIndicator extends StatelessWidget {
const WaitingForParticipantsIndicator({super.key});
@override
Widget build(BuildContext context) {
return Selector<WatchTogetherProvider, bool>(
selector: (_, provider) => provider.isDeferredPlay,
builder: (context, isDeferredPlay, child) {
if (!isDeferredPlay) return const SizedBox.shrink();
return _StatusPill(tvIcon: Symbols.hourglass_empty_rounded, label: t.watchTogether.waitingForParticipants);
},
);
}
}
class _StatusPill extends StatelessWidget {
final IconData tvIcon;
final String label;
const _StatusPill({required this.tvIcon, required this.label});
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 80,
left: 0,
right: 0,
child: Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: const BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 14,
height: 14,
child: PlatformDetector.isTV()
? Icon(tvIcon, size: 14, color: Colors.white)
: const CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
),
const SizedBox(width: 8),
Text(label, style: const TextStyle(color: Colors.white, fontSize: 12)),
],
),
),
),
);
}
}