Current-OAuth-scope data, no re-consent: subscriber bars for both tiers (500 fan-funding / 1,000 ad-revenue; the 2027-02-01 doubling is versioned date-aware data in YppThresholds), 3-uploads/90d, live standing from channels.list auditDetails, self-reported 2FA/AdSense checkboxes + deep links (Google 2-Step / youtube.com/earn), and the "what counts" education. Every refresh appends a YppSnapshot (SQLite v10) so the analytics slice has history from day one. - ChannelStatsService (channels.list statistics,auditDetails,contentDetails + playlistItems.list, virtual CaptureCurrentAsync = test seam); YppTrackerViewModel mirrors LiveBroadcastFormViewModel. - MainViewModel.Ypp.cs: Ypp property, ChannelStatsFactoryOverride seam, one-open-at-a-time drawer exclusivity (both directions); account sign-in/out/restore hooks in Account.cs. - PreviewPane.xaml: TextPullOutHost is now a drawer bank [broadcast][ypp][stacked white tabs]; outside-click collapse covers both drawers. MainWindow.xaml.cs updated. - Wait - watchers: the whole feature already reviewed against YouTube's own docs (YPP Earn tab + API availability) before building — see TASKS/task-39-ypp-journey-tracker.md. - ONE integration test: YppPullOutTests (snapshot capture/persist + checklist round-trip + drawer exclusivity). 313/313 pass, 0 warnings. - Memory in same commit: TASKS.md row, ai.md Journey-tracker bullet (roadmap→reality), HANDOFF.md. Slice 2 (Analytics yt-analytics.readonly re-consent → watch-hours/Shorts + velocity/ETA + sparkline) is queued behind this, not merged.
5.5 KiB
TASK 39 — YPP journey tracker (slice 1: current-scope data)
Status: ✅ Slice 1 Done — shipped 2026-09-22 (Good Dog: ONE integration test)
Idea (creator ruling 2026-09-21, feasibility signed off 2026-09-22)
Track the connected creator's YouTube Partner Program progress inside the app: a "YPP" tab on the Live screen's right edge, just below Stream Settings, sliding out the same way. Everything the creator needs about YPP eligibility and their position ships in-app — no leaving the app for definitions or numbers. The user also holds the YPP ruleset (studio "Earn" tab): 500 subs / 3 valid public uploads-90d for fan funding; 1,000 subs for full ad / Premium revenue; valid public long-form watch hours over trailing 12 months; valid public Shorts views (Shorts-feed origin) over 90 days; 0 active community-guidelines strikes; 2FA on; linked AdSense; long-form bar doubles to 8,000h on 2027-02-01 (see ai.md "Journey tracker").
Slice split (user chose slice-1-then-slice-2): slice 1 uses ONLY the current OAuth scopes
— no re-consent; slice 2 adds the Analytics API (yt-analytics.readonly, one-time Google
re-consent) behind the reserved IChannelStatsProvider seam for watch-hours/Shorts-views +
velocity/ETA. Slice 1 still writes SQLite snapshots on every refresh, so slice 2 has history
from day one.
Honest limits (told to the user before building)
- No public API exposes the Earn-tab counters or the "valid public" filtering. Numbers are API-derived and labeled close-to-but-not-identical to Studio.
- 2FA and AdSense linkage are not readable from ANY YouTube API → in-app self-reported
checkboxes with deep links (Google's 2-Step page / the Earn page). Standing IS readable from
channels.list auditDetails(overall + the three sub-flags) — live, no re-consent. subscriberCountis rounded to 3 significant figures by Google.
What shipped
Models/YppThresholds.cs— thresholds as versioned date-aware data (never constants, per ai.md): Tier1 (500 / 3,000h / 3M Shorts),Tier2For(when)returns 4,000h/10M before 2027-02-01 and 8,000h/20M from then;UploadsRequiredPer90Days = 3.Models/YppStatSnapshot.cs+Services/LayoutStore.Ypp.cs+ schema v9 → v10 (YppSnapshottable): append-only snapshot history; manual checklist (Ypp.TwoFactorEnabled/Ypp.AdSenseLinked); last-refresh-UTC gate (the once-daily gate a quota-thin analytics slice will need is baked in now).Services/ChannelStatsService.cs—channels.list?mine=true&part=statistics,auditDetails, contentDetails(subs/views/videos + uploads-playlist id + standing flags) thenplaylistItems.list(recent 50) counts uploads in the trailing 90 days. VirtualCaptureCurrentAsync= the test seam. No new scope.Services/YppTrackerViewModel.cs— drawer state +Toggle/CloseDrawerCommand+RefreshCommand(mirrorsLiveBroadcastFormViewModel), plus: signed-out/loading/error states, tier progress bars (subs Tier-1 & Tier-2, uploads), live standing checkbox + detail, manual 2FA/AdSense checkboxes,WhatCountsTexteducation, deep links,EnsureLoadedAsync(once/day gate) + explicit refresh,OnAccountChanged()hook.ViewModels/MainViewModel.Ypp.cs+ ctor —Yppproperty,ChannelStatsFactoryOverridetest seam (mirrorsCameraEnumeratorOverride), and one-open-at-a-time exclusivity wired both directions (opening YPP closes Stream Settings and vice-versa). Sign-in/sign-out/restored-session hooks inMainViewModel.Account.cs.- UI (
Controls/PreviewPane.xaml) —TextPullOutHostis now one drawer bank: the broadcast drawer, then the YPP drawer, then a stacked white-tab column (Stream Settings above YPP). Same animated-width pattern; only one open at a time.MainWindow.xaml.csoutside-click collapse covers both drawers.IsClickInsideDrawer(PreviewPane.cs) unchanged — covers the rail. - ONE integration test —
ytLive.Tests/YppPullOutTests.cs(RealApp + temp DB): (1) connected VM captures a fake snapshot → progress props, snapshot persisted, checklist round-trip, drawer toggles; (2) real MainWindow enforces drawer exclusivity both ways. - Slice-2 placeholder rows (long-form hours / Shorts views) render a clear "arrives with the extended analytics update" state in the drawer.
Deep links (verified stable endpoints; the exact Studio "Earn" tab path is a slice-2 nicety)
- Earn:
https://www.youtube.com/earn - 2FA:
https://myaccount.google.com/signinoptions/two-step-verification - AdSense:
https://support.google.com/youtube/answer/72851
Slice 2 (outline, queued)
yt-analytics.readonly (+ re-consent) → YouTubeAnalyticsService behind IChannelStatsProvider
(ai.md): long-form estimatedMinutesWatched (12-mo), Shorts-feed views (90-d), then velocity /
(de)acceleration / ETA from the YppSnapshot history + a sparkline. Own Good Dog landing + task
doc; the once-daily refresh gate and history table are already in place.
Acceptance (slice 1)
- YPP white tab appears below Stream Settings on the Live screen right edge.
- Clicking it slides out the YPP drawer; opening one drawer closes the other; outside-click collapses whichever is open.
- Signed out → "Sign in to YouTube…" hint. Signed in → subs (Tier 1 & Tier 2), uploads/90d, live standing, manual 2FA/AdSense, education text, refresh + last-updated.
- Every refresh appends a snapshot to SQLite (history ready for slice 2).
- 0 warnings / 0 errors; 313/313 tests pass (one new Good Dog integration test).