feat(plex): S5a HU-overlay backend — schema, guid, overlay sync
Adds the storage + sync for the Hungarian-metadata overlay (the browsable EN sections gain HU
twin fields written from a paired HU section, matched by guid; no browsable HU rows).
Schema (2 migrations, ADD-only; run before the first HU sync while *_hu is NULL so the generated
column build is trivial):
- 0063: plex_libraries.metadata_lang ('en' default) + self-FK overlay_of.
- 0064: plex_items/plex_shows.guid (indexed) + title_hu/summary_hu/genres_hu (+ GIN) + a STORED
generated search_vector_hu mirroring the EN vector (title_hu A / people_text B / summary_hu C) + GIN.
Sync:
- includeGuids=1 on section_items so the canonical guid rides the cheap listing.
- _canonical_guid(meta) (tmdb > imdb > tvdb) captured onto every EN row in _apply_item/_sync_shows.
- Two-pass sync(): pass 1 mirrors browsable (non-overlay) sections; pass 2 reads plex_overlay_map
('huKey:enKey', new sysconfig), designates each HU section (metadata_lang='hu' + overlay_of), and
_sync_overlay writes title_hu/summary_hu/genres_hu onto the guid-matched EN rows.
Browse: _content_libs now excludes overlay libraries (overlay_of is None); the unified_library and
facets inline library partitions fold into it, and list_libraries/collections exclude overlays too.
Tests: DB-lane coverage of the overlay write (matched vs unmatched rows, no browsable HU rows, the
generated search_vector_hu populates), the guid parse, and the browse exclusion. Full suite 272
passed; migrations verified reversible (upgrade→downgrade→upgrade). S5b (persist the live detail
fields + serve item/show detail from DB) is the follow-up.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
"""plex library HU-overlay columns (E5 S5a)
|
||||
|
||||
Adds `metadata_lang` (default 'en') and a self-referential `overlay_of` to plex_libraries. An
|
||||
OVERLAY library is the Hungarian-metadata twin of another section: it is never browsed on its own —
|
||||
its titles' HU fields are written onto the matched EN rows (paired by `guid`, see 0064). Cheap:
|
||||
metadata-only, no table rewrite.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "0063_plex_library_overlay"
|
||||
down_revision: Union[str, None] = "0062_drop_redundant_idx"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"plex_libraries",
|
||||
sa.Column("metadata_lang", sa.String(8), nullable=False, server_default="en"),
|
||||
)
|
||||
op.add_column(
|
||||
"plex_libraries",
|
||||
sa.Column(
|
||||
"overlay_of",
|
||||
sa.Integer(),
|
||||
sa.ForeignKey("plex_libraries.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
),
|
||||
)
|
||||
op.create_index("ix_plex_libraries_overlay_of", "plex_libraries", ["overlay_of"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index("ix_plex_libraries_overlay_of", table_name="plex_libraries")
|
||||
op.drop_column("plex_libraries", "overlay_of")
|
||||
op.drop_column("plex_libraries", "metadata_lang")
|
||||
@@ -0,0 +1,58 @@
|
||||
"""plex HU-overlay item/show columns + guid (E5 S5a)
|
||||
|
||||
On plex_items and plex_shows: `guid` (canonical external id, the EN<->HU pairing key), the Hungarian
|
||||
twin fields `title_hu`/`summary_hu`/`genres_hu`, and a STORED generated `search_vector_hu` mirroring
|
||||
the EN vector (title_hu A / people_text B / left(summary_hu,1000) C) + a GIN index. people_text is
|
||||
language-neutral, reused for both vectors.
|
||||
|
||||
Run BEFORE the first HU overlay sync: while the *_hu columns are all NULL the generated-column build
|
||||
is trivial. ADD only (no drop/rebuild of the EN search_vector). NOTE: adding a STORED generated
|
||||
column + its GIN takes an ACCESS EXCLUSIVE lock and rewrites the table — brief, but flag it on prod.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
revision: str = "0064_plex_hu_overlay_cols"
|
||||
down_revision: Union[str, None] = "0063_plex_library_overlay"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
_TABLES = ("plex_items", "plex_shows")
|
||||
|
||||
# Mirror of the EN search_vector (models.py) but over the HU twin columns; people_text is shared.
|
||||
_VECTOR_HU = (
|
||||
"setweight(to_tsvector('public.unaccent_simple', coalesce(title_hu, '')), 'A') || "
|
||||
"setweight(to_tsvector('public.unaccent_simple', coalesce(people_text, '')), 'B') || "
|
||||
"setweight(to_tsvector('public.unaccent_simple', left(coalesce(summary_hu, ''), 1000)), 'C')"
|
||||
)
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
for t in _TABLES:
|
||||
op.add_column(t, sa.Column("guid", sa.String(64), nullable=True))
|
||||
op.add_column(t, sa.Column("title_hu", sa.String(512), nullable=True))
|
||||
op.add_column(t, sa.Column("summary_hu", sa.Text(), nullable=True))
|
||||
op.add_column(t, sa.Column("genres_hu", postgresql.JSONB(), nullable=True))
|
||||
op.create_index(f"ix_{t}_guid", t, ["guid"])
|
||||
op.create_index(f"ix_{t}_genres_hu_gin", t, ["genres_hu"], postgresql_using="gin")
|
||||
# Generated column can't be expressed via add_column — raw DDL, matching the EN idiom (0044).
|
||||
op.execute(
|
||||
f"ALTER TABLE {t} ADD COLUMN search_vector_hu tsvector "
|
||||
f"GENERATED ALWAYS AS ({_VECTOR_HU}) STORED"
|
||||
)
|
||||
op.execute(f"CREATE INDEX ix_{t}_search_vector_hu ON {t} USING gin (search_vector_hu)")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
for t in _TABLES:
|
||||
op.execute(f"DROP INDEX IF EXISTS ix_{t}_search_vector_hu")
|
||||
op.execute(f"ALTER TABLE {t} DROP COLUMN IF EXISTS search_vector_hu")
|
||||
op.drop_index(f"ix_{t}_genres_hu_gin", table_name=t)
|
||||
op.drop_index(f"ix_{t}_guid", table_name=t)
|
||||
op.drop_column(t, "genres_hu")
|
||||
op.drop_column(t, "summary_hu")
|
||||
op.drop_column(t, "title_hu")
|
||||
op.drop_column(t, "guid")
|
||||
Reference in New Issue
Block a user