fix(shorts): anti-Forbidden Dailymotion + selecteur icon-only
CI / build-and-test (push) Successful in 14m55s

- DM: embed mute par defaut (comme /watch) + son sur action explicite
- helmet: Referrer-Policy strict-origin-when-cross-origin (Dailymotion exige un Referer)
- searchVideosPage DM: champs allow_embed/width/height/language/owner.id
- iframes shorts/watch: allow fullscreen (conforme embed officiel DM)
- selecteur shorts icon-only (pastilles + globe cyclique)
This commit is contained in:
2026-09-30 19:55:23 -04:00
parent 49859f4e81
commit 6b20b1ce79
6 changed files with 61 additions and 32 deletions
@@ -15,50 +15,51 @@
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M19 12H5m7-7l-7 7 7 7"/></svg>
</a>
<!-- Sélecteur : Pour toi + fournisseurs (logos) + filtre de langues préférées -->
<!-- Sélecteur icon-only : Pour toi + fournisseurs (logos) + filtre de langues.
Aucun texte visible (logos + title/aria-label) ; le filtre actif se lit
dans le tooltip et l'anneau ambré du globe. -->
<div class="scroll-rail no-scrollbar shorts-chrome relative z-10 flex w-full max-w-full items-center justify-start gap-2 overflow-x-auto px-3 pt-3 sm:justify-center" role="tablist" aria-label="Fournisseur des Shorts" (click)="$event.stopPropagation()">
<button (click)="toggleForYou()"
[attr.aria-pressed]="forYou()"
class="chip flex shrink-0 items-center gap-1.5 px-3 py-2 rounded-full text-[13px] border transition-colors min-h-[44px]"
[class]="forYou() ? 'bg-violet-600 border-violet-500 text-white' : 'bg-black/60 border-white/15 text-slate-200 hover:bg-white/10'"
title="Shorts personnalisés multi-fournisseurs à partir de vos abonnements, groupes, recherches, likes et téléchargements">
<span class="flex h-6 w-6 items-center justify-center rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 text-white shadow" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2c.7 4.8 3.2 7.3 8 8-4.8.7-7.3 3.2-8 8-.7-4.8-3.2-7.3-8-8 4.8-.7 7.3-3.2 8-8z"/><path d="M19 3c.3 2 1.3 3 3.3 3.3-2 .3-3 1.3-3.3 3.2-.3-1.9-1.3-2.9-3.2-3.2 1.9-.3 2.9-1.3 3.2-3.3z"/></svg>
aria-label="Pour toi"
title="Pour toi — Shorts personnalisés multi-fournisseurs à partir de vos abonnements, groupes, recherches, likes et téléchargements"
class="chip flex h-11 w-11 shrink-0 items-center justify-center rounded-full border transition-colors"
[class]="forYou() ? 'bg-violet-600 border-violet-500 text-white' : 'bg-black/60 border-white/15 text-slate-200 hover:bg-white/10'">
<span class="flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 text-white shadow" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2c.7 4.8 3.2 7.3 8 8-4.8.7-7.3 3.2-8 8-.7-4.8-3.2-7.3-8-8 4.8-.7 7.3-3.2 8-8z"/><path d="M19 3c.3 2 1.3 3 3.3 3.3-2 .3-3 1.3-3.3 3.2-.3-1.9-1.3-2.9-3.2-3.2 1.9-.3 2.9-1.3 3.2-3.3z"/></svg>
</span>
Pour toi
</button>
<button *ngFor="let p of providerButtons()"
(click)="selectProvider(p.id)"
[attr.aria-pressed]="!forYou() && provider() === p.id"
class="chip flex shrink-0 items-center gap-1.5 px-3 py-2 rounded-full text-[13px] border transition-colors min-h-[44px]"
[attr.aria-label]="p.label"
[title]="p.label"
class="chip flex h-11 w-11 shrink-0 items-center justify-center rounded-full border transition-colors"
[class]="!forYou() && provider() === p.id ? 'bg-red-600 border-red-500 text-white' : 'bg-black/60 border-white/15 text-slate-200 hover:bg-white/10'">
<span class="flex h-6 w-6 items-center justify-center rounded-full text-white shadow" [class]="p.disc" aria-hidden="true">
<span class="flex h-7 w-7 items-center justify-center rounded-full text-white shadow" [class]="p.disc" aria-hidden="true">
@switch (p.glyph.kind) {
@case ('play') {
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg>
}
@case ('chat') {
<svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="4" y="3" width="16" height="13" rx="2"/><path d="M9 16v4l4-4"/><line x1="10" y1="7" x2="10" y2="11"/><line x1="14" y1="7" x2="14" y2="11"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="4" y="3" width="16" height="13" rx="2"/><path d="M9 16v4l4-4"/><line x1="10" y1="7" x2="10" y2="11"/><line x1="14" y1="7" x2="14" y2="11"/></svg>
}
@default {
<span class="text-[13px] font-extrabold leading-none">{{ glyphLetter(p) }}</span>
<span class="text-[15px] font-extrabold leading-none">{{ glyphLetter(p) }}</span>
}
}
</span>
{{ p.label }}
</button>
<!-- Filtre de langues préférées : traverse TOUS les boutons (chaque flux
passe par `effectiveShortsLangs`). Persisté, invité compris. -->
<span class="flex shrink-0 items-center gap-1 rounded-full border border-white/15 bg-black/60 px-2.5 text-[13px] text-slate-200 min-h-[44px]" [title]="langFilterHint()">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 shrink-0 opacity-80" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
<select [ngModel]="langFilter()" (ngModelChange)="setLangFilter($event)" class="bg-transparent py-2 pr-0.5 outline-none cursor-pointer" aria-label="Langues préférées des Shorts">
<option value="profile">Profil</option>
<option value="fr">Français</option>
<option value="en">Anglais</option>
<option value="fren">FR + EN</option>
<option value="all">Toutes</option>
</select>
</span>
<!-- Filtre de langues préférées (globe cyclique, sans texte) : chaque cran
change le filtre de TOUS les flux ; l'anneau ambré signale un filtre
actif (non-profil), le détail est dans le tooltip. -->
<button (click)="cycleLangFilter()"
[title]="langFilterHint()"
[attr.aria-label]="langFilterHint()"
class="flex h-11 w-11 shrink-0 items-center justify-center rounded-full border transition-colors"
[class]="langFilter() !== 'profile' ? 'bg-black/60 border-amber-400/70 text-amber-200 hover:bg-white/10 ring-2 ring-amber-400/40' : 'bg-black/60 border-white/15 text-slate-200 hover:bg-white/10'">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
</button>
</div>
<!-- Scène immersive : flèches (bureau) + cadre 9:16 -->
@@ -77,7 +78,7 @@
<div #shortsFrame class="shorts-frame relative shrink-0 overflow-hidden bg-black rounded-none ring-0 shadow-none sm:rounded-2xl sm:ring-1 sm:ring-white/10 sm:shadow-2xl">
@if (embedUrl(); as url) {
<iframe [src]="url" [title]="current()?.title || 'Short video'" class="absolute inset-0 w-full h-full" frameborder="0" playsinline
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture; web-share"
allowfullscreen referrerpolicy="strict-origin-when-cross-origin"></iframe>
} @else if (loading()) {
<div class="absolute inset-0 flex flex-col gap-3 items-center justify-center bg-black text-slate-400 text-sm">