Skip to content

Library Provider API

AniBridge library provider base package.

HistoryEntry dataclass

User history event for a library item.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@dataclass(frozen=True, slots=True)
class HistoryEntry:
    """User history event for a library item."""

    library_key: str
    viewed_at: datetime  # Must be timezone-aware

LibraryEntity dataclass

Bases: ABC

Base class for library entities.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@dataclass(slots=True, eq=False)
class LibraryEntity[ProviderT: LibraryProvider](ABC):
    """Base class for library entities."""

    _provider: ProviderT = field(repr=False, compare=False)
    _key: str
    _title: str = field(compare=False)
    _media_kind: MediaKind

    @property
    def key(self) -> str:
        """Return the unique key for this entity."""
        return self._key

    @property
    def media_kind(self) -> MediaKind:
        """Return the high-level media kind of this entity."""
        return self._media_kind

    def provider(self) -> ProviderT:
        """Return the provider associated with this entity."""
        return self._provider

    @property
    def title(self) -> str:
        """Return the entity title."""
        return self._title

    def __hash__(self) -> int:
        """Compute a hash based on provider namespace, class name, and key."""
        return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

    def __eq__(self, other: object) -> bool:
        """Compare entities by provider namespace and key."""
        if other.__class__ is not self.__class__:
            return NotImplemented
        other_ent = cast(LibraryEntity, other)
        return (
            getattr(self._provider, "NAMESPACE", None)
            == getattr(other_ent._provider, "NAMESPACE", None)
            and self.key == other_ent.key
        )

    def __repr__(self) -> str:
        """Return a short representation for debugging."""
        return f"<{self.__class__.__name__}:{self.key}:{self.title[:32]}>"

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

title property

Return the entity title.

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a short representation for debugging.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Return a short representation for debugging."""
    return f"<{self.__class__.__name__}:{self.key}:{self.title[:32]}>"

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

LibraryEntry dataclass

Bases: LibraryEntity[ProviderT], ABC

Base class for library entries.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibraryEntry[ProviderT: LibraryProvider](LibraryEntity[ProviderT], ABC):
    """Base class for library entries."""

    @abstractmethod
    async def history(self) -> Sequence[HistoryEntry]:
        """Return user history entries for this media item (tz-aware timestamps)."""
        ...

    @abstractmethod
    def mapping_descriptors(self) -> Sequence[MappingDescriptor]:
        """Return possible mapping descriptors for the media item.

        The returned descriptors refer to providers in the mapping database. They are
        not related to the library provider of this item (although, they might match).
        """
        ...

    @abstractmethod
    def media(self) -> LibraryMedia[ProviderT]:
        """Return the media item associated with this entry."""
        ...

    @property
    @abstractmethod
    def on_watching(self) -> bool:
        """Whether the item is on the user's current watching list."""
        ...

    @property
    @abstractmethod
    def on_watchlist(self) -> bool:
        """Whether the item is on the user's watchlist."""
        ...

    @property
    @abstractmethod
    async def review(self) -> str | None:
        """Return the user's review text for this item, if any."""
        ...

    @abstractmethod
    def section(self) -> LibrarySection[LibraryProviderT]:
        """Return the parent library section for this media item."""
        ...

    @property
    @abstractmethod
    def user_rating(self) -> int | None:
        """User rating on a 0-100 scale, or None if not rated."""
        ...

    @property
    @abstractmethod
    def view_count(self) -> int:
        """Total view count for the item (including children)."""
        ...

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

on_watching abstractmethod property

Whether the item is on the user's current watching list.

on_watchlist abstractmethod property

Whether the item is on the user's watchlist.

review abstractmethod async property

Return the user's review text for this item, if any.

title property

Return the entity title.

user_rating abstractmethod property

User rating on a 0-100 scale, or None if not rated.

view_count abstractmethod property

