diff --git a/Architecture.md b/Architecture.md new file mode 100644 index 0000000..c1e04ca --- /dev/null +++ b/Architecture.md @@ -0,0 +1,43 @@ +# Architecture + +One Python file, no dependencies beyond ffmpeg. Intentionally. + +## server.py + +`ThreadingHTTPServer` bound to 127.0.0.1. Each request is handled by +`PlayerHandler`: + +- `GET /` - serves index.html +- `GET /api/recordings?date=YYYYMMDD` - clips for one day plus the list of all days +- `GET /video?date=...&name=...&offset=...` - streams a clip +- `POST /api/upload?date=...&filename=...` - raw body upload into the archive +- `POST /api/encode` - starts the background encoder +- `GET /api/encode/status` - encoder progress +- `GET/POST/DELETE /api/highlights` - saved highlight periods +- `GET /api/system-highlights?date=...` - events parsed from the recorder's fileindex.dat + +The recording index lives in memory (`server.recordings`), built at startup +by `scan_recordings()` and updated when uploads arrive. Restarting the server +rescans the folder from disk, which is the only state that matters. + +### Video streaming + +MP4 clips are served with HTTP Range support (`send_range_file`), so seeking +is instant. AVI clips are transcoded on the fly by an ffmpeg subprocess +(H.264/AAC, fragmented MP4 piped to the client) and killed when the client +disconnects. + +### Upload safety + +Uploads stream to `.uploading.mp4` first, then atomically rename. +A dropped connection leaves a temp file, never a truncated clip in the +index. Duplicate timestamps get HTTP 409; nothing is ever overwritten. + +## index.html + +Single file, no framework. A `