iPhone: step probes + full hls.js error stream to server
The play-probe now lands (iPhone: iOS 18.7 Safari, hls.js path, MSE ok), but the /stream XHR never reaches the server on the failing attempts — impossible under the known code path. Instrument each step (new-hls-ok / loadsource-called / attach-ok) and log EVERY hls.js error (fatal+non-fatal, with retry counter) so the client timeline is visible server-side. Also fixed JSV constant drift (was stuck at 10). events endpoint: accept hls-err, forward step/fatal/details/buffer/retry/http.
This commit is contained in:
1 parent
3df074a5fe
commit
aba266ee4b
3 files changed
+14
-5
No files matched your search
+3
-2
@@ -285,12 +285,13 @@ def api_events(evt: dict = Body(...), request: Request = None):
|
|||||||
# client-reported events; the diagnostics the browser can uniquely see
|
# client-reported events; the diagnostics the browser can uniquely see
|
||||||
# (playback failures, capability probes) happen client-side, not in the
|
# (playback failures, capability probes) happen client-side, not in the
|
||||||
# request log — so the phone reports them here
|
# request log — so the phone reports them here
|
||||||
if evt.get("type") not in ("error", "client-fail", "play-probe"):
|
if evt.get("type") not in ("error", "client-fail", "play-probe", "hls-err"):
|
||||||
raise HTTPException(422, "unknown event type")
|
raise HTTPException(422, "unknown event type")
|
||||||
c = db.connect()
|
c = db.connect()
|
||||||
db.event(c, request.state.user, evt["type"],
|
db.event(c, request.state.user, evt["type"],
|
||||||
{k: evt[k] for k in ("ep_id", "ep", "lang", "msg", "detail",
|
{k: evt[k] for k in ("ep_id", "ep", "lang", "msg", "detail",
|
||||||
"jsv", "branch", "hls_lib", "mse", "native_hls", "ua") if k in evt})
|
"jsv", "branch", "hls_lib", "mse", "native_hls", "ua",
|
||||||
|
"step", "fatal", "details", "buffer", "retry", "http") if k in evt})
|
||||||
c.commit()
|
c.commit()
|
||||||
c.close()
|
c.close()
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|||||||
+9
-1
@@ -1,5 +1,5 @@
|
|||||||
// app.js — ani-web frontend: search -> detail -> play, vanilla JS + hls.js
|
// app.js — ani-web frontend: search -> detail -> play, vanilla JS + hls.js
|
||||||
const JSV = 10; // bump with every app.js change; play-probe reports it so a
|
const JSV = 12; // bump with every app.js change; play-probe reports it so a
|
||||||
// stale-cache phone is detectable server-side
|
// stale-cache phone is detectable server-side
|
||||||
// BASE = this page's directory, so the app works at / or behind a
|
// BASE = this page's directory, so the app works at / or behind a
|
||||||
// prefixing reverse proxy (e.g. https://host/ani/)
|
// prefixing reverse proxy (e.g. https://host/ani/)
|
||||||
@@ -915,14 +915,22 @@ async function play(ep_id, ep_no) {
|
|||||||
timeoutRetry: { maxNumRetry: 6, retryDelayMs: 1000, maxRetryDelayMs: 20000, backoff: "linear" },
|
timeoutRetry: { maxNumRetry: 6, retryDelayMs: 1000, maxRetryDelayMs: 20000, backoff: "linear" },
|
||||||
errorRetry: { maxNumRetry: 8, retryDelayMs: 1000, maxRetryDelayMs: 20000, backoff: "linear" },
|
errorRetry: { maxNumRetry: 8, retryDelayMs: 1000, maxRetryDelayMs: 20000, backoff: "linear" },
|
||||||
} } });
|
} } });
|
||||||
|
// iPhone debugging: log EVERY hls.js error (fatal and not) so the
|
||||||
|
// client-side timeline is visible server-side
|
||||||
hls.on(Hls.Events.ERROR, (_, data) => {
|
hls.on(Hls.Events.ERROR, (_, data) => {
|
||||||
|
try { postEvent({type: "hls-err", jsv: JSV, fatal: data.fatal, details: data.details,
|
||||||
|
buffer: data.buffer || null, retry: data.retry ?? null,
|
||||||
|
http: data.response ? data.response.code : null}); } catch {}
|
||||||
if (data.fatal) {
|
if (data.fatal) {
|
||||||
stopHls();
|
stopHls();
|
||||||
playerFail(data.details + (data.response ? ` (HTTP ${data.response.code})` : ""));
|
playerFail(data.details + (data.response ? ` (HTTP ${data.response.code})` : ""));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
try { postEvent({type: "play-probe", jsv: JSV, step: "new-hls-ok"}); } catch {}
|
||||||
hls.loadSource("/stream/" + s.token);
|
hls.loadSource("/stream/" + s.token);
|
||||||
|
try { postEvent({type: "play-probe", jsv: JSV, step: "loadsource-called"}); } catch {}
|
||||||
hls.attachMedia(v);
|
hls.attachMedia(v);
|
||||||
|
try { postEvent({type: "play-probe", jsv: JSV, step: "attach-ok"}); } catch {}
|
||||||
}
|
}
|
||||||
currentEp = {ep_id, ep_no, v};
|
currentEp = {ep_id, ep_no, v};
|
||||||
lastSavedPos = 0;
|
lastSavedPos = 0;
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
<title>ani-web</title>
|
<title>ani-web</title>
|
||||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%23f97316'/%3E%3Ctext x='32' y='45' font-family='sans-serif' font-size='42' font-weight='700' text-anchor='middle' fill='%230e0f13'%3Ea%3C/text%3E%3C/svg%3E">
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%23f97316'/%3E%3Ctext x='32' y='45' font-family='sans-serif' font-size='42' font-weight='700' text-anchor='middle' fill='%230e0f13'%3Ea%3C/text%3E%3C/svg%3E">
|
||||||
|
|
||||||
<link rel="stylesheet" href="style.css?v=11">
|
<link rel="stylesheet" href="style.css?v=12">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
@@ -30,6 +30,6 @@
|
|||||||
max-age=300 in browser AND possibly in your HTTPS proxy, and a stale
|
max-age=300 in browser AND possibly in your HTTPS proxy, and a stale
|
||||||
copy is exactly how users keep seeing an old UI after a deploy -->
|
copy is exactly how users keep seeing an old UI after a deploy -->
|
||||||
<script src="hls.min.js"></script>
|
<script src="hls.min.js"></script>
|
||||||
<script src="app.js?v=11"></script>
|
<script src="app.js?v=12"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in new issue
Block a user