Total view count for the item (including children).

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a short representation for debugging.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Return a short representation for debugging."""
    return f"<{self.__class__.__name__}:{self.key}:{self.title[:32]}>"

history() abstractmethod async

Return user history entries for this media item (tz-aware timestamps).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
async def history(self) -> Sequence[HistoryEntry]:
    """Return user history entries for this media item (tz-aware timestamps)."""
    ...

mapping_descriptors() abstractmethod

Return possible mapping descriptors for the media item.

The returned descriptors refer to providers in the mapping database. They are not related to the library provider of this item (although, they might match).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def mapping_descriptors(self) -> Sequence[MappingDescriptor]:
    """Return possible mapping descriptors for the media item.

    The returned descriptors refer to providers in the mapping database. They are
    not related to the library provider of this item (although, they might match).
    """
    ...

media() abstractmethod

Return the media item associated with this entry.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def media(self) -> LibraryMedia[ProviderT]:
    """Return the media item associated with this entry."""
    ...

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

section() abstractmethod

Return the parent library section for this media item.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def section(self) -> LibrarySection[LibraryProviderT]:
    """Return the parent library section for this media item."""
    ...

LibraryEpisode dataclass

Bases: LibraryEntry[LibraryProviderT], ABC

Episode item within a season/show.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibraryEpisode(LibraryEntry[LibraryProviderT], ABC):
    """Episode item within a season/show."""

    index: int
    season_index: int

    @abstractmethod
    def season(self) -> LibrarySeason[LibraryProviderT]:
        """Get the parent season of the episode.

        Returns:
            LibrarySeason: The parent season.
        """
        ...

    @abstractmethod
    def show(self) -> LibraryShow[LibraryProviderT]:
        """Get the parent show of the episode.

        Returns:
            LibraryShow: The parent show.
        """
        ...

    def __repr__(self) -> str:
        """Short representation including show title, season and episode indexes."""
        return (
            f"<{self.__class__.__name__}:{self.key}:{self.show().title[:32]}:"
            f"S{self.season_index:02d}E{self.index:02d}>"
        )

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

on_watching abstractmethod property

Whether the item is on the user's current watching list.

on_watchlist abstractmethod property

Whether the item is on the user's watchlist.

review abstractmethod async property

Return the user's review text for this item, if any.

title property

Return the entity title.

user_rating abstractmethod property

User rating on a 0-100 scale, or None if not rated.

view_count abstractmethod property

Total view count for the item (including children).

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Short representation including show title, season and episode indexes.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Short representation including show title, season and episode indexes."""
    return (
        f"<{self.__class__.__name__}:{self.key}:{self.show().title[:32]}:"
        f"S{self.season_index:02d}E{self.index:02d}>"
    )

history() abstractmethod async

Return user history entries for this media item (tz-aware timestamps).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
async def history(self) -> Sequence[HistoryEntry]:
    """Return user history entries for this media item (tz-aware timestamps)."""
    ...

mapping_descriptors() abstractmethod

Return possible mapping descriptors for the media item.

The returned descriptors refer to providers in the mapping database. They are not related to the library provider of this item (although, they might match).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def mapping_descriptors(self) -> Sequence[MappingDescriptor]:
    """Return possible mapping descriptors for the media item.

    The returned descriptors refer to providers in the mapping database. They are
    not related to the library provider of this item (although, they might match).
    """
    ...

media() abstractmethod

Return the media item associated with this entry.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def media(self) -> LibraryMedia[ProviderT]:
    """Return the media item associated with this entry."""
    ...

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

season() abstractmethod

Get the parent season of the episode.

Returns:

Name Type Description
LibrarySeason LibrarySeason[LibraryProviderT]

The parent season.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def season(self) -> LibrarySeason[LibraryProviderT]:
    """Get the parent season of the episode.

    Returns:
        LibrarySeason: The parent season.
    """
    ...

section() abstractmethod

Return the parent library section for this media item.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def section(self) -> LibrarySection[LibraryProviderT]:
    """Return the parent library section for this media item."""
    ...

show() abstractmethod

Get the parent show of the episode.

Returns:

Name Type Description
LibraryShow LibraryShow[LibraryProviderT]

The parent show.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def show(self) -> LibraryShow[LibraryProviderT]:
    """Get the parent show of the episode.

    Returns:
        LibraryShow: The parent show.
    """
    ...

LibraryMedia dataclass

Bases: LibraryEntity[LibraryProviderT], ABC

Base class for library media items.

Implementations should provide concrete behaviour for the abstract properties and methods below.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibraryMedia(LibraryEntity[LibraryProviderT], ABC):
    """Base class for library media items.

    Implementations should provide concrete behaviour for the abstract
    properties and methods below.
    """

    @property
    def external_url(self) -> str | None:
        """URL to the provider's media item, if available."""
        return None

    @property
    def poster_image(self) -> str | None:
        """Primary poster or cover image URL, if available."""
        return None

external_url property

URL to the provider's media item, if available.

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

poster_image property

Primary poster or cover image URL, if available.

title property

