Skip to content

v3.6.7

Choose a tag to compare

@github-actions github-actions released this 13 Nov 02:29

Automated release: version and notes generated from pull requests merged since 3.6.6.

Changed

  • Tests no longer depend on the order they run in (#467: A227, A228, A229, and the gappy.mp4
    item). emoji_src.mp4 was built by three classes with different content, so EmojiTests
    usually ran without its sound; export_reels.mp4 was a --fit crop fixture in test_analysis
    and a --fit pad output of test_delivery; gappy.mp4 had two layouts; and
    test_sequence_no_match_refused used a directory another test creates, so it tested the
    missing-directory refusal instead. Each now has its own file, and the sequence test makes its
    directory and asserts the "no files match" refusal.
  • Three test defects (#467: A230, A231, A232). ContractTests and SilentPathTests remove
    the temp folders they make; _ass_advance escapes the path it puts into ass= (a colon in it
    made the emoji-placeholder test skip); test_json_determinism compared a document with itself
    and now compares the emitted key order with the sorted one at every depth. The second A232
    item, a "cached" test that passes without the cache, could not be identified in the tree and is
    not changed.
  • tests/sweep.py runs every one-file tool (#467: A225; #466: A216). Seven were left out
    (probe, check, cropdetect, scenes, sphere, verify, waveform): the table has 32 rows
    for 31 tools, a tool that takes no -o is run without one, and a --baseline run uses its own
    folder. BY_HAND names the eleven tools the table cannot run, and a test fails when a script is
    in neither list. CONTRIBUTING's "25 single-input tools" (24, cut counted twice) is corrected.
  • CI: a release no longer rebases its bump onto a merge it did not read (#467: A25). A PR
    merged while the bump was being prepared was put under the release commit by git pull --rebase: it shipped in the tag without its label or changelog line, and its own run found
    nothing to release. The push is tried once; when main has moved, that run drops the bump and
    releases nothing, and the newer merge's queued run releases both.
  • CI: GitHub Pages is rebuilt after a release (#467: A28). The bump commit carries
    [skip ci], so the site kept the previous version and showed the shipped notes under
    "Unreleased". release.yml now dispatches pages.yml once the tag exists (the job gains
    actions: write); a failed dispatch does not stop the npm publish and turns the job red at its
    end.
  • Installer (#467: A29, A30, A31, A37). npx ffmpeg-skill from a git checkout copies what
    the npm package ships (2.6 MB) instead of all of docs/ and references/ (17 MB), and copies
    LICENSE. An unknown argument, or --dir with no value, is refused with exit 2 before anything
    is installed or removed (--uninstall --cursr used to remove the Claude Code install). A failed
    install removes its ffmpeg-skill.tmp-<pid>/ scratch folder, and --uninstall removes ones an
    interrupted install left.
  • Docs corrected against the code (#466: A203, A204, A205, A206, A207, A208, A213, A214, A215;
    #467: A35, A248, A249). README: contract_version is 2.0, not 1.0; a PR does write
    CHANGELOG.md notes; three demos use AI-generated footage; the Gemini CLI install is the whole
    repository; the no-Node route copies docs/contract.md, not docs/. CONTRIBUTING: the MCP
    Registry entry is published by release.yml. Roadmap: look.py --best-frame never shipped, and
    the document ends at 2.0.0. CHANGELOG: the two ## 1.15.0 headings are one section, so the
    release-notes extraction reads all of it. The notes under .claude/skills/ no longer say
    there is no release workflow, that an existing output is replaced without a word, that CI has
    no concurrency, that there are 22 tools, or that probe is an analysis_only tool.
  • Evals, demos and examples (#467: A242, A245, A246, A250, A251, A252, A253, A272, A273).
    Gallery commands are shell-quoted (an unquoted # colour made the rest of a pasted line a
    comment). examples/make_demo.sh passes --overwrite, so a second run works, and says it runs
    13 of the 42 scripts. evals/write_fixtures.py builds source.mp4, so kw1, ff1 and --all
    stage; evals/grade_runs.py reads agent_prompts.json; evals/grade_runs_25.py pairs each
    command with its own output by tool-use id, and grades a run that never started NOT_RUN;
    evals/README.md counts 99 + 11 prompts and says which paths in them are the maintainer's.

Fixed

  • In the paths below, a measurement that did not happen is reported as not measured instead of as a value or a pass
    (A60, A67, A76, A97, A111, A113, A114, A117, A131, A172, A182, A193, A236, A237, A239, A240,
    A241, A257). check.py PASSed the duration of a file that states no length (read as 0.00 s) and
    dropped its --content black/frozen/silence rows; those rows are now "could not measure"
    (duration FAILs under a length limit), as are an unread frame rate and file size. Its aspect row
    reads the displayed frame, so a 16:9 anamorphic file (1440x1080 at SAR 4:3) is 16:9, not 4:3,
    and --platform custom --tp N without --lufs now measures the true peak instead of reporting
    0 failed. multicam.py --fix-drift reports drift_ppm: null and a drift_skipped_reason
    (too_short / low_confidence, a drift_correction_skipped skip) for a camera whose drift it
    did not measure, as sync.py does, where it said 0.0. A clip shorter than one 400 ms loudness
    block is not silent: loudness.py --measure-only says too_short with its true peak and a
    normalisation is refused (error.reason: audio_too_short) without telling the caller to replace
    the audio; export.py records loudness_not_measured with the measured peak instead of
    output_audio_silent, and check.py's loudness row WARNs. A loudness step on an output with no
    integrated loudness fails (not_measured) where a silent one passed. loudness.py reports
    loudnorm_mode and, when loudnorm fell back to dynamic mode, its shortfall note says so instead
    of blaming the encoder. caption.py reports size_used: null when no fit ran (a
    --transcribe --dry-run), fit_scope: file with a note when --fit-size-scope cue met the
    plain SRT burn (which draws one size), and a kept subtitle track's own default disposition in
    --mode mux (probe.py's subtitle_stream_details carries default), clearing it when
    --default-track makes a new track the default. render.py --dry-run plans a clip's speed on
    its in..out (a stand-in 10 s only when nothing can be read, recorded as
    speed_length_placeholder), and a pack row whose check did not run says not checked, not
    pass. overlay.py and graphics.py refuse a --start at or after the end of the video,
    where they completed verified: true with nothing drawn. pad.py records the even scale it
    adds (odd_dimensions_scaled_even). A stopped run says "nothing was written" only when no file
    it wrote is on disk. verify.py --dry-run (accepted and ignored) says dry_run: false and a
    note. The contract lists metadata (--auto-chapters), cut (--snap beats), render and
    batch as analysis_only under --dry-run, which a test now derives from what each tool's dry
    run decodes; report's note no longer claims contact sheets are measured, and commands no
    longer claims every ffmpeg command line.
  • fix: a measurement that did not happen is reported as not measured (#490)
  • chore: tests, CI, installer, evals and docs findings from the audit (#489)

What's Changed

  • chore: tests, CI, installer, evals and docs findings from the audit (#467, #466) by @kajisho5 in #489
  • fix: a measurement that did not happen is reported as not measured by @kajisho5 in #490

Full Changelog: v3.6.6...v3.6.7