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:
@@ -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
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user