feat(plex): TV-show metadata sync + series filters (Phase 1 of series view)
Give TV shows the same filterable metadata as movies so the TV grid can be filtered/sorted, not just library+sort. Backend: migration 0052 adds rating/content_rating/studio/originally_available_at/genres/directors/cast_names/ people_text to plex_shows (+ GIN/indexes, people_text folded into search_vector); _sync_shows populates them cheaply from the show section listing (no per-item calls). /browse show-branch gains the movie filter set (minus duration) plus an aggregate per-user watch-state (a show rolls up its episodes: all watched=watched, any progress=in_progress, none=new) with year/rating/release sorts; /facets returns show facets. Frontend: PlexSidebar renders the metadata filters + watch-state for TV libraries (duration stays movie-only); show cards show a watched/in-progress badge. i18n plex.inProgress (en/hu/de). Needs a Plex re-sync to populate the new columns.
This commit is contained in:
parent
f0bab315ad
commit
569d31235d
9 changed files with 266 additions and 52 deletions
|
|
@ -956,9 +956,17 @@ class PlexLibrary(Base, TimestampMixin, UpdatedAtMixin):
|
|||
|
||||
|
||||
class PlexShow(Base, TimestampMixin, UpdatedAtMixin):
|
||||
"""A TV show (grandparent of episodes) — holds poster + summary for the drill-down page."""
|
||||
"""A TV show (grandparent of episodes) — holds poster + summary for the drill-down page, plus
|
||||
the same filterable metadata as movies (genres/rating/content_rating/studio/people) so the TV
|
||||
grid can be filtered/sorted like the movie grid. All mirrored cheaply from the show listing."""
|
||||
|
||||
__tablename__ = "plex_shows"
|
||||
__table_args__ = (
|
||||
Index("ix_plex_shows_genres_gin", "genres", postgresql_using="gin"),
|
||||
Index("ix_plex_shows_directors_gin", "directors", postgresql_using="gin"),
|
||||
Index("ix_plex_shows_cast_gin", "cast_names", postgresql_using="gin"),
|
||||
# ix_plex_shows_collections_gin already created with collections (migration 0047).
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
rating_key: Mapped[str] = mapped_column(String(32), unique=True, index=True)
|
||||
|
|
@ -973,10 +981,20 @@ class PlexShow(Base, TimestampMixin, UpdatedAtMixin):
|
|||
child_count: Mapped[int | None] = mapped_column(Integer) # seasons
|
||||
collection_keys: Mapped[list | None] = mapped_column(JSONB) # Plex collections this show is in
|
||||
added_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
|
||||
# Filterable / orderable metadata mirrored from the show section listing (parallels PlexItem).
|
||||
rating: Mapped[float | None] = mapped_column(Float, index=True) # Plex audienceRating (~IMDb)
|
||||
content_rating: Mapped[str | None] = mapped_column(String(16), index=True)
|
||||
studio: Mapped[str | None] = mapped_column(String(255), index=True) # network/studio
|
||||
originally_available_at: Mapped[Date | None] = mapped_column(Date, index=True) # first air date
|
||||
genres: Mapped[list | None] = mapped_column(JSONB)
|
||||
directors: Mapped[list | None] = mapped_column(JSONB)
|
||||
cast_names: Mapped[list | None] = mapped_column(JSONB)
|
||||
people_text: Mapped[str | None] = mapped_column(Text) # cast+directors, folded into search_vector
|
||||
search_vector: Mapped[object | None] = mapped_column(
|
||||
TSVECTOR,
|
||||
Computed(
|
||||
"setweight(to_tsvector('public.unaccent_simple', coalesce(title, '')), 'A') || "
|
||||
"setweight(to_tsvector('public.unaccent_simple', coalesce(people_text, '')), 'B') || "
|
||||
"setweight(to_tsvector('public.unaccent_simple', left(coalesce(summary, ''), 1000)), 'C')",
|
||||
persisted=True,
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue