Automated release: version and notes generated from pull requests merged since 1.15.1.
Added
- Audiogram:
waveform.py --imageandrender.py --template audiogram. The visualisation
over a still plate, which is what turns a podcast episode into something postable.
--image PATH(a local file),--image-fit cover|contain|blur,--position bottom|centre|top|strip,--vis-height FRAC,--opacity,--platform NAME(the frame from
the delivery table; a destination with no frame is refused),--title TEXT(drawn through
graphics.py) and--srt/--text(burnt bycaption.pyafterwards, so neither code path is
re-implemented). Result keyaudiogram. Nothing is fetched and no cover art is ever invented:
give an image or a colour. No new tool — the tool count stays 42;docs/design-decisions.md
records why anaudiogram.pywould have been a second spelling ofwaveform.pyand, under the
1.x guarantee, permanent surface. metadata.py --auto-chapters: chapter markers proposed from measured structure. Pauses
(silencedetect) and scene changes (scdet) become candidates; a scene cut within 1 s of a
silence end merges into onesilence+scenemarker.--min-chapter(default 60),
--max-chapters,--from silence|scenes|both,--silence-threshold/--silence-min,
--scene-threshold,--chapters-out FILE(this tool's own--chaptersformat, so the titles
can be edited and fed back) and--description-out FILE(the YouTube00:00 Chapter 1block).
Result keyauto_chapters, with each marker's evidence. Every title isChapter Nand the
result says"titles": "placeholder"— the skill proposes where a chapter starts, it cannot
know what is in one; naming them is the caller's job. Output is still-c copy; two detectors
mean two decodes, which--helpandnotesboth say.- Several language-tagged subtitle tracks in one
caption.py --mode mux.--srtis
repeatable and each file may carry a:langsuffix (--srt en.srt:en --srt ja.srt:ja). The
suffix splits on the last colon and only when the tail is a BCP-47-shaped code and the whole
token is not itself a file, soC:\subs\en.srtand a file nameda:b.srtsurvive.
--track-title(a frozen display-name table by default — data, never a translation) and
--default-track LANG. Result keystracksandsubtitle_tracks. Empirically (ffmpeg 6.1),
.mp4/.movhold severalmov_texttracks but drop a two-letter language code without a
word, so the code is converted to ISO-639-2 (en→eng); Matroska stores what it is given,
and past two tracks in an MPEG-4 container the result recommends.mkvinnotes. The skill
never translates and never generates a second language. check.py: an informationalsubtitlesrow on every platform.PASSwhen every soft
subtitle stream carries a language tag,WARNwhen one does not or when there are none. Like
the podcastchannels/chaptersrows it is never counted infailed.
Fixed
- The audiogram ignored
--fps/--platform. A looped still is fed to ffmpeg at 25 fps
unless told otherwise, andoverlaytakes its rate from the first input, so
--platform tiktokannounced 30 fps and wrote a 25 fps file (the colour-plate path was never
affected:color=carries its ownr=). The still now gets-framerate, and the frame rate
joined the frame size and duration in whataudiogram.verifiedactually checks. --mode muxmarked a trackdefaultthat nobody asked for. With two or more new subtitle
streams and no--default-track, ffmpeg flags the first one itself — the opposite of what the
flag promises, andtracks[].defaultthen described a file that did not exist. Every
disposition is stated explicitly now (defaultfor the chosen track,0for the rest).
MPEG-4 cannot express "no default subtitle track" at all — it always enables its first one —
so there the track is reporteddefault: truewith a note, because the result document
describes the file, not the request.audiogram.verifiedwastrueunder--dry-run, where nothing had been rendered to
verify, while the common top-levelverifiedsaidfalsefor the same run.--imagethat ffmpeg cannot decode is refused withkind: inputnaming the file, before
any encode starts, instead of surfacing as a raw ffmpeg failure.- A malformed
:langsuffix (--srt en.srt:zzzz) is reported as a bad language code naming
the token, not as a missing file calleden.srt:zzzz. から,までandよりare matched as whole words. They had been left in the
character table of Japanese particles, which madeか,ら,ま,で,よandり
one-character particles of their own — none of them is — so a break beforeかwas forbidden
and a break after it preferred.- MPEG-4 subtitle track titles are no longer claimed.
-metadata:s:s:N title=is dropped by
the MPEG-4 muxer, sotracks[].titlereported a name the file did not carry; it isnull
there now, with a note saying.mkvkeeps the names. metadata.py --auto-chapters' "no chapters found" hint no longer suggests the value already
in use (--min-chapter 1was answered with "try--min-chapter 1").graphics.py --template stickerwraps its label to the chip's own width rather than the
frame's, so a long--textcannot overflow the plate it is drawn on.
Changed
caption.pyandgraphics.pywrap phrase-aware by default (--wrap measuredrestores
1.15.1). Four rules, all penalties over break positions that already fit, so no line is
widened and the line count never changes: never inside a word or on the wrong side of a hyphen;
no line that is a lone digit, one or two punctuation characters or a single kana, checked at
every boundary rather than only the last; Japanese/Chinese breaks preferred after。、!?」』)
and after a particle — a particle is enclitic, so kinsoku keeps it with the word before it and a
break before one is forbidden — discouraged between a kanji stem and its okurigana and
forbidden before a small kana; and an article or preposition kept with the phrase it governs, by
preferring the break before it and penalising the break after it. Therebalancedcount in
thecues:line can differ for an unchanged input, and a cue whose break moves is the point
of the release — the text itself is never rewritten, shortened or translated. graphics.py's hook
card, meme lines and sticker chip are now wrapped to the frame's safe width instead of running
off the edge; a label that already fits is untouched.
Eval 16's two open cues come out whole:dl1breaks asA third line / the tool times for me
(1.15:A third line the / tool times for me) anddl3as自動でタイミングが / 決まる行
(1.15:自動でタイミングが決ま / る行).--wrap measuredreproduces the old splits.layout_cueswraps each cue once instead of three times. The greedy fill is identical for
every mode, sowrap_variants()does it once and repeats only the post-passes, which is what
rebalancedandphrase_breaksneed to be counted against.- The caption breaker moved from
caption.pyinto_common/text.py, and the two structure
detectors (silence.detect,scenes.detect_scenes) into_common/probe.py, so
metadata.py --auto-chaptersmeasures without any tool inscripts/importing a sibling tool.
Both moves are byte-for-byte;caption.wrap_text,silence.detectandscenes.detect_scenes
still resolve where they always did. - feat: 1.16.0 — long-form delivery (audiogram, auto chapters, multi-language subtitle tracks, phrase-aware captions) (#231)
What's Changed
- feat: 1.16.0 — long-form delivery (audiogram, auto chapters, multi-language subtitle tracks, phrase-aware captions) by @claude[bot] in #231
Full Changelog: v1.15.1...v1.16.0