feat(channels): channel-explore backend — about metadata, ephemeral provenance, cleanup

Adds the backend for a dedicated channel page + ephemeral browsing of un-subscribed
channels:
- migration 0033: channels.{total_view_count,published_at,banner_url,external_links,
  from_explore}, videos.via_explore, explored_channels (per-user, grace-clocked).
- enrich channels with part=brandingSettings (banner/links) + statistics.viewCount +
  snippet.publishedAt — no extra quota.
- GET /api/channels/{id}: About detail + this user's relationship; lazy-enriches the new
  About fields (published_at sentinel).
- POST /api/channels/{id}/explore (require_human, quota-reserve guarded): records the
  exploration, flags the channel ephemeral unless followed, ingests one page of uploads
  (via_explore) + enriches; returns next_page_token for load-more.
- feed: per-explorer gate — a via_explore video is visible only to users who explored or
  subscribe to its channel, so exploration never leaks into everyone's catalog.
- subscribe = keep: clears from_explore + via_explore on the channel's videos.
- scheduler explore_cleanup job + explore_grace_days config: hard-delete explored-but-
  unkept channels and their untouched ephemeral videos after a grace period.
This commit is contained in:
npeter83 2026-06-30 02:52:44 +02:00
parent bb7b9972fd
commit bc4c362423
11 changed files with 481 additions and 4 deletions

View file

@ -118,6 +118,12 @@ class Settings(BaseSettings):
# items). Deleted/private/paywalled get a recovery window; abandoned upcoming and
# confirmed-dead stuck-live have no age grace (deleted on the sweep that confirms them).
maintenance_grace_days: int = 7
# --- Channel-explore cleanup job ---
# Runs daily by default (admin-tunable via the Scheduler dashboard). An explored-but-never-
# kept channel (no subscription, no per-user state on its videos) is hard-deleted this many
# days after it was last explored — the grace clock is ExploredChannel.created_at.
explore_cleanup_minutes: int = 1440
explore_grace_days: int = 7
# live_status values hidden from the feed by default. Completed-stream VODs
# ("was_live") are real watchable content and stay visible.
feed_default_hidden_live: str = "live,upcoming"