diff --git a/src/components/shorts/watch-short.component.ts b/src/components/shorts/watch-short.component.ts index d010927..99457db 100644 --- a/src/components/shorts/watch-short.component.ts +++ b/src/components/shorts/watch-short.component.ts @@ -131,9 +131,10 @@ export class WatchShortComponent implements OnDestroy { * auquel cas l'utilisateur tape une fois sur la vidéo (geste qui débloque * les suivantes). * - * Exceptions (muet forcé, autoplay garanti) : + * Cas particuliers : * - Twitch clips : l'embed `clips.twitch.tv` n'expose AUCUNE API de son — - * `muted=true` est la seule combinaison qui démarre sans geste ; + * l'état muet passe par le paramètre `muted` de l'URL (défaut : son ON, + * piloté par le bouton Son du menu) ; * - Rumble : `autoplay=1` (son) par défaut, `autoplay=2` (muet) si * l'utilisateur coupe le son. */ @@ -454,11 +455,9 @@ export class WatchShortComponent implements OnDestroy { // Les Shorts Twitch = clips uniquement (jamais de live/channel). const clipId = (v as any).kind === 'clip' ? v.videoId : null; if (clipId) { - // `muted=true` FORCÉ (indépendant du bouton Son) : l'embed clips - // n'expose aucune API de volume — muet est la seule combinaison - // qui démarre sans geste. Le son reste disponible via « Ouvrir - // chez le fournisseur » / Watch. - const u = `https://clips.twitch.tv/embed?clip=${encodeURIComponent(clipId)}&${parentParams}&autoplay=true&muted=true${nudge}`; + // Son piloté par le bouton Son du menu (`muted`, défaut = son ON) — + // le rechargement de l'embed applique le nouvel état. + const u = `https://clips.twitch.tv/embed?clip=${encodeURIComponent(clipId)}&${parentParams}&autoplay=true&muted=${this.muted()}${nudge}`; return this.sanitizer.bypassSecurityTrustResourceUrl(u); } // Repli : player channel/VOD (ne devrait pas arriver, les lives @@ -466,8 +465,8 @@ export class WatchShortComponent implements OnDestroy { const base = 'https://player.twitch.tv/'; const chan = !/^\d+$/.test(String(v.videoId)) ? v.videoId : null; const qs = chan - ? `?channel=${encodeURIComponent(chan)}&${parentParams}&autoplay=true&muted=true${nudge}` - : `?video=${encodeURIComponent(v.videoId)}&${parentParams}&autoplay=true&muted=true${nudge}`; + ? `?channel=${encodeURIComponent(chan)}&${parentParams}&autoplay=true&muted=${this.muted()}${nudge}` + : `?video=${encodeURIComponent(v.videoId)}&${parentParams}&autoplay=true&muted=${this.muted()}${nudge}`; return this.sanitizer.bypassSecurityTrustResourceUrl(base + qs); } if (p === 'peertube' && v.videoId) { @@ -805,9 +804,10 @@ export class WatchShortComponent implements OnDestroy { } catch {} this.autoAdvanceKey = ''; if (!v?.videoId) return; - // Embed Twitch en attente d'un geste : il n'y a rien à l'écran, inutile de - // faire défiler le flux (sinon les videos « switchent » sans jamais jouer). - if (this.providerOf(v) === 'twitch' && !this.twitchCanLoad()) return; + // Twitch : l'embed clips est cross-origin et n'expose AUCUNE API d'état — + // on ne peut pas savoir si la vidéo est en pause. Pas d'avance auto pour + // ne jamais changer de vidéo pendant une pause (navigation manuelle). + if (this.providerOf(v) === 'twitch') return; const known = Number((v as Video).duration || 0); const secs = (Number.isFinite(known) && known > 0) ? known : this.estimatedDuration(v); if (!Number.isFinite(secs) || secs <= 0) return; diff --git a/src/components/watch/watch.component.ts b/src/components/watch/watch.component.ts index a4ffd45..c7aedfc 100644 --- a/src/components/watch/watch.component.ts +++ b/src/components/watch/watch.component.ts @@ -484,10 +484,9 @@ export class WatchComponent implements OnDestroy, AfterViewInit { // Clips : player dédié clips.twitch.tv (le player VOD rejette les slugs). const clipId = clip || (/^\d+$/.test(String(id || '')) ? null : ((this.video() as any)?.kind === 'clip' ? id : null)); if (clipId) { - // autoplay+muet, comme les autres fournisseurs de la page (YouTube - // mute=1, Dailymotion mute=1, Odysee muted=1) : le lecteur démarre - // tout seul, le son se réactive via les commandes du player. - const u = `https://clips.twitch.tv/embed?clip=${encodeURIComponent(clipId)}&${parentParams}&autoplay=true&muted=true${nudge}`; + // Son PAR DÉFAUT (demande utilisateur) : le chargement a lieu dans un + // geste (voir twitchCanLoad), donc l'autoplay sonore est autorisé. + const u = `https://clips.twitch.tv/embed?clip=${encodeURIComponent(clipId)}&${parentParams}&autoplay=true${nudge}`; return this.sanitizer.bypassSecurityTrustResourceUrl(u); } const base = 'https://player.twitch.tv/'; @@ -497,8 +496,8 @@ export class WatchComponent implements OnDestroy, AfterViewInit { // Les VOD acceptent time=1h2m3s (seek au chargement, ex. transcript). const timeParam = !chan && start > 0 ? `&time=${encodeURIComponent(this.formatTwitchTime(start))}` : ''; const qs = chan - ? `?channel=${encodeURIComponent(chan)}&${parentParams}&autoplay=true&muted=true${nudge}` - : `?video=${encodeURIComponent(id)}&${parentParams}&autoplay=true&muted=true${timeParam}${nudge}`; + ? `?channel=${encodeURIComponent(chan)}&${parentParams}&autoplay=true${nudge}` + : `?video=${encodeURIComponent(id)}&${parentParams}&autoplay=true${timeParam}${nudge}`; const u = base + qs; return this.sanitizer.bypassSecurityTrustResourceUrl(u); }