Return the entity title.

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a short representation for debugging.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Return a short representation for debugging."""
    return f"<{self.__class__.__name__}:{self.key}:{self.title[:32]}>"

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

LibraryMovie dataclass

Bases: LibraryEntry[LibraryProviderT], ABC

Movie item in a media library.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibraryMovie(LibraryEntry[LibraryProviderT], ABC):
    """Movie item in a media library."""

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

on_watching abstractmethod property

Whether the item is on the user's current watching list.

on_watchlist abstractmethod property

Whether the item is on the user's watchlist.

review abstractmethod async property

Return the user's review text for this item, if any.

title property

Return the entity title.

user_rating abstractmethod property

User rating on a 0-100 scale, or None if not rated.

view_count abstractmethod property

Total view count for the item (including children).

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a short representation for debugging.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Return a short representation for debugging."""
    return f"<{self.__class__.__name__}:{self.key}:{self.title[:32]}>"

history() abstractmethod async

Return user history entries for this media item (tz-aware timestamps).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
async def history(self) -> Sequence[HistoryEntry]:
    """Return user history entries for this media item (tz-aware timestamps)."""
    ...

mapping_descriptors() abstractmethod

Return possible mapping descriptors for the media item.

The returned descriptors refer to providers in the mapping database. They are not related to the library provider of this item (although, they might match).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def mapping_descriptors(self) -> Sequence[MappingDescriptor]:
    """Return possible mapping descriptors for the media item.

    The returned descriptors refer to providers in the mapping database. They are
    not related to the library provider of this item (although, they might match).
    """
    ...

media() abstractmethod

Return the media item associated with this entry.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def media(self) -> LibraryMedia[ProviderT]:
    """Return the media item associated with this entry."""
    ...

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

section() abstractmethod

Return the parent library section for this media item.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def section(self) -> LibrarySection[LibraryProviderT]:
    """Return the parent library section for this media item."""
    ...

LibraryProvider

Bases: ABC

Abstract base provider that exposes a user media library.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibraryProvider(ABC):
    """Abstract base provider that exposes a user media library."""

    NAMESPACE: ClassVar[str]

    def __init__(self, *, config: dict | None = None) -> None:
        """Initialize the provider with optional configuration.

        Args:
            config (dict | None): Any configuration options that were detected with the
                provider's namespace as a prefix.
        """
        return None

    async def initialize(self) -> None:
        """Asynchronous initialization hook.

        Put any async logic that should be run after construction here.
        """
        return None

    async def clear_cache(self) -> None:
        """Clear any cached data held by the provider.

        For more efficient implementations, it is a good idea to cache data
        fetched from the provider to minimize network requests. AniBridge uses
        this method occasionally to clear such caches.
        """
        return None

    async def close(self) -> None:
        """Close the provider and release resources."""
        return None

    @abstractmethod
    async def get_sections(self) -> Sequence[LibrarySection[LibraryProviderT]]:
        """Return available library sections for the provider.

        Returns:
            Sequence[LibrarySection[LibraryProviderT]]: The available library sections.
        """
        ...

    @abstractmethod
    async def list_items(
        self,
        section: LibrarySection[LibraryProviderT],
        *,
        min_last_modified: datetime | None = None,
        require_watched: bool = False,
        keys: Sequence[str] | None = None,
    ) -> Sequence[LibraryEntry[LibraryProviderT]]:
        """List items in a library section with optional filtering.

        Args:
            section (LibrarySection[LibraryProviderT]): The library section to list
                items from.
            min_last_modified (datetime | None): If provided, only include items
                modified after this timezone-aware timestamp.
            require_watched (bool): If True, only include items marked as watched.
            keys (Sequence[str] | None): If provided, only include items whose keys are
                in this sequence.

        Returns:
            Sequence[LibraryEntry[LibraryProviderT]]: The list of library items.
        """
        ...

    async def parse_webhook(self, request: Request) -> tuple[bool, Sequence[str]]:
        """Parse an incoming webhook and return the affected item keys.

        Args:
            request (Request): The incoming HTTP request.

        Returns:
            tuple[bool, Sequence[str]]: A tuple where the first element indicates
                whether the webhook applies to the current provider, and the second
                element is a sequence of affected item keys.
        """
        return False, ()

    @abstractmethod
    def user(self) -> LibraryUser | None:
        """Return the associated user object, if any.

        Returns:
            LibraryUser | None: The associated user object, if any.
        """
        ...

__init__(*, config=None)

Initialize the provider with optional configuration.

Parameters:

Name Type Description Default
config dict | None

Any configuration options that were detected with the provider's namespace as a prefix.

None
Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __init__(self, *, config: dict | None = None) -> None:
    """Initialize the provider with optional configuration.

    Args:
        config (dict | None): Any configuration options that were detected with the
            provider's namespace as a prefix.
    """
    return None

clear_cache() async

Clear any cached data held by the provider.

For more efficient implementations, it is a good idea to cache data fetched from the provider to minimize network requests. AniBridge uses this method occasionally to clear such caches.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
async def clear_cache(self) -> None:
    """Clear any cached data held by the provider.

    For more efficient implementations, it is a good idea to cache data
    fetched from the provider to minimize network requests. AniBridge uses
    this method occasionally to clear such caches.
    """
    return None

close() async

Close the provider and release resources.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
async def close(self) -> None:
    """Close the provider and release resources."""
    return None

get_sections() abstractmethod async

Return available library sections for the provider.

Returns:

Type Description
Sequence[LibrarySection[LibraryProviderT]]

Sequence[LibrarySection[LibraryProviderT]]: The available library sections.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
async def get_sections(self) -> Sequence[LibrarySection[LibraryProviderT]]:
    """Return available library sections for the provider.

    Returns:
        Sequence[LibrarySection[LibraryProviderT]]: The available library sections.
    """
    ...

initialize() async

Asynchronous initialization hook.

Put any async logic that should be run after construction here.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
async def initialize(self) -> None:
    """Asynchronous initialization hook.

    Put any async logic that should be run after construction here.
    """
    return None

list_items(section, *, min_last_modified=None, require_watched=False, keys=None) abstractmethod async

List items in a library section with optional filtering.

Parameters:

Name Type Description Default
section LibrarySection[LibraryProviderT]

The library section to list items from.

required
min_last_modified datetime | None

If provided, only include items modified after this timezone-aware timestamp.

None
require_watched bool

If True, only include items marked as watched.

False
keys Sequence[str] | None

If provided, only include items whose keys are in this sequence.

None

Returns:

Type Description
Sequence[LibraryEntry[LibraryProviderT]]

Sequence[LibraryEntry[LibraryProviderT]]: The list of library items.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
async def list_items(
    self,
    section: LibrarySection[LibraryProviderT],
    *,
    min_last_modified: datetime | None = None,
    require_watched: bool = False,
    keys: Sequence[str] | None = None,
) -> Sequence[LibraryEntry[LibraryProviderT]]:
    """List items in a library section with optional filtering.

    Args:
        section (LibrarySection[LibraryProviderT]): The library section to list
            items from.
        min_last_modified (datetime | None): If provided, only include items
            modified after this timezone-aware timestamp.
        require_watched (bool): If True, only include items marked as watched.
        keys (Sequence[str] | None): If provided, only include items whose keys are
            in this sequence.

    Returns:
        Sequence[LibraryEntry[LibraryProviderT]]: The list of library items.
    """
    ...

parse_webhook(request) async

Parse an incoming webhook and return the affected item keys.

Parameters:

Name Type Description Default
request Request

The incoming HTTP request.

required

Returns:

Type Description
tuple[bool, Sequence[str]]

tuple[bool, Sequence[str]]: A tuple where the first element indicates whether the webhook applies to the current provider, and the second element is a sequence of affected item keys.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
async def parse_webhook(self, request: Request) -> tuple[bool, Sequence[str]]:
    """Parse an incoming webhook and return the affected item keys.

    Args:
        request (Request): The incoming HTTP request.

    Returns:
        tuple[bool, Sequence[str]]: A tuple where the first element indicates
            whether the webhook applies to the current provider, and the second
            element is a sequence of affected item keys.
    """
    return False, ()

user() abstractmethod

Return the associated user object, if any.

Returns:

Type Description
LibraryUser | None

LibraryUser | None: The associated user object, if any.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def user(self) -> LibraryUser | None:
    """Return the associated user object, if any.

    Returns:
        LibraryUser | None: The associated user object, if any.
    """
    ...

LibraryProviderRegistry

