Wiki: Home, Architecture, Upload Guide, Highlights API, Encoding, Shortcuts

2026-08-28 14:49:54 +01:00
parent c8b63eb074
commit 1aab082b87
6 changed files with 211 additions and 1 deletions
+43
@@ -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 `<target>.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 `<video>` element, a canvas timeline, and a
handful of panels. The timeline draws recorded minutes, highlights, and
system events as colored bars over a 24h axis, with zoom (up to 24x).
Clip transitions: `seekBy()` and the `ended` handler walk the clip list so
playback jumps across clip boundaries and empty gaps seamlessly.
+36
@@ -0,0 +1,36 @@
# Encoding
The C41 records HEVC video in AVI containers, which browsers cannot play.
The player needs H.264/AAC MP4 copies.
## The Encoding menu
Open **Encoding** at the bottom of the page and press **Start**. The server
walks the source folder, converts every AVI that has no MP4 yet into
`media-mp4`, preserving dated folder structure, four files in parallel.
The panel shows completed/skipped/failed counts and finishes with a summary.
- Existing MP4s are skipped, never re-encoded or overwritten.
- Each file converts through a temporary name, so an interrupted encode
never corrupts an existing clip or leaves a half-file in the index.
- Originals are never modified.
- You can keep using the player while encoding runs.
## What it runs
```sh
ffmpeg -i input.avi -vf scale=854:-2 \
-c:v libx264 -preset ultrafast -tune zerolatency -crf 32 \
-pix_fmt yuv420p -c:a aac -b:a 64k -movflags +faststart output.mp4
```
## On-the-fly playback
For a single AVI without an MP4 copy, the player transcodes on the fly at
higher quality (CRF 28) when you play it. Fine for one clip, wasteful for
hundreds. Use the Encoding menu for bulk.
## Uploading AVI
AVI uploads land in the source folder. After uploading a batch, run the
Encoding menu once and everything becomes playable.
+45
@@ -0,0 +1,45 @@
# Highlights API
Highlights are named time periods saved to `media-mp4/highlights.json`.
The API is deliberately simple so an AI agent or script can append
detections without touching any video.
## List
```sh
curl http://127.0.0.1:8765/api/highlights
```
## Create
With clock times:
```sh
curl -X POST http://127.0.0.1:8765/api/highlights \
-H 'Content-Type: application/json' \
-d '{"date":"2026-08-27","start_time":"09:26:40","end_time":"09:28:40","title":"Person at entrance"}'
```
With seconds-from-midnight and a duration:
```sh
curl -X POST http://127.0.0.1:8765/api/highlights \
-H 'Content-Type: application/json' \
-d '{"date":"20260827","start":34116,"duration":120,"title":"Delivery"}'
```
Accepted fields: `date` (YYYY-MM-DD or YYYYMMDD), `start`/`start_time`,
`end`/`end_time` or `duration`, `label`/`title` (max 120 chars).
Responses return the stored highlight including its assigned `id`.
## Delete
```sh
curl -X DELETE "http://127.0.0.1:8765/api/highlights?id=3"
```
## In the UI
Press **Mark start**, then **Mark end** at the end of the interesting part,
type a name, press **Add highlight**. Highlights show in yellow on the
timeline; click a highlight or the legend label to jump to the next one.
+38 -1
@@ -1 +1,38 @@
Welcome to the Wiki. # C41 Recorder Player
A small local web player for CCTV recordings made by a C41 recorder. It turns
the recorder's dated folder structure into a 24-hour timeline that is easy to
browse, play, and annotate.
## Pages
- [[Architecture]] - how the server and UI work internally
- [[Upload Guide]] - adding recordings to the archive through the web UI
- [[Highlights API]] - marking and automating interesting moments
- [[Encoding]] - converting AVI sources to playable MP4s
- [[Keyboard Shortcuts]] - quick reference
## Quick start
```sh
python3 server.py [output-media-folder] --source [source-avi-folder] --port 8765
```
Then open <http://127.0.0.1:8765>.
## Folder layout
The recorder stores clips in dated folders:
```
media/
20260827/
09.28.36.avi
09.30.36.avi
media-mp4/
20260827/
09.28.36.mp4
```
Each clip filename is its start time. The timeline places clips by name, not
by file metadata, so names must follow `HH.MM.SS` exactly.
+14
@@ -0,0 +1,14 @@
# Keyboard Shortcuts
| Key | Action |
| --- | --- |
| `Space` | Play / pause |
| `←` | Back 10 seconds (crosses clips and gaps) |
| `→` | Forward 10 seconds (crosses clips and gaps) |
| `M` | Mute / unmute |
| `+` | Zoom timeline in |
| `-` | Zoom timeline out |
Clicking the timeline seeks to that moment; clicking a highlight or system
event bar jumps to it. The legend labels jump to the next recorded section,
highlight, system event, or gap.
+35
@@ -0,0 +1,35 @@
# Upload Guide
The Upload panel (bottom of the page) adds recordings from your browser.
## Rules
- Filenames must be `HH.MM.SS.mp4` or `HH.MM.SS.avi`, e.g. `09.28.36.mp4`.
`8.28.36.mp4` also works; it is padded to `08.28.36.mp4` automatically.
- MP4 files go into the playable archive (`media-mp4`) and appear on the
timeline immediately.
- AVI files go into the source folder (`media`), matching where the recorder
keeps originals. Run the Encoding menu afterwards to make them playable.
- Files land under the date selected in the top-right dropdown. Pick the
date first.
- Duplicate timestamps are rejected with HTTP 409. Existing recordings are
never overwritten.
## Bulk upload
Drag a whole batch of files onto the panel or select many in the file
dialog. Up to three files upload in parallel, each with its own progress
bar. Failed files stay in the queue for retry; successful ones are cleared
when the whole batch finishes.
## API
For scripts and agents:
```sh
curl -X POST "http://127.0.0.1:8765/api/upload?date=20260827&filename=09.28.36.mp4" \
--data-binary @clip.mp4
```
Responses: `200 {"ok": true, "file": "20260827/09.28.36.mp4"}`, `400` for a
bad name/date, `409` if that timestamp already exists, `413` over 2 GiB.