bug fixes

This commit is contained in:
Theo Browne
2026-02-22 16:40:30 -08:00
parent 9763cdf0de
commit 4d56f95f9f
3 changed files with 71 additions and 18 deletions

View File

@@ -191,15 +191,53 @@ function textLines(
const words = text.split(/\s+/).filter(Boolean); const words = text.split(/\s+/).filter(Boolean);
const lines: string[] = []; const lines: string[] = [];
let current = ""; let current = "";
const maxChunkLength = 1;
const splitWordToFit = (word: string): string[] => {
if (!word) return [];
if (ctx.measureText(word).width <= maxWidth) return [word];
const pieces: string[] = [];
let remaining = word;
while (remaining.length > 0) {
let low = maxChunkLength;
let high = remaining.length;
let best = maxChunkLength;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
const candidate = remaining.slice(0, mid);
if (ctx.measureText(candidate).width <= maxWidth) {
best = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
pieces.push(remaining.slice(0, best));
remaining = remaining.slice(best);
}
return pieces;
};
for (const word of words) { for (const word of words) {
const candidate = current ? `${current} ${word}` : word; const segments = splitWordToFit(word);
if (ctx.measureText(candidate).width <= maxWidth) { let isFirstSegment = true;
current = candidate; for (const segment of segments) {
continue; const prefix = isFirstSegment && current ? " " : "";
const candidate = current ? `${current}${prefix}${segment}` : segment;
if (ctx.measureText(candidate).width <= maxWidth) {
current = candidate;
isFirstSegment = false;
continue;
}
if (current) lines.push(current);
current = segment;
isFirstSegment = false;
if (lines.length >= maxLines - 1) break;
} }
if (current) lines.push(current);
current = word;
if (lines.length >= maxLines - 1) break; if (lines.length >= maxLines - 1) break;
} }

View File

@@ -149,6 +149,7 @@ async function main() {
stdout: mode === "dryrun" ? "pipe" : "inherit", stdout: mode === "dryrun" ? "pipe" : "inherit",
stderr: "inherit", stderr: "inherit",
}); });
let ffmpegWritable = true;
let ffplay: Bun.Subprocess | null = null; let ffplay: Bun.Subprocess | null = null;
let ffplayPump: Promise<void> | null = null; let ffplayPump: Promise<void> | null = null;
@@ -187,6 +188,7 @@ async function main() {
firstChunkResolve = resolve; firstChunkResolve = resolve;
firstChunkReject = reject; firstChunkReject = reject;
}); });
let shutdown: (() => Promise<void>) | null = null;
const chunkServer = Bun.serve({ const chunkServer = Bun.serve({
port: 0, port: 0,
@@ -199,7 +201,9 @@ async function main() {
}, },
websocket: { websocket: {
message(_ws, message) { message(_ws, message) {
if (!ffmpeg.stdin) return; if (!ffmpegWritable || !ffmpeg.stdin || typeof ffmpeg.stdin === "number") {
return;
}
if (typeof message === "string") return; if (typeof message === "string") return;
let chunk: Uint8Array | null = null; let chunk: Uint8Array | null = null;
@@ -220,8 +224,12 @@ async function main() {
firstChunkResolve = null; firstChunkResolve = null;
firstChunkReject = null; firstChunkReject = null;
} catch (error) { } catch (error) {
ffmpegWritable = false;
const detail = error instanceof Error ? error : new Error(String(error)); const detail = error instanceof Error ? error : new Error(String(error));
firstChunkReject?.(detail); firstChunkReject?.(detail);
firstChunkResolve = null;
firstChunkReject = null;
void shutdown?.();
} }
}, },
}, },
@@ -263,9 +271,10 @@ async function main() {
console.log(`Streaming from ${broadcastUrl} in ${mode} mode`); console.log(`Streaming from ${broadcastUrl} in ${mode} mode`);
let shuttingDown = false; let shuttingDown = false;
const shutdown = async () => { shutdown = async () => {
if (shuttingDown) return; if (shuttingDown) return;
shuttingDown = true; shuttingDown = true;
ffmpegWritable = false;
try { try {
chunkServer.stop(true); chunkServer.stop(true);
} catch {} } catch {}
@@ -290,14 +299,20 @@ async function main() {
} }
}; };
process.on("SIGINT", () => { const ffmpegExit = ffmpeg.exited.then((code) => {
void shutdown(); ffmpegWritable = false;
}); void shutdown?.();
process.on("SIGTERM", () => { return code;
void shutdown();
}); });
const exitCode = await ffmpeg.exited; process.on("SIGINT", () => {
void shutdown?.();
});
process.on("SIGTERM", () => {
void shutdown?.();
});
const exitCode = await ffmpegExit;
if (ffplayPump) { if (ffplayPump) {
await ffplayPump.catch(() => { await ffplayPump.catch(() => {
// Ignore downstream pipe failures on shutdown. // Ignore downstream pipe failures on shutdown.
@@ -306,7 +321,7 @@ async function main() {
if (ffplay) { if (ffplay) {
await ffplay.exited; await ffplay.exited;
} }
await shutdown(); await shutdown?.();
if (exitCode !== 0) { if (exitCode !== 0) {
process.exit(exitCode); process.exit(exitCode);

View File

@@ -5,6 +5,6 @@
## TODO Bugfixes ## TODO Bugfixes
- [ ] Text wrapping on long single-strings - [x] Text wrapping on long single-strings
- [ ] Stream crashing on fresh deployment (because the ffmpeg pipe dies, we should handle this in the ffmpeg script) - [x] Stream crashing on fresh deployment (because the ffmpeg pipe dies, we should handle this in the ffmpeg script)
- [ ] Throttle view count updates - [x] Throttle view count updates