Spaces:
Running
Running
Upload kimi-videos.js
Browse files- kimi-js/kimi-videos.js +42 -7
kimi-js/kimi-videos.js
CHANGED
@@ -58,6 +58,8 @@ class KimiVideoManager {
|
|
58 |
this._recentFailures = new Map();
|
59 |
this._failureCooldown = 5000;
|
60 |
this._consecutiveErrorCount = 0;
|
|
|
|
|
61 |
}
|
62 |
|
63 |
//Centralized crossfade transition between two videos.
|
@@ -1105,6 +1107,17 @@ class KimiVideoManager {
|
|
1105 |
|
1106 |
loadAndSwitchVideo(videoSrc, priority = "normal") {
|
1107 |
const startTs = performance.now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1108 |
// Guard: ignore if recently failed and still in cooldown
|
1109 |
const lastFail = this._recentFailures.get(videoSrc);
|
1110 |
if (lastFail && performance.now() - lastFail < this._failureCooldown) {
|
@@ -1170,6 +1183,7 @@ class KimiVideoManager {
|
|
1170 |
|
1171 |
// Stocker les références aux handlers pour pouvoir les nettoyer
|
1172 |
let fired = false;
|
|
|
1173 |
const onReady = () => {
|
1174 |
if (fired) return;
|
1175 |
fired = true;
|
@@ -1210,16 +1224,27 @@ class KimiVideoManager {
|
|
1210 |
const networkState = mediaEl ? mediaEl.networkState : -1;
|
1211 |
let mediaErrorCode = null;
|
1212 |
if (mediaEl && mediaEl.error) mediaErrorCode = mediaEl.error.code;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1213 |
console.warn(
|
1214 |
-
`
|
1215 |
);
|
1216 |
this._loadingInProgress = false;
|
1217 |
if (this._loadTimeout) {
|
1218 |
clearTimeout(this._loadTimeout);
|
1219 |
this._loadTimeout = null;
|
1220 |
}
|
1221 |
-
|
1222 |
-
|
|
|
|
|
|
|
1223 |
// Stop runaway fallback loop: pause if too many sequential errors relative to pool size
|
1224 |
if (this._fallbackPool && this._consecutiveErrorCount >= this._fallbackPool.length * 2) {
|
1225 |
console.error("Temporarily pausing fallback loop after repeated failures. Retrying in 2s.");
|
@@ -1276,12 +1301,17 @@ class KimiVideoManager {
|
|
1276 |
// Cap by clip length ratio if we know (assume 10000ms default when metadata absent)
|
1277 |
const currentClipMs = 10000; // All clips are 10s
|
1278 |
adaptiveTimeout = Math.min(adaptiveTimeout, Math.floor(currentClipMs * this._timeoutCapRatio));
|
|
|
|
|
|
|
|
|
1279 |
this._loadTimeout = setTimeout(() => {
|
1280 |
if (!fired) {
|
1281 |
// If metadata is there but not canplay yet, extend once
|
1282 |
if (this.inactiveVideo.readyState >= 1 && this.inactiveVideo.readyState < 2) {
|
|
|
1283 |
console.debug(
|
1284 |
-
`Extending timeout for ${videoSrc}
|
1285 |
);
|
1286 |
this._loadTimeout = setTimeout(() => {
|
1287 |
if (!fired) {
|
@@ -1292,15 +1322,19 @@ class KimiVideoManager {
|
|
1292 |
return;
|
1293 |
}
|
1294 |
// Grace retry: still fetching over network (networkState=2) with no data (readyState=0)
|
|
|
1295 |
if (
|
1296 |
this.inactiveVideo.networkState === 2 &&
|
1297 |
this.inactiveVideo.readyState === 0 &&
|
1298 |
-
(this._graceRetryCounts?.[videoSrc] || 0) <
|
1299 |
) {
|
1300 |
if (!this._graceRetryCounts) this._graceRetryCounts = {};
|
1301 |
this._graceRetryCounts[videoSrc] = (this._graceRetryCounts[videoSrc] || 0) + 1;
|
1302 |
-
const extra = this._timeoutExtension +
|
1303 |
-
|
|
|
|
|
|
|
1304 |
this._loadTimeout = setTimeout(() => {
|
1305 |
if (!fired) {
|
1306 |
if (this.inactiveVideo.readyState >= 2) onReady();
|
@@ -1312,6 +1346,7 @@ class KimiVideoManager {
|
|
1312 |
if (this.inactiveVideo.readyState >= 2) {
|
1313 |
onReady();
|
1314 |
} else {
|
|
|
1315 |
this._currentErrorHandler();
|
1316 |
}
|
1317 |
}
|
|
|
58 |
this._recentFailures = new Map();
|
59 |
this._failureCooldown = 5000;
|
60 |
this._consecutiveErrorCount = 0;
|
61 |
+
// Track per-video load attempts to adapt timeouts & avoid faux échecs
|
62 |
+
this._videoAttempts = new Map();
|
63 |
}
|
64 |
|
65 |
//Centralized crossfade transition between two videos.
|
|
|
1107 |
|
1108 |
loadAndSwitchVideo(videoSrc, priority = "normal") {
|
1109 |
const startTs = performance.now();
|
1110 |
+
// Register attempt count (used for adaptive backoff)
|
1111 |
+
const prevAttempts = this._videoAttempts.get(videoSrc) || 0;
|
1112 |
+
const attempts = prevAttempts + 1;
|
1113 |
+
this._videoAttempts.set(videoSrc, attempts);
|
1114 |
+
// Light trimming to avoid unbounded growth
|
1115 |
+
if (this._videoAttempts.size > 300) {
|
1116 |
+
for (const key of this._videoAttempts.keys()) {
|
1117 |
+
if (this._videoAttempts.size <= 200) break;
|
1118 |
+
this._videoAttempts.delete(key);
|
1119 |
+
}
|
1120 |
+
}
|
1121 |
// Guard: ignore if recently failed and still in cooldown
|
1122 |
const lastFail = this._recentFailures.get(videoSrc);
|
1123 |
if (lastFail && performance.now() - lastFail < this._failureCooldown) {
|
|
|
1183 |
|
1184 |
// Stocker les références aux handlers pour pouvoir les nettoyer
|
1185 |
let fired = false;
|
1186 |
+
let errorCause = "error-event"; // will be overwritten if timeout based
|
1187 |
const onReady = () => {
|
1188 |
if (fired) return;
|
1189 |
fired = true;
|
|
|
1224 |
const networkState = mediaEl ? mediaEl.networkState : -1;
|
1225 |
let mediaErrorCode = null;
|
1226 |
if (mediaEl && mediaEl.error) mediaErrorCode = mediaEl.error.code;
|
1227 |
+
const stillLoading = !mediaEl?.error && networkState === 2;
|
1228 |
+
const realMediaError = !!mediaEl?.error;
|
1229 |
+
// Differentiate timeout vs real media error for clarity
|
1230 |
+
const tag = realMediaError
|
1231 |
+
? "VideoLoadFail:media-error"
|
1232 |
+
: errorCause.startsWith("timeout")
|
1233 |
+
? `VideoLoadFail:${errorCause}`
|
1234 |
+
: "VideoLoadFail:unknown";
|
1235 |
console.warn(
|
1236 |
+
`[${tag}] src=${videoSrc} readyState=${readyState} networkState=${networkState} mediaError=${mediaErrorCode} attempts=${attempts} fallback=${fallbackVideo}`
|
1237 |
);
|
1238 |
this._loadingInProgress = false;
|
1239 |
if (this._loadTimeout) {
|
1240 |
clearTimeout(this._loadTimeout);
|
1241 |
this._loadTimeout = null;
|
1242 |
}
|
1243 |
+
// Only mark as failure if c'est une vraie erreur décodage OU plusieurs timeouts persistants
|
1244 |
+
if (realMediaError || (!stillLoading && errorCause.startsWith("timeout")) || attempts >= 3) {
|
1245 |
+
this._recentFailures.set(videoSrc, performance.now());
|
1246 |
+
this._consecutiveErrorCount++;
|
1247 |
+
}
|
1248 |
// Stop runaway fallback loop: pause if too many sequential errors relative to pool size
|
1249 |
if (this._fallbackPool && this._consecutiveErrorCount >= this._fallbackPool.length * 2) {
|
1250 |
console.error("Temporarily pausing fallback loop after repeated failures. Retrying in 2s.");
|
|
|
1301 |
// Cap by clip length ratio if we know (assume 10000ms default when metadata absent)
|
1302 |
const currentClipMs = 10000; // All clips are 10s
|
1303 |
adaptiveTimeout = Math.min(adaptiveTimeout, Math.floor(currentClipMs * this._timeoutCapRatio));
|
1304 |
+
// First ever attempt for a video: be more lenient if no historical avg yet
|
1305 |
+
if (attempts === 1 && !this._avgLoadTime) {
|
1306 |
+
adaptiveTimeout = Math.floor(adaptiveTimeout * 1.8); // ~5400ms au lieu de 3000ms typique
|
1307 |
+
}
|
1308 |
this._loadTimeout = setTimeout(() => {
|
1309 |
if (!fired) {
|
1310 |
// If metadata is there but not canplay yet, extend once
|
1311 |
if (this.inactiveVideo.readyState >= 1 && this.inactiveVideo.readyState < 2) {
|
1312 |
+
errorCause = "timeout-metadata";
|
1313 |
console.debug(
|
1314 |
+
`Extending timeout (metadata) for ${videoSrc} readyState=${this.inactiveVideo.readyState} +${this._timeoutExtension}ms`
|
1315 |
);
|
1316 |
this._loadTimeout = setTimeout(() => {
|
1317 |
if (!fired) {
|
|
|
1322 |
return;
|
1323 |
}
|
1324 |
// Grace retry: still fetching over network (networkState=2) with no data (readyState=0)
|
1325 |
+
const maxGrace = 2; // allow up to two grace extensions
|
1326 |
if (
|
1327 |
this.inactiveVideo.networkState === 2 &&
|
1328 |
this.inactiveVideo.readyState === 0 &&
|
1329 |
+
(this._graceRetryCounts?.[videoSrc] || 0) < maxGrace
|
1330 |
) {
|
1331 |
if (!this._graceRetryCounts) this._graceRetryCounts = {};
|
1332 |
this._graceRetryCounts[videoSrc] = (this._graceRetryCounts[videoSrc] || 0) + 1;
|
1333 |
+
const extra = this._timeoutExtension + 900;
|
1334 |
+
errorCause = "timeout-grace";
|
1335 |
+
console.debug(
|
1336 |
+
`Grace retry #${this._graceRetryCounts[videoSrc]} for ${videoSrc} (still NETWORK_LOADING). Ext +${extra}ms`
|
1337 |
+
);
|
1338 |
this._loadTimeout = setTimeout(() => {
|
1339 |
if (!fired) {
|
1340 |
if (this.inactiveVideo.readyState >= 2) onReady();
|
|
|
1346 |
if (this.inactiveVideo.readyState >= 2) {
|
1347 |
onReady();
|
1348 |
} else {
|
1349 |
+
errorCause = errorCause === "error-event" ? "timeout-final" : errorCause;
|
1350 |
this._currentErrorHandler();
|
1351 |
}
|
1352 |
}
|