refactor: R12 S3 — invert scheduler->routes, publish cross-module privates

Layering cleanup, no behaviour change (ruff clean backend+scripts, pytest 193):

- Invert the scheduler->routes dependency: reset_demo_state (+ its demo-seed
  helpers) move from routes/admin.py to a new service module app/demo.py.
  Both routes/admin.py and scheduler.py now import it from app.demo, so the
  scheduler no longer reaches into the routes layer — its lazy import (the
  routes<->scheduler cycle workaround) is gone, hoisted to a top-level import.

- Publish the private symbols that were imported across module boundaries (the
  underscore convention was lying): feed._serialize -> serialize,
  feed._to_tsquery_str -> to_tsquery_str, videos._insert_stubs -> insert_stubs,
  videos._stub_from_playlist_item -> stub_from_playlist_item,
  worker._uploader_url -> uploader_url, plex._shared._item_or_404 ->
  item_or_404. Scoped renames — the 5 other route modules keep their own truly
  private _serialize helpers.

- _release_asset moves from routes/downloads.py to app/downloads/service.py as
  the public release_asset (asset bookkeeping is a service concern); the routes
  call it via service.release_asset.
This commit is contained in:
2026-08-11 22:58:10 +02:00
parent ac6383e92c
commit 51463b226e
16 changed files with 153 additions and 148 deletions
+3 -3
View File
@@ -23,7 +23,7 @@ from app.models import (
Video,
VideoState,
)
from app.sync.videos import _insert_stubs, _stub_from_playlist_item, apply_video_details
from app.sync.videos import insert_stubs, stub_from_playlist_item, apply_video_details
from app.utils import now_utc as _now
from app.youtube.client import YouTubeClient
@@ -71,11 +71,11 @@ def explore_ingest_page(
data = yt.get_playlist_items(channel.uploads_playlist_id, page_token)
stubs: list[dict] = []
for item in data.get("items", []):
stub = _stub_from_playlist_item(item, channel.id)
stub = stub_from_playlist_item(item, channel.id)
if stub is not None:
stub["via_explore"] = True
stubs.append(stub)
inserted = _insert_stubs(db, stubs)
inserted = insert_stubs(db, stubs)
self_enriched = _enrich_new(db, yt, [s["id"] for s in stubs])
log.info(
"explore ingest channel=%s ingested=%s enriched=%s", channel.id, inserted, self_enriched
+7 -7
View File
@@ -44,7 +44,7 @@ def parse_dt(value: str | None) -> datetime | None:
return None
def _insert_stubs(db: Session, rows: list[dict]) -> int:
def insert_stubs(db: Session, rows: list[dict]) -> int:
"""Idempotently insert video stubs; returns the number of newly inserted rows.
Uses RETURNING so the count is exact (ON CONFLICT DO NOTHING leaves rowcount
@@ -76,7 +76,7 @@ def apply_rss_feed(db: Session, channel: Channel, entries: list[dict]) -> int:
"""Insert new video stubs from an already-fetched feed and stamp the channel's last RSS
time. DB-only — must run on the session's own thread; the network fetch
(fetch_channel_feed) is kept separate so callers like run_rss_poll can parallelise it."""
inserted = _insert_stubs(db, entries)
inserted = insert_stubs(db, entries)
channel.last_rss_at = _now()
db.add(channel)
db.commit()
@@ -84,7 +84,7 @@ def apply_rss_feed(db: Session, channel: Channel, entries: list[dict]) -> int:
# --- Backfill (uploads playlist; costs quota) ---
def _stub_from_playlist_item(item: dict, channel_id: str) -> dict | None:
def stub_from_playlist_item(item: dict, channel_id: str) -> dict | None:
content = item.get("contentDetails", {})
snippet = item.get("snippet", {})
video_id = content.get("videoId")
@@ -120,7 +120,7 @@ def backfill_channel_recent(db: Session, yt: YouTubeClient, channel: Channel) ->
data = yt.get_playlist_items(channel.uploads_playlist_id, page_token)
stopped_mid_page = False
for item in data.get("items", []):
stub = _stub_from_playlist_item(item, channel.id)
stub = stub_from_playlist_item(item, channel.id)
if stub is None:
continue
if stub["published_at"] and stub["published_at"] < cutoff:
@@ -147,7 +147,7 @@ def backfill_channel_recent(db: Session, yt: YouTubeClient, channel: Channel) ->
channel.recent_synced_at = _now()
channel.backfill_cursor = next_cursor
channel.backfill_done = done
inserted = _insert_stubs(db, collected)
inserted = insert_stubs(db, collected)
db.add(channel)
db.commit()
return inserted
@@ -166,11 +166,11 @@ def backfill_channel_deep(
rows = [
s
for s in (
_stub_from_playlist_item(it, channel.id) for it in data.get("items", [])
stub_from_playlist_item(it, channel.id) for it in data.get("items", [])
)
if s is not None
]
total += _insert_stubs(db, rows)
total += insert_stubs(db, rows)
page_token = data.get("nextPageToken")
channel.backfill_cursor = page_token
if not page_token: