v1 task queue: add TASK 19-23 (transitions, hotkeys, media source, audio sync, studio mode)

- TASKS.md: added 5 new tasks for v1 feature completeness, execution order, updated backlog
- MONETIZATION.md: added v1 feature completeness section (competitive gap analysis)
- ai.md: added v1 task queue note in Current limitations/TODOs
- HANDOFF.md: updated session state and next steps
- ViewModels/SocialsDialogViewModel.cs: removed isPremium parameter (social slot locks)
- ViewModels/MainViewModel.cs: removed isPremium argument from SocialsDialogViewModel constructor
- ytLive.Tests/SocialsDialogViewModelTests.cs: updated 5 tests for unlocked slots
This commit is contained in:
2026-08-19 11:51:32 -07:00
parent f9ad7c2cb1
commit e97406d649
6 changed files with 375 additions and 103 deletions
+265 -13
View File
@@ -694,19 +694,27 @@ the validator → persisted), compositor bar overlay (top/bottom + above-flash),
---
## TASK 10 — Monetization: subscription billing + in-app unlock + support
## TASK 10 — Monetization: watermark-only subscription + Polar billing
**Goal:** annual subscription billing, in-app unlock seam, and in-app bug-report → git-issues support loop.
**Goal:** annual subscription via Polar.sh that removes the branding watermark. All features are
free — the only difference between free and paid is the watermark.
**Business details (pricing, billing research, revenue projections) in `MONETIZATION.md` (gitignored).**
**Business details (pricing, Polar product/checkout/discounts) in `MONETIZATION.md` (gitignored).**
### Status: 🔶 Scoped — nothing built
**Polar product:** `d105dfa1-497e-423b-8cd4-e0ee2e3abbc0` (LlamaCasty, $99/yr, currently `private`)
**Checkout:** `llamacasty.com` → Polar hosted page (~80% configured)
**Discounts:** `LLAMAFOUNDER` (100% off, 50 uses, `once`), `LLAMA50` (50% off, 12 months, unlimited)
1. ☐ **Billing provider decision** — Gumroad vs Lemon Squeezy (see MONETIZATION.md for comparison)
2. ☐ **Unlock mechanism** — license-key entry + online/offline verification + persisted entitlement (extends `LayoutStore`, schema bump) + renewal/lapse handling
3. ☐ **In-app bug-report → git issues** — built-in report control; good error codes first, optional log attachment with transparency about what's being sent
4. ☐ **"Unlock Premium" seam** — the About hub's greyed button (`PremiumUrl`) lights up and routes to billing/unlock
5. ☐ **Alerts gating** — Super Chat pop-ins gated behind subscription state
### Status: 🔶 Scoped — Polar infra live, in-app plumbing not built
1. ✅ **Billing provider** — Polar (polar.sh) chosen and configured. Product, price, license key benefit, discounts, checkout link all created.
2. ☐ **License key entry + verification UI** — About overlay or dedicated dialog; key entry field + validate against Polar API
3. ☐ **Offline entitlement** — cached license state in `LayoutStore` (schema bump); online check at startup, offline grace period
4. ☐ **BrandFlash → watermark toggle** — `BrandFlashEnabled = false` when licensed; the flash asset image needs to be created (the XAML `TextBlock` is preview-only, the compositor needs a `VideoFrame`)
5. ☐ **Remove social slot locks** — all 6 social bar slots open to everyone (no `IsPremium` gating on slots 3-6)
6. ☐ **"Unlock Premium" button** — `PremiumUrl` set to `llamacasty.com` checkout; `IsPremiumAvailable` becomes true
7. ☐ **Renewal/lapse handling** — key expiry check, grace period, re-prompt
8. ☐ **In-app bug-report → git issues** — built-in report control; good error codes first, optional log attachment
---
@@ -832,10 +840,254 @@ No changes to `MusicPlayer`, the 0.20 cap, the ducker, or the meter zones. Build
---
## Backlog (future versions)
## TASK 15 — Stock scene background images
1. v0.2 — Recording to local file (recordings carry the branding flash — see TASK 3 / `ai.md` Monetization)
2. v0.4 — Multi-destination restreaming (if needed; casual streamers may outgrow ytLlive first — "congrats, you're ready for OBS")
3. v0.5 — Stream clipping
**Goal:** branded background images for each canonical scene (Starting, Live, BRB, Chat, Ending)
that both provide working hints to creators and self-promote LlamaCasty.
### Status: ☐ Waiting on user to provide images
1. ☐ User provides 5 background images (one per canonical scene), PNG format, 1920×1080
2. ☐ Wire images as default `Background` sources, auto-populated on DB seed (empty DB → each scene gets its branded bg)
3. ☐ Assets stored in DB via `Asset` table (BLOB, SHA-256 keyed) — survives file deletion
4. ☐ Used by TASK 16 (infinity display fix) as the static fallback when no game is detected
### Design decisions
- Images are user-provided, not generated — the creator owns the brand look
- Each scene gets its own image (Starting = "Starting Soon", Live = live branding, BRB = "Be Right Back", etc.)
- The images double as the no-game backdrop fallback (TASK 16), so the Live scene image should work as a desktop replacement
---
## TASK 16 — Kill infinity display (no-game backdrop fallback)
**Goal:** when no full-screen game is detected, show a static branded placeholder instead of
capturing the primary display (which causes the infinity mirror effect).
### Status: ☐ Not started — depends on TASK 15
1. ☐ Modify `ResolveAutoCaptureKey()` (`MainViewModel.cs:1482`) — return null when no game detected
instead of falling back to primary monitor
2. ☐ Modify `EnsureBackdrop()` — when `CaptureKey` is null, show the scene's `Background` image
(the stock image from TASK 15) instead of creating a capture session
3. ☐ "Change Capture…" still available — user can manually pick a display/window via the picker
4. ☐ `ReacquireScreenCaptures()` handles null capture key gracefully (no `ScreenCaptureManager.AcquireAsync` call)
5. ☐ Update `ai.md` screen backdrop section to document the null-capture-key behavior
### Design decisions
- **Option A from user review:** static placeholder when no game, not multi-monitor fallback
- The user-provided stock bg image from TASK 15 IS the placeholder — no separate asset needed
- Manual capture pick still works — the user can always force a display capture via right-click → "Change Capture…"
- Single-monitor setups no longer show the infinity mirror
---
## TASK 17 — Web source rendering (WebView2)
**Goal:** make the web source actually render URLs into the preview and stream output.
### Status: ☐ Not started — required for v1
1. ☐ Add `Microsoft.Web.WebView2` NuGet package
2. ☐ Schema v10: `WebUri TEXT` column on `Source` table + migration in `LayoutStore.cs`
3. ☐ Persist `Source.WebUri` on save/load (currently in-memory only — lost on restart)
4. ☐ Hidden `WebView2` control per web source — navigates to `WebUri`, renders in-app
5. ☐ Frame capture from WebView2 (`CoreWebView2.CapturePreviewAsync` or `CompositionSurface`) → `VideoFrame` (BGRA8)
6. ☐ Wire into `FramePump` resolver — `Source { Type: WebSource }` → latest WebView2 frame
7. ☐ Wire into `SceneCompositor` — render web source as an image element at its position/size
8. ☐ Preview shows live web content (not just a blank rectangle)
9. ☐ Handle navigation errors, invalid URIs, timeout gracefully
### Design decisions
- WebView2 is the only option for Windows — it's pre-installed on Windows 10 20H2+ and Windows 11
- The web source is a standard element — positioned/sized/opacitied like any image source
- Frame capture rate can be lower than video FPS (5-10 fps for web content is fine)
- This enables Streamlabs/StreamElements overlays via web URLs
---
## TASK 18 — Local recording
**Goal:** record the stream output to a local file, with or without simultaneously streaming.
### Status: ☐ Not started — required for v1
1. ☐ Extend `EncoderOptions` with `OutputPath?` and recording mode flags
2. ☐ `FfmpegArgs.Build` gains a local-file branch: MP4/MKV container for file output
3. ☐ Three modes:
- **Record only** — no RTMP push, just local file (for pre-recorded content)
- **Stream only** — RTMP push, no local file (current behavior)
- **Stream + record** — dual output via `-f flv rtmp://... -f mp4 file.mp4`
4. ☐ Recording controls in UI (REC button, file path picker, duration display)
5. ☐ Schema migration for recording preferences (default output path, format)
6. ☐ Branding flash appears in local recordings too (free tier)
7. ☐ Output folder: `%APPDATA%\ytLlive\recordings\` with timestamped filenames
### Design decisions
- FFmpeg supports multiple outputs natively (`-f flv rtmp://... -f mp4 file.mp4`) — no second subprocess needed
- MP4 is the default container (widely compatible); MKV as an option (crash-safe, can be remuxed)
- Record-only mode is useful for pre-recorded content or testing without going live
- The branding flash carries into local recordings (free tier billboard extends to recordings)
---
## v1 execution order
The tasks below are ordered by dependency and risk. Each task builds on the previous.
1. **TASK 9.4** — Live chat (right panel) — wire `YouTubeChatService.Start()`, parse messages, render in panel. Foundation for chat box source.
2. **TASK 20** — Hotkeys — global keyboard shortcuts. Small scope, huge UX impact.
3. **TASK 19** — Scene transitions — Cut/Fade/Move/Custom. Depends on compositor.
4. **TASK 17** — Web source (WebView2) — enables alert ecosystem.
5. **TASK 18** — Local recording — independent, but pairs with stream.
6. **TASK 21** — Media source — video file playback for non-Live scenes.
7. **TASK 22** — Audio sync offset — small, quality-of-life.
8. **TASK 23** — Studio mode — lightweight preview before live.
9. **TASK 3.18** — Chat box source — renders chat ON the stream. Depends on TASK 9.4 (same message parsing).
10. **TASK 15** — Stock bg images — waiting on user.
11. **TASK 16** — Infinity display fix — depends on TASK 15.
12. **TASK 10** — Polar billing — in-app license key entry + watermark toggle.
13. **TASK 13** — Social media launch kit — after all v1 features ship.
---
## TASK 19 — Scene transitions
**Goal:** smooth visual transitions between scenes — the four types from the design spec (Cut, Fade, Move, Custom).
### Status: ☐ Not started — required for v1
1. ☐ `TransitionType` enum: `Cut`, `Fade`, `Move`, `Custom`
2. ☐ Global transition setting (`Cut` default, 300ms) with per-scene override
3. ☐ `TransitionDuration` property (configurable, default 300ms)
4. ☐ Compositor support — blend two frames during transition:
- **Cut:** instant switch (current behavior, zero cost)
- **Fade:** crossfade — source fades out, destination fades in
- **Move:** simple positional transition — source slides out, destination slides in (economical)
- **Custom:** media/stinger playback during transition (heavier, but creators pay for these)
5. ☐ Preview shows the transition (WYSIWYG) — the preview panel renders the same blend
6. ☐ Encoder output renders the transition too (the live stream sees it)
7. ☐ Transition state management in MainViewModel (`_isTransitioning`, current/next scene tracking)
8. ☐ Custom transitions: media resource loading (video file → frame sequence during transition)
9. ☐ UI: transition type picker (dropdown or buttons), duration slider
10. ☐ Tests: transition timing, frame blending, Cut is instant, Fade produces blended frames
### Design decisions
- **Cut is free** — instant switch, no blending, zero CPU cost. This is the default.
- **Fade is the minimum expectation** — 300ms crossfade is universal. Every streaming tool ships this.
- **Move is economical** — simple slide animation, no video decoding needed. Good middle ground.
- **Custom is premium** — media/stinger transitions require video playback. Heavy but expected by mid-tier streamers.
- Transitions happen in the compositor, not post-encode. The preview sees the same blend as the live output.
---
## TASK 20 — Hotkeys (keyboard shortcuts)
**Goal:** keyboard shortcuts for scene switching and common actions — the single biggest UX gap.
### Status: ☐ Not started — required for v1
1. ☐ `HotkeyManager` service — register/unregister hotkeys per scene + common actions
2. ☐ Scene switching hotkeys — F1-F5 for the 5 canonical scenes (configurable)
3. ☐ Common action hotkeys — Start/Stop stream, Mute/Unmute mic, Mute/Unmute game, Toggle TRAX
4. ☐ Hotkey configuration UI — Settings dialog or per-scene hotkey picker
5. ☐ Global hotkeys — work even when app is not focused (for gaming)
6. ☐ Conflict detection — warn when two actions share the same hotkey
7. ☐ Schema migration for hotkey bindings (persist across restarts)
8. ☐ Tests: hotkey registration, conflict detection, scene switch via hotkey
### Design decisions
- **Global hotkeys are mandatory** — streamers are in-game and cannot alt-tab. F1-F5 must work from anywhere.
- **Simple config** — one row per action: [Action] [Current Key] [Change]. No complex macro system.
- **No Stream Deck yet** — bare keyboard first. Stream Deck support (physical devices) is v1.1+.
---
## TASK 21 — Media source (video file playback)
**Goal:** play video files (MP4, MOV, AVI) into scenes — starting soon videos, BRB loops, intro/outro clips.
### Status: ☐ Not started — required for v1
1. ☐ `MediaSourceType` enum: `Video`, `Audio` (audio-only files via media source)
2. ☐ `SourceType.MediaSource` addition to the enum
3. ☐ `MediaSourceModel`: `FilePath`, `IsLooping`, `Volume` (0-1), `PlaybackState`
4. ☐ `VideoFrameSource`: FFmpeg-based video decoder → `VideoFrame` pipeline
5. ☐ Frame capture from video file (decode at native FPS, output BGRA8 frames)
6. ☐ Wire into `FramePump` resolver — `Source { Type: MediaSource }` → latest video frame
7. ☐ Wire into `SceneCompositor` — render media source as an image element at its position/size
8. ☐ Loop control — `IsLooping` property, restart on end
9. ☐ Volume control — per-source volume slider for audio playback
10. ☐ UI: file picker (filtered to video formats), loop toggle, volume slider
11. ☐ Schema migration for media source settings (file path, loop, volume)
12. ☐ Tests: video frame extraction, loop behavior, volume scaling, file validation
### Design decisions
- **FFmpeg handles all formats** — no codec-specific code. FFmpeg already in the project.
- **Audio plays through the desktop channel** — media source audio is captured by the WASAPI loopback (like TRAX/game audio). No separate audio routing needed.
- **This pairs with TRAX** — TRAX is background music, media source is background video. Together they make non-Live scenes (Starting/BRB/Ending) feel polished.
- **Not a full NLE** — no trimming, no multi-track, no effects. Just "play this video in the scene."
---
## TASK 22 — Audio sync offset
**Goal:** per-source audio delay compensation to prevent lip-sync drift from USB mics and capture cards.
### Status: ☐ Not started — required for v1
1. ☐ `AudioSyncOffset` property on AudioSource models (default 0ms, range -500ms to +500ms)
2. ☐ Apply offset in `AudioMixer` — delay or advance audio samples relative to video
3. ☐ UI: offset slider per audio source (or global offset for simplicity)
4. ☐ Persist offset in `LayoutStore` (schema migration)
5. ☐ Tests: offset application, positive/negative delay, boundary values
### Design decisions
- **Global offset first** — one setting for all audio sources. Per-source is v1.1+.
- **Simple slider** — -500ms to +500ms, default 0. No numeric input needed.
- **Visual feedback** — show a "sync OK" indicator when offset is applied.
---
## TASK 23 — Studio mode (lightweight preview)
**Goal:** preview the next scene before transitioning — lightweight version of OBS Studio Mode.
### Status: ☐ Not started — required for v1
1. ☐ "Preview" button per scene — clicking shows that scene in a preview pane without going live
2. ☐ Preview pane — dedicated area showing the selected scene (not the current live scene)
3. ☐ "Go" button — transitions from preview to live (applies the scene change)
4. ☐ Transition applies when switching — if Fade is selected, the preview-to-live transition uses it
5. ☐ Disable during live transitions — prevent rapid-fire preview switching while transitioning
6. ☐ Tests: preview isolation, go button triggers transition, disable during transition
### Design decisions
- **Lightweight implementation** — not the full OBS dual-canvas setup. Just a "preview this scene" button + a preview pane.
- **Optional feature** — creators can ignore it and just switch scenes directly (current behavior).
- **Pairs with transitions** — studio mode + transitions = professional workflow. Preview the fade, then commit.
---
## Backlog (future versions)
1. v1.1 — Stream Deck / Loupedeck integration (requires hotkey foundation from TASK 20)
2. v1.1 — Per-source audio sync offset (global offset ships in TASK 22)
3. v1.1 — Multiple profiles/presets (save different configs for different stream types)
4. v1.1 — Chroma key filter (green screen removal, or ONNX background removal)
5. v1.1 — Virtual camera output (Zoom/Discord/Teams)
6. v1.1 — Replay buffer (instant replay with hotkey)
7. v2 — Multi-destination restreaming (if needed; casual streamers may outgrow LlamaCasty first)
8. v2 — Stream clipping
9. v2 — Export/import settings
---