feat(search): ephemeral results, count selector, blocklist, new-first ordering (backend)
- Live-search results stay ephemeral: the discovery-cleanup job now also reclaims un-kept via_search videos (no watch state / not playlisted / channel not subscribed) after a grace period (search_grace_days), and POST /api/search/clear discards a given result set 'as if never added' (drops the user's search-finds + deletes the now-orphaned, un-kept videos). Admin POST /api/admin/purge-discovery runs it on demand (grace 0). - Count selector: GET /api/search/youtube gains a 'limit' — the free scrape source pages through continuations until that many results are gathered (no manual load-more); the API source stays one ≤50 page (cost). - Per-user channel blocklist (migration 0034 blocked_channels): block/unblock/list endpoints; blocked channels' videos are dropped from live search before ingest and hidden from the feed / Library / explore / channel page; explore refuses a blocked channel. - New-first ordering: results you already have (subscribed channel, watched/in-progress/saved/ playlisted) sink below genuinely-new discoveries, preserving relevance within each group.
This commit is contained in:
parent
77707a3cbe
commit
f27f31b6db
10 changed files with 396 additions and 102 deletions
|
|
@ -435,6 +435,25 @@ class ExploredChannel(Base, TimestampMixin):
|
|||
)
|
||||
|
||||
|
||||
class BlockedChannel(Base, TimestampMixin):
|
||||
"""Per-user channel blocklist. Videos from a blocked channel are excluded from this user's
|
||||
live YouTube search (and not ingested on their behalf) and hidden from their feed / explore
|
||||
views — a personal "never show me this channel again" that doesn't affect other users."""
|
||||
|
||||
__tablename__ = "blocked_channels"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user_id", "channel_id", name="uq_user_blocked_channel"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
user_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("users.id", ondelete="CASCADE"), index=True
|
||||
)
|
||||
channel_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("channels.id", ondelete="CASCADE"), index=True
|
||||
)
|
||||
|
||||
|
||||
class ApiQuotaUsage(Base):
|
||||
"""Tracks YouTube Data API units spent per Pacific-time day (the quota resets at
|
||||
midnight Pacific). The whole app shares one daily budget."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue