fix(catalog): a follow from YouTube keeps an explored channel, and the list counts only what can come
A subscription pulled in from YouTube left an explored channel flagged as an exploration's, so its later videos came in hidden from the Library with their watched marks waiting for good; a subscribe landing while an exploration's videos came in left those flagged too. One step, settle_explored, now holds the rule by who follows the channel now: the subscribe here, the subscription import, the watched-channel videos and the explore page all end with it. Migration 0116 clears what the gap left behind. The list's "may still grow" count no longer counts a channel it already shows from a stub row, and the comments on the cleanup's keep rule and on the in-memory daily shares say what they do.
This commit is contained in:
@@ -12,6 +12,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.catalog import queries, search, service
|
||||
from app.db import SessionLocal
|
||||
from app.catalog.models import (
|
||||
BlockedChannel,
|
||||
Channel,
|
||||
@@ -23,6 +24,7 @@ from app.catalog.models import (
|
||||
from app.reader import queries as reader_queries
|
||||
from app.reader import service as reader_service
|
||||
from app.sync import explore as sync_explore
|
||||
from app.sync import subscriptions as sync_subscriptions
|
||||
|
||||
|
||||
def _channel(db, cid="UCc", **kw):
|
||||
@@ -166,6 +168,65 @@ def test_exploring_a_channel_someone_follows_brings_its_uploads_in_as_the_catalo
|
||||
assert (ch.from_explore, db.get(Video, "v1").via_explore) == (False, False)
|
||||
|
||||
|
||||
def test_a_follow_pulled_in_from_youtube_makes_an_explored_channel_the_catalogs_own(
|
||||
db, make_user, monkeypatch
|
||||
):
|
||||
explorer, follower = make_user(), make_user()
|
||||
ch = _channel(db, details_synced_at=datetime.now(timezone.utc))
|
||||
service.explore(db, explorer, ch)
|
||||
db.commit()
|
||||
_video(db, "v1", via_explore=True)
|
||||
|
||||
class _Yt:
|
||||
def __init__(self, session, user):
|
||||
pass
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
return False
|
||||
|
||||
def iter_subscriptions(self):
|
||||
yield {"channel_id": "UCc", "title": "UCc", "yt_subscription_id": "sub-UCc"}
|
||||
|
||||
def get_channels(self, ids):
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(sync_subscriptions, "YouTubeClient", _Yt)
|
||||
sync_subscriptions.import_subscriptions(db, follower)
|
||||
db.commit()
|
||||
db.expire_all()
|
||||
# Followed on youtube.com is followed all the same: left flagged, the channel's later videos
|
||||
# came in as an exploration's, hidden from the Library and their watched marks waiting for good.
|
||||
assert (db.get(Channel, "UCc").from_explore, db.get(Video, "v1").via_explore) == (False, False)
|
||||
|
||||
|
||||
def test_a_follow_landing_while_an_explored_page_comes_in_leaves_none_of_it_flagged(
|
||||
db, make_user, monkeypatch
|
||||
):
|
||||
explorer, follower = make_user(), make_user()
|
||||
ch = _channel(db, uploads_playlist_id="UUc")
|
||||
service.explore(db, explorer, ch)
|
||||
db.commit()
|
||||
|
||||
class _Yt:
|
||||
def get_playlist_items(self, playlist_id, page_token):
|
||||
# Someone subscribes, in a request of their own, while YouTube answers this one.
|
||||
with SessionLocal() as other:
|
||||
service.record_subscription(other, follower, other.get(Channel, "UCc"), "sub-UCc")
|
||||
other.commit()
|
||||
item = {"contentDetails": {"videoId": "v1", "videoPublishedAt": "2026-05-01T00:00:00Z"}}
|
||||
return {"items": [item]}
|
||||
|
||||
monkeypatch.setattr(sync_explore, "_enrich_new", lambda session, yt, ids: 0)
|
||||
sync_explore.explore_ingest_page(db, _Yt(), ch)
|
||||
db.commit()
|
||||
db.expire_all()
|
||||
# The subscribe cleared what was flagged before it; a page that lands after it must not be.
|
||||
assert (db.get(Channel, "UCc").from_explore, db.get(Video, "v1").via_explore) == (False, False)
|
||||
|
||||
|
||||
def test_a_block_is_once_and_ends_the_exploration(db, make_user):
|
||||
user = make_user()
|
||||
ch = _channel(db)
|
||||
|
||||
Reference in New Issue
Block a user