7f14ffb11e
The creator confirmed the clip fix ("the video plays now"), then asked where the
scrolling text was — it was invisible, for a structural reason. AlertTickerFrame
existed only as a frame-pump callback blitted into the OUTPUT; PreviewPane.xaml had
no element for it, because the strip is master-width and global, not a Source, so it
cannot ride a per-element Image. Nothing was wrong in the renderer: there was no
consumer in the preview. Same class of defect as the missing IsAlertBox trigger, one
layer up (MyMistakes RULE 5/6).
- tickerPreviewSink on AlertOverlayLayer, published from RefreshAlertPreviews() so it
is always the UI thread; MainViewModel.AlertTicker writes it into one reused
WriteableBitmap bound to a new global AlertTickerElement, mirroring SocialBarElement.
- Source.AlertDisplayMethod + panel "Display" selector: TickerScroll / Flash / Solid.
Flash pulses 0.5s on / 0.5s off for the whole alert; Solid is centred and still.
- The marquee was also unreadable: a fixed 140px/s took ~17s per pass, so a 10s alert
showed the text once, entering from the right and never crossing. Paced in reads per
alert instead (TickerReadsPerAlert = 3 inside the alert's own length, speed derived
from it) — never px/s. Research (websearch: how do OBS/Streamlabs/StreamElements
alert boxes present announcement timing?) settled the unit: Streamlabs exposes "Alert
Duration: choose how long your alert stays on your stream" and "Text Delay", never a
scroll-speed slider (https://support.streamlabs.com/hc/en-us/articles/52499995174299-Setting-up-Your-Streamlabs-Alerts).
Run is phase-started half a frame in so the first frame isn't blank.
- Persistence: AlertDisplayMethod INTEGER NOT NULL DEFAULT 0 via the idempotent
table_info migration, appended LAST in the SELECT because the Source reader is
positional (GetInt32(32..34)) — a mid-list insert would silently shift a neighbour.
Tests: 15 new facts (suite 339/339). RealApp STA host: the pane draws the strip and
collapses at alert end; the layer publishes a real 1920x48 frame for all three methods
and nothing when the ticker is off; three passes counted in 10s by the pill's leading
edge resetting (a seamless marquee never blanks, so an empty frame cannot count a pass);
Solid byte-identical at every moment; Flash on for half of each second; the panel shows
and writes back the choice; the DB round-trips all three alert fields together.
Incidental finding: a bound ItemsSource ComboBox in LeftPanel.xaml broke
LayerReorderPersistenceTests.RealMouseDrag (that test injects PHYSICAL mouse input, so a
load-time re-measure moves the rows out from under the cursor). Rewritten as inline
ComboBoxItems, the shape the chat Font selector already uses in that panel. Recorded as
MyMistakes RULE (8).
110 lines
7.1 KiB
Markdown
110 lines
7.1 KiB
Markdown
# HANDOFF — 2026-09-26, end of session (take four: ticker display methods)
|
|
|
|
## Where we are
|
|
`main`, **5 local commits ahead of `origin/main` (= `ecb329e`), NOTHING PUSHED.**
|
|
Last pushed commit is still `ecb329e`. Do not push without the creator saying so.
|
|
|
|
| commit | what |
|
|
|---|---|
|
|
| `a11b15e` | TASK 47 alert video (first landing) |
|
|
| `7b940b6` | poller-thread marshal crash fix |
|
|
| `80038ff` | silent-decoder fallback + diagnostics |
|
|
| `aea0670` | video pacing via `FfmpegFrameRateProbe` |
|
|
| `e2ecc24` | **the real alert fix: `PreviewPane.xaml` `IsAlertBox` trigger** + 2 Good Dog tests + resolver/preview diagnostics |
|
|
| (last, unpushed) | **alert announcement strip: visible in the preview + 3 display methods** (Scroll / Flash / Solid), paced in reads-per-alert |
|
|
|
|
## THE ANSWER, take two (do not re-derive this)
|
|
The **ticker was output-only**. `AlertTickerFrame` existed solely as a frame-pump
|
|
callback (blitted at 0,0 into the OUTPUT). `PreviewPane.xaml` had no element for it
|
|
because the strip is master-width and GLOBAL, not a `Source`, so it cannot ride a
|
|
per-element `Image`. Nothing was broken in the renderer — there was simply no consumer
|
|
in the preview. Fixed with a `tickerPreviewSink` on `AlertOverlayLayer` (published from
|
|
`RefreshAlertPreviews()`, UI thread only) + `MainViewModel.AlertTickerImageSource` +
|
|
a global `AlertTickerElement` in `PreviewPane.xaml` mirroring `SocialBarElement`.
|
|
|
|
The marquee was also **unreadably paced**: a fixed `140px/s` needed ~17s per pass, so a
|
|
10s alert showed the message barely once, entering from the right and never crossing. It
|
|
is now paced in **reads per alert** (`TickerReadsPerAlert = 3` inside the alert's own
|
|
length), which is the incumbent's own unit (Streamlabs: "Alert Duration" + "Text Delay",
|
|
never a scroll-speed slider).
|
|
|
|
## THE ANSWER (do not re-derive this)
|
|
The alert video was **never drawn**, never mis-decoded. Build 90's diagnostics showed
|
|
`Alert preview: frame=680x200 a255` and `Output resolver: frame 680x200 playing=True`
|
|
every second for the whole clip — a real, opaque, correctly-sized frame reaching BOTH
|
|
consumers. `Controls/PreviewPane.xaml` keeps the per-element `Image` `Collapsed` unless a
|
|
DataTrigger fires, and there was **no `IsAlertBox` trigger** (only IsImageSource /
|
|
IsChatBox / IsWebSource / IsWebcam). Chat boxes rendered; alert boxes never could.
|
|
Removing/re-adding the layer could not have fixed it.
|
|
|
|
Clip itself is fine: DB asset `941785b0-d022-45fa-a8a2-2cd59ae2ba48` is byte-identical
|
|
(md5 `0ee1f4960dd3b23dee5930a2af79d410`) to the creator's
|
|
`C:\Users\gramp\Downloads\llamacasty-dancingLlama-thankyou.mp4` — h264 1280x720 10s,
|
|
every sampled frame full bright content.
|
|
|
|
## Working tree
|
|
Clean apart from the uncommitted work unit above (it is committed; check `git status`).
|
|
If the app is running, `ytLive.dll`/`.exe` are LOCKED and any build fails with MSB3021/
|
|
MSB3027 — the creator must close ytLive first. That is normal, not a broken build.
|
|
|
|
## Build / test commands (Windows host, always)
|
|
```bash
|
|
"/mnt/c/Program Files/dotnet/dotnet.exe" build "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.csproj" --no-restore
|
|
"/mnt/c/Program Files/dotnet/dotnet.exe" vstest "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.Tests\bin\Debug\net8.0-windows10.0.19041.0\ytLive.Tests.dll"
|
|
```
|
|
Last full suite: **339/339 pass** (15 new facts for the ticker feature). Run it with ytLive CLOSED or the RealMouseDrag env
|
|
flake fires.
|
|
|
|
## Queued next (creator's words, 2026-09-26) — the Good Dog queue
|
|
1. **Test-console chat must not clear the chat window.** "when I add a chat message from
|
|
the test console, or from any console, that chat text should not clear the current
|
|
chat window."
|
|
2. **Test pull-out chat input: 20px right padding** — the text box is clipped at the right
|
|
edge of the div; needs 20px padding against the div's right side.
|
|
3. **Test pull-out chat input: Enter inserts a newline**, it must not send.
|
|
4. ~~Verify the alert video live~~ — **DONE, the creator confirmed it plays.**
|
|
5. **Verify the three ticker display methods live** (Scroll / Flash / Solid) and pick a
|
|
default. Flash is 0.5s on / 0.5s off; Solid is centred; Scroll does 3 passes per 10s
|
|
alert. Nothing here has been SEEN on screen yet — the tests prove pixels and bindings,
|
|
not the look.
|
|
6. **Decide whether the once-per-second alert diagnostics stay.** `ViewModels/MainViewModel.cs`
|
|
(`ResolveOutputFrame`) and `Services/AlertOverlayLayer.cs` (`UpdatePreview`) still log
|
|
`Alert preview:` / `Output resolver:` once a second while an alert plays. They earned
|
|
their keep; they are noise in normal use. Creator's call.
|
|
|
|
## Landmines / facts worth keeping
|
|
- The pinned BtbN ffmpeg has **no libx264** — generate test clips with `-c:v mpeg4`.
|
|
- **A bound `ItemsSource` ComboBox in `LeftPanel.xaml` breaks
|
|
`LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder`** (it
|
|
injects PHYSICAL mouse input, so a load-time re-measure moves the rows out from under
|
|
the cursor). Use inline `<ComboBoxItem>`s like the chat Font selector does. If a UI-only
|
|
change breaks a real-input test, suspect the control's binding STYLE first, and bisect
|
|
with `git checkout <the one xaml file>`.
|
|
- The `Source` row is read back by **POSITIONAL** index in `LayoutStore.Load.cs`
|
|
(`GetInt32(32..34)`). Append new Source columns to the END of the SELECT, or a
|
|
mid-list insert silently shifts a neighbour instead of failing to load. New columns use
|
|
the idempotent `PRAGMA table_info` guard in `LayoutStore.Migrations.cs` (no
|
|
`user_version` bump needed).
|
|
- **A seamless marquee never goes blank** (the wrapped copy enters from the right as the
|
|
pill clears the left), so an "empty frame" cannot count a pass — count the pill's
|
|
leading edge resetting to the right edge.
|
|
- `SceneCompositor.Render` fills its base with **opaque black**, so "any non-zero byte"
|
|
is not proof a layer painted. Compare against an idle render, or count COLOURFUL px.
|
|
- `sqlite3` on WSL: `/home/gramps/android-sdk/platform-tools/sqlite3`. Extract a BLOB with
|
|
`SELECT writefile('/tmp/opencode/x.bin', Data) FROM Asset WHERE Id='…'` (Linux paths
|
|
only — a `C:/…` path creates a junk `C:` dir IN THE REPO; one was made and removed).
|
|
- Layout DB: `/mnt/c/Users/gramp/AppData/Roaming/ytLlive/ytLlive.db` (tables: `Asset`
|
|
`Id/Hash/Data`, `Settings`, `Source`, `Scene`, …). Alert default video setting key:
|
|
`AlertDefaultVideoAssetId`. ffmpeg/ffprobe live in
|
|
`/mnt/c/Users/gramp/AppData/Roaming/ytLlive/tools/` (run the `.exe` directly from WSL).
|
|
- Two diagnostics were ADDED and are still in the tree (once-per-second, alert only):
|
|
`Output resolver: alert box …` (MainViewModel.cs) and `Alert preview: box …`
|
|
(AlertOverlayLayer.cs). Decide with the creator whether to keep them or strip them now
|
|
that the root cause is known.
|
|
- `PreviewPane.xaml` has the same latent gap for `IsMediaSource` (no trigger either) —
|
|
NOT fixed (out of scope), noted as a follow-up.
|
|
- Known perf item, untouched: FramePump stalls ~140-180ms renders
|
|
(`render=full-render split=0 … totalMs=140`). Unrelated to the alert bug.
|
|
- YouTube rejects the `complete` transition (403) at end-of-stream; `enableAutoStop`
|
|
finishes the broadcast. Harmless, already handled.
|