Merge fix/live-reenrich: re-enrich transient live videos until they settle
This commit is contained in:
commit
e81a494436
2 changed files with 52 additions and 1 deletions
|
|
@ -21,6 +21,7 @@ from app.sync.videos import (
|
||||||
enrich_pending,
|
enrich_pending,
|
||||||
poll_rss_channel,
|
poll_rss_channel,
|
||||||
reconcile_full_history,
|
reconcile_full_history,
|
||||||
|
refresh_live,
|
||||||
run_shorts_classification,
|
run_shorts_classification,
|
||||||
)
|
)
|
||||||
from app.youtube.client import YouTubeClient
|
from app.youtube.client import YouTubeClient
|
||||||
|
|
@ -65,6 +66,10 @@ def run_enrich(db: Session, max_batches: int = 200, floor: int = 200) -> int:
|
||||||
total += n
|
total += n
|
||||||
if n == 0:
|
if n == 0:
|
||||||
break
|
break
|
||||||
|
# Revisit transient live/upcoming videos (enrich_pending is one-shot) so an ended
|
||||||
|
# stream gains its final duration + was_live status instead of staying "live" forever.
|
||||||
|
if quota.remaining_today(db) > floor:
|
||||||
|
total += refresh_live(db, yt)
|
||||||
return total
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import re
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
from sqlalchemy import func, or_, select, update
|
from sqlalchemy import and_, func, or_, select, update
|
||||||
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
|
@ -260,6 +260,52 @@ def enrich_pending(db: Session, yt: YouTubeClient, limit: int | None = None) ->
|
||||||
return enriched
|
return enriched
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_live(db: Session, yt: YouTubeClient, limit: int | None = None) -> int:
|
||||||
|
"""Re-fetch videos in a transient live state so they pick up their final shape.
|
||||||
|
|
||||||
|
`enrich_pending` is one-shot (it only touches enriched_at IS NULL), but a live broadcast
|
||||||
|
is not a final state: a stream ends and becomes a normal VOD — gaining a real duration
|
||||||
|
and flipping live_status to was_live — and an upcoming premiere eventually goes live then
|
||||||
|
ends. So we revisit anything still live/upcoming, plus a just-ended stream that hasn't got
|
||||||
|
its duration yet (YouTube can lag a bit after the broadcast), until it settles. Cheap:
|
||||||
|
videos.list is 1 unit per 50 ids, and the live/upcoming set is normally tiny."""
|
||||||
|
limit = limit or settings.enrich_batch_size
|
||||||
|
videos = (
|
||||||
|
db.execute(
|
||||||
|
select(Video)
|
||||||
|
.where(
|
||||||
|
or_(
|
||||||
|
Video.live_status.in_(("live", "upcoming")),
|
||||||
|
and_(
|
||||||
|
Video.live_status == "was_live",
|
||||||
|
Video.duration_seconds.is_(None),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(limit)
|
||||||
|
)
|
||||||
|
.scalars()
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
if not videos:
|
||||||
|
return 0
|
||||||
|
items = {it["id"]: it for it in yt.get_videos([v.id for v in videos])}
|
||||||
|
now = _now()
|
||||||
|
updated = 0
|
||||||
|
for video in videos:
|
||||||
|
item = items.get(video.id)
|
||||||
|
if item is None:
|
||||||
|
# The broadcast vanished (deleted/private). Stop treating it as live so it
|
||||||
|
# doesn't get refreshed forever; it just becomes an ordinary (dead) row.
|
||||||
|
video.live_status = "none"
|
||||||
|
continue
|
||||||
|
apply_video_details(video, item)
|
||||||
|
video.enriched_at = now
|
||||||
|
updated += 1
|
||||||
|
db.commit()
|
||||||
|
return updated
|
||||||
|
|
||||||
|
|
||||||
def run_shorts_classification(db: Session, limit: int | None = None) -> dict:
|
def run_shorts_classification(db: Session, limit: int | None = None) -> dict:
|
||||||
"""Finalize Shorts classification: cheaply mark non-candidates, then probe the
|
"""Finalize Shorts classification: cheaply mark non-candidates, then probe the
|
||||||
youtube.com/shorts URL for short-enough, non-live, enriched videos."""
|
youtube.com/shorts URL for short-enough, non-live, enriched videos."""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue