refactor(plex): extract card serializers + query builder from browse.py
Second half of the 2026-08-17 review's "Plex query/cards extraction" — slim the 1841-line browse.py god-module ahead of S6 (which reworks the library query's column selection for the HU overlay). - app/plex/cards.py: the pure grid/leaf card serializers (_movie_card, _show_card, _episode_card, _leaf_card). - app/plex/query.py: the library query-shaping layer — field-scope tsvector rechecks (_TS_CONFIG, _FIELD_COLUMNS, _field_recheck) and the shared sidebar metadata filters (_csv, _added_cutoff, _meta_filter_conds, _apply_meta_filters, LibraryFilters, _like_escape). This is the seam S6 will touch. browse.py imports both and keeps its route handlers; the entangled collection/playlist card cluster (_collection_source/_collection_editable) stays put deliberately. All helpers were browse-internal, so no external importers change. Pure code motion — DB-lane suite 287 passed / 2 skipped; ruff clean. browse.py 1841 -> 1667 lines.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""Plex list/leaf card serializers: the small `model -> dict` projections the library grid,
|
||||
playlists, and detail views render. Extracted from routes/plex/browse.py so the row-shaping
|
||||
lives beside the other Plex read-model modules (detail.py, query.py) rather than inside the
|
||||
router. Pure functions — no DB, no request state."""
|
||||
|
||||
from app.models import PlexItem, PlexShow, PlexState
|
||||
|
||||
|
||||
def _movie_card(it: PlexItem, st: PlexState | None) -> dict:
|
||||
return {
|
||||
"id": it.rating_key,
|
||||
"type": "movie",
|
||||
"title": it.title,
|
||||
"year": it.year,
|
||||
"duration_seconds": it.duration_s,
|
||||
"thumb": f"/api/plex/image/{it.rating_key}",
|
||||
"playable": it.playable,
|
||||
"status": st.status if st else "new",
|
||||
"position_seconds": st.position_seconds if st else 0,
|
||||
}
|
||||
|
||||
|
||||
def _show_card(sh: PlexShow, status: str = "new") -> dict:
|
||||
return {
|
||||
"id": sh.rating_key,
|
||||
"type": "show",
|
||||
"title": sh.title,
|
||||
"year": sh.year,
|
||||
"thumb": f"/api/plex/image/{sh.rating_key}",
|
||||
"season_count": sh.child_count,
|
||||
# Aggregate watch-state across the show's episodes (new | in_progress | watched) → grid badge.
|
||||
"status": status,
|
||||
}
|
||||
|
||||
|
||||
def _episode_card(e: PlexItem, st: PlexState | None) -> dict:
|
||||
return {
|
||||
"id": e.rating_key,
|
||||
"type": "episode",
|
||||
"title": e.title,
|
||||
"summary": e.summary,
|
||||
"season_number": e.season_number,
|
||||
"episode_number": e.episode_number,
|
||||
"duration_seconds": e.duration_s,
|
||||
"thumb": f"/api/plex/image/{e.rating_key}",
|
||||
"playable": e.playable,
|
||||
"status": st.status if st else "new",
|
||||
"position_seconds": st.position_seconds if st else 0,
|
||||
}
|
||||
|
||||
|
||||
def _leaf_card(
|
||||
it: PlexItem,
|
||||
st: PlexState | None,
|
||||
show_title: str | None = None,
|
||||
show_rk: str | None = None,
|
||||
) -> dict:
|
||||
"""A playable-leaf card (movie or episode). Used by playlists, which can mix both kinds.
|
||||
`show_rk` (the show's rating_key) lets the client group a playlist's episodes by show/season."""
|
||||
if it.kind == "episode":
|
||||
card = _episode_card(it, st)
|
||||
card["show_title"] = show_title
|
||||
card["show_id"] = show_rk
|
||||
return card
|
||||
return _movie_card(it, st)
|
||||
Reference in New Issue
Block a user