"""Where-to-watch offerings (JustWatch data via TMDb's watch/providers endpoint) -- section 17, additive. Attribution note: TMDb's own terms require this specific data to be sourced back to JustWatch, not just TMDb itself -- see docs/api.md.""" from __future__ import annotations import uuid from sqlalchemy import ForeignKey, Index, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.models.base import Base, UUIDPrimaryKeyMixin class WatchProvider(Base, UUIDPrimaryKeyMixin): """A streaming/rental/purchase service catalog entry (Netflix, Apple TV, etc.) -- TMDb's own provider_id links here via ExternalID, same pattern as Company/Collection/Person. Its logo lives in the shared polymorphic Artwork table (entity_type="watch_provider"), same as a Company logo.""" __tablename__ = "watch_providers" name: Mapped[str] = mapped_column(String(200), nullable=False) class WatchProviderOffering(Base, UUIDPrimaryKeyMixin): """One (entity, country, monetization type, provider) row -- where a movie/series can be streamed/rented/bought/watched free/with ads in a given region. Polymorphic across movie/series, same shape as ReleaseDate.""" __tablename__ = "watch_provider_offerings" __table_args__ = ( Index("ix_watch_provider_offerings_entity", "entity_type", "entity_id"), ) entity_type: Mapped[str] = mapped_column(String(30), nullable=False) entity_id: Mapped[uuid.UUID] = mapped_column(nullable=False) country_code: Mapped[str] = mapped_column(ForeignKey("countries.code"), nullable=False) # "flatrate" (subscription) | "rent" | "buy" | "ads" | "free" monetization_type: Mapped[str] = mapped_column(String(20), nullable=False) watch_provider_id: Mapped[uuid.UUID] = mapped_column( ForeignKey("watch_providers.id", ondelete="CASCADE"), nullable=False ) display_priority: Mapped[int | None] = mapped_column(Integer, nullable=True) # TMDb/JustWatch's deep link for this (entity, country) -- identical # across every provider row for the same country, denormalized here # rather than a separate link-per-country table since it's cheap and # keeps the read path to a single table. link: Mapped[str | None] = mapped_column(Text, nullable=True) watch_provider: Mapped[WatchProvider] = relationship() __all__ = ["WatchProvider", "WatchProviderOffering"]