refactor(plex): convert service-layer db.query() to 2.0 select() (wave 1)

Mechanical 1.x->2.0 conversion of the self-contained plex modules, behaviour-
equivalent (the characterization net + existing plex tests stay green, 39
passed): app/plex/{sync(15),watch_sync(9),people(1),imgcache(4)} and
app/routes/plex/{admin(4),playback(8),_shared(1)}. Query builders become
select(); .first()/.all()->execute().scalars(); a bulk .update() ->
execute(update()); a single-column .first() -> db.scalar().

browse.py (61) + rollup.agg_subquery (the atomic unit with unified_library)
remain for wave 2.
This commit is contained in:
2026-08-16 05:52:40 +02:00
parent ecbf5737b6
commit 59d50615c4
7 changed files with 174 additions and 67 deletions
+34 -11
View File
@@ -16,6 +16,7 @@ from fastapi import (
)
from fastapi.responses import FileResponse
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.auth import current_user, resolved_user_id
@@ -207,7 +208,11 @@ def _push_watch(
return
# Capture the row's freshest timestamp NOW so the background push can tell if a newer local edit
# lands before it settles synced_to_plex (which would otherwise strand that newer value).
st = db.query(PlexState).filter_by(user_id=user_id, item_id=item_id).first()
st = db.execute(
select(PlexState).where(
PlexState.user_id == user_id, PlexState.item_id == item_id
)
).scalars().first()
background.add_task(
plex_watch.push_state_to_plex,
user_id,
@@ -246,7 +251,11 @@ def item_progress(
duration = int(body.duration_seconds)
final = body.final
now = datetime.now(timezone.utc)
st = db.query(PlexState).filter_by(user_id=user.id, item_id=it.id).first()
st = db.execute(
select(PlexState).where(
PlexState.user_id == user.id, PlexState.item_id == it.id
)
).scalars().first()
was_watched = st is not None and st.status == "watched"
near_end = duration > 0 and position > duration - _FINISH_MARGIN_S
@@ -316,7 +325,11 @@ def item_state(
status = body.status
if status not in ("new", "watched", "hidden"):
raise HTTPException(status_code=400, detail="Invalid status")
st = db.query(PlexState).filter_by(user_id=user.id, item_id=it.id).first()
st = db.execute(
select(PlexState).where(
PlexState.user_id == user.id, PlexState.item_id == it.id
)
).scalars().first()
prev_status = st.status if st is not None else "new"
if status == "new":
if st is not None:
@@ -351,10 +364,12 @@ def _bulk_state(
now = datetime.now(timezone.utc)
existing = {
s.item_id: s
for s in db.query(PlexState).filter(
PlexState.user_id == user_id,
PlexState.item_id.in_([e.id for e in eps] or [0]),
)
for s in db.execute(
select(PlexState).where(
PlexState.user_id == user_id,
PlexState.item_id.in_([e.id for e in eps] or [0]),
)
).scalars()
}
pushable = plex_watch.link_for_push(db, user_id) is not None
to_push: list[tuple[int, str, str]] = []
@@ -397,11 +412,15 @@ def show_state(
db: Session = Depends(get_db),
) -> dict:
"""Mark a whole show watched/unwatched (all its episodes) for the current user + push to Plex."""
sh = db.query(PlexShow).filter_by(rating_key=str(rating_key)).first()
sh = db.execute(
select(PlexShow).where(PlexShow.rating_key == str(rating_key))
).scalars().first()
if sh is None:
raise HTTPException(status_code=404, detail="Unknown Plex show")
watched = body.watched
eps = db.query(PlexItem).filter_by(show_id=sh.id, kind="episode").all()
eps = db.execute(
select(PlexItem).where(PlexItem.show_id == sh.id, PlexItem.kind == "episode")
).scalars().all()
changed = _bulk_state(background, db, user.id, eps, watched)
return {"changed": changed, "watched": watched}
@@ -415,11 +434,15 @@ def season_state(
db: Session = Depends(get_db),
) -> dict:
"""Mark a whole season watched/unwatched (all its episodes) for the current user + push to Plex."""
se = db.query(PlexSeason).filter_by(rating_key=str(rating_key)).first()
se = db.execute(
select(PlexSeason).where(PlexSeason.rating_key == str(rating_key))
).scalars().first()
if se is None:
raise HTTPException(status_code=404, detail="Unknown Plex season")
watched = body.watched
eps = db.query(PlexItem).filter_by(season_id=se.id, kind="episode").all()
eps = db.execute(
select(PlexItem).where(PlexItem.season_id == se.id, PlexItem.kind == "episode")
).scalars().all()
changed = _bulk_state(background, db, user.id, eps, watched)
return {"changed": changed, "watched": watched}