Registry for LibraryProvider implementations.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
class LibraryProviderRegistry:
    """Registry for `LibraryProvider` implementations."""

    def __init__(self) -> None:
        """Initialize an empty registry."""
        self._providers: dict[str, type[LibraryProvider]] = {}

    def clear(self) -> None:
        """Remove all provider registrations."""
        self._providers.clear()

    def create(self, namespace: str, *, config: dict | None = None) -> LibraryProvider:
        """Instantiate the provider registered under `namespace`.

        Args:
            namespace (str): The namespace identifier to create.
            config (dict | None): Optional configuration dictionary to pass to the
                provider constructor.

        Returns:
            LibraryProvider: An instance of the registered provider.
        """
        provider_cls = self.get(namespace)
        return provider_cls(config=config)

    def get(self, namespace: str) -> type[LibraryProvider]:
        """Return the provider class registered under `namespace`.

        Args:
            namespace (str): The namespace identifier to look up.

        Returns:
            type[LibraryProvider]: The registered provider class.
        """
        try:
            return self._providers[namespace]
        except KeyError as exc:
            raise LookupError(
                f"No provider registered for namespace '{namespace}'."
            ) from exc

    def namespaces(self) -> tuple[str, ...]:
        """Return a tuple of registered namespace identifiers.

        Returns:
            tuple[str, ...]: The registered namespace identifiers.
        """
        return tuple(self._providers)

    @overload
    def register(
        self,
        provider_cls: type[LP],
        *,
        namespace: str | None = None,
    ) -> type[LP]: ...

    @overload
    def register(
        self,
        provider_cls: None = None,
        *,
        namespace: str | None = None,
    ) -> Callable[[type[LP]], type[LP]]: ...

    def register(
        self,
        provider_cls: type[LP] | None = None,
        *,
        namespace: str | None = None,
    ) -> type[LP] | Callable[[type[LP]], type[LP]]:
        """Register a provider class, optionally used as a decorator.

        Args:
            provider_cls (type[LP] | None): The provider class to register. If `None`,
                the method acts as a decorator factory.
            namespace (str | None): Explicit namespace override. Defaults to the class'
                `NAMESPACE` attribute.

        Returns:
            type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or
                a decorator that registers the class.
        """

        def decorator(cls: type[LP]) -> type[LP]:
            """Register `cls` as a provider."""
            resolved_namespace = namespace or getattr(cls, "NAMESPACE", None)
            if not isinstance(resolved_namespace, str) or not resolved_namespace:
                raise ValueError(
                    "Library providers must define a non-empty string `NAMESPACE` "
                    "attribute or pass `namespace=` when registering."
                )

            existing = self._providers.get(resolved_namespace)
            if existing is not None and existing is not cls:
                raise ValueError(
                    f"A provider is already registered for namespace "
                    f"'{resolved_namespace}'."
                )

            self._providers[resolved_namespace] = cls
            return cls

        if provider_cls is None:
            return decorator
        return decorator(provider_cls)

    def unregister(self, namespace: str) -> None:
        """Remove a provider registration if it exists.

        Args:
            namespace (str): The namespace identifier to unregister.
        """
        self._providers.pop(namespace, None)

    def __contains__(self, namespace: object) -> bool:
        """Check if a provider is registered under `namespace`."""
        return isinstance(namespace, str) and namespace in self._providers

    def __iter__(self) -> Iterator[tuple[str, type[LibraryProvider]]]:
        """Iterate over `(namespace, provider_class)` pairs."""
        return iter(self._providers.items())

__contains__(namespace)

Check if a provider is registered under namespace.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def __contains__(self, namespace: object) -> bool:
    """Check if a provider is registered under `namespace`."""
    return isinstance(namespace, str) and namespace in self._providers

__init__()

Initialize an empty registry.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def __init__(self) -> None:
    """Initialize an empty registry."""
    self._providers: dict[str, type[LibraryProvider]] = {}

__iter__()

Iterate over (namespace, provider_class) pairs.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def __iter__(self) -> Iterator[tuple[str, type[LibraryProvider]]]:
    """Iterate over `(namespace, provider_class)` pairs."""
    return iter(self._providers.items())

clear()

Remove all provider registrations.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def clear(self) -> None:
    """Remove all provider registrations."""
    self._providers.clear()

create(namespace, *, config=None)

Instantiate the provider registered under namespace.

Parameters:

Name Type Description Default
namespace str

The namespace identifier to create.

required
config dict | None

Optional configuration dictionary to pass to the provider constructor.

None

Returns:

Name Type Description
LibraryProvider LibraryProvider

An instance of the registered provider.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def create(self, namespace: str, *, config: dict | None = None) -> LibraryProvider:
    """Instantiate the provider registered under `namespace`.

    Args:
        namespace (str): The namespace identifier to create.
        config (dict | None): Optional configuration dictionary to pass to the
            provider constructor.

    Returns:
        LibraryProvider: An instance of the registered provider.
    """
    provider_cls = self.get(namespace)
    return provider_cls(config=config)

get(namespace)

Return the provider class registered under namespace.

Parameters:

Name Type Description Default
namespace str

The namespace identifier to look up.

required

Returns:

Type Description
type[LibraryProvider]

