111 lines
6.3 KiB
Markdown
111 lines
6.3 KiB
Markdown
# ytLlive — AI Guide
|
|
|
|
## Run
|
|
|
|
```bash
|
|
dotnet build # Windows only — WPF requires Windows target
|
|
dotnet run
|
|
```
|
|
|
|
Note: `EnableWindowsTargeting=true` is set in `ytLive.csproj`, so the project can be restored/built from WSL, but running requires Windows.
|
|
|
|
## Tests
|
|
|
|
No test framework set up yet. When added: `dotnet test`.
|
|
|
|
## Architecture
|
|
|
|
C# / WPF (.NET 8) following MVVM:
|
|
|
|
| Path | Role |
|
|
|------|------|
|
|
| `Models/` | Plain data types — Scene, Source, StreamConfig, StreamHealth, YouTubeChannel, ChatMessage |
|
|
| `ViewModels/` | MainViewModel — exposes collections + commands for the UI |
|
|
| `Services/` | YouTube OAuth2, stream/broadcast management, live chat polling, LayoutStore (SQLite) |
|
|
| `Helpers/` | ViewModelBase (INotifyPropertyChanged), RelayCommand, ImageCache |
|
|
| `MainWindow.xaml` | Dark theme; layout: top bar (controls), center (preview), left (scenes/sources), right (chat), bottom (health) |
|
|
|
|
### Key patterns
|
|
|
|
- `ViewModelBase.SetProperty<T>()` for property change notifications
|
|
- `RelayCommand` for all button actions; commands gate on state (e.g. Start only when Offline)
|
|
- ViewModels are constructed in XAML (`<vm:MainViewModel/>` as DataContext)
|
|
- Services are currently instantiated in MainViewModel's constructor — no DI container yet
|
|
- Layout persists to SQLite (`Microsoft.Data.Sqlite`); scenes/sources/asset bytes stored in the DB, asset identity is a SHA-256 content hash (1:M reuse, no file paths — assets are always available)
|
|
|
|
### Current limitations / TODOs
|
|
|
|
- `OAuthCredentials.ClientId` / `ClientSecret` in `Helpers/OAuthCredentials.cs` are empty — the app
|
|
owner fills them in once (developer task, baked into the binary; creators never configure anything)
|
|
- `GoLiveViewModel.SignIn`/`ChangeAccount` removed — Connect (OAuth) is the only entry to streaming
|
|
- No token persistence yet (Windows DPAPI planned) — scene/source/asset layout *does* persist (SQLite)
|
|
- `YouTubeStreamService` uses hardcoded `1080p`/`60fps` and per-broadcast streams — must switch to the v3 `variable` reusable stream
|
|
- No capture/encoding/RTMP yet
|
|
- Stream config (title/description/visibility/quality) still in-memory
|
|
|
|
## Design Principle
|
|
|
|
> This software is so intuitive that even the most right-brained person can easily intuit and use it.
|
|
|
|
Apply this to every UI decision:
|
|
- One-click go-live with working defaults
|
|
- Prefilled YouTube defaults (RTMP URL, bitrate, resolution, latency)
|
|
- Visual/drag-and-drop scene building over property panels
|
|
- Every action produces a visible outcome — no dead ends
|
|
|
|
## Monetization (design decision — the watermark is the sword)
|
|
|
|
Free forever: all streams unlimited, no time caps, no subscription, no per-feature paywalls. The
|
|
**one paid line is a one-time unlock** (delivered via itch.io — they handle hosting, payment, and key
|
|
delivery; we never own a server or a key shop):
|
|
|
|
- **Free:** a small "made with ytLlive" watermark is always on, every frame, every stream — the sword
|
|
of Damocles. Standard practice; only Streamlabs runs watermark-nagging to a capitalist extreme.
|
|
- **Paid (one-time):** watermark removed + **Alerts** (Super Chat / membership / subscribe pop-ins).
|
|
|
|
Deliberately rejected: hard stream-time cutoffs (the worst dead end — a stream dying mid-broadcast
|
|
reads as broken, and YouTube streams routinely run 2-4 hours), soft-limit nagging, freemium tiers,
|
|
and donation-only (relies on the kindness of strangers). Resolution/quality ceilings are **deferred** —
|
|
that decision belongs to the resolution & streaming-constraints conversation, not monetization.
|
|
|
|
## Auth gates Go Live, but not exploration
|
|
|
|
The app is fully usable without authentication: users can build scenes, add sources, compose
|
|
previews, and audition the software with zero commitment. But **going live requires authentication** —
|
|
it's the one capability gated behind YouTube sign-in. The sign-in button should never pressure the
|
|
user ("sign in (optional)", not a modal wall), but "Go Live" only appears once connected.
|
|
|
|
## Account assumption (do not build an account setup flow)
|
|
|
|
Connecting uses Google OAuth ("Sign in with Google") to link an **existing** YouTube creator
|
|
account. ytLlive **never creates or sets up accounts** — that is YouTube's job. If the creator has no
|
|
YouTube channel, they go to YouTube first. This assumption is explicit and must never be silently
|
|
replaced by an in-app account-creation step. Zero state = a Connect button that starts OAuth; going
|
|
live is unreachable until the account is connected.
|
|
|
|
## YouTube Live API — design constraints (do not violate)
|
|
|
|
These are the hard facts behind every decision. Full list in `TASKS.md`.
|
|
|
|
- **One-click go-live** — never call `transition(live)`. Insert the broadcast with
|
|
`enableAutoStart=true`, `enableAutoStop=true`, `enableMonitorStream=false`,
|
|
`selfDeclaredMadeForKids=false`, `latencyPreference=low`. The encoder starting brings YouTube live.
|
|
`enableMonitorStream=false` is what lets us skip the testing stage.
|
|
- **Variable reusable stream** — `liveStreams.insert` once per channel with
|
|
`cdn.resolution=variable`, `cdn.frameRate=variable`, `isReusable=true`; cache the ingestion URL +
|
|
stream name and reuse for every broadcast. Any quality tier works without recreating the stream,
|
|
and auto step-down is done by us dropping bitrate on the fly (zero API calls).
|
|
- **Quality is greyed out while live** — resolution/frameRate/ingestionType are immutable after
|
|
stream creation; editing title/description/privacy is fine at any time.
|
|
- **Report-by-exception health** — poll `liveStreams.list`; render nothing on `good`/`ok`, surface a
|
|
banner only on `configurationIssues[]` with `warning`/`error` severity. Bottom strip = YouTube logo
|
|
+ green/red connection dot (clickable → opens the dialog).
|
|
- **One dialog, three states** — not connected / connected-offline (all editable) / live
|
|
(title + description + visibility editable; quality + account greyed out). Both entry points
|
|
(Start Stream button + bottom strip) open it; prefilled from saved session profile.
|
|
- **End stream** — stop encoder → `transition(complete)`, `enableAutoStop` as the safety net.
|
|
- **Encoder compliance** — keyframes ≤ 4s (gopSizeLong), closed GOP, H.264, AAC/MP3 @ 44.1/48kHz,
|
|
mono/stereo only. YouTube flags violations via health status.
|
|
- **Broadcast ID == Video ID** — one ID tracks status, health, and the auto-created VOD
|
|
(`recordFromStart` + `enableDvr` default true).
|