TASK 49 is retired (creator ruling): YouTube owns chat moderation. Its blocked-words filter and hold-for-review run upstream of `liveChatMessages.list` — held messages are never returned (https://stackoverflow.com/questions/74511524/) — so a second local list would duplicate a switch the creator already owns, with a second set of false positives and a second support surface. What we actually owed the platform, and had wrong: - The parser added every item to the overlay. A deleted message arrives as a `tombstone` carrying no text at all ("it has since been deleted … only liveChatId, type and publishedAt are present" — https://developers.google.com/youtube/data/live/reference/rest/v3/liveChatMessages), so every deletion rendered as a BLANK chat row. `hasDisplayContent:false` messages and chat-mode events did the same. - Deletion events (`messageDeletedEvent`/`messageRetractedEvent`/`userBannedEvent`) are documented but have not been delivered to clients for years (https://stackoverflow.com/questions/44346893/), so nothing is built on them; they are skipped if they ever arrive. - Found while testing: Super Chat read `displayString`, but YouTube's field is `amountDisplayString` — every amount silently fell back to a recomposed string, which mangles non-dollar locales. The model's own comment said this was the reason the field exists. Test: `ChatModerationParseTests` — tombstone, no-display-content, chat-mode events all produce no row; a real Super Chat still parses its pre-rendered amount (constraint 4: reward fields are never reinterpreted). Docs: TASK 49 marked retired with the citations, the rule recorded in decisions.md, HANDOFF updated. verify.sh: 550 passed, 0 failed, 0 warnings.
37 lines
2.3 KiB
Markdown
37 lines
2.3 KiB
Markdown
# HANDOFF — 2026-11-05 (TASK 64 — green/blue screen for clips)
|
||
|
||
## State
|
||
|
||
- `main`. TASK 21 complete (video file layer + its soundtrack). TASK 64 shipped through slice E:
|
||
a clip with a green or blue backdrop is **offered** a key on add ("Looks like a green screen —
|
||
Remove it"), accepting it keys the background **inside the decode pipe** (bundled ffmpeg
|
||
`colorkey`/`despill`), with Amount/Softness/Fringe in the panel and the settings persisted.
|
||
Sessions are keyed by **path + look**, so identical looks share one decoder.
|
||
- Detection stays silent on lawns/pitches (confidence = coverage × hue tightness) and ignores
|
||
letterbox bars. It never acts on its own.
|
||
- Slice F shipped too: **App Settings → Video clips → "Auto Green/Blue Screen"** (off by default,
|
||
high confidence only, and the panel states when a key was automatic) and **alert clips** key through
|
||
the same settings block (one alert box per layout).
|
||
- Tests: `ChromaKeyDetectorTests` + `ChromaKeyApplyTests` (pipe asks ffmpeg to key; offer → accept →
|
||
keyed session feeds the resolver; survives save; auto-apply; alert clip keyed). App build 0 warnings.
|
||
- **TASK 49 retired (creator ruling):** YouTube owns chat moderation — blocked words and hold-for-review
|
||
run before the API. What we owed it was not a wordlist but not painting what YouTube removed: the chat
|
||
parser now skips `tombstone` (a deleted message's text is never sent) and `hasDisplayContent:false`.
|
||
Same commit fixed a live bug: Super Chat read `displayString` instead of `amountDisplayString`, so every
|
||
amount fell back to a recomposed string.
|
||
|
||
## Next action
|
||
|
||
- TASK 64 is complete. Next queued: TASK 49 (chat profanity filter). Day 3's TASK 32/33 resilience slices remain outstanding —
|
||
32·4 (fragmented MP4) is the smallest.
|
||
- Do not push absent a checkpoint request.
|
||
|
||
## Landmines
|
||
|
||
- `LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder` injects physical
|
||
mouse input. Do not run it while the desktop is in use.
|
||
- Integration tests constructing `MainWindow` must set a temporary `LayoutPathOverride` and clear it in `finally`.
|
||
- Media sessions are keyed by **path + look** (`MainViewModel.MediaSessionKey`); never acquire by raw
|
||
path, and release through `ReleaseMediaIfUnused`.
|
||
- Bundled FFmpeg stays outside the single-file exe (`ExcludeFromSingleFile="true"`).
|