diff --git a/README.md b/README.md index 0e7e2f3..7ba90d8 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ dated video folders into a simple daily view that is easy to search and play. - Shows encoding progress, completed files, skipped files, and errors. - Never overwrites an existing transcoded file. - Keeps all original AVI recordings unchanged. +- Saves named highlight periods for later review or automated processing. ## Start The Player @@ -58,6 +59,30 @@ using the player while encoding runs. Files that already have an MP4 copy are reported as skipped. A temporary file is used during each conversion, so an interrupted conversion cannot damage an existing file. +## Highlights And AI Automation + +To create a highlight in the player, play the recording, press **Mark start**, +move to the end of the interesting part, press **Mark end**, enter a name, and +press **Add highlight**. Highlights are saved in `media-mp4/highlights.json`. + +The highlight API is intentionally simple. An AI or script can list highlights: + +```sh +curl http://127.0.0.1:8765/api/highlights +``` + +It can append a highlight with a date, seconds from midnight, and a label: + +```sh +curl -X POST http://127.0.0.1:8765/api/highlights \ + -H 'Content-Type: application/json' \ + -d '{"date":"20260827","start":34000,"end":34120,"label":"Person at entrance"}' +``` + +The API stores the entry without changing any video. Times are seconds from +midnight, so `34000` means `09:26:40`. This makes it straightforward for an +AI to append detections, labels, or review segments programmatically. + ## Custom Folders And Ports To use a different output folder or port: diff --git a/index.html b/index.html index 892698a..fb5235d 100644 --- a/index.html +++ b/index.html @@ -23,9 +23,13 @@ /* player wrapper */ .player-wrap { position: relative; background: #000; border-radius: 12px; overflow: hidden; } .player-wrap video { display: block; width: 100%; aspect-ratio: 16/9; object-fit: contain; background: #000; } + .empty-state { position: absolute; inset: 0; display: grid; place-content: center; justify-items: center; gap: 10px; color: #222; background: #f1f1f1; pointer-events: none; } + .empty-state svg { width: 52px; height: 52px; fill: #222; } + .empty-state span { font-size: 14px; } + .player-wrap.has-video .empty-state { display: none; } /* big center play */ - .big-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 68px; height: 48px; background: rgba(0,0,0,.6); border-radius: 12px; display: grid; place-items: center; pointer-events: none; opacity: 0; transition: opacity .15s; } + .big-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 68px; height: 48px; background: rgba(255,255,255,.92); color: #111; border-radius: 12px; display: grid; place-items: center; pointer-events: none; opacity: 0; transition: opacity .15s; box-shadow: 0 2px 12px rgba(0,0,0,.35); } .player-wrap.paused .big-play { opacity: 1; } .big-play svg { width: 28px; height: 28px; } @@ -65,6 +69,17 @@ .legend span::before { content: ""; display: block; width: 14px; height: 6px; border-radius: 2px; } .legend .rec::before { background: var(--accent); } .legend .gap::before { background: #2a2a2a; border: 1px solid #444; } + .highlights { margin-top: 18px; background: var(--surface); border-radius: 8px; padding: 14px 16px; } + .highlights h2 { font-size: 14px; font-weight: 500; margin-bottom: 10px; } + .highlight-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } + .highlight-actions button, .highlight-actions input { background: #333; border: 1px solid #555; color: var(--text); padding: 7px 10px; border-radius: 3px; font-size: 12px; } + .highlight-actions button { cursor: pointer; } + .highlight-actions button:hover { background: #444; } + .mark-time { color: var(--accent); font-size: 12px; min-width: 70px; } + .highlight-list { margin-top: 12px; display: grid; gap: 6px; } + .highlight-item { display: flex; align-items: center; gap: 10px; padding: 8px; background: #1a1a1a; border-radius: 4px; font-size: 12px; } + .highlight-item .highlight-label { flex: 1; } + .highlight-item button { background: none; border: none; color: var(--dim); cursor: pointer; } /* encoding panel */ .encode-panel { margin-top: 16px; background: var(--surface); border-radius: 8px; overflow: hidden; } @@ -109,6 +124,7 @@