Merge: auto-complete full history when stored >= video_count

This commit is contained in:
npeter83 2026-06-14 19:01:04 +02:00
commit 21b9e2710e
2 changed files with 40 additions and 3 deletions

View file

@ -20,6 +20,7 @@ from app.sync.videos import (
backfill_channel_recent,
enrich_pending,
poll_rss_channel,
reconcile_full_history,
run_shorts_classification,
)
from app.youtube.client import YouTubeClient
@ -118,7 +119,12 @@ def run_recent_backfill(
except Exception:
db.rollback()
log.exception("Recent backfill failed for channel %s", channel.id)
return {"channels_processed": processed, "videos_added": videos_added}
reconciled = reconcile_full_history(db)
return {
"channels_processed": processed,
"videos_added": videos_added,
"reconciled_full": reconciled,
}
def run_deep_backfill(db: Session, max_channels: int = 10, max_pages: int = 5) -> dict:
@ -158,7 +164,12 @@ def run_deep_backfill(db: Session, max_channels: int = 10, max_pages: int = 5) -
except Exception:
db.rollback()
log.exception("Deep backfill failed for channel %s", channel.id)
return {"channels_processed": processed, "videos_added": videos_added}
reconciled = reconcile_full_history(db)
return {
"channels_processed": processed,
"videos_added": videos_added,
"reconciled_full": reconciled,
}
def estimate_deep_backfill(db: Session, channels: list[Channel]) -> dict:

View file

@ -5,7 +5,7 @@ import re
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timedelta, timezone
from sqlalchemy import or_, select, update
from sqlalchemy import func, or_, select, update
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.orm import Session
@ -173,6 +173,32 @@ def backfill_channel_deep(
return total
def reconcile_full_history(db: Session) -> int:
"""Mark channels fully backfilled once their stored uploads meet or exceed YouTube's
advertised video_count. The uploads playlist length is normally <= the advertised
count, so 'stored >= video_count' means we already hold the whole history even if
the deep cursor never reached the end (e.g. a small channel that was never
deep-requested, so the demand-driven deep job never ran). Without this such a channel
sits in 'needs full history' forever despite having every video. Idempotent, no API
quota; returns the number of channels flipped."""
stored = (
select(func.count(Video.id))
.where(Video.channel_id == Channel.id)
.scalar_subquery()
)
result = db.execute(
update(Channel)
.where(
Channel.backfill_done.is_(False),
Channel.video_count.is_not(None),
stored >= Channel.video_count,
)
.values(backfill_done=True, backfill_cursor=None)
)
db.commit()
return result.rowcount
# --- Enrichment (videos.list) ---
def _live_status(snippet: dict, live_details: dict | None) -> str:
broadcast = snippet.get("liveBroadcastContent")