mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
Lecture : un échec de chargement de mpegts.js n'est plus fatal
L'écran « Échec du chargement : vendor/mpegts.min.js » était un cul-de-sac : la moindre requête ratée sur la lib condamnait la chaîne, alors que le flux restait parfaitement lisible autrement. - xf-player-core : le chargement des libs est retenté une fois en contournant le cache HTTP (les libs sont servies en max-age=86400, une entrée tronquée bloquait le lecteur jusqu'au vidage manuel du cache), et seul un succès est mémorisé — la promesse rejetée restait en cache et rendait tout réessai impossible pour le reste de la session. - Repli automatique : si mpegts.js reste introuvable, le live bascule sur la route HLS équivalente (où FFmpeg réencode déjà l'audio en AAC) ; si hls.js reste introuvable, on tente le HLS natif. Plus d'écran d'erreur quand un chemin de lecture est encore disponible. - Message d'erreur exploitable : la cause réelle est affichée (HTTP 404, 429, réseau injoignable) au lieu d'un échec indifférencié. - Préchargement corrigé : les trois players préchargeaient hls.js en dur, soit 618 Ko téléchargés pour rien à chaque zap TV — c'est mpegts.js qui sert au direct — au détriment du flux et du chargement de mpegts.js lui-même. Le préchargement suit désormais le flux demandé, avec la même décision que XFPlayer.start() (et ne charge rien sur Safari/iOS). - Serveur : avertissement explicite au démarrage si web/vendor/*.min.js manque, au lieu de laisser le navigateur échouer sans explication. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FSovdudi3KBwZK3aC42fw
This commit is contained in:
6 files changed
+183
-9
No files matched your search
+29
-1
@@ -11,7 +11,35 @@
|
||||
et environ 200 Ko de JS jamais téléchargés dans le cas courant.
|
||||
-->
|
||||
<link rel="stylesheet" href="xf-player.css">
|
||||
<link rel="preload" href="vendor/hls.min.js" as="script">
|
||||
<script>
|
||||
// Précharge la SEULE lib utile à CE flux, dès le parse du <head>.
|
||||
// Un préchargement fixe de hls.js faisait télécharger 618 Ko inutiles
|
||||
// à chaque zap TV — la lib réellement nécessaire au direct est
|
||||
// mpegts.js — et cette bande passante était prise sur le flux
|
||||
// lui-même et sur mpegts.js, jusqu'à faire échouer son chargement sur
|
||||
// les liens contraints. Même décision que XFPlayer.start().
|
||||
(function () {
|
||||
var p = new URLSearchParams(location.search);
|
||||
var url = (p.get('url') || '').toLowerCase();
|
||||
var isHls = url.indexOf('.m3u8') !== -1;
|
||||
var nativeHls =
|
||||
document.createElement('video')
|
||||
.canPlayType('application/vnd.apple.mpegurl') !== '';
|
||||
var lib = null;
|
||||
if (isHls) {
|
||||
// Safari/iOS lit le HLS nativement : aucune lib à charger.
|
||||
if (!nativeHls) lib = 'hls.min.js';
|
||||
} else if (p.get('type') === 'live' || url.indexOf('.ts') !== -1) {
|
||||
lib = 'mpegts.min.js';
|
||||
}
|
||||
if (!lib) return;
|
||||
var l = document.createElement('link');
|
||||
l.rel = 'preload';
|
||||
l.as = 'script';
|
||||
l.href = 'vendor/' + lib;
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
+29
-1
@@ -5,7 +5,35 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>XtremFlow Player Lite</title>
|
||||
<link rel="stylesheet" href="xf-player.css">
|
||||
<link rel="preload" href="vendor/hls.min.js" as="script">
|
||||
<script>
|
||||
// Précharge la SEULE lib utile à CE flux, dès le parse du <head>.
|
||||
// Un préchargement fixe de hls.js faisait télécharger 618 Ko inutiles
|
||||
// à chaque zap TV — la lib réellement nécessaire au direct est
|
||||
// mpegts.js — et cette bande passante était prise sur le flux
|
||||
// lui-même et sur mpegts.js, jusqu'à faire échouer son chargement sur
|
||||
// les liens contraints. Même décision que XFPlayer.start().
|
||||
(function () {
|
||||
var p = new URLSearchParams(location.search);
|
||||
var url = (p.get('url') || '').toLowerCase();
|
||||
var isHls = url.indexOf('.m3u8') !== -1;
|
||||
var nativeHls =
|
||||
document.createElement('video')
|
||||
.canPlayType('application/vnd.apple.mpegurl') !== '';
|
||||
var lib = null;
|
||||
if (isHls) {
|
||||
// Safari/iOS lit le HLS nativement : aucune lib à charger.
|
||||
if (!nativeHls) lib = 'hls.min.js';
|
||||
} else if (p.get('type') === 'live' || url.indexOf('.ts') !== -1) {
|
||||
lib = 'mpegts.min.js';
|
||||
}
|
||||
if (!lib) return;
|
||||
var l = document.createElement('link');
|
||||
l.rel = 'preload';
|
||||
l.as = 'script';
|
||||
l.href = 'vendor/' + lib;
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
+29
-1
@@ -8,7 +8,35 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>XtremFlow Player Mobile</title>
|
||||
<link rel="stylesheet" href="xf-player.css">
|
||||
<link rel="preload" href="vendor/hls.min.js" as="script">
|
||||
<script>
|
||||
// Précharge la SEULE lib utile à CE flux, dès le parse du <head>.
|
||||
// Un préchargement fixe de hls.js faisait télécharger 618 Ko inutiles
|
||||
// à chaque zap TV — la lib réellement nécessaire au direct est
|
||||
// mpegts.js — et cette bande passante était prise sur le flux
|
||||
// lui-même et sur mpegts.js, jusqu'à faire échouer son chargement sur
|
||||
// les liens contraints. Même décision que XFPlayer.start().
|
||||
(function () {
|
||||
var p = new URLSearchParams(location.search);
|
||||
var url = (p.get('url') || '').toLowerCase();
|
||||
var isHls = url.indexOf('.m3u8') !== -1;
|
||||
var nativeHls =
|
||||
document.createElement('video')
|
||||
.canPlayType('application/vnd.apple.mpegurl') !== '';
|
||||
var lib = null;
|
||||
if (isHls) {
|
||||
// Safari/iOS lit le HLS nativement : aucune lib à charger.
|
||||
if (!nativeHls) lib = 'hls.min.js';
|
||||
} else if (p.get('type') === 'live' || url.indexOf('.ts') !== -1) {
|
||||
lib = 'mpegts.min.js';
|
||||
}
|
||||
if (!lib) return;
|
||||
var l = document.createElement('link');
|
||||
l.rel = 'preload';
|
||||
l.as = 'script';
|
||||
l.href = 'vendor/' + lib;
|
||||
document.head.appendChild(l);
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
+79
-6
@@ -60,17 +60,67 @@
|
||||
// ------------------------------------------------------------------
|
||||
var _scriptCache = {};
|
||||
|
||||
function loadScript(src) {
|
||||
if (_scriptCache[src]) return _scriptCache[src];
|
||||
_scriptCache[src] = new Promise(function (resolve, reject) {
|
||||
function _delay(ms) {
|
||||
return new Promise(function (resolve) { setTimeout(resolve, ms); });
|
||||
}
|
||||
|
||||
function _injectScript(src) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var s = document.createElement('script');
|
||||
s.src = src;
|
||||
s.async = true;
|
||||
s.onload = resolve;
|
||||
s.onerror = function () { reject(new Error('Échec du chargement : ' + src)); };
|
||||
s.onload = function () { resolve(); };
|
||||
s.onerror = function () {
|
||||
// Laisser une balise morte dans le <head> ferait échouer une
|
||||
// éventuelle nouvelle tentative sur le même src.
|
||||
if (s.parentNode) s.parentNode.removeChild(s);
|
||||
reject(new Error('script error'));
|
||||
};
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
return _scriptCache[src];
|
||||
}
|
||||
|
||||
/**
|
||||
* Précise la cause d'un échec pour le message affiché et les logs :
|
||||
* 404 = lib absente de l'image déployée, 429 = quota du rate limiter,
|
||||
* échec réseau = lien coupé. Sans cela, toutes les causes se
|
||||
* ressemblaient à l'écran.
|
||||
*/
|
||||
function _describeFailure(src) {
|
||||
if (typeof fetch !== 'function') return Promise.resolve('');
|
||||
return fetch(src, { credentials: 'same-origin', cache: 'no-store' })
|
||||
.then(function (r) { return r.ok ? '' : ' (HTTP ' + r.status + ')'; })
|
||||
.catch(function () { return ' (réseau injoignable)'; });
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge une lib vendorisée, avec une seconde tentative qui contourne le
|
||||
* cache HTTP : les libs sont servies en `max-age=86400`, donc une entrée
|
||||
* tronquée par une coupure réseau condamnait le lecteur jusqu'au vidage
|
||||
* manuel du cache navigateur. Seuls les succès sont mémorisés — une
|
||||
* promesse rejetée gardée en cache rendait tout réessai impossible pour
|
||||
* le reste de la session.
|
||||
*/
|
||||
function loadScript(src) {
|
||||
if (_scriptCache[src]) return _scriptCache[src];
|
||||
|
||||
var attempt = _injectScript(src)
|
||||
.catch(function () {
|
||||
return _delay(400).then(function () {
|
||||
return _injectScript(
|
||||
src + (src.indexOf('?') === -1 ? '?' : '&') + 'reload=' + Date.now()
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(function () {
|
||||
return _describeFailure(src).then(function (why) {
|
||||
throw new Error('Échec du chargement : ' + src + why);
|
||||
});
|
||||
});
|
||||
|
||||
_scriptCache[src] = attempt;
|
||||
attempt.catch(function () { delete _scriptCache[src]; });
|
||||
return attempt;
|
||||
}
|
||||
|
||||
/** Ouvre la connexion vers l'hôte du flux avant même de connaître l'URL finale. */
|
||||
@@ -253,6 +303,11 @@
|
||||
}
|
||||
});
|
||||
}).catch(function (err) {
|
||||
// hls.js indisponible : plutôt que d'échouer, tenter les chemins qui
|
||||
// ne demandent aucune lib (HLS natif Safari/iOS, sinon lecture directe
|
||||
// si le navigateur sait décoder le conteneur).
|
||||
self.log(err.message);
|
||||
if (self._canPlayNativeHls()) { self._playDirect(self.url); return; }
|
||||
self.onError(err.message);
|
||||
});
|
||||
};
|
||||
@@ -267,6 +322,24 @@
|
||||
}
|
||||
self._createMpegts(isLive);
|
||||
}).catch(function (err) {
|
||||
// mpegts.js indisponible (build incomplet, coupure réseau, quota du
|
||||
// rate limiter…) : le même flux reste lisible par la route HLS, où
|
||||
// FFmpeg réencode déjà l'audio en AAC. Une seconde de latence en plus
|
||||
// vaut mieux qu'un écran d'erreur sur une chaîne parfaitement lisible.
|
||||
var hlsUrl = self._hlsEquivalent(self.url);
|
||||
self.log(err.message + (hlsUrl ? ' — bascule sur le chemin HLS' : ''));
|
||||
if (hlsUrl) {
|
||||
self.url = hlsUrl;
|
||||
self.onLoading('Ouverture du flux');
|
||||
if (self._canPlayNativeHls()) { self._playDirect(hlsUrl); return; }
|
||||
return self._startHls();
|
||||
}
|
||||
// Aucun équivalent HLS (flux direct fourni par la playlist) : dernier
|
||||
// recours, laisser le navigateur décoder lui-même le conteneur.
|
||||
if (self.video.canPlayType('video/mp2t') !== '') {
|
||||
self._playDirect(self.url);
|
||||
return;
|
||||
}
|
||||
self.onError(err.message);
|
||||
});
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user