feat(reader): the channels a Takeout history shows watched but not followed
The import asks YouTube how long the watched videos of the candidate channels are (five or more watched, none the user follows, blocked, put out or owns), keeps the length on the Takeout row and names the channels that make the list: five videos of five minutes or more. The shorts job asks what an import could not. A channel's counted videos come in as an exploration's when its list is opened, ready to play; a channel can be put out of the list and back.
This commit is contained in:
@@ -109,6 +109,21 @@ def apply_channel_details(db: Session, items: list[dict], asked: Iterable[str])
|
||||
channel.details_synced_at = now
|
||||
|
||||
|
||||
def describe_channels(db: Session, yt: YouTubeClient, channel_ids: list[str]) -> None:
|
||||
"""Make sure each given channel is in the catalog with its details: a channel it lacks is added
|
||||
as a stub, then all of them are asked for (`channels.list`, 1 unit a 50). Flushes; the caller
|
||||
commits."""
|
||||
if not channel_ids:
|
||||
return
|
||||
have = set(db.execute(select(Channel.id).where(Channel.id.in_(channel_ids))).scalars())
|
||||
for cid in dict.fromkeys(channel_ids):
|
||||
if cid not in have:
|
||||
db.add(Channel(id=cid))
|
||||
db.flush()
|
||||
apply_channel_details(db, yt.get_channels(channel_ids), channel_ids)
|
||||
db.flush()
|
||||
|
||||
|
||||
def should_prune(fetched_count: int, existing_count: int) -> bool:
|
||||
"""Whether an import may delete the local subscriptions missing from this fetch.
|
||||
|
||||
|
||||
@@ -420,11 +420,15 @@ def run_shorts_classification(db: Session, limit: int | None = None) -> dict:
|
||||
return {"probed": probed, "shorts": shorts}
|
||||
|
||||
|
||||
def ensure_videos(db: Session, yt: YouTubeClient, video_ids: list[str]) -> None:
|
||||
def ensure_videos(
|
||||
db: Session, yt: YouTubeClient, video_ids: list[str], *, explore: bool = False
|
||||
) -> None:
|
||||
"""Make sure every given video id exists in the catalog, fetching + ingesting the
|
||||
missing ones (and a stub channel for any unknown channel). Deleted/private videos that
|
||||
the API won't return are simply left out. The catalog's write for a playlist sync that names
|
||||
videos it has never seen; commits."""
|
||||
videos it has never seen; commits. `explore` adds the new ones as an exploration's
|
||||
(`via_explore`): visible to who explores or follows their channel, and reclaimed by the cleanup
|
||||
once nobody keeps them."""
|
||||
if not video_ids:
|
||||
return
|
||||
have = set(db.execute(select(Video.id).where(Video.id.in_(video_ids))).scalars().all())
|
||||
@@ -458,7 +462,12 @@ def ensure_videos(db: Session, yt: YouTubeClient, video_ids: list[str]) -> None:
|
||||
seen.add(vid)
|
||||
# published_at is NOT NULL (0068); apply_video_details re-fills a missing date, but seed a now()
|
||||
# fallback here too so the constructor never carries an explicit NULL.
|
||||
v = Video(id=vid, channel_id=cid, published_at=parse_dt(sn.get("publishedAt")) or now)
|
||||
v = Video(
|
||||
id=vid,
|
||||
channel_id=cid,
|
||||
published_at=parse_dt(sn.get("publishedAt")) or now,
|
||||
via_explore=explore,
|
||||
)
|
||||
apply_video_details(v, it)
|
||||
v.enriched_at = now
|
||||
db.add(v)
|
||||
|
||||
Reference in New Issue
Block a user