Skip to content

Commit 31b26d9

Browse files
authored
Release v1.6.0 (#12)
Release v1.6.0
2 parents 9d5b8db + f973e5f commit 31b26d9

325 files changed

Lines changed: 33302 additions & 15263 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.devcontainer/devcontainer.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
}
3535
},
3636
"onCreateCommand": "grep -qF scripts/shell_aliases.sh ~/.bashrc || echo \"source ${containerWorkspaceFolder}/scripts/shell_aliases.sh\" >> ~/.bashrc",
37-
"postCreateCommand": "uv sync --inexact --all-extras && HADOLINT_DEST_DIR=/usr/local/bin bash scripts/install_hadolint.sh && uv run pre-commit install && (cd viewer && bun install)",
37+
"postCreateCommand": "uv sync --inexact --all-extras && ln -sf ${containerWorkspaceFolder}/.venv/bin/videoannotator /usr/local/bin/videoannotator && ln -sf ${containerWorkspaceFolder}/.venv/bin/videoannotator /usr/local/bin/va && HADOLINT_DEST_DIR=/usr/local/bin bash scripts/install_hadolint.sh && uv run pre-commit install && (cd viewer && bun install)",
3838
"postStartCommand": "bash scripts/check_workspace_mount.sh",
3939
"mounts": [
4040
"source=videoannotator-claude-home,target=/root/.claude,type=volume",

‎.github/workflows/docs-site.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ name: Docs site
55
on:
66
push:
77
branches: [master]
8-
paths: [docs/**, mkdocs.yml, scripts/mkdocs_hooks.py]
8+
# pyproject.toml: a version bump changes the version shown on every page.
9+
paths: [docs/**, mkdocs.yml, scripts/mkdocs_hooks.py, pyproject.toml]
910
workflow_dispatch:
1011

1112
permissions:

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ htmlcov/
234234

235235
# Database files
236236
videoannotator.db
237+
# The Hugging Face token saved next to the database (spec 031)
238+
huggingface_token
239+
huggingface_token.tmp
237240
test_*.db
238241
*.sqlite
239242
*.sqlite3

‎.specify/feature.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"feature_directory": "specs/024-container-feels-local"
2+
"feature_directory": "specs/030-model-defaults"
33
}

‎CHANGELOG.md‎

Lines changed: 161 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
Release candidate **1.6.0-rc1** (2026-10-14) is everything below, for testing the install a new
11-
user follows from GitHub.
10+
## [1.6.0] - 2026-10-18
11+
12+
The consolidation release: one repository, current dependencies, a clean first contact, results
13+
out in formats researchers use (a tidy table, ELAN files, a methods paragraph), a guide for every
14+
pipeline, and new default models. Released quietly: the viewer is being redesigned for v1.7.0,
15+
the public release, so expect its pages to change. Results from v1.6.0 differ from v1.5's because
16+
the default models changed (see Changed); v1.6.0-rc1 was the last version with openai-whisper.
17+
v1.6.0-rc1 (2026-10-14) was a release candidate for testing the install from GitHub.
1218

1319
### Added
1420

21+
- **Add the Hugging Face token from the viewer, no restart** (spec 031). Speaker turns needs a
22+
Hugging Face token to download its models. Its card now walks through it: make a Read token,
23+
accept each model's licence (each shown as accepted or not), paste the token and press **Save
24+
and check**. The server checks it with Hugging Face, names the account, saves it next to the
25+
database (readable only by the server's user, so it survives container recreation), and uses
26+
it from the next job, with no file to edit and no restart. Admin only; anyone else is told who to
27+
ask. **Settings → Hugging Face token** shows, changes or removes it, and
28+
`videoannotator settings huggingface-token [--status|--remove]` does the same from a terminal.
29+
The token is never sent back to the viewer, logged or exported. A token in the server's
30+
environment (`HUGGINGFACE_TOKEN`, `HF_AUTH_TOKEN`, `HF_TOKEN`) still takes priority.
31+
32+
- **A guide for every pipeline** (spec 026, roadmap v1.6.0 Phase 3b). Each pipeline now says what
33+
it finds in plain words ("People: where they are and how they move"), splits its settings into the few
34+
about *what* is measured (shown first, with units) and the technical ones (collapsed, with how
35+
many differ from the defaults), hides settings that don't apply (the OpenCV-only face
36+
thresholds, burst offsets outside burst mode) unless they've been changed, and gives its
37+
homepage, its published benchmarks quoted from their sources, and what to cite. The figures
38+
say what they measure: none of the face figures is for infants, and Whisper's are for English
39+
read aloud by adults. Written once in each pipeline's metadata, so the viewer (Pipelines page,
40+
the job wizard, Copy BibTeX), the API (new fields on `/pipelines`, `/pipelines/{name}` and
41+
`/schema`, nothing renamed) and the CLI agree; `docs/pipelines_spec.md` has a section per
42+
pipeline.
43+
- **`videoannotator pipelines describe <name> [--json]`** prints a pipeline's guide, settings and
44+
sources without a server. `pipelines list` shows each pipeline's title.
45+
46+
- **Results out: one table, ELAN files and a methods paragraph** (spec 025, roadmap Phase 3). An
47+
outside lab can now take results into R or Python without writing a parser per pipeline.
48+
- **The results table**: every pipeline's results in one CSV (or Parquet), one row per event
49+
and measure, the same 19 columns for every pipeline (`run`, `video`, `pipeline`, `event`,
50+
`track`, `start`, `end`, `measure`, `label`, `value`, `confidence`, a box, `model`,
51+
`videoannotator_version`, `file`, …). Times are seconds from the start of the video for
52+
every pipeline. Keypoints, landmarks and embeddings stay in the native files. Format version
53+
1; later versions only add columns or measures.
54+
- **ELAN files** (`.eaf`, EAF 3.0, validated against ELAN's schema): one per video, one tier per
55+
pipeline track (scenes, speech, each speaker, each tracked person, faces, VLM labels), linked
56+
to the video. Frame detections become "in view" spans; per-detection measures stay in the
57+
table. The viewer still opens a file after a coder adds tiers in ELAN.
58+
- **A methods paragraph** with BibTeX: each pipeline, its models with exact weight revisions,
59+
settings that differ from the defaults, deterministic mode, VideoAnnotator's version, and
60+
citations. A run's videos processed the same way are described once; differences are counted.
61+
Anything not recorded says so. No video names, paths or ids. Pipeline metadata gains an
62+
optional `citations` key (filled in for all eight shipped pipelines).
63+
- **Everywhere**: the viewer's **Export** menu on job and run pages (and a table button on
64+
saved datasets); `videoannotator export table|elan|methods <folder>`, which works on any
65+
results folder with no server; and `GET /api/v1/{jobs,batches,datasets}/{id}/export/table`,
66+
`.../export/elan`, `GET /api/v1/{jobs,batches}/{id}/methods`. All three read the same files
67+
the same way, so they give the same result. Results from before provenance (v1.5.0) export
68+
too, marked `not recorded`.
69+
- **Parquet** is an optional extra, `pip install videoannotator[parquet]` (in `all`), so the core
70+
install stays slim; an admin can install it from the viewer when it's first asked for.
71+
- **Two worked notebooks** in `examples/notebooks/`, R Markdown and Jupyter, reading the demo
72+
clip's table: speaking time per speaker, the transcript, who is in view when, facial
73+
expression over time, going wide, and who is in view while someone speaks. The Python one
74+
runs in CI; the R one before each release (`tests/manual/results_out_r_notebook.md`).
75+
- Docs: [`docs/usage/exporting_results.md`](docs/usage/exporting_results.md).
76+
1577
- **Start VideoAnnotator: a container that feels local** (spec 024). For researchers on their
1678
own computer, with Docker Desktop or Podman Desktop and no Python.
1779
- **`videoannotator-start`** (sh for Linux and macOS, PowerShell for Windows, with a one-line
@@ -192,6 +254,14 @@ user follows from GitHub.
192254

193255
### Removed
194256

257+
- **openai-whisper** (spec 030): speech recognition runs on faster-whisper. The `audio` extra no
258+
longer builds a package from source at install. The unused Whisper embedding helpers and the
259+
Hugging Face transformers branch of `WhisperBasePipeline` (its transcription was never
260+
implemented) went with it.
261+
262+
- **Two settings that did nothing** (spec 026): `scene_detection`'s `enable_clip_labels` and
263+
`audio_processing`'s `enable_diarization` were never read by the code. A config that still sets
264+
them runs as before; the validator says the field will be ignored.
195265
- Internal working documents moved out of the public docs into `docs/archive/`: the 2025
196266
client/server team handoffs and QA reports from `docs/testing/`, `docs/Figure 1.docx`, dated
197267
fix notes, the coding-agent setup guide and the v1.3 scripts audit.
@@ -213,6 +283,75 @@ user follows from GitHub.
213283

214284
### Changed
215285

286+
- **Security updates for known advisories in dependencies** (Trivy and `bun audit` before the
287+
release). Server: FastAPI 0.146 with Starlette 1.9 (was 0.47), anyio 4.14, urllib3 2.8, aiohttp
288+
3.14, fsspec, mako, msgpack, soupsieve; lightning 2.6 for pyannote. Viewer: React Router's
289+
router 1.23.4 (open-redirect fixes) in the shipped page; development tools (Lighthouse CI 0.15,
290+
Playwright 1.64, and pinned fixes for tmp, basic-ftp and path-to-regexp). The demo-clip outputs
291+
are identical before and after. Two high advisories have no fixed release yet, both in
292+
development tools that never ship or run in CI: `braces` (Tailwind's build) and `extract-zip`
293+
(Lighthouse's browser download).
294+
295+
- **New default models** (spec 030, roadmap Phase 5c), so the public release's results come from
296+
the defaults it keeps. Each changed on its upstream's published evaluation, after a run of the
297+
demo clip compared with the same machine's previous run, one change at a time (library upgrades
298+
first, with the old model). `videoannotator pipelines describe <name>` and the Pipelines page
299+
give each default's reason and source ("Why this default"). Output formats are unchanged; their
300+
content is not, so results from v1.5 and v1.6 differ. The comparisons are in
301+
`specs/030-model-defaults/checks.md`.
302+
- **Speech: Whisper `large-v3-turbo` on faster-whisper** (was `base` on openai-whisper). OpenAI's
303+
card: large-v3 with its decoder cut from 32 layers to 4, "way faster, at the expense of a minor
304+
quality degradation"; base is a tenth of its size. The new runtime alone gave the identical
305+
transcript with `base`. With turbo, the demo clip keeps its 4 cues at the same moments and 3 are
306+
reworded; it hears "Peek-a-boo!" in a peekaboo game where base heard "Take a move!". First use
307+
downloads 1.6 GB, and it needs about 3 GB of GPU memory; on a CPU, `whisper_model: small` is
308+
much faster. turbo can't translate, so `task: translate` with it now fails with a message
309+
naming `large-v3` or `medium` (it used to return untranslated text). New setting
310+
`compute_type`; the precision used is recorded in provenance and the methods paragraph ("at
311+
float32"). To get the old model: `{"speech_recognition": {"whisper_model": "base"}}` (same
312+
model on the new runtime; **v1.6.0rc1 is the last version with openai-whisper**, so v1.5
313+
transcripts can't be reproduced bit for bit).
314+
- **Speaker turns: `pyannote/speaker-diarization-community-1`** (was `speaker-diarization-3.1`).
315+
pyannote's benchmark gives it a lower diarization error rate on 10 of 12 sets (AMI headset
316+
18.8 → 17.0 %, DIHARD 3 21.4 → 20.2 %), equal on VoxConverse, worse on REPERE (broadcast TV);
317+
it counts speakers better, and setup asks for one model licence instead of three. Demo clip:
318+
3 turns from one speaker where 3.1 gave 4 from two; 3.1 had split the mother's "Gotta smile,
319+
huh?" and given its last half-second to a second speaker (checked by listening). To get the
320+
old model: `{"speaker_diarization": {"model": "pyannote/speaker-diarization-3.1"}}`, with its
321+
licences accepted.
322+
- **People and pose: `yolo26n-pose`** (was `yolo11n-pose`), on ultralytics 8.4. COCO pose mAP
323+
50-95 57.2 vs 50.0 at the same size (Ultralytics). ultralytics 8.4 alone gave the same output.
324+
Demo clip: the same people, tracks and labels; boxes overlap the old ones by a median IoU of
325+
0.92, keypoints moved a median 19 px. `iou_threshold` has no effect with YOLO26 (it has no
326+
suppression step), and says so. To get the old model:
327+
`{"person_tracking": {"model": "yolo11n-pose.pt"}}`.
328+
- **Faces: age and gender are off by default**, and when off their models are no longer
329+
downloaded or run (they used to run whenever any attribute was on, and were only left out of
330+
the file). Trained on adult faces, they say nothing useful about infants. A first face run
331+
downloads 125 MB instead of 1.2 GB. Demo clip: the same 19 faces in 11 frames with identical
332+
boxes and expressions. Face analysis is now part of the demo-clip output check. To get them
333+
back: `{"face_analysis": {"detect_age": true, "detect_gender": true}}`.
334+
- The deprecated `audio_processing` pipeline follows the speech and speaker-turn defaults.
335+
336+
- **The server's terminal says only what matters.** It used to print two lines for every request
337+
(including every viewer asset and the health check every two minutes) and a page of startup
338+
internals. Now it shows a ready line, each job started, finished (with how long it took and
339+
where the results are) or failed (with the reason), pipeline installs, warnings and errors, as
340+
`15:12:03 Finished clip.mp4 [1a2b3c4d] in 1m 12s; results in ~/VideoAnnotator/…`. Everything
341+
still goes to the log files in `logs/`. If the viewer is calling the server without a working
342+
API key, the terminal says so once (and how to connect it) instead of a stream of 401s.
343+
`videoannotator server --verbose` brings back every line, including uvicorn's per-request log.
344+
345+
- **The docs say which version they document.** Every page's header and browser tab read
346+
"VideoAnnotator 1.6.0", and the footer says how to check yours. The version comes from
347+
`pyproject.toml` when the site is built, so it can't drift, and a version bump now rebuilds the
348+
site. `videoannotator --version` works too (before, only `videoannotator version` did).
349+
350+
- **The viewer's docs links go to the documentation site.** The footer's "Docs" and Getting
351+
started's "Documentation" pointed at a DeepWiki of the archived `video-annotation-viewer` repo;
352+
they now open https://infantlab.github.io/VideoAnnotator/. Getting started no longer calls the
353+
viewer "VAV".
354+
216355
- **A container never shows its own folders** (spec 024). In a container with no folder shared,
217356
My folders lists nothing (it used to list the container's home, `/root`) and says how to share
218357
a folder; upload still works. Plain installs keep the home-folder default.
@@ -387,6 +526,26 @@ user follows from GitHub.
387526

388527
### Fixed
389528

529+
- **API keys were written into the log files** (security). Every request's headers, including
530+
`Authorization: Bearer va_…`, went into `logs/api_server.log`, and rejected or expired keys were
531+
logged with their first 10–16 characters. Logs now identify a key only by a short fingerprint
532+
(`key:1a2b3c4d`) and never record headers. **If you ran an earlier version, delete the files in
533+
`logs/` and make new API keys** (`videoannotator generate-token`); the old files may contain
534+
your keys.
535+
536+
- **The results player's timeline no longer misleads** (found 2026-10-16 in spec 023's
537+
inventory). Every speaker had the same colour on the timeline and the overlay, because the
538+
colour came from the ID's first letter and every diarization ID starts with "S"; VLM labels and
539+
ELAN tiers could collide the same way. Each value now gets its own colour. The faces and
540+
emotions rows had timeline switches with no lane behind them; they now say "overlay only". The
541+
lane labelled as person tracking's timeline plots the largest person box's area, and now says
542+
so ("size, not movement") instead of passing for motion.
543+
544+
- **Person tracking's first run records its weights** (found 2026-10-16 with spec 025's methods
545+
paragraph). The weights were hashed for provenance before ultralytics downloaded them, so a
546+
first run recorded "weights file not found" and the methods said "revision not recorded". They
547+
are now hashed after loading, from where ultralytics loaded them.
548+
390549
- **A new install couldn't install pipelines from the viewer** (found in the first-time-user run,
391550
2026-10-05). Two faults, each enough on its own:
392551
- The API key the server makes on first start asked for admin rights, but they were never

‎CITATION.cff‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ authors:
2828
orcid: "https://orcid.org/0000-0001-5846-3444"
2929
license: "MIT"
3030
repository-code: "https://github.com/InfantLab/VideoAnnotator"
31-
version: "1.5.0"
32-
date-released: "2026-07-19"
31+
version: "1.6.0"
32+
date-released: "2026-10-18"
33+
doi: "10.5281/zenodo.16961751"

‎CLAUDE.md‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ viewer/ # Video Annotation Viewer (React/Vite, Bun); built into src/
2525
tests/
2626
├── unit/ integration/ pipelines/ api/ contract/
2727
specs/<NNN>-<slug>/ # spec-kit feature specs (spec.md, plan.md, tasks.md, ...)
28-
docs/development/roadmap_v1.{5,6,7}.0.md, roadmap_v1.7_to_v2.0.md # release roadmap (v1.6.0 = public release)
28+
docs/development/roadmap_v1.{5,6,7,8}.0.md, roadmap_v1.8_to_v2.0.md # release roadmap (v1.6.0 = quiet consolidation, v1.7.0 = public release with the new viewer, v1.8.0 = plugins)
2929
```
3030

3131
## Commands
@@ -54,16 +54,13 @@ Backward Compatibility by Default). `/speckit-plan`'s Constitution Check gate ev
5454
against it.
5555

5656
## Current Plan
57-
<!-- SPECKIT START -->
58-
`specs/024-container-feels-local/plan.md`: researchers start VideoAnnotator with `launcher/
59-
videoannotator-start` (sh, plus a PowerShell twin), which finds Docker or Podman, shares chosen
60-
folders read-only at their real paths (remembered in a host `start.conf`), runs the slim GHCR
61-
image on 127.0.0.1 and opens the viewer connected. App, installed pipelines (restored after
62-
updates and restarts), model weights, activity and the researcher's files each persist on their
63-
own. Inside a container the server never lists its own filesystem. After 024:
64-
`specs/023-viewer-overhaul` (needs research first).
65-
<!-- SPECKIT END -->
66-
57+
`specs/030-model-defaults/plan.md` (roadmap v1.6.0 Phase 5c), built 2026-10-18: faster-whisper
58+
`large-v3-turbo` (openai-whisper removed; Principle V exception), pyannote
59+
`speaker-diarization-community-1`, `yolo26n-pose` (ultralytics 8.4), face age/gender off and not
60+
run. Open: the diarization demo-clip check and `.rttm` fixture need a Hugging Face token
61+
(`docs/development/handover_v1.6.0_release.md`). Then the quiet v1.6.0 release; v1.7.0 is the
62+
public one with the viewer overhaul (design `specs/023-viewer-overhaul`, built by `027-ask-ai`,
63+
`028-video-page`, `029-videos-and-projects`).
6764
## Recent Changes
6865
- 012-python-313-support: `requires-python` widens to `>=3.12,<3.14`; CI tests both;
6966
`.python-version` makes 3.13 the dev/Docker default; ruff/mypy targets stay at 3.12 (oldest

0 commit comments

Comments
 (0)