Deep-review round 3 of bug hunt 2026-09. - `failed` is now final only for a track that can never be text (bitmap, no item/track/file). A Plex hiccup or a stretch ffmpeg could not read answers `unavailable`: said, polled again every 5 s, and the <track> stays mounted when cues exist elsewhere (gen > 0). Re-picking the selected track asks the server again at once. - Within LOOKAHEAD_S of a covered stretch's end the next window is read ahead, instead of only once the playhead has fallen off it (subtitles dropped out at every edge while the full pass was backed off or slower than a window lasts). - Windows and full passes run on separate pools with separate in-flight caps, so a window never queues behind (or is refused by) minute-long full passes — the pool comment promised that and nothing enforced it. - A pass that times out is TERMinated and keeps what it reached (published as partial) instead of being killed and discarded. - Lapsed failure memos are swept on every write (they were only pruned when the same key was read again, so polling fresh keys grew the process without bound). - `publish` no longer merges into a sidecar with no VTT behind it, or claims a region whose VTT write failed, or overwrites an unreadable track with one window. - A NUL in a rating_key names no row (it was a DataError, a 500, on every Plex route) and no longer 500s the status poll. Tests: 16 new, each mutation-checked (18 mutations, all red on the test that names them).
21 lines
885 B
Python
21 lines
885 B
Python
"""Small shared Plex row lookups, so the get-by-rating_key pattern lives in one place."""
|
|
|
|
from typing import TypeVar
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
_M = TypeVar("_M")
|
|
|
|
|
|
def by_rating_key(db: Session, model: type[_M], rating_key: str) -> _M | None:
|
|
"""The mirrored row for a Plex rating_key, or None. `model` is any of the rating_key-keyed Plex
|
|
tables (PlexItem / PlexShow / PlexSeason / PlexCollection); the return type is that model, so call
|
|
sites keep their `Model | None` static type.
|
|
|
|
A key with a NUL in it (`%00` in a path segment) names no row — and Postgres refuses NUL in text,
|
|
so asking would be a DataError, i.e. a 500, from every route that looks an item up."""
|
|
if "\x00" in str(rating_key):
|
|
return None
|
|
return db.execute(select(model).where(model.rating_key == str(rating_key))).scalars().first()
|