fix(quota): keep the demo account off three quota-burning paths (C-3.6)

The demo account could spend real YouTube quota where the human-only guard was
missing (the channel_detail enrichment already had it):
- channels PATCH: an immediate run_recent_backfill when deep_requested is turned on;
- discovery: the channel-detail enrich on the Discover tab;
- feed get_video_detail: the off-catalog videos.list lookup for a video we don't
  store (the DB-hit path stays free; demo now gets a 404 for unknown videos).
This commit is contained in:
2026-07-21 21:47:45 +02:00
parent 3dfa35f396
commit 0dfc5e9ea2
2 changed files with 15 additions and 2 deletions
+10 -2
View File
@@ -180,7 +180,8 @@ def discover_channels(
# (fresh subscriber counts/thumbnails carry through without a re-query). Only the title can
# change, which affects the tie-break order → re-sort in Python instead of re-hitting the DB.
need = [ch.id for ch, _, _ in rows if ch.details_synced_at is None][:DISCOVERY_ENRICH_CAP]
if need:
# Skip the quota-spending enrich for the demo account (it just sees the stub metadata).
if need and not user.is_demo:
try:
with quota.attribute(user.id, quota.QuotaAction.CHANNELS_DISCOVER), YouTubeClient(db, user) as yt:
apply_channel_details(db, yt.get_channels(need))
@@ -402,7 +403,14 @@ def update_channel(
# Opting a channel into full history should give an immediate feed: if we haven't even
# fetched its recent uploads yet, do that one channel now (cheap) instead of waiting for
# the scheduler. Deep paging still follows on the scheduler's next run (recent-then-deep).
if deep_turned_on and channel is not None and channel.recent_synced_at is None:
# Not for the demo account: it must not be able to trigger an immediate quota-burning backfill
# (the scheduler's deep paths are already human-only). Same guard as channel_detail's enrichment.
if (
deep_turned_on
and channel is not None
and channel.recent_synced_at is None
and not user.is_demo
):
with quota.attribute(user.id, quota.QuotaAction.VIDEOS_BACKFILL_RECENT):
run_recent_backfill(db, [channel], max_channels=1)
+5
View File
@@ -737,6 +737,11 @@ def get_video_detail(
"duration_seconds": v.duration_seconds,
}
# The DB path above is free; the off-catalog YouTube lookup below spends a quota unit, so the
# demo account can't reach it — it just sees "unknown" for a video we don't already store.
if user.is_demo:
raise HTTPException(status_code=404, detail="Unknown video")
try:
with quota.attribute(user.id, quota.QuotaAction.VIDEOS_LOOKUP), YouTubeClient(db, user) as yt:
items = yt.get_videos([video_id])