Skip to content

Keep differential ad tails, hold interior cues, operator notes block, UI consistency pass (2.100.22) - #889

Merged
ttlequals0 merged 71 commits into
mainfrom
fix/patch-2.100.19
Oct 24, 2026
Merged

ttlequals0 merged 71 commits into
mainfrom
fix/patch-2.100.19

Conversation

@ttlequals0

@ttlequals0 ttlequals0 commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Why

Three detection reports, seven open issues, a batch of phone layout problems and the foldable-phone viewports, shipped as 2.100.19. A second round on the same branch, 2.100.20, fixes what a UI audit found after the first deploy. A third, 2.100.21, applies the full audit. A fourth, 2.100.22, fixes two things 2.100.21 got wrong. #886 and #894 are already on main.

Closes #874, #875, #876, #879, #888, #891, #895.

What changed

  • Same-sponsor merge: a fold only extends an end. When the later fragment sat inside the first, the merged marker took the inner end and a cross-fetch differential span lost its measured tail (2.7 s at an episode start, 4.5 s before the credits on one episode).
  • Reviewer: an inward trim now stops at a removable template cue inside the LLM span, not only at an edge the cue snap already moved. An ad-break stinger 7 s into a span was being left in the output.
  • Detection notes: per-feed notes go to the detector, the verification pass and the reviewer as their own prompt block marked as operator instructions, outside the 800-character description budget. Notes are stripped of HTML only, so a cited time survives.
  • Cue templates can cut every match on their own (Allow cues to be cut completely #874). Global setting, feed override and per-template value, explicit beating inherited. Each match becomes a marker with the cue type's category, and the feed's segment category actions decide the outcome. Content transition cues are excluded. Cue-only safety still holds an unproven template.
  • Issue fixes. The pattern dialog's podcast link went to the API path (Invalid Podcast URL in Pattern Details view #876). The current-position pin drag follows the play-while-dragging setting (Don't always play audio while dragging current position pin #875). The cue pair override is named like the global setting (Naming: "Create ads from cue pairs" vs "Pair synthesis" #879). ffmpeg's big-endian ID3v2.4 frame sizes no longer fail chapter restore, and a chapter tag that cannot be read is skipped instead of failing the episode (Chapter restore fails with 'Invalid ID3 synchsafe integer': ffmpeg writes non-synchsafe CHAP sizes in ID3v2.4 #888, fix from ChaosHat). Recut works for cue-only feeds without a transcript (Cue only processing without transcription requires transcript to recut #891).
  • Web UI: Calls by phase is a table. The pattern details dialog uses the shared button recipes. On phones its footer is a row of equal-width text buttons, with Save as the edit label like every other dialog. The Feeds header shows compact actions below 768 px and two columns from 600 px. Copy buttons center their icon when the label is hidden. Play buttons are one square size everywhere, 32 px inside the 44 px target on phones. Overlapping cut markers render as one block with a segments toggle. Dialog footers, close buttons, destructive outlines and icon buttons share one set of recipes, documented in the design guide. The transcript viewer groups lines into paragraphs at render time, with a Lines toggle, and the stored transcript is unchanged (Transform chunked transcript into paragraph-like segments #887).
  • Settings > System Status drops Time Saved, LLM Tokens and Cost and links to the Stats page. Reject buttons on non-sponsor markers read "Keep" (Confirming a non-sponsor segment says "Confirm ad" and learns it as a sponsor pattern #877 follow-up).
  • Foldable phones (466x678 folded, 890x626 unfolded). The shell and fixed bars pad for the side safe-area insets. Grids use even column counts between 640 and 1023 px. Dialogs stay inside one viewport segment when the browser reports two.
  • MP3 stream copy fixes ported from batram's plusminuspod fork, kept as their own commits so the squash credits the author. Review follow-ups on top: the disk-space estimate no longer counts PCM files that are not written, the final-cut extension checks protected audio, and ffmpeg's stderr is drained so a chatty decode cannot stall.
  • After Use minimal timestamps in LLM prompts to help reduce token usage #886, the startup refresh of shipped defaults moves never-customized prompt rows to the new timestamp form.
  • After Insert gap information into segment_id transcript for long pauses #884, the System One adapter skips the new gap cue rows when it reads a segment-ID prompt back into segments; before, the first gap row ended the transcript.
  • Image build: the 30 upstream security patches for ffmpeg, x264, libass, libsndfile, glibc, cairo, cjson and glib ship in the repo instead of being downloaded from their git forges at build time. One forge now answers automated fetches with a bot challenge page and another changed its patch rendering, so the pinned checksums no longer matched.
  • 2.100.20: the coarse-pointer floor was stretching four icon buttons and leaving their icons off center: edit feed title, open in editor, and the ad editor's add and close. They now use the icon recipe or flex centering, and lucide icons replace the hand-rolled paths. Held-marker and Ad Review action rows share one button height. A browser contract measures icon centering on the routes the contract suite reaches without a backend. The data-driven pages were measured with the mocked harness. The reject button and badges read "Not ad". Settings > System Status drops the sentence pointing at the Stats page.
  • 2.100.21: UI consistency pass. Headings, buttons and labels are sentence case. Errors read "Could not ." and empty lists "No yet." Held markers are "Needs review"; decisions are "Confirm" or "Not ad" everywhere, including the editor footer; reruns are "Re-detect ads" or "Full reprocess"; the nav says "Feeds" and "Add feed"; destructive confirms repeat verb and noun under question titles. Percents and durations share two formatters ("99% confidence", "2m 5s"). Every button, field, label and badge composes a shared recipe. Stat grids use one tile, episode status one badge, close buttons one component, and lucide replaces the hand-drawn icons. Dialogs pin their header and footer and scroll only the body. Stats and Search use the underline tabs. On phones the Add feed modes stack, the status bar shows the stage only, the Sponsors search spans the row, episode lists drop the artwork, review action rows are 44 px tall and icon buttons fill their target.
  • Auto-process window (eposodes remain "discovered" and don´t get processed #895). New episodes were auto-queued only when published in the last 48 hours, and the value was hardcoded. An instance that runs a few times a week never processed what arrived while it was off. The window is now a global setting, default 48 hours and up to 720, with a per-feed override that inherits when empty.
  • 2.100.22: the reviewer trimmed a cut back to the last spoken word whenever the wordless stretch past it was under 8 s. A pre-roll's closing music (2.7 s) and a post-roll's tagline music (4.7 s) survived the cut that way. It now holds the edge at the file head or tail and at a differential-measured edge at any length; an edge snapped to a labelled boundary cue keeps the cue. On phones, Detected ads cards keep their four controls on one line; only the five-control Ad review card wraps below 430 px. The Podping listener polls the Hive node through one keep-alive session instead of a new connection and DNS lookup per call (two calls every 3 s); health probes still open their own connections, since they fan out across threads.
  • brotli 1.2.1 for PYSEC-2026-4322, filelock 4.2.4 for CVE-2026-112940, zod 4.6.7 for GHSA-mrhx-c7xc-m3w5, happy-dom 21.0.4 for GHSA-834v-8h66-3h79, plus fast-uri 3.1.9 and serialize-javascript 7.1.4.

Verification

  • PYTHONPATH=src pytest tests/ (12246 passed, 2 skipped) and ruff check src/ tests/ pass.
  • npx tsc --noEmit, npm run lint, npm run test (158 files, 1883 tests), npm run design:check and npm audit --audit-level=high pass. The browser contract suite passes on the four Chromium projects (34 tests).
  • Browser checks with a mocked API and coarse pointer at 320, 360, 390, 466, 626, 678, 768, 890 and 1280 px for the Feeds page, episode page, pattern dialog (view, edit and delete states), settings sections, cue templates panel and play buttons.
  • Before and after captures of 19 states at 390 px (coarse pointer) and 1280 px with a mocked API. At 390 no state has a button under 44 px, horizontal overflow or clipped text, and the three dialogs keep their footer inside the viewport.
  • One episode was reprocessed against sharper detection notes and came back as one contiguous block instead of three cuts.
  • Trivy on the 2.100.19 through 2.100.22 GPU images: 0 critical, 0 high, 348 medium, all Ubuntu ffmpeg-family packages with no fix available. The previous image scans today at 5 critical and 23 high. The secret scan's only hits are Anthropic SDK docstrings, now allowed by rule.

ttlequals0 and others added 26 commits October 24, 2026 11:35
… phone UI fixes

Same-sponsor folds only extend an end, so a nested LLM span no longer
shrinks a cross-fetch differential marker. The reviewer stops an inward
trim at a removable template cue inside the span. Per-feed detection
notes reach the detector, verification pass and reviewer as their own
prompt block outside the description budget.

Web UI: Calls by phase table, pattern dialog button recipes and phone
wrapping, Feeds header compact actions below 768 px, centered copy
icons, 32 px play visuals inside 44 px targets, Cue Matches on the
shared play button, System Status without the Stats duplicates, and a
"Keep" reject label on non-sponsor markers.
…tream end

An ad that runs into the credits snaps its end a frame or two short of the decoded end (encoder padding puts the last boundary before it). The tail left over is below the two-frame warm-up, and copy_mp3 refused with 'Insufficient final retained audio', so the whole episode fell back to a libmp3lame re-encode: 9+ minutes on an e2-micro versus seconds for the copy.

When that tail belongs to the last cut and its end is not locked, fold it into the cut (interval extended to the stream end, applied end = decoded end) and continue; a locked end still refuses. Tests cover both, using a cut on the boundary two frames before the end so the guard is always reached.
A dynamically stitched podcast MP3 can start with a frame that disagrees with the rest (plain stereo in front of joint stereo, no Xing header). ffmpeg's mp3 demuxer logs 'Skipping N bytes of junk' and decodes the file one frame short. The copy keeps that frame too, so both decodes come out short by the same 1152 samples, and the PCM validation refused with 'MP3 decoded duration mismatch', sending a 72-minute episode into a 10-minute libmp3lame re-encode on an e2-micro.

When source and copy are short by the same whole frames (at most LEAD_JUNK_MAX_FRAMES) and the copy begins with those same source frames, treat them as part of the decoder skip for the span comparison and continue; every retained span still verifies sample-exact. A cut that starts at the stream start replaces the junk frame with silence and no longer shares the deficit, so that case still refuses.

Fixture: one plain-stereo frame spliced in front of joint-stereo frames, skipped if the local ffmpeg does not discard it.
The PCM validation decoded source and copy to 32-bit PCM files first: ~1.5 GB each for a 70-minute stereo episode. On a small cloud disk that is minutes of I/O wait (55% iowait, load 11 on a 2-vCPU e2-micro) and it stalled SQLite for the rest of the app. Every comparison walks both decodes in ascending order, so a forward-only reader over an ffmpeg pipe does the same job with no disk: _PcmPipe advances by reading and discarding, spans are compared chunk by chunk, and the decoded lengths are checked by draining to EOF at the end.

The junk-lead-frame detection no longer needs file sizes: the first packet's byte position from ffprobe names the frame the demuxer really started on, for source and copy alike.

Same 72-minute episode, same five cuts, byte-identical output: 52 s -> 23 s on an 8-core WSL box, and no temporary files.
…r tags (#888)

ffmpeg writes CHAP frame sizes as plain big-endian integers even in a
v2.4 tag. Pick whichever reading lands on the next frame header, padding
or the end of the tag, mutagen-style. The tag written back stays synchsafe.

Co-authored-by: ChaosHat <[email protected]>
…edicate for recut, indexed ad blocks, shared dialog footer scale
…stderr drain; ad block gap matches the merge; recut keeps the transcript guard
…, intro and outro only in the opening window, strict overlap, cue-only eligibility at every call site
…nd buttons, one field chrome and label recipe, lucide icons, formatDateTime, percent readouts, stacked feed modes on narrow phones
…d Modal owns header and footer chrome; one duration formatter and a shared sentenceCase; formatPercent everywhere; Stats section links are anchors on the tab strip classes; redundant recipe tokens removed; changelog for 2.100.21
…of being cut; pattern detail stat tiles fit three across on a phone
…itionPlayButton take disabled; score deltas keep one decimal; threshold edits accept whole percents only; Search tabs wrap on phones; contract tests follow the new tab roles and labels
@ttlequals0 ttlequals0 changed the title Keep differential ad tails, hold interior cues, operator notes block, phone UI fixes (2.100.19) Keep differential ad tails, hold interior cues, operator notes block, UI consistency pass (2.100.19) Oct 24, 2026
… hours, up to 720, for instances that run only occasionally (#895)
…r, the three API checks and the frontend; resolver takes the podcast row and clamps; integer-only commits on both fields
@ttlequals0 ttlequals0 changed the title Keep differential ad tails, hold interior cues, operator notes block, UI consistency pass (2.100.19) Keep differential ad tails, hold interior cues, operator notes block, UI consistency pass (2.100.21) Oct 24, 2026
@ttlequals0 ttlequals0 changed the title Keep differential ad tails, hold interior cues, operator notes block, UI consistency pass (2.100.21) Keep differential ad tails, hold interior cues, operator notes block, UI consistency pass (2.100.22) Oct 24, 2026
…n row, tab wrap, and examples for the new recipes
@ttlequals0
ttlequals0 merged commit ae2237b into main Oct 24, 2026
15 checks passed
@ttlequals0
ttlequals0 deleted the fix/patch-2.100.19 branch October 24, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow cues to be cut completely

2 participants