type[LibraryProvider]: The registered provider class.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def get(self, namespace: str) -> type[LibraryProvider]:
    """Return the provider class registered under `namespace`.

    Args:
        namespace (str): The namespace identifier to look up.

    Returns:
        type[LibraryProvider]: The registered provider class.
    """
    try:
        return self._providers[namespace]
    except KeyError as exc:
        raise LookupError(
            f"No provider registered for namespace '{namespace}'."
        ) from exc

namespaces()

Return a tuple of registered namespace identifiers.

Returns:

Type Description
tuple[str, ...]

tuple[str, ...]: The registered namespace identifiers.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def namespaces(self) -> tuple[str, ...]:
    """Return a tuple of registered namespace identifiers.

    Returns:
        tuple[str, ...]: The registered namespace identifiers.
    """
    return tuple(self._providers)

register(provider_cls=None, *, namespace=None)

register(provider_cls: type[LP], *, namespace: str | None = None) -> type[LP]
register(provider_cls: None = None, *, namespace: str | None = None) -> Callable[[type[LP]], type[LP]]

Register a provider class, optionally used as a decorator.

Parameters:

Name Type Description Default
provider_cls type[LP] | None

The provider class to register. If None, the method acts as a decorator factory.

None
namespace str | None

Explicit namespace override. Defaults to the class' NAMESPACE attribute.

None

Returns:

Type Description
type[LP] | Callable[[type[LP]], type[LP]]

type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or a decorator that registers the class.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def register(
    self,
    provider_cls: type[LP] | None = None,
    *,
    namespace: str | None = None,
) -> type[LP] | Callable[[type[LP]], type[LP]]:
    """Register a provider class, optionally used as a decorator.

    Args:
        provider_cls (type[LP] | None): The provider class to register. If `None`,
            the method acts as a decorator factory.
        namespace (str | None): Explicit namespace override. Defaults to the class'
            `NAMESPACE` attribute.

    Returns:
        type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or
            a decorator that registers the class.
    """

    def decorator(cls: type[LP]) -> type[LP]:
        """Register `cls` as a provider."""
        resolved_namespace = namespace or getattr(cls, "NAMESPACE", None)
        if not isinstance(resolved_namespace, str) or not resolved_namespace:
            raise ValueError(
                "Library providers must define a non-empty string `NAMESPACE` "
                "attribute or pass `namespace=` when registering."
            )

        existing = self._providers.get(resolved_namespace)
        if existing is not None and existing is not cls:
            raise ValueError(
                f"A provider is already registered for namespace "
                f"'{resolved_namespace}'."
            )

        self._providers[resolved_namespace] = cls
        return cls

    if provider_cls is None:
        return decorator
    return decorator(provider_cls)

unregister(namespace)

Remove a provider registration if it exists.

Parameters:

Name Type Description Default
namespace str

The namespace identifier to unregister.

required
Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def unregister(self, namespace: str) -> None:
    """Remove a provider registration if it exists.

    Args:
        namespace (str): The namespace identifier to unregister.
    """
    self._providers.pop(namespace, None)

LibrarySeason dataclass

Bases: LibraryEntry[LibraryProviderT], ABC

Season container within a show.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibrarySeason(LibraryEntry[LibraryProviderT], ABC):
    """Season container within a show."""

    index: int

    @abstractmethod
    def episodes(self) -> Sequence[LibraryEpisode[LibraryProviderT]]:
        """Get child episodes belonging to the season.

        Returns:
            Sequence[LibraryEpisode]: Child episodes.
        """
        ...

    @abstractmethod
    def show(self) -> LibraryShow[LibraryProviderT]:
        """Get the parent show of the season.

        Returns:
            LibraryShow: The parent show.
        """
        ...

    def __repr__(self) -> str:
        """Short representation including show title and season index."""
        return (
            f"<{self.__class__.__name__}:{self.key}:{self.show().title[:32]}:"
            f"S{self.index:02d}>"
        )

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

on_watching abstractmethod property

Whether the item is on the user's current watching list.

on_watchlist abstractmethod property

Whether the item is on the user's watchlist.

review abstractmethod async property

Return the user's review text for this item, if any.

title property

Return the entity title.

user_rating abstractmethod property

User rating on a 0-100 scale, or None if not rated.

view_count abstractmethod property

