Diagnostic : le lecteur remonte ses freezes dans les logs du serveur

Les freezes (image et son figés puis reprise) se produisent dans le
navigateur de l'utilisateur ; le serveur n'en voyait rien. Le lecteur
envoie désormais chaque coupure (durée, buffer), saut (de → vers),
erreur MPEG-TS/HLS, recréation, repli HLS et changement de profil, plus
un bilan toutes les 60 s, à POST /api/player-log (authentifié). Le
serveur écrit une ligne « [Player] … » par événement.

Entrée filtrée : événements et champs connus seulement, texte borné,
URL masquées (une erreur réseau peut recopier les identifiants Xtream),
pas d'injection de lignes. Envoi jamais bloquant pour la lecture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5.5 committed 2026-10-10 23:38:17 +02:00
1 parent 775caad0ee
commit a739d6451c
5 files changed
+292 -1

No files matched your search

+48
View File
@@ -101,6 +101,54 @@ test('live : le retard est résorbé en douceur (liveSync) plutôt que par sauts
assert.ok(c.liveSyncMaxLatency <= c.liveBufferLatencyMaxLatency);
});
test('télémétrie : une coupure (waiting → playing) est remontée avec sa durée', async () => {
const sent = [];
const listeners = {};
const video = {
addEventListener(type, fn) { (listeners[type] ||= []).push(fn); },
removeEventListener() {},
canPlayType: () => '',
play: () => Promise.resolve(),
buffered: { length: 0 },
seekable: { length: 0 },
currentTime: 12,
playbackRate: 1,
};
const fire = (type) => (listeners[type] || []).forEach((fn) => fn());
const window = {
addEventListener() {}, removeEventListener() {},
console: { log() {} },
location: { origin: 'https://xf.test', href: 'https://xf.test/', search: '' },
parent: { postMessage() {} },
localStorage: { getItem: () => 'tok' },
};
let now = 1000;
const context = vm.createContext({
window,
location: window.location,
document: { addEventListener() {}, removeEventListener() {} },
URLSearchParams,
setInterval: () => 0,
clearInterval() {},
setTimeout,
Date: { now: () => now },
fetch: (url, opts) => { sent.push({ url, body: JSON.parse(opts.body) }); return Promise.resolve(); },
});
vm.runInContext(source, context);
const player = new window.XFPlayer({ video, url: '/api/live/554034/turbo.ts', type: 'live' });
player._wireVideoEvents();
fire('playing'); // premier affichage
now += 1000; fire('waiting');
now += 4200; fire('playing'); // reprise 4,2 s plus tard
const stall = sent.map((s) => s.body).find((b) => b.event === 'stall');
assert.ok(stall, 'aucun événement stall envoyé');
assert.equal(stall.ms, 4200);
assert.equal(stall.ch, '554034');
assert.equal(sent[0].url, '/api/player-log');
});
test('live : le buffer déjà lu est purgé (sinon QuotaExceeded en séance longue)', () => {
const c = liveMpegtsConfig();
assert.equal(c.autoCleanupSourceBuffer, true);