feat(providers): phases 7.3/7.4/7.6/8.1 — provenance, health, NDJSON, contrat unique
CI / build-and-test (push) Successful in 14m43s

7.3: capturedAt/source au registre + 6 adaptateurs + module provenance.ts + ?debug=1 (search-transport.mjs). 7.4: ProviderHealthService + badge source degradee. 7.6: squelettes par provider + snapshots progressifs + transport NDJSON /api/search. 8.1: ProviderAdapter unifie (search enveloppe + channelContent/channelMeta/capabilities) via getProviderAdapter + test de contrat offline.
This commit is contained in:
2026-09-30 07:57:11 -04:00
parent 37681c4f53
commit 665a0f0ebd
87 changed files with 8850 additions and 550 deletions
+13 -3
View File
@@ -123,10 +123,14 @@ export function itemDurationSec(item) {
if (s.includes(':')) {
const parts = s.split(':').map((p) => Number(p));
if (parts.length && parts.every((p) => Number.isFinite(p))) {
return parts.reduce((acc, p) => acc * 60 + p, 0);
const total = parts.reduce((acc, p) => acc * 60 + p, 0);
if (total > 0) return total;
}
}
return 0;
// Phase 2.2 — « durée inconnue » vaut `undefined`, jamais `0`. Le 0 signifiait
// « vidéo de 0 s » et rendait la règle « verticale sans durée => pas un short »
// inopérante, tout en faisant afficher "0:00" sur les cartes.
return undefined;
}
export function itemPublishedTs(item) {
@@ -191,7 +195,13 @@ export function isShortItem(item, providerId) {
// font ≤ 60 s par construction, une durée > 75 s sur un clip est du bruit
// de métadonnées (le test historique « shorts keeps clips » l'impose).
if (kind === 'clip') return true;
if (item.isShort === true || type === 'short') {
// Règle (c) : `kind === 'clip'` ci-dessusprime sur `type: 'video'`. Sans cet
// early return, un item `kind:'clip', type:'video'` (ce que produisait
// `channel-content.mjs` pour les lives Twitch) retombait en règle 3/4.
if (item.isShort === true || type === 'short' || String(item.url || '').includes('/shorts/')) {
// Règle (a) : flag natif + durée INCONNUE => short. « Le provider a parlé »
// prime quand rien ne le contredit. Règle (b) : un flag natif prime aussi
// sur un 1:1 (repost Instagram/TikTok), mais JAMAIS sur la seule durée.
if (hasKnownDuration && d > SHORT_MAX_SECONDS) return false;
return true;
}