Total view count for the item (including children).

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Short representation including show title and season index.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Short representation including show title and season index."""
    return (
        f"<{self.__class__.__name__}:{self.key}:{self.show().title[:32]}:"
        f"S{self.index:02d}>"
    )

episodes() abstractmethod

Get child episodes belonging to the season.

Returns:

Type Description
Sequence[LibraryEpisode[LibraryProviderT]]

Sequence[LibraryEpisode]: Child episodes.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def episodes(self) -> Sequence[LibraryEpisode[LibraryProviderT]]:
    """Get child episodes belonging to the season.

    Returns:
        Sequence[LibraryEpisode]: Child episodes.
    """
    ...

history() abstractmethod async

Return user history entries for this media item (tz-aware timestamps).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
async def history(self) -> Sequence[HistoryEntry]:
    """Return user history entries for this media item (tz-aware timestamps)."""
    ...

mapping_descriptors() abstractmethod

Return possible mapping descriptors for the media item.

The returned descriptors refer to providers in the mapping database. They are not related to the library provider of this item (although, they might match).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def mapping_descriptors(self) -> Sequence[MappingDescriptor]:
    """Return possible mapping descriptors for the media item.

    The returned descriptors refer to providers in the mapping database. They are
    not related to the library provider of this item (although, they might match).
    """
    ...

media() abstractmethod

Return the media item associated with this entry.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def media(self) -> LibraryMedia[ProviderT]:
    """Return the media item associated with this entry."""
    ...

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

section() abstractmethod

Return the parent library section for this media item.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def section(self) -> LibrarySection[LibraryProviderT]:
    """Return the parent library section for this media item."""
    ...

show() abstractmethod

Get the parent show of the season.

Returns:

Name Type Description
LibraryShow LibraryShow[LibraryProviderT]

The parent show.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def show(self) -> LibraryShow[LibraryProviderT]:
    """Get the parent show of the season.

    Returns:
        LibraryShow: The parent show.
    """
    ...

LibrarySection dataclass

Bases: LibraryEntity[LibraryProviderT], ABC

Represents a logical collection/section within the media library.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibrarySection(LibraryEntity[LibraryProviderT], ABC):
    """Represents a logical collection/section within the media library."""

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

title property

Return the entity title.

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a short representation for debugging.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Return a short representation for debugging."""
    return f"<{self.__class__.__name__}:{self.key}:{self.title[:32]}>"

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

LibraryShow dataclass

Bases: LibraryEntry[LibraryProviderT], ABC

Episodic show/series in a media library.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class LibraryShow(LibraryEntry[LibraryProviderT], ABC):
    """Episodic show/series in a media library."""

    @abstractmethod
    def episodes(self) -> Sequence[LibraryEpisode[LibraryProviderT]]:
        """Get child episodes belonging to the show.

        Returns:
            Sequence[LibraryEpisode]: Child episodes.
        """
        ...

    @abstractmethod
    def seasons(self) -> Sequence[LibrarySeason[LibraryProviderT]]:
        """Get child seasons belonging to the show.

        Returns:
            Sequence[LibrarySeason]: Child seasons.
        """
        ...

key property

Return the unique key for this entity.

media_kind property

Return the high-level media kind of this entity.

on_watching abstractmethod property

Whether the item is on the user's current watching list.

on_watchlist abstractmethod property

Whether the item is on the user's watchlist.

review abstractmethod async property

Return the user's review text for this item, if any.

title property

Return the entity title.

user_rating abstractmethod property

User rating on a 0-100 scale, or None if not rated.

view_count abstractmethod property

Total view count for the item (including children).

__eq__(other)

Compare entities by provider namespace and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __eq__(self, other: object) -> bool:
    """Compare entities by provider namespace and key."""
    if other.__class__ is not self.__class__:
        return NotImplemented
    other_ent = cast(LibraryEntity, other)
    return (
        getattr(self._provider, "NAMESPACE", None)
        == getattr(other_ent._provider, "NAMESPACE", None)
        and self.key == other_ent.key
    )

__hash__()

