fix(shorts): flux vide + menu absent + flèches invisibles (Rumble)
CI / build-and-test (push) Successful in 14m55s
CI / build-and-test (push) Successful in 14m55s
Trois symptômes, deux racines — diagnostiqués au navigateur (Playwright
desktop + mobile) et hors navigateur (vraies réponses API + fonctions
front réelles importées via ts-node) :
1. Double dedup() dans handleShortsResponse : show() re-dédupliquait une
liste déjà dédupliquée par son appelant → le second passage marquait
tout comme « déjà vu » et posait items=[] en silence → écran
« Aucun short à afficher » sans erreur ni requête suivante, dès que
le catalogue type=shorts rendait moins de 3 items (aléa de la
requête tirée au sort). Racine des écrans morts, présente depuis le
commit 983c6cb.
2. Menu FAB et flèches Préc./Suiv. vivaient dans @if (current()) :
absents de TOUS les états vides, donc impossibles à utiliser
exactement quand le flux meurt (impossible de changer de
fournisseur). Le menu est désormais toujours monté (z-30 au-dessus
des placeholders), et la flèche désactivée passe en opacity 0.4
(au 1er item, Préc. était quasi invisible à 0.3).
3. Format : Rumble n'expose AUCUNE dimension — vérifié : vignettes og:image
toutes en crop fixe 16:9 (9/9 échantillons, 1280x720 et 480x270) et
aucun width/height dans les pages vidéo → un filtre d'orientation
par vignette est impossible, la classification reste durée-seule.
En revanche le flux natif rumble.com/shorts (les vrais shorts
officiels) devient la voie primaire : loadFeed va droit au chemin
historique au lieu du catalogue qui ne rendait que des brives 16:9.
Tests verts avant commit : npm run build, test:section,
test:shorts-catalog, test:filters.
This commit is contained in:
@@ -54,7 +54,6 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (current(); as v) {
|
|
||||||
<!-- Menu FAB : toutes les options (fournisseur, langue, actions). -->
|
<!-- Menu FAB : toutes les options (fournisseur, langue, actions). -->
|
||||||
<app-player-menu [(open)]="menuOpen">
|
<app-player-menu [(open)]="menuOpen">
|
||||||
<div class="pm-section-label">Fournisseur</div>
|
<div class="pm-section-label">Fournisseur</div>
|
||||||
@@ -146,6 +145,7 @@
|
|||||||
<span class="pm-item-label">Passer (vidéo illisible)</span>
|
<span class="pm-item-label">Passer (vidéo illisible)</span>
|
||||||
</button>
|
</button>
|
||||||
</app-player-menu>
|
</app-player-menu>
|
||||||
|
@if (current(); as v) {
|
||||||
|
|
||||||
<!-- Capteur tactile (mobile uniquement) : l'iframe mange les
|
<!-- Capteur tactile (mobile uniquement) : l'iframe mange les
|
||||||
touchers, donc le swipe up/down passerait à la trappe. Cette
|
touchers, donc le swipe up/down passerait à la trappe. Cette
|
||||||
@@ -208,12 +208,12 @@
|
|||||||
<!-- Navigation précédent / suivant : SUR la vidéo (hors auto-hide du
|
<!-- Navigation précédent / suivant : SUR la vidéo (hors auto-hide du
|
||||||
chrome, hors geste tactile — stopPropagation dans les handlers). -->
|
chrome, hors geste tactile — stopPropagation dans les handlers). -->
|
||||||
<button type="button" (click)="prev(); $event.stopPropagation()" [disabled]="!canPrev()"
|
<button type="button" (click)="prev(); $event.stopPropagation()" [disabled]="!canPrev()"
|
||||||
class="absolute left-1.5 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-30"
|
class="absolute left-1.5 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-40"
|
||||||
aria-label="Short précédent">
|
aria-label="Short précédent">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M15.5 4.5 8 12l7.5 7.5z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M15.5 4.5 8 12l7.5 7.5z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" (click)="next(); $event.stopPropagation()" [disabled]="!canNext()"
|
<button type="button" (click)="next(); $event.stopPropagation()" [disabled]="!canNext()"
|
||||||
class="absolute right-1.5 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-30"
|
class="absolute right-1.5 top-1/2 z-20 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-full bg-black/45 text-white ring-1 ring-white/15 backdrop-blur-md transition hover:bg-black/70 disabled:opacity-40"
|
||||||
aria-label="Short suivant">
|
aria-label="Short suivant">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8.5 4.5 16 12l-7.5 7.5z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8.5 4.5 16 12l-7.5 7.5z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1492,6 +1492,16 @@ export class WatchShortComponent implements OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const provider = this.provider();
|
const provider = this.provider();
|
||||||
|
// Rumble : on va droit au chemin historique (flux natif rumble.com/shorts
|
||||||
|
// + remplissage recherche). Le catalogue `type=shorts` ne rend que des
|
||||||
|
// vidéos <=75 s HORizontales (Rumble n'expose aucune dimension : ni largeur
|
||||||
|
// ni hauteur dans les pages, vignettes en crop fixe 16:9 — vérifié 9/9),
|
||||||
|
// ce qui donnait un feed de « briques » 16:9 au lieu des shorts officiels.
|
||||||
|
if (provider === 'rumble') {
|
||||||
|
this.catalogActive = false;
|
||||||
|
this.loadLegacyFeed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Shorts via le CATALOGUE d'abord (`/api/search?type=shorts` : cache,
|
// Shorts via le CATALOGUE d'abord (`/api/search?type=shorts` : cache,
|
||||||
// provenance, feature-flags). Twitch garde son flux de clips historique
|
// provenance, feature-flags). Twitch garde son flux de clips historique
|
||||||
// (pas d'équivalent catalogue : la recherche serveur renvoie des VOD).
|
// (pas d'équivalent catalogue : la recherche serveur renvoie des VOD).
|
||||||
@@ -1646,8 +1656,12 @@ export class WatchShortComponent implements OnDestroy {
|
|||||||
const raw = (res.items || []).filter(v => !!v.videoId);
|
const raw = (res.items || []).filter(v => !!v.videoId);
|
||||||
this.nextCursor.set(res.nextCursor || null);
|
this.nextCursor.set(res.nextCursor || null);
|
||||||
if (!res.nextCursor) this.exhaustedQueries.add(this.currentQuery());
|
if (!res.nextCursor) this.exhaustedQueries.add(this.currentQuery());
|
||||||
|
// NOTE : `list` est DÉJÀ dédupliqué par chaque appelant (ligne 1671 /
|
||||||
|
// branche YouTube). Un second passage ici marquait tout comme « déjà vu »
|
||||||
|
// et posait une liste VIDE en silence — écran « Aucun short » sans erreur
|
||||||
|
// ni requête suivante (bug reproduit au navigateur, fix racine).
|
||||||
const show = (list: Video[]) => {
|
const show = (list: Video[]) => {
|
||||||
const deduped = this.dedup(list);
|
const deduped = list;
|
||||||
this.items.set(deduped);
|
this.items.set(deduped);
|
||||||
this.index.set(0);
|
this.index.set(0);
|
||||||
this.error.set(null);
|
this.error.set(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user