Fix SSE event type lost across reader chunks (blank screen post-process)

Symptom: after a long video finishes processing, the screen goes blank
even though the video and chunks save correctly to history (visible
after a refresh + library click).

Cause: the manual SSE parser in processUrl declared `let eventType = ""`
inside the `while (await reader.read())` loop, so the variable reset on
every chunk. The server emits each event as a single write of
`event: X\ndata: Y\n\n`, but for a long video the result payload (entries
+ chunks + logs) is tens of KB and gets split across reader chunks by
the browser. When the split landed between the `event: result\n` line
and the `data: ...` line, the event type was lost and `handleSSE("",
data)` matched no branch — the result event was silently dropped, and
state.chunks stayed at the empty array set during processUrl reset.

Fix: hoist eventType outside the loop so it persists across chunks, and
reset it after each dispatch (per the SSE spec, event type returns to
default after an event is fired).

Short videos with small result payloads fit in a single chunk and so
were unaffected — which is why the bug looked intermittent.
This commit is contained in:
Keysat
2026-05-08 11:04:50 -05:00
parent 2621f2cdbe
commit 7d71150439
+8 -1
View File
@@ -1428,6 +1428,13 @@
const reader = res.body.getReader(); const reader = res.body.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buffer = ""; let buffer = "";
// Persist eventType across reader chunks. SSE events are
// `event: X\ndata: Y\n\n`, but a single TCP/fetch chunk can split
// between those two lines — when that happens, we'd lose the event
// type and silently drop the event (e.g. the final `result` for a
// long video, where the payload is tens of KB). Reset only after
// dispatch, per the SSE spec.
let eventType = "";
while (true) { while (true) {
const { done, value } = await reader.read(); const { done, value } = await reader.read();
@@ -1437,13 +1444,13 @@
const lines = buffer.split("\n"); const lines = buffer.split("\n");
buffer = lines.pop() || ""; buffer = lines.pop() || "";
let eventType = "";
for (const line of lines) { for (const line of lines) {
if (line.startsWith("event: ")) { if (line.startsWith("event: ")) {
eventType = line.slice(7); eventType = line.slice(7);
} else if (line.startsWith("data: ")) { } else if (line.startsWith("data: ")) {
const data = JSON.parse(line.slice(6)); const data = JSON.parse(line.slice(6));
handleSSE(eventType, data); handleSSE(eventType, data);
eventType = "";
} }
} }
} }