Compute a hash based on provider namespace, class name, and key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Compute a hash based on provider namespace, class name, and key."""
    return hash((self._provider.NAMESPACE, self.__class__.__name__, self.key))

__repr__()

Return a short representation for debugging.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __repr__(self) -> str:
    """Return a short representation for debugging."""
    return f"<{self.__class__.__name__}:{self.key}:{self.title[:32]}>"

episodes() abstractmethod

Get child episodes belonging to the show.

Returns:

Type Description
Sequence[LibraryEpisode[LibraryProviderT]]

Sequence[LibraryEpisode]: Child episodes.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def episodes(self) -> Sequence[LibraryEpisode[LibraryProviderT]]:
    """Get child episodes belonging to the show.

    Returns:
        Sequence[LibraryEpisode]: Child episodes.
    """
    ...

history() abstractmethod async

Return user history entries for this media item (tz-aware timestamps).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
async def history(self) -> Sequence[HistoryEntry]:
    """Return user history entries for this media item (tz-aware timestamps)."""
    ...

mapping_descriptors() abstractmethod

Return possible mapping descriptors for the media item.

The returned descriptors refer to providers in the mapping database. They are not related to the library provider of this item (although, they might match).

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def mapping_descriptors(self) -> Sequence[MappingDescriptor]:
    """Return possible mapping descriptors for the media item.

    The returned descriptors refer to providers in the mapping database. They are
    not related to the library provider of this item (although, they might match).
    """
    ...

media() abstractmethod

Return the media item associated with this entry.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def media(self) -> LibraryMedia[ProviderT]:
    """Return the media item associated with this entry."""
    ...

provider()

Return the provider associated with this entity.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def provider(self) -> ProviderT:
    """Return the provider associated with this entity."""
    return self._provider

seasons() abstractmethod

Get child seasons belonging to the show.

Returns:

Type Description
Sequence[LibrarySeason[LibraryProviderT]]

Sequence[LibrarySeason]: Child seasons.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def seasons(self) -> Sequence[LibrarySeason[LibraryProviderT]]:
    """Get child seasons belonging to the show.

    Returns:
        Sequence[LibrarySeason]: Child seasons.
    """
    ...

section() abstractmethod

Return the parent library section for this media item.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@abstractmethod
def section(self) -> LibrarySection[LibraryProviderT]:
    """Return the parent library section for this media item."""
    ...

LibraryUser dataclass

User information for a library provider.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
@dataclass(frozen=True, slots=True)
class LibraryUser:
    """User information for a library provider."""

    key: str
    title: str

    def __hash__(self) -> int:
        """Return a hash based on the user key."""
        return hash(self.key)

__hash__()

Return a hash based on the user key.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
def __hash__(self) -> int:
    """Return a hash based on the user key."""
    return hash(self.key)

MediaKind

Bases: StrEnum

Supported high-level media kinds within a library provider.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/base.py
class MediaKind(StrEnum):
    """Supported high-level media kinds within a library provider."""

    MOVIE = "movie"
    SHOW = "show"
    SEASON = "season"
    EPISODE = "episode"

library_provider(cls=None, *, namespace=None, registry=None)

library_provider(cls: type[LP], *, namespace: str | None = None, registry: LibraryProviderRegistry | None = None) -> type[LP]
library_provider(cls: None = None, *, namespace: str | None = None, registry: LibraryProviderRegistry | None = None) -> Callable[[type[LP]], type[LP]]

Class decorator that registers LibraryProvider implementations.

This helper lets third-party providers register themselves declaratively:

```
from anibridge.library import library_provider


@library_provider(namespace="plex")
class PlexLibraryProvider:
    NAMESPACE = "plex"
    ...
```

Parameters:

Name Type Description Default
cls type[LP] | None

The provider class to register. If None, the function acts as a decorator factory.

None
namespace str | None

Explicit namespace override. Defaults to the class' NAMESPACE attribute.

None
registry LibraryProviderRegistry | None

Alternate registry to insert into. Defaults to the module-level one.

None

Returns:

Type Description
type[LP] | Callable[[type[LP]], type[LP]]

type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or a decorator that registers the class.

Source code in .venv/lib/python3.14/site-packages/anibridge/library/registry.py
def library_provider[LP: LibraryProvider](
    cls: type[LP] | None = None,
    *,
    namespace: str | None = None,
    registry: LibraryProviderRegistry | None = None,
) -> type[LP] | Callable[[type[LP]], type[LP]]:
    """Class decorator that registers `LibraryProvider` implementations.

    This helper lets third-party providers register themselves declaratively:

        ```
        from anibridge.library import library_provider


        @library_provider(namespace="plex")
        class PlexLibraryProvider:
            NAMESPACE = "plex"
            ...
        ```

    Args:
        cls (type[LP] | None): The provider class to register. If `None`, the function
            acts as a decorator factory.
        namespace (str | None): Explicit namespace override. Defaults to the class'
            `NAMESPACE` attribute.
        registry (LibraryProviderRegistry | None): Alternate registry to insert into.
            Defaults to the module-level one.

    Returns:
        type[LP] | Callable[[type[LP]], type[LP]]: The registered provider class, or
            a decorator that registers the class.
    """
    active_registry = registry or provider_registry
    return active_registry.register(cls, namespace=namespace)