feat: détection short par orientation vidéo
CI / build-and-test (push) Successful in 14m6s

Règle partagée front/back : flag natif, verticale stricte (ratio ≤ 0.8, ≤ 90 s), verticale sans durée jamais short, repli durée (70 YT / 75 autres). Dimensions alimentées par Dailymotion/Odysee/PeerTube, flag maison Rumble supprimé, condition innertube morte simplifiée. Shorts : langues utilisateur (UI + transcripts) via relevanceLanguage/languageOneOf/Twitch language, son activé par défaut.
This commit is contained in:
2026-09-29 12:17:10 -04:00
parent 3f4cd56f14
commit f443a470f9
22 changed files with 454 additions and 73 deletions
+71
View File
@@ -7,6 +7,7 @@ import {
normalizeFilters, parseSearchFilters, activeFilters, filtersCacheKey,
periodMs, durationBounds, matchesFilters, applySearchFilters,
innertubeSearchFilters, apiSearchParams, itemDurationSec, itemPublishedTs,
isShortItem, SHORT_MAX_SECONDS, VERTICAL_SHORT_MAX_SECONDS, VERTICAL_MAX_RATIO,
} from '../search-filters.mjs';
function expect(cond, msg) {
@@ -186,4 +187,74 @@ expect(
);
logOk('innertubeSearchFilters / apiSearchParams');
// ---------- Détection Short par orientation ----------
eq(typeof SHORT_MAX_SECONDS, 'number', 'seuils exportés');
eq(VERTICAL_SHORT_MAX_SECONDS, 90, 'verticale short jusqu’à 90 s');
eq(VERTICAL_MAX_RATIO, 0.8, 'ratio vertical max 0.8');
// Règle 2 : verticale 60 s (ex. repost TikTok 608x1080) ⇒ short.
expect(
isShortItem({ title: 'repost', duration: 60, width: 608, height: 1080, type: 'video' }),
'vertical 60s is a short',
);
// Règle 2 : verticale 200 s ⇒ PAS short (vidéo verticale, pas short).
expect(
!isShortItem({ title: 'vlog', duration: 200, width: 720, height: 1280, type: 'video' }),
'vertical 200s is not a short',
);
// Règle 2 : un carré 80 s (> 75 s) n'est PAS short : l'orientation seule
// (ratio ~1) ne suffit pas ET le repli durée ne s'applique plus.
expect(
!isShortItem({ title: 'clip carré', duration: 80, width: 1080, height: 1080, type: 'video' }),
'square 80s is not a short (orientation alone is not enough)',
);
// …mais à 30 s, la règle 4 (durée seule) le garde : non-régression historique.
expect(
matchesFilters({ id: 'a', title: 'clip carré', duration: 30, width: 1080, height: 1080, type: 'video' }, { type: 'shorts' }),
'square 30s still matches shorts via duration fallback',
);
// Règle 4 : horizontale 50 s ⇒ short (non-régression durée seule).
expect(
isShortItem({ title: 'paysage', duration: 50, width: 1280, height: 720, type: 'video' }),
'horizontal 50s is a short (duration fallback)',
);
// Règle 3 : verticale SANS durée connue ⇒ PAS short.
expect(
!isShortItem({ title: 'inconnu', width: 608, height: 1080, type: 'video' }),
'vertical without duration is not a short',
);
// Règle 1 : flag contredit par une durée > 75 s ⇒ PAS short.
expect(
!isShortItem({ title: 'faux short', duration: 300, isShort: true, type: 'video' }),
'native flag contradicted by duration is not a short',
);
// Règle 1 : un vrai Short titré « …shorts » n'est plus exclu (ancien bug
// `!/short/i.test(title)`).
expect(
isShortItem({ title: 'mon voyage #shorts', duration: 40, type: 'video' }),
'title containing shorts is kept',
);
// Seuil YouTube (70) vs autres (75) : 72 s.
expect(
!isShortItem({ title: 'yt', duration: 72, type: 'video', provider: 'youtube' }, 'yt'),
'youtube 72s is not a short',
);
expect(
isShortItem({ title: 'dm', duration: 72, type: 'video' }, 'dm'),
'dailymotion 72s is a short',
);
// §4 : une verticale 60 s ne remonte jamais dans le filtre « vidéos »…
expect(
!matchesFilters({ id: 'b', title: 'v', duration: 60, width: 608, height: 1080, type: 'video' }, { type: 'video' }),
'vertical 60s excluded from video filter',
);
// …et les dimensions comptent comme signal : l'item n'est plus "muet",
// donc le filtre `live` l'exclut (une verticale n'est pas un direct).
expect(
!matchesFilters({ id: 'c', title: 'v', duration: 0, width: 608, height: 1080 }, { type: 'live' }),
'dimensions are a signal: vertical without live flag is dropped from live',
);
logOk('isShortItem (orientation + durée)');
console.log('\nAll search-filters tests passed.');