"""Plex show-level filterable/orderable metadata Revision ID: 0052_plex_show_meta Revises: 0051_plex_link Create Date: 2026-07-10 Gives TV shows the same filterable metadata movies already have (0045/0046), mirrored cheaply from the Plex show section listing (no per-item calls): rating (audienceRating ~ IMDb), content rating, studio/network, first-air date, and GIN-indexed genre / director / cast arrays + a `people_text` blob folded into the generated `search_vector` (weight B). This lets the TV grid be filtered and sorted like the movie grid. A generated column's expression can't be altered in place, so the search_vector is dropped + recreated. All new columns are populated on the next Plex sync. """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql revision: str = "0052_plex_show_meta" down_revision: Union[str, None] = "0051_plex_link" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None _CFG = "public.unaccent_simple" _NEW_VECTOR = ( f"setweight(to_tsvector('{_CFG}', coalesce(title, '')), 'A') || " f"setweight(to_tsvector('{_CFG}', coalesce(people_text, '')), 'B') || " f"setweight(to_tsvector('{_CFG}', left(coalesce(summary, ''), 1000)), 'C')" ) _OLD_VECTOR = ( f"setweight(to_tsvector('{_CFG}', coalesce(title, '')), 'A') || " f"setweight(to_tsvector('{_CFG}', left(coalesce(summary, ''), 1000)), 'C')" ) def _rebuild_vector(expr: str) -> None: op.execute("DROP INDEX IF EXISTS ix_plex_shows_search_vector") op.execute("ALTER TABLE plex_shows DROP COLUMN search_vector") op.execute( f"ALTER TABLE plex_shows ADD COLUMN search_vector tsvector " f"GENERATED ALWAYS AS ({expr}) STORED" ) op.execute("CREATE INDEX ix_plex_shows_search_vector ON plex_shows USING gin (search_vector)") def upgrade() -> None: op.add_column("plex_shows", sa.Column("rating", sa.Float(), nullable=True)) op.add_column("plex_shows", sa.Column("content_rating", sa.String(length=16), nullable=True)) op.add_column("plex_shows", sa.Column("studio", sa.String(length=255), nullable=True)) op.add_column("plex_shows", sa.Column("originally_available_at", sa.Date(), nullable=True)) op.add_column("plex_shows", sa.Column("genres", postgresql.JSONB(), nullable=True)) op.add_column("plex_shows", sa.Column("directors", postgresql.JSONB(), nullable=True)) op.add_column("plex_shows", sa.Column("cast_names", postgresql.JSONB(), nullable=True)) op.add_column("plex_shows", sa.Column("people_text", sa.Text(), nullable=True)) op.create_index("ix_plex_shows_rating", "plex_shows", ["rating"]) op.create_index("ix_plex_shows_content_rating", "plex_shows", ["content_rating"]) op.create_index("ix_plex_shows_studio", "plex_shows", ["studio"]) op.create_index("ix_plex_shows_originally_available_at", "plex_shows", ["originally_available_at"]) op.create_index("ix_plex_shows_genres_gin", "plex_shows", ["genres"], postgresql_using="gin") op.create_index("ix_plex_shows_directors_gin", "plex_shows", ["directors"], postgresql_using="gin") op.create_index("ix_plex_shows_cast_gin", "plex_shows", ["cast_names"], postgresql_using="gin") # NB: ix_plex_shows_collections_gin already exists (created with collections in 0047). _rebuild_vector(_NEW_VECTOR) def downgrade() -> None: _rebuild_vector(_OLD_VECTOR) for ix in ( "ix_plex_shows_cast_gin", "ix_plex_shows_directors_gin", "ix_plex_shows_genres_gin", "ix_plex_shows_originally_available_at", "ix_plex_shows_studio", "ix_plex_shows_content_rating", "ix_plex_shows_rating", ): op.drop_index(ix, table_name="plex_shows") for col in ( "people_text", "cast_names", "directors", "genres", "originally_available_at", "studio", "content_rating", "rating", ): op.drop_column("plex_shows", col)