Files
NewTube/server/tests/live_providers.test.mjs
T
bruno 326455adfe
CI / build-and-test (push) Successful in 14m56s
feat: lives PeerTube/Rumble, page Admin, TTL par provider, import/export playlists
#1 Live
- PeerTube: API native ?isLive=true (recherche sepiasearch + videos de chaine),
  mapPeertubeItem exporte la regle live (type/kind/isLive, viewers, jamais de
  duree), caps pt live:true, themes « En direct » branches pt/ru
- Rumble: mode 'live' de scrapeRumbleList (rumble.com/browse/live, SSR), items
  type:live + extraction channelId (/c/<slug>), route GET /api/rumble/live,
  onglet chaine live filtre par channelId (plafond page 1 = 50 directs)
- Odysee: N/A prouve (proxy na-backend sous jeton, probe 2026-10), caps inchangees
- Tests: npm run test:live (25 assertions), assertion adapter_contract mise a
  jour (seuls tw/pt/ru declarent live)

#2 Playlists natives
- Twitch: NON FAISABLE — probe avec token app valide: GET/POST /helix/playlists
  et /helix/playlists/{id}/videos -> 404 (controle /helix/users -> 400).
  Matrice README: Twitch/Odysee/Rumble en N/A avec preuves

#3 Quick wins
- Page Administration /#/admin (entree sidebar Informations + i18n nav.admin
  FR/EN): healthz + providers/metrics rendus (statut, yt-dlp, clés actives/
  bannies, anti-ban, cache par provider, compteurs 1h, quota du jour) ;
  healthz sert aussi sous /proxy/api/healthz
- TTL par provider: server/env-ttl.mjs (<BASE>_<P> -> <BASE> -> defaut, jamais
  nul), branche sur les caches details et transcripts ; suggest reste global
  (cle agregee multi-providers) ; tests npm run test:cache
- Import/Export playlists JSON: GET /playlists/export (newtube-playlists-v1,
  avant /playlists/:id) + POST /playlists/import (dedoublonnage par titre),
  boutons [Exporter] [Importer] sur la page listes de lecture

README: matrice fournisseurs (Live pt/ru OK, od N/A, playlists N/A prouves),
roadmap deplace en ✅ (Live, import/export, Admin, TTL) + 🟡 mis a jour.
2026-10-02 20:25:08 -04:00

74 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Live PeerTube + Rumble — capacité « direct » testée hors réseau.
// - mapPeertubeItem : règle `isLive` → type/kind/isLive (doctrine phase 3.2),
// durée jamais émise sur un direct, viewers = spectateurs.
// - parseCardsHtml mode 'live' : items de rumble.com/browse/live typés live,
// jamais des Shorts, slug de chaîne extrait (filtre channel-content).
import { mapPeertubeItem } from '../providers/peertube.mjs';
import { parseCardsHtml } from '../providers/rumble.mjs';
import { isLiveItem, isShortItem } from '../search-filters.mjs';
let pass = 0;
const eq = (a, b, m) => {
const x = JSON.stringify(a), y = JSON.stringify(b);
if (x !== y) throw new Error(`${m}\n expected ${y}\n got ${x}`);
pass++; console.log(` V ${m}`);
};
// --- PeerTube : item en direct ---
const live = mapPeertubeItem({
uuid: 'u1', name: 'Direct', url: 'https://instance/w/u1',
isLive: true, viewers: 42, duration: 123, views: 5,
account: { displayName: 'A' }, channel: { name: 'ch' },
});
eq(live.type, 'live', 'PT live: type live');
eq(live.kind, 'live', 'PT live: kind live');
eq(live.isLive, true, 'PT live: isLive posé');
eq(live.duration, undefined, 'PT live: durée jamais émise (temps écoulé)');
eq(live.viewers, 42, 'PT live: viewers = spectateurs');
eq(live.views, 5, 'PT live: vues conservées (live a les deux compteurs)');
eq(isLiveItem(live), true, 'isLiveItem serveur le classe live');
eq(isShortItem(live, 'peertube'), false, 'un direct n’est jamais un Short');
// --- PeerTube : VOD classique inchangée ---
const vod = mapPeertubeItem({ uuid: 'u2', name: 'Vid', duration: 123, isLive: false });
eq(vod.type, 'video', 'PT vod: type video');
eq(vod.kind, 'vod', 'PT vod: kind vod');
eq(vod.duration, 123, 'PT vod: durée conservée');
eq(vod.isLive, undefined, 'PT vod: pas de champ isLive inventé');
eq(isLiveItem(vod), false, 'PT vod n’est pas live');
// --- Rumble : mode live sur une carte type browse ---
const html = `<html><body><ul>
<li class="video-listing-entry">
<a class="video-item--a" href="/v9z1live-title.html?e9s=track"></a>
<h3 class="video-item--title">Live title</h3>
<a href="/c/WarRoom">War Room</a>
<img data-src="https://cdn.example/t.jpg" />
</li>
<li class="video-listing-entry">
<a class="video-item--a" href="/v8y2other.html"></a>
<h3 class="video-item--title">Autre titre</h3>
<a href="/c/OtherChan">Other</a>
</li>
</ul></body></html>`;
const items = parseCardsHtml(html, { mode: 'live' });
eq(items.length, 2, 'mode live: 2 cartes parsées');
const [i1] = items;
eq(i1.videoId, 'v9z1live', 'mode live: id extrait au slug');
eq(i1.type, 'live', 'mode live: type live');
eq(i1.kind, 'live', 'mode live: kind live');
eq(i1.isLive, true, 'mode live: isLive posé');
eq(i1.isShort, false, 'mode live: jamais un Short');
eq(i1.channelId, 'WarRoom', 'mode live: slug de chaîne extrait');
eq(isLiveItem(i1), true, 'mode live passe isLiveItem');
// --- Même HTML en mode browse : tout reste une VOD (pas de régression) ---
const browse = parseCardsHtml(html, { mode: 'browse' });
eq(browse[0].type, 'video', 'mode browse: type video (pas de fuite live)');
eq(browse[0].isLive, undefined, 'mode browse: pas de isLive inventé');
eq(browse[0].isShort, false, 'mode browse: durée inconnue => pas short');
eq(browse[0].channelId, 'WarRoom', 'mode browse: channelId extrait (additif)');
console.log(`\nOK ${pass} assertions`);