feat(ypp): YPP journey tracker slice 1 — the YPP pull-out below Stream Settings (TASK 39, Good Dog ONE test)

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.
This commit is contained in:
2026-09-22 08:07:54 -07:00
parent 4e0915a36e
commit a9d5f29084
16 changed files with 1103 additions and 117 deletions
+17 -1
View File
@@ -109,6 +109,22 @@ public partial class LayoutStore : IDisposable
Value TEXT NOT NULL
);
""",
// v9 → v10 (TASK 39): YPP journey-tracker snapshots, appended on every
// stats refresh. The history is the raw data a later analytics slice
// reads to compute velocity/(de)acceleration/ETA.
"""
CREATE TABLE IF NOT EXISTS YppSnapshot (
CapturedAtUtc TEXT NOT NULL PRIMARY KEY,
SubscriberCount INTEGER NOT NULL,
VideoCount INTEGER NOT NULL,
TotalViews INTEGER NOT NULL,
UploadsLast90Days INTEGER NOT NULL,
CommunityGuidelinesGoodStanding INTEGER NOT NULL,
CopyrightStrikesGoodStanding INTEGER NOT NULL,
ContentIdClaimsGoodStanding INTEGER NOT NULL,
OverallGoodStanding INTEGER NOT NULL
);
""",
};
foreach (var sql in statements)
{
@@ -126,7 +142,7 @@ public partial class LayoutStore : IDisposable
MigrateToV3();
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "PRAGMA user_version = 9;";
cmd.CommandText = "PRAGMA user_version = 10;";
cmd.ExecuteNonQuery();
}
}