feat(plex): expanded metadata filters, clickable info page, image disk cache
Backend (migration 0045_plex_filter_meta): plex_items gains rating (audienceRating ~IMDb), content_rating, studio, originally_available_at, and GIN-indexed genres / directors / cast_names — all mirrored from the cheap section listing (no per-item API calls; they also seed a future watch-habit recommender). /browse gains genre / content- rating / year / rating / duration / added-within / director / actor / studio filters (@> containment, GIN) + sort by year|rating|duration|release; new /facets endpoint returns available genres+ratings (with counts) and the year/rating/duration bounds. A thin on-disk image cache (.plex-img-cache) serves posters/art/cast photos from local disk after first fetch (~7-14x faster repeat loads). Frontend: PlexSidebar grows the full filter set (facet-driven genre/age chips, rating steps, year range inputs, duration buckets, added-within, active people/studio chips, clear-all); filters persist per-account as one JSON blob. PlexInfo metadata (year, genre, director, cast, studio, IMDb score) is clickable → sets the matching filter and returns to the filtered grid (page variant only; the in-player overlay stays read-only so a stray click can't stop playback). i18n en/hu/de.
This commit is contained in:
parent
690e17611c
commit
eefd7e3abd
15 changed files with 842 additions and 122 deletions
62
backend/alembic/versions/0045_plex_filter_meta.py
Normal file
62
backend/alembic/versions/0045_plex_filter_meta.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
"""Plex filterable/orderable metadata
|
||||
|
||||
Revision ID: 0045_plex_filter_meta
|
||||
Revises: 0044_plex_catalog
|
||||
Create Date: 2026-07-05
|
||||
|
||||
Adds the metadata the expanded Plex filter sidebar filters + orders on, mirrored cheaply from the
|
||||
Plex section listing (no per-item API calls): rating (audienceRating ~ IMDb), content rating, studio,
|
||||
release date, and GIN-indexed genre / director / cast arrays. These also double as the content
|
||||
signals a future watch-habit recommender would use.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
revision: str = "0045_plex_filter_meta"
|
||||
down_revision: Union[str, None] = "0044_plex_catalog"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("plex_items", sa.Column("rating", sa.Float(), nullable=True))
|
||||
op.add_column("plex_items", sa.Column("content_rating", sa.String(length=16), nullable=True))
|
||||
op.add_column("plex_items", sa.Column("studio", sa.String(length=255), nullable=True))
|
||||
op.add_column("plex_items", sa.Column("originally_available_at", sa.Date(), nullable=True))
|
||||
op.add_column("plex_items", sa.Column("genres", postgresql.JSONB(), nullable=True))
|
||||
op.add_column("plex_items", sa.Column("directors", postgresql.JSONB(), nullable=True))
|
||||
op.add_column("plex_items", sa.Column("cast_names", postgresql.JSONB(), nullable=True))
|
||||
|
||||
op.create_index("ix_plex_items_rating", "plex_items", ["rating"])
|
||||
op.create_index("ix_plex_items_content_rating", "plex_items", ["content_rating"])
|
||||
op.create_index("ix_plex_items_studio", "plex_items", ["studio"])
|
||||
op.create_index("ix_plex_items_originally_available_at", "plex_items", ["originally_available_at"])
|
||||
op.create_index("ix_plex_items_genres_gin", "plex_items", ["genres"], postgresql_using="gin")
|
||||
op.create_index("ix_plex_items_directors_gin", "plex_items", ["directors"], postgresql_using="gin")
|
||||
op.create_index("ix_plex_items_cast_gin", "plex_items", ["cast_names"], postgresql_using="gin")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for ix in (
|
||||
"ix_plex_items_cast_gin",
|
||||
"ix_plex_items_directors_gin",
|
||||
"ix_plex_items_genres_gin",
|
||||
"ix_plex_items_originally_available_at",
|
||||
"ix_plex_items_studio",
|
||||
"ix_plex_items_content_rating",
|
||||
"ix_plex_items_rating",
|
||||
):
|
||||
op.drop_index(ix, table_name="plex_items")
|
||||
for col in (
|
||||
"cast_names",
|
||||
"directors",
|
||||
"genres",
|
||||
"originally_available_at",
|
||||
"studio",
|
||||
"content_rating",
|
||||
"rating",
|
||||
):
|
||||
op.drop_column("plex_items", col)
|
||||
Loading…
Add table
Add a link
Reference in a new issue