feat: add native Windows broadcaster and live score modules
- streamer/windows: vash-broadcast.exe (setup/login/start/stop/status/doctor) with per-instance OBS, tosu and FFmpeg, a supervised worker, OCR room selection and OBS scene reconciliation; built and tested on Linux only - streamer/relay.ts shared with Vash TV; Cloudflare Access proxy for dev/staging - worker status validation and live score helpers for the site - STREAMING_HANDOFF.md and streamer/ALPHA_TEST.md record what is unverified Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
40
streamer/windows/log.ts
Normal file
40
streamer/windows/log.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/** Appends timestamped lines to the instance's log, keeping the last few files. */
|
||||
import { appendFileSync, existsSync, mkdirSync, renameSync, rmSync, statSync } from 'node:fs';
|
||||
import { dirname } from 'node:path';
|
||||
|
||||
const MAX_BYTES = 10 * 1024 * 1024;
|
||||
const KEEP = 5;
|
||||
|
||||
/** Rotation keeps a multi-hour broadcast's log bounded: worker.log, worker.1.log, … */
|
||||
function rotate(path: string) {
|
||||
if (!existsSync(path) || statSync(path).size < MAX_BYTES) return;
|
||||
const numbered = (n: number) => path.replace(/\.log$/, `.${n}.log`);
|
||||
rmSync(numbered(KEEP), { force: true });
|
||||
for (let n = KEEP - 1; n >= 1; n--)
|
||||
if (existsSync(numbered(n))) renameSync(numbered(n), numbered(n + 1));
|
||||
renameSync(path, numbered(1));
|
||||
}
|
||||
|
||||
export function fileLogger(path: string, alsoConsole = false) {
|
||||
mkdirSync(dirname(path), { recursive: true });
|
||||
let lines = 0;
|
||||
return (...parts: unknown[]) => {
|
||||
const text = parts
|
||||
.map((part) =>
|
||||
part instanceof Error
|
||||
? part.message
|
||||
: typeof part === 'string'
|
||||
? part
|
||||
: JSON.stringify(part)
|
||||
)
|
||||
.join(' ');
|
||||
const line = `${new Date().toISOString()} ${text}\n`;
|
||||
if (alsoConsole) process.stdout.write(line);
|
||||
try {
|
||||
if (++lines % 200 === 0) rotate(path);
|
||||
appendFileSync(path, line);
|
||||
} catch {
|
||||
/* a full or locked disk must not stop the broadcast */
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user