I — PY-ANILIBERTY

    API reference

    Everything below is importable from the top-level aniliberty package unless noted.

    Client

    aniliberty.AniLiberty

    class
    AniLiberty(
        *,
        base_url: str = DEFAULT_BASE_URL,
        timeout: float = DEFAULT_TIMEOUT,
        max_concurrency: int = DEFAULT_MAX_CONCURRENCY,
        headers: Mapping[str, str] | None = None,
        http_client: httpx.AsyncClient | None = None,
        token: str | None = None,
        cache_dir: str | os.PathLike[str] | None = None,
        cache_ttl: float | None = None,
    )

    Async client for the AniLibria API.

    Groups the API into resource namespaces that follow the tags of its OpenAPI spec (client.app, ...). Use it as an async context manager so the connection pool is closed when you're done, or call aclose() yourself.

    Attributes

    NameTypeDescription
    http

    The low-level client every resource uses. client.http.request(...) is the escape hatch for endpoints the SDK doesn't cover yet — it returns raw JSON.

    app

    Service endpoints — API status and quick release search.

    releases

    Anime releases — latest, random, recommended, by ID/alias, members.

    episodes

    Episodes of releases, with HLS links.

    catalog

    The release catalog; catalog.references lists the values its filters accept.

    schedule

    The weekly airing schedule.

    genres

    Genres, and the releases of each.

    franchises

    Franchises — groups of related releases.

    torrents

    Torrents, .torrent file downloads and RSS feeds.

    teams

    AniLibria's own teams, roles and members.

    media

    Front-page promo banners and AniLibria's YouTube videos.

    auth

    Logging in and out — by password, one-time code or social network.

    me

    The logged-in user's own account — profile, favorites, collections, watch progress and history (needs a session).

    SOURCE ↗

    token

    attributeproperty
    token: str | None

    The session token sent with every request, or None when not logged in.

    Set by client.auth.login() (and the other login methods), cleared by client.auth.logout(). Save it to reuse the session later as AniLiberty(token=...); assign None to stop sending it without ending the session. It is a secret — don't log it.

    SOURCE ↗

    aclose

    methodasync
    aclose() -> None

    Close the connection pool. Safe to call more than once.

    SOURCE ↗

    Resources

    The namespaces of an AniLiberty instance: client.app, client.releases, client.releases.ratings, client.episodes, client.catalog (with client.catalog.references), client.schedule, client.genres, client.franchises, client.torrents, client.teams, client.media, client.auth (with client.auth.otp, client.auth.social, client.auth.password), client.me (with client.me.favorites, client.me.collections and their references, client.me.views). Not meant to be created directly — import from aniliberty.resources only for type annotations.

    aniliberty.resources.AppResource

    class
    AppResource(http: HttpClient)

    Bases BaseResource

    Service endpoints of the API, available as client.app.

    SOURCE ↗

    status

    methodasync
    status() -> ApiStatus

    Get the status of the API.

    A cheap request that is handy as a health check. The response also lists the hosts the API is available at, which can be used as base_url mirrors.

    Returns

    TypeDescription
    ApiStatus

    The API status.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ApiStatus.

    SOURCE ↗

    search_releases

    methodasync
    search_releases(query: str, *, refresh: bool = False) -> list[Release]

    Quick search of releases by name — the search box of the site.

    Matches Russian and English names and aliases, fuzzily: "frieren", "Фрирен" and "sousou-no-frieren" find the same release. There is no paging and no limit: the API returns a short list (at most 20 releases in every query checked), in an order it chooses. For filtered, paginated search use the catalog instead.

    Parameters

    NameTypeDescriptionDefault
    querystr

    Search text.

    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[Release]

    Matching releases, without the per-endpoint extras (genres etc. are empty);

    list[Release]

    an empty list when nothing matches.

    Raises

    TypeDescription
    ValidationError

    query is empty.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    aniliberty.resources.ReleasesResource

    class
    ReleasesResource(http: HttpClient)

    Bases BaseResource

    Anime releases, available as client.releases.

    For filtering and sorting the whole catalog, see client.catalog; this namespace covers single releases and the ready-made selections.

    limit on every method here must be between 1 and 50 — the API answers anything else with a 422, raised as ValidationError (see docs/api-notes.md).

    Attributes

    NameTypeDescription
    ratings

    The logged-in user's own scores of releases (needs a session).

    SOURCE ↗

    latest

    methodasync
    latest(
        *,
        limit: int | None = None,
        refresh: bool = False,
    ) -> builtins.list[Release]

    Get the latest releases — the ones that got new episodes most recently.

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    How many releases to return, 1 to 50. Omitted, the API picks the number.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[Release]

    The releases, with genres filled in.

    Raises

    TypeDescription
    ValidationError

    limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    random

    methodasync
    random(*, limit: int | None = None) -> builtins.list[Release]

    Get random releases.

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    How many releases to return, 1 to 50.

    None

    Returns

    TypeDescription
    builtins.list[Release]

    The releases, with genres and background_covers filled in.

    Raises

    TypeDescription
    ValidationError

    limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    recommended

    methodasync
    recommended(
        *,
        release: int | Release | None = None,
        limit: int | None = None,
    ) -> builtins.list[Release]

    Get recommended releases, optionally ones similar to a given release.

    Parameters

    NameTypeDescriptionDefault
    releaseint | Release | None

    Release ID, or a Release, to base recommendations on. Without it, the API returns general recommendations.

    None
    limitint | None

    How many releases to return, 1 to 50.

    None

    Returns

    TypeDescription
    builtins.list[Release]

    The releases, with genres filled in.

    Raises

    TypeDescription
    TypeError

    release is not an int or a Release (aliases don't work here).

    ValidationError

    limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    list

    methodasync
    list(
        *,
        ids: Sequence[int] | None = None,
        aliases: Sequence[str] | None = None,
        page: int | None = None,
        limit: int | None = None,
        refresh: bool = False,
    ) -> Page[Release]

    Get several releases at once by their IDs and/or aliases.

    Releases given both by ID and by alias are returned once. The order of the result doesn't follow the order of ids/aliases. To fetch all pages, use iter_list().

    Parameters

    NameTypeDescriptionDefault
    idsSequence[int] | None

    Release IDs.

    None
    aliasesSequence[str] | None

    Release aliases.

    None
    pageint | None

    Page number, starting at 1.

    None
    limitint | None

    Page size, 1 to 50.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    Page[Release]

    One page of releases, with genres, members and sponsors filled in.

    Raises

    TypeDescription
    ValueError

    Neither ids nor aliases was given (the API requires one).

    ValidationError

    page or limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    iter_list

    method
    iter_list(
        *,
        ids: Sequence[int] | None = None,
        aliases: Sequence[str] | None = None,
        limit: int | None = None,
        refresh: bool = False,
    ) -> AsyncIterator[Release]

    Iterate over all releases matching ids/aliases, fetching page by page.

    Same as calling list() with page=1, 2, ... until the last page; the next page is requested only once the current one has been consumed.

    Parameters

    NameTypeDescriptionDefault
    idsSequence[int] | None

    Release IDs.

    None
    aliasesSequence[str] | None

    Release aliases.

    None
    limitint | None

    Page size, 1 to 50 — affects how many requests are made, not the result.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    AsyncIterator[Release]

    An async iterator over the releases.

    Raises

    TypeDescription
    ValueError

    Neither ids nor aliases was given — raised right away, not when iteration starts.

    SOURCE ↗

    get

    methodasync
    get(release: int | str | Release, *, refresh: bool = False) -> Release

    Get one release by its ID or alias.

    Parameters

    NameTypeDescriptionDefault
    releaseint | str | Release

    Release ID (10297), alias ("sousou-no-frieren"), or a Release (e.g. from a list, to get its full details).

    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    Release

    The release, with genres, members, sponsors and

    Release

    background_covers filled in.

    Raises

    TypeDescription
    NotFoundError

    No release with this ID or alias.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    members

    methodasync
    members(
        release: int | str | Release,
        *,
        refresh: bool = False,
    ) -> builtins.list[ReleaseMember]

    Get the people who worked on a release (voice actors, translators, ...).

    Parameters

    NameTypeDescriptionDefault
    releaseint | str | Release

    Release ID, alias, or a Release.

    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[ReleaseMember]

    The release's members.

    Raises

    TypeDescription
    NotFoundError

    No release with this ID or alias.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ReleaseMember.

    SOURCE ↗

    timecodes

    methodasync
    timecodes(release: int | str | Release) -> builtins.list[EpisodeView]

    Get the logged-in user's watch records of a release's episodes. Needs a session.

    Records appear about a minute after client.me.views.save_timecodes().

    Parameters

    NameTypeDescription
    releaseint | str | Release

    Release ID, alias, or a Release.

    Returns

    TypeDescription
    builtins.list[EpisodeView]

    One record per episode the user has started; empty if none.

    Raises

    TypeDescription
    NotFoundError

    No release with this ID or alias.

    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match EpisodeView.

    SOURCE ↗

    aniliberty.resources.RatingsResource

    class
    RatingsResource(http: HttpClient)

    Bases BaseResource

    The logged-in user's own scores of releases, available as client.releases.ratings.

    Needs a session (client.auth.login() or AniLiberty(token=...)); without a valid one every method raises AuthRequiredError. Releases are identified by numeric ID only: the API answers an alias with a 404, so a str is rejected.

    The release's overall Release.rating is computed by the API on its own schedule: it didn't change within several minutes of a score being set (see docs/api-notes.md).

    SOURCE ↗

    get

    methodasync
    get(release: int | Release) -> UserRating

    Get the user's score for a release.

    Parameters

    NameTypeDescription
    releaseint | Release

    Release ID, or a Release.

    Returns

    TypeDescription
    UserRating

    The score; score is None if the user hasn't rated the release.

    Raises

    TypeDescription
    TypeError

    release is not an int or a Release.

    AuthRequiredError

    No valid session.

    NotFoundError

    No release with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match UserRating.

    SOURCE ↗

    set

    methodasync
    set(release: int | Release, *, score: int) -> UserRating

    Rate a release, or change the user's score for it.

    Parameters

    NameTypeDescription
    releaseint | Release

    Release ID, or a Release.

    scoreint

    Whole number from 1 to 10.

    Returns

    TypeDescription
    UserRating

    The saved score.

    Raises

    TypeDescription
    TypeError

    release is not an int or a Release, or score isn't an int.

    ValueError

    score is outside 1..10.

    AuthRequiredError

    No valid session.

    NotFoundError

    No release with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match UserRating.

    SOURCE ↗

    remove

    methodasync
    remove(release: int | Release) -> UserRating

    Withdraw the user's score for a release. A no-op if there is none.

    Parameters

    NameTypeDescription
    releaseint | Release

    Release ID, or a Release.

    Returns

    TypeDescription
    UserRating

    The rating afterwards, with score None.

    Raises

    TypeDescription
    TypeError

    release is not an int or a Release.

    AuthRequiredError

    No valid session.

    NotFoundError

    No release with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match UserRating.

    SOURCE ↗

    aniliberty.resources.EpisodesResource

    class
    EpisodesResource(http: HttpClient)

    Bases BaseResource

    Episodes of releases, available as client.episodes.

    Episode IDs come from Release.episodes (client.releases.get()/list()) or Release.latest_episode (client.releases.latest()).

    SOURCE ↗

    get

    methodasync
    get(episode: str | Episode) -> Episode

    Get one episode, with its HLS links and its release.

    Parameters

    NameTypeDescription
    episodestr | Episode

    Episode ID, a UUID string such as "a159552a-9fa7-4737-867b-d22cf7115602", or an Episode (e.g. from Release.episodes, to get it with its release).

    Returns

    TypeDescription
    Episode

    The episode, with release filled in — the release (without genres) and

    Episode

    all of its episodes.

    Raises

    TypeDescription
    NotFoundError

    No episode with this ID (also for IDs that aren't UUIDs).

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Episode.

    SOURCE ↗

    timecode

    methodasync
    timecode(episode: str | Episode) -> EpisodeView

    Get the logged-in user's watch record of an episode. Needs a session.

    Records appear about a minute after client.me.views.save_timecodes(); for the positions saved just now use client.me.views.timecodes().

    Parameters

    NameTypeDescription
    episodestr | Episode

    Episode ID (a UUID string), or an Episode.

    Returns

    TypeDescription
    EpisodeView

    Where the user stopped in the episode and whether they finished it.

    Raises

    TypeDescription
    NotFoundError

    The user has no record for this episode (yet), or there is no such episode.

    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match EpisodeView.

    SOURCE ↗

    aniliberty.resources.CatalogResource

    class
    CatalogResource(http: HttpClient)

    Bases BaseResource

    The release catalog with filters and sorting, available as client.catalog.

    How the filters combine (checked against the live API, see docs/api-notes.md):

    • genres: a release must have all of the given genres (AND);
    • types, seasons, age_ratings, publish_statuses, production_statuses: any of the given values (OR);
    • different filters: all of them must match (AND);
    • year_from/year_to: inclusive on both ends;
    • search: the same matching as client.app.search_releases(), and likewise capped at about 20 matches in total, whatever the other filters and the page size.

    Without sorting, releases come in "FRESH_AT_DESC" order (recently updated first).

    Attributes

    NameTypeDescription
    references

    Values the catalog filters accept.

    SOURCE ↗

    releases

    methodasync
    releases(
        *,
        genres: Sequence[int | Genre] | None = None,
        types: Sequence[ReleaseType | ValueDescription[str]] | None = None,
        seasons: Sequence[Season | ValueDescription[str]] | None = None,
        year_from: int | None = None,
        year_to: int | None = None,
        search: str | None = None,
        sorting: CatalogSorting | SortingOption | None = None,
        age_ratings: Sequence[AgeRatingValue | ValueDescription[str]] | None = None,
        publish_statuses: Sequence[PublishStatus | ValueDescription[str]] | None = None,
        production_statuses: Sequence[ProductionStatus | ValueDescription[str]] | None = None,
        page: int | None = None,
        limit: int | None = None,
        refresh: bool = False,
    ) -> Page[Release]

    Get one page of catalog releases matching the filters.

    Every filter is optional; without any, this is the whole catalog. Values can be passed as plain strings/IDs or as the items client.catalog.references returns. To go through all pages, use iter_releases().

    Parameters

    NameTypeDescriptionDefault
    genresSequence[int | Genre] | None

    Genre IDs (or Genre objects); a release must have all of them.

    None
    typesSequence[ReleaseType | ValueDescription[str]] | None

    Release types, e.g. ["TV", "MOVIE"]; any of them.

    None
    seasonsSequence[Season | ValueDescription[str]] | None

    Seasons, e.g. ["winter"]; any of them.

    None
    year_fromint | None

    Earliest release year, inclusive.

    None
    year_toint | None

    Latest release year, inclusive.

    None
    searchstr | None

    Text to search in names (capped at about 20 matches by the API).

    None
    sortingCatalogSorting | SortingOption | None

    Sort order, e.g. "RATING_DESC". Default: "FRESH_AT_DESC".

    None
    age_ratingsSequence[AgeRatingValue | ValueDescription[str]] | None

    Age ratings, e.g. ["R16_PLUS"]; any of them.

    None
    publish_statusesSequence[PublishStatus | ValueDescription[str]] | None

    "IS_ONGOING" and/or "IS_NOT_ONGOING".

    None
    production_statusesSequence[ProductionStatus | ValueDescription[str]] | None

    "IS_IN_PRODUCTION" and/or "IS_NOT_IN_PRODUCTION".

    None
    pageint | None

    Page number, starting at 1. A page past the last one is empty.

    None
    limitint | None

    Page size, 1 to 50.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    Page[Release]

    One page of releases, with genres filled in.

    Raises

    TypeDescription
    ValueError

    A filter value isn't one the API knows, or year_from is after year_to. The API itself would silently return no releases (or ignore the filter) instead of reporting these, so they are checked here.

    TypeError

    A list filter got a single string (types="TV") or a value of the wrong type.

    ValidationError

    page or limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    iter_releases

    method
    iter_releases(
        *,
        genres: Sequence[int | Genre] | None = None,
        types: Sequence[ReleaseType | ValueDescription[str]] | None = None,
        seasons: Sequence[Season | ValueDescription[str]] | None = None,
        year_from: int | None = None,
        year_to: int | None = None,
        search: str | None = None,
        sorting: CatalogSorting | SortingOption | None = None,
        age_ratings: Sequence[AgeRatingValue | ValueDescription[str]] | None = None,
        publish_statuses: Sequence[PublishStatus | ValueDescription[str]] | None = None,
        production_statuses: Sequence[ProductionStatus | ValueDescription[str]] | None = None,
        limit: int | None = None,
        refresh: bool = False,
    ) -> AsyncIterator[Release]

    Iterate over all catalog releases matching the filters, fetching page by page.

    Takes the same filters as releases(). The next page is requested only once the current one has been consumed, so breaking out of the loop early stops the requests. The whole catalog is a couple of thousand releases — iterate all of it only when you really need everything.

    Parameters

    NameTypeDescriptionDefault
    genresSequence[int | Genre] | None

    See releases().

    None
    typesSequence[ReleaseType | ValueDescription[str]] | None

    See releases().

    None
    seasonsSequence[Season | ValueDescription[str]] | None

    See releases().

    None
    year_fromint | None

    See releases().

    None
    year_toint | None

    See releases().

    None
    searchstr | None

    See releases().

    None
    sortingCatalogSorting | SortingOption | None

    See releases().

    None
    age_ratingsSequence[AgeRatingValue | ValueDescription[str]] | None

    See releases().

    None
    publish_statusesSequence[PublishStatus | ValueDescription[str]] | None

    See releases().

    None
    production_statusesSequence[ProductionStatus | ValueDescription[str]] | None

    See releases().

    None
    limitint | None

    Page size, 1 to 50 — affects how many requests are made, not the result.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    AsyncIterator[Release]

    An async iterator over the releases.

    Raises

    TypeDescription
    ValueError

    Invalid filter values — raised right away, not when iteration starts.

    TypeError

    A list filter got a single string or a value of the wrong type.

    SOURCE ↗

    aniliberty.resources.CatalogReferencesResource

    class
    CatalogReferencesResource(http: HttpClient)

    Bases BaseResource

    Values the catalog filters accept, available as client.catalog.references.

    Each method returns the list the site shows in its catalog filters. Use them instead of hardcoding values: the value (or id for genres, the number for years) of an item is exactly what the corresponding catalog filter takes.

    The lists are small and change rarely — fetching them once per program run is enough.

    SOURCE ↗

    genres

    methodasync
    genres(*, refresh: bool = False) -> list[Genre]

    Get the genres the catalog can be filtered by.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[Genre]

    Genres with id and name only — image and total_releases are

    list[Genre]

    None here, this list doesn't include them.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Genre.

    SOURCE ↗

    types

    methodasync
    types(*, refresh: bool = False) -> list[ValueDescription[str]]

    Get the release types, e.g. value="TV", description="ТВ".

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[ValueDescription[str]]

    The release types.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ValueDescription.

    SOURCE ↗

    seasons

    methodasync
    seasons(*, refresh: bool = False) -> list[ValueDescription[str]]

    Get the seasons, e.g. value="winter", description="Зима".

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[ValueDescription[str]]

    The seasons.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ValueDescription.

    SOURCE ↗

    years

    methodasync
    years(*, refresh: bool = False) -> list[int]

    Get the years that have releases in the catalog, oldest first.

    Only years with at least one release are listed, so the range can have gaps.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[int]

    The years.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response wasn't a list of integers.

    SOURCE ↗

    age_ratings

    methodasync
    age_ratings(*, refresh: bool = False) -> list[AgeRating]

    Get the age ratings, e.g. value="R16_PLUS", label="16+".

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[AgeRating]

    The age ratings (is_adult is None here — this list doesn't include it).

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match AgeRating.

    SOURCE ↗

    publish_statuses

    methodasync
    publish_statuses(
        *,
        refresh: bool = False,
    ) -> list[ValueDescription[str]]

    Get the airing statuses, e.g. value="IS_ONGOING", description="Онгоинг".

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[ValueDescription[str]]

    The publish statuses.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ValueDescription.

    SOURCE ↗

    production_statuses

    methodasync
    production_statuses(
        *,
        refresh: bool = False,
    ) -> list[ValueDescription[str]]

    Get the dubbing statuses, e.g. value="IS_IN_PRODUCTION".

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[ValueDescription[str]]

    The production statuses.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ValueDescription.

    SOURCE ↗

    sorting

    methodasync
    sorting(*, refresh: bool = False) -> list[SortingOption]

    Get the ways the catalog can be sorted, e.g. value="FRESH_AT_DESC".

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[SortingOption]

    The sorting options.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match SortingOption.

    SOURCE ↗

    aniliberty.resources.ScheduleResource

    class
    ScheduleResource(http: HttpClient)

    Bases BaseResource

    The weekly airing schedule of ongoing releases, available as client.schedule.

    SOURCE ↗

    now

    methodasync
    now(*, refresh: bool = False) -> ScheduleNow

    Get the releases scheduled for yesterday, today and tomorrow.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    ScheduleNow

    The schedule for the three days. Each item says whether this week's episode is

    ScheduleNow

    already out (published_release_episode) and which one comes next.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ScheduleNow.

    SOURCE ↗

    week

    methodasync
    week(*, refresh: bool = False) -> list[ScheduledRelease]

    Get the schedule for the whole current week.

    The list is not ordered by weekday; group or sort it by item.release.publish_day.value (1 = Monday ... 7 = Sunday) as needed.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[ScheduledRelease]

    All releases in this week's schedule.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ScheduledRelease.

    SOURCE ↗

    aniliberty.resources.GenresResource

    class
    GenresResource(http: HttpClient)

    Bases BaseResource

    Anime genres with their images and release counts, available as client.genres.

    To filter the catalog by several genres at once, use client.catalog.releases(genres=...); releases() here lists the releases of one genre.

    SOURCE ↗

    list

    methodasync
    list(*, refresh: bool = False) -> builtins.list[Genre]

    Get all genres.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[Genre]

    Every genre, with image and total_releases filled in.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Genre.

    SOURCE ↗

    get

    methodasync
    get(genre: int | Genre, *, refresh: bool = False) -> Genre

    Get one genre.

    Parameters

    NameTypeDescriptionDefault
    genreint | Genre

    Genre ID, or a Genre (e.g. from client.catalog.references.genres(), which has no image) to fetch its full data.

    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    Genre

    The genre, with image and total_releases.

    Raises

    TypeDescription
    NotFoundError

    No genre with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Genre.

    SOURCE ↗

    random

    methodasync
    random(*, limit: int | None = None) -> builtins.list[Genre]

    Get random genres.

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    How many genres to return. A value outside 1..(number of genres) isn't an error: the API returns all genres instead.

    None

    Returns

    TypeDescription
    builtins.list[Genre]

    The genres, in random order.

    Raises

    TypeDescription
    ValidationError

    limit isn't a number.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Genre.

    SOURCE ↗

    releases

    methodasync
    releases(
        genre: int | Genre,
        *,
        page: int | None = None,
        limit: int | None = None,
        refresh: bool = False,
    ) -> Page[Release]

    Get one page of the releases of a genre.

    The order is the API's own (neither by update date nor by year). For sorting and for combining genres with other filters, use client.catalog.releases(genres=[...]).

    Parameters

    NameTypeDescriptionDefault
    genreint | Genre

    Genre ID or Genre.

    pageint | None

    Page number, starting at 1. The API treats 0 and negative numbers as 1.

    None
    limitint | None

    Page size, 1 to 30 here (not 50 as in the catalog). Omitted, the API uses 15.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    Page[Release]

    One page of releases, with genres filled in.

    Raises

    TypeDescription
    NotFoundError

    No genre with this ID.

    ValidationError

    limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    iter_releases

    method
    iter_releases(
        genre: int | Genre,
        *,
        limit: int | None = None,
        refresh: bool = False,
    ) -> AsyncIterator[Release]

    Iterate over all releases of a genre, fetching page by page.

    Parameters

    NameTypeDescriptionDefault
    genreint | Genre

    Genre ID or Genre.

    limitint | None

    Page size, 1 to 30 — affects how many requests are made, not the result.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    AsyncIterator[Release]

    An async iterator over the releases.

    Raises

    TypeDescription
    TypeError

    genre isn't an ID or a Genre — raised right away.

    SOURCE ↗

    aniliberty.resources.FranchisesResource

    class
    FranchisesResource(http: HttpClient)

    Bases BaseResource

    Franchises — groups of related releases, available as client.franchises.

    SOURCE ↗

    list

    methodasync
    list(*, refresh: bool = False) -> builtins.list[Franchise]

    Get all franchises (a few hundred, in one response).

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[Franchise]

    Every franchise, without franchise_releases.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Franchise.

    SOURCE ↗

    get

    methodasync
    get(franchise: str | Franchise, *, refresh: bool = False) -> Franchise

    Get one franchise with its releases.

    Parameters

    NameTypeDescriptionDefault
    franchisestr | Franchise

    Franchise ID (a UUID string), or a Franchise from list() / random() to fetch its releases.

    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    Franchise

    The franchise, with franchise_releases in order.

    Raises

    TypeDescription
    NotFoundError

    No franchise with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Franchise.

    SOURCE ↗

    random

    methodasync
    random(*, limit: int | None = None) -> builtins.list[Franchise]

    Get random franchises.

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    How many franchises to return. Omitted, 0, or more than there are, the API returns all franchises (a few hundred) — pass a limit.

    None

    Returns

    TypeDescription
    builtins.list[Franchise]

    The franchises, without franchise_releases.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Franchise.

    SOURCE ↗

    for_release

    methodasync
    for_release(
        release: int | Release,
        *,
        refresh: bool = False,
    ) -> builtins.list[Franchise]

    Get the franchises a release belongs to.

    Parameters

    NameTypeDescriptionDefault
    releaseint | Release

    Release ID, or a Release. Only numeric IDs work here: the API answers an alias with a 404 even for an existing release, so a str is rejected.

    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[Franchise]

    The franchises (usually one), with franchise_releases; an empty list if the

    builtins.list[Franchise]

    release isn't part of any franchise.

    Raises

    TypeDescription
    TypeError

    release is not an int or a Release.

    NotFoundError

    No release with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Franchise.

    SOURCE ↗

    aniliberty.resources.TorrentsResource

    class
    TorrentsResource(http: HttpClient)

    Bases BaseResource

    Torrents of releases, their .torrent files and RSS feeds, as client.torrents.

    A torrent is identified by its numeric id or its info hash; every method that takes a torrent also accepts a Torrent object.

    SOURCE ↗

    list

    methodasync
    list(
        *,
        page: int | None = None,
        limit: int | None = None,
    ) -> Page[Torrent]

    Get one page of all torrents.

    Parameters

    NameTypeDescriptionDefault
    pageint | None

    Page number, starting at 1.

    None
    limitint | None

    Page size, 1 to 50.

    None

    Returns

    TypeDescription
    Page[Torrent]

    One page of torrents, each with its release, most recently updated first.

    Raises

    TypeDescription
    ValidationError

    page or limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Torrent.

    SOURCE ↗

    iter_list

    method
    iter_list(*, limit: int | None = None) -> AsyncIterator[Torrent]

    Iterate over all torrents (a few thousand), fetching page by page.

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    Page size, 1 to 50 — affects how many requests are made, not the result.

    None

    Returns

    TypeDescription
    AsyncIterator[Torrent]

    An async iterator over the torrents.

    Raises

    TypeDescription
    ValidationError

    limit is out of range — raised once iteration starts.

    APIError

    The API answered with another error, while iterating.

    TransportError

    A request failed without a response, while iterating.

    SOURCE ↗

    get

    methodasync
    get(torrent: int | str | Torrent) -> Torrent

    Get one torrent by its ID or info hash.

    Parameters

    NameTypeDescription
    torrentint | str | Torrent

    Torrent ID, info hash (any letter case), or a Torrent.

    Returns

    TypeDescription
    Torrent

    The torrent, with release and torrent_members.

    Raises

    TypeDescription
    NotFoundError

    No torrent with this ID or hash.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Torrent.

    SOURCE ↗

    for_release

    methodasync
    for_release(release: int | Release) -> builtins.list[Torrent]

    Get the torrents of a release.

    Parameters

    NameTypeDescription
    releaseint | Release

    Release ID, or a Release. Only numeric IDs work here: the API answers an alias with a 404 even for an existing release, so a str is rejected.

    Returns

    TypeDescription
    builtins.list[Torrent]

    The release's torrents; an empty list if it has none.

    Raises

    TypeDescription
    TypeError

    release is not an int or a Release.

    NotFoundError

    No release with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Torrent.

    SOURCE ↗

    file

    methodasync
    file(torrent: int | str | Torrent) -> TorrentFile

    Download a .torrent file into memory.

    The files checked were 22 to 388 KB. To save one to disk without holding it in memory, use download().

    Parameters

    NameTypeDescription
    torrentint | str | Torrent

    Torrent ID, info hash, or a Torrent.

    Returns

    TypeDescription
    TorrentFile

    The file's bytes and the file name the API suggests.

    Raises

    TypeDescription
    NotFoundError

    No torrent with this ID or hash.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    download

    methodasync
    download(
        torrent: int | str | Torrent,
        *,
        path: str | os.PathLike[str],
    ) -> Path

    Download a .torrent file to disk, streaming it in chunks.

    The file is written to a temporary <name>.part next to the target and renamed only once complete, so a failed download never leaves a truncated .torrent behind. An existing file at the target path is overwritten.

    Parameters

    NameTypeDescription
    torrentint | str | Torrent

    Torrent ID, info hash, or a Torrent.

    pathstr | os.PathLike[str]

    Where to save it: a file path, or an existing directory — then the file is named as the API suggests (falling back to <id or hash>.torrent).

    Returns

    TypeDescription
    Path

    The path of the saved file.

    Raises

    TypeDescription
    NotFoundError

    No torrent with this ID or hash.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    OSError

    The file couldn't be written.

    SOURCE ↗

    rss

    methodasync
    rss(*, limit: int | None = None) -> str

    Get the RSS feed of the latest torrents, as XML text.

    The feed is meant for torrent clients and feed readers; each <item> has the torrent's hash (<guid>), <torrentId>, <releaseId> and an <enclosure> linking to the .torrent file. For the same data as models, use list().

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    How many torrents to include. Omitted, the API includes 10.

    None

    Returns

    TypeDescription
    str

    The RSS 2.0 document.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    rss_for_release

    methodasync
    rss_for_release(release: int | Release) -> str

    Get the RSS feed of a release's torrents, as XML text.

    Parameters

    NameTypeDescription
    releaseint | Release

    Release ID, or a Release (aliases don't work here, as in for_release()).

    Returns

    TypeDescription
    str

    The RSS 2.0 document.

    Raises

    TypeDescription
    TypeError

    release is not an int or a Release.

    NotFoundError

    No release with this ID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    aniliberty.resources.TeamsResource

    class
    TeamsResource(http: HttpClient)

    Bases BaseResource

    AniLibria's own teams, roles and members, available as client.teams.

    Each endpoint returns its whole list in one response (a few dozen teams and roles, a couple of hundred members); there is no pagination or filtering, so filter in Python.

    SOURCE ↗

    list

    methodasync
    list(*, refresh: bool = False) -> builtins.list[Team]

    Get all teams.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[Team]

    Every team, ordered by sort_order.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Team.

    SOURCE ↗

    roles

    methodasync
    roles(*, refresh: bool = False) -> builtins.list[TeamRole]

    Get all roles team members can have.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[TeamRole]

    Every role, ordered by sort_order.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match TeamRole.

    SOURCE ↗

    users

    methodasync
    users(*, refresh: bool = False) -> builtins.list[TeamMember]

    Get all team members, each with their team and roles.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    builtins.list[TeamMember]

    Every member. Not sorted by team: the API interleaves teams, so group by

    builtins.list[TeamMember]

    member.team and sort by member.sort_order yourself if order matters.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match TeamMember.

    SOURCE ↗

    aniliberty.resources.MediaResource

    class
    MediaResource(http: HttpClient)

    Bases BaseResource

    Front-page promo banners and AniLibria's YouTube videos, available as client.media.

    The ads endpoints that live under /media as well (/media/vasts, /media/manifest.xml, spec tag Реклама.Vasts) are deliberately not covered.

    SOURCE ↗

    promotions

    methodasync
    promotions() -> list[Promotion]

    Get the promo banners shown on the site's front page.

    Returns

    TypeDescription
    list[Promotion]

    Every current promotion (a handful), in random order — a new order on each

    list[Promotion]

    call. Paid ads are included; filter them out with promo.is_ad.

    Raises

    TypeDescription
    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Promotion.

    SOURCE ↗

    videos

    methodasync
    videos(
        *,
        limit: int | None = None,
        refresh: bool = False,
    ) -> list[Video]

    Get the latest videos from AniLibria's YouTube playlists, newest first.

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    How many videos to return, 1 to 50. Omitted, the API returns 10. 0 or a negative number returns all videos (over a thousand, more than 1 MB of JSON); more than 50 is a ValidationError.

    None
    refreshbool

    Fetch from the API even if the response is cached (see AniLiberty(cache_dir=...)); the new response replaces the cached one.

    False

    Returns

    TypeDescription
    list[Video]

    The videos, ordered by created_at from newest.

    Raises

    TypeDescription
    ValidationError

    limit is over 50.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Video.

    SOURCE ↗

    aniliberty.resources.AuthResource

    class
    AuthResource(http: HttpClient)

    Bases _SessionResource

    Logging in and out, available as client.auth.

    A successful login stores the session token on the client (client.token), so account methods work right after it. To reuse a session across runs, save client.token and pass it back as AniLiberty(token=...) — logging in on every run piles up sessions on the account.

    Attributes

    NameTypeDescription
    otp

    Logging a device in with a one-time code.

    social

    Logging in with a social network.

    password

    Recovering a forgotten password.

    SOURCE ↗

    login

    methodasync
    login(*, login: str, password: str) -> AuthToken

    Log in with a login (or e-mail) and password, and use the session from now on.

    Parameters

    NameTypeDescription
    loginstr

    The account's login or e-mail; the login is case-insensitive.

    passwordstr

    The account's password.

    Returns

    TypeDescription
    AuthToken

    The new session's token, also stored as client.token.

    Raises

    TypeDescription
    AuthRequiredError

    Wrong login or password (HTTP 401); the API's reason is in message.

    ValidationError

    login or password is empty.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response. Not retried: login is a POST.

    ResponseParseError

    The response didn't match AuthToken.

    SOURCE ↗

    logout

    methodasync
    logout() -> None

    End the client's session and stop sending its token.

    Only this session is ended; other sessions of the account (other devices, other logins) stay valid. client.token is None afterwards, also when the API rejects the token as already invalid.

    Raises

    TypeDescription
    AuthRequiredError

    The client has no token, or the API no longer accepts it (logged out or expired) — the session is over either way (HTTP 403).

    APIError

    The API answered with another error; the token is kept.

    TransportError

    The request failed without a response; the token is kept.

    SOURCE ↗

    aniliberty.resources.OtpResource

    class
    OtpResource(http: HttpClient)

    Bases _SessionResource

    Logging a device in with a one-time code, available as client.auth.otp.

    Meant for devices where typing a password is awkward (a TV app):

    1. The device calls issue(device_id=...) and shows the code.
    2. The user enters the code on a device where they are logged in, which calls accept(code).
    3. The device calls login(code=..., device_id=...) and gets its own session.

    A code lives 5 minutes and logs in once.

    SOURCE ↗

    issue

    methodasync
    issue(*, device_id: str) -> IssuedOtp

    Issue a one-time code for a device. No session needed.

    Asking again for the same device_id while the code is alive returns the same code.

    Parameters

    NameTypeDescription
    device_idstr

    Any string identifying the device, e.g. a UUID it keeps; the same value must be passed to login().

    Returns

    TypeDescription
    IssuedOtp

    The code and the seconds it has left.

    Raises

    TypeDescription
    ValidationError

    device_id is empty.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match IssuedOtp.

    SOURCE ↗

    accept

    methodasync
    accept(code: str | int) -> None

    Approve a one-time code with the client's session, letting its device log in.

    Parameters

    NameTypeDescription
    codestr | int

    The code the other device shows. Pass it as the string issue() returned — as an int a leading 0 would be lost.

    Raises

    TypeDescription
    AuthRequiredError

    The client has no valid session.

    NotFoundError

    No live code like that.

    ValidationError

    code is malformed.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    login

    methodasync
    login(*, code: str | int, device_id: str) -> AuthToken

    Log the device in with an accepted code, and use the session from now on.

    Sent without the client's current token, if it has one: the API refuses this request from a logged-in caller (403). The new token replaces the old one.

    Parameters

    NameTypeDescription
    codestr | int

    The code from issue(), after it was accepted.

    device_idstr

    The same device_id that was passed to issue().

    Returns

    TypeDescription
    AuthToken

    The new session's token, also stored as client.token.

    Raises

    TypeDescription
    NotFoundError

    No live code for this device — wrong code or device_id, expired, or already used.

    ServerError

    The code hasn't been accepted yet — the API answers 500 to that.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match AuthToken.

    SOURCE ↗

    aniliberty.resources.SocialAuthResource

    class
    SocialAuthResource(http: HttpClient)

    Bases _SessionResource

    Logging in with a social network, available as client.auth.social.

    1. login(provider) returns an OAuth url and a state key.
    2. The user opens url in a browser and logs in with the social network.
    3. authenticate(state) exchanges the key for a session.
    SOURCE ↗

    login

    methodasync
    login(provider: SocialProvider) -> SocialLogin

    Start logging in with a social network.

    Parameters

    NameTypeDescription
    providerSocialProvider

    "vk", "google", "patreon" or "discord".

    Returns

    TypeDescription
    SocialLogin

    The OAuth page to send the user to, and the key to finish with.

    Raises

    TypeDescription
    ValueError

    provider isn't one of the above — checked before sending (the API answers 500 to unknown names).

    APIError

    The API answered with an error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match SocialLogin.

    SOURCE ↗

    authenticate

    methodasync
    authenticate(state: str) -> AuthToken

    Finish a social login, and use the session from now on.

    Parameters

    NameTypeDescription
    statestr

    SocialLogin.state from login(), after the user logged in on the OAuth page.

    Returns

    TypeDescription
    AuthToken

    The new session's token, also stored as client.token.

    Raises

    TypeDescription
    NotFoundError

    Unknown state, or the user hasn't finished logging in yet.

    ValidationError

    state is empty.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match AuthToken.

    SOURCE ↗

    aniliberty.resources.PasswordResource

    class
    PasswordResource(http: HttpClient)

    Bases BaseResource

    Recovering a forgotten password, available as client.auth.password.

    SOURCE ↗

    forget

    methodasync
    forget(*, email: str) -> None

    Ask the API to e-mail a password reset link.

    The answer is the same whether or not an account has this e-mail, so it can't be used to check for one.

    Parameters

    NameTypeDescription
    emailstr

    The account's e-mail.

    Raises

    TypeDescription
    ValidationError

    email is empty or not an e-mail address.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    reset

    methodasync
    reset(*, token: str, password: str) -> None

    Set a new password with the token from the reset e-mail.

    Doesn't log in: call client.auth.login() with the new password afterwards.

    Parameters

    NameTypeDescription
    tokenstr

    The token from the link in the e-mail sent by forget().

    passwordstr

    The new password (sent as both password and its confirmation).

    Raises

    TypeDescription
    NotFoundError

    Unknown or used token.

    ValidationError

    The password doesn't meet the API's rules, or a field is empty.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    aniliberty.resources.MeResource

    class
    MeResource(http: HttpClient)

    Bases BaseResource

    The logged-in user's own account, available as client.me.

    Every method needs a session: log in with client.auth.login() or pass AniLiberty(token=...). Without a valid one the API answers 403, raised as AuthRequiredError.

    Attributes

    NameTypeDescription
    favorites

    The user's favorite releases.

    collections

    The user's collections (planned, watched, watching, ...).

    views

    The user's watch progress (timecodes) and history.

    SOURCE ↗

    profile

    methodasync
    profile() -> UserProfile

    Get the logged-in user's profile.

    Returns

    TypeDescription
    UserProfile

    The profile, with the account's e-mail and tracker passkey.

    Raises

    TypeDescription
    AuthRequiredError

    No session token, or the API doesn't accept it (logged out or expired).

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match UserProfile.

    SOURCE ↗

    aniliberty.resources.FavoritesResource

    class
    FavoritesResource(http: HttpClient)

    Bases BaseResource

    The logged-in user's favorite releases, available as client.me.favorites.

    Needs a session (client.auth.login() or AniLiberty(token=...)); without a valid one every method raises AuthRequiredError.

    The API checks little here (see docs/api-notes.md): add() accepts IDs of releases that don't exist — they end up in ids(), though never in releases() — and adding a release twice or removing one that isn't there is silently a no-op.

    Attributes

    NameTypeDescription
    references

    Values the favorites filters accept.

    SOURCE ↗

    ids

    methodasync
    ids() -> list[int]

    Get the IDs of all releases in favorites, in the order they were added.

    Cheaper than releases() for checking whether a release is a favorite.

    Returns

    TypeDescription
    list[int]

    Release IDs.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response wasn't a list of integers.

    SOURCE ↗

    add

    methodasync
    add(*releases: int | Release) -> list[int]

    Add releases to favorites.

    Parameters

    NameTypeDescriptionDefault
    *releasesint | Release

    Release IDs or Release objects, one or more. Releases already in favorites are skipped.

    ()

    Returns

    TypeDescription
    list[int]

    IDs of all releases in favorites afterwards, like ids().

    Raises

    TypeDescription
    ValueError

    No releases given.

    TypeError

    A value is neither an int nor a Release.

    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response. Not retried: it's a POST, though adding again would do no harm.

    ResponseParseError

    The response wasn't a list of integers.

    SOURCE ↗

    remove

    methodasync
    remove(*releases: int | Release) -> list[int]

    Remove releases from favorites.

    Parameters

    NameTypeDescriptionDefault
    *releasesint | Release

    Release IDs or Release objects, one or more. Releases that aren't in favorites are skipped.

    ()

    Returns

    TypeDescription
    list[int]

    IDs of the releases left in favorites, like ids().

    Raises

    TypeDescription
    ValueError

    No releases given.

    TypeError

    A value is neither an int nor a Release.

    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response wasn't a list of integers.

    SOURCE ↗

    releases

    methodasync
    releases(
        *,
        genres: Sequence[int | Genre] | None = None,
        types: Sequence[ReleaseType | ValueDescription[str]] | None = None,
        years: Sequence[int] | None = None,
        search: str | None = None,
        sorting: FavoritesSorting | SortingOption | None = None,
        age_ratings: Sequence[AgeRatingValue | ValueDescription[str]] | None = None,
        page: int | None = None,
        limit: int | None = None,
    ) -> Page[Release]

    Get one page of favorite releases matching the filters.

    The filters combine like the catalog's: a release must have all of genres and any of the types, years and age_ratings; different filters must all match. To go through all pages, use iter_releases().

    Parameters

    NameTypeDescriptionDefault
    genresSequence[int | Genre] | None

    Genre IDs (or Genre objects); a release must have all of them.

    None
    typesSequence[ReleaseType | ValueDescription[str]] | None

    Release types, e.g. ["TV", "MOVIE"]; any of them.

    None
    yearsSequence[int] | None

    Release years; any of them. A range works too: range(2020, 2026).

    None
    searchstr | None

    Text to search in names.

    None
    sortingFavoritesSorting | SortingOption | None

    Sort order. Default: "CREATED_AT_DESC" (added to favorites most recently first). "RATING_*" sorts by how many users have the release in favorites.

    None
    age_ratingsSequence[AgeRatingValue | ValueDescription[str]] | None

    Age ratings, e.g. ["R16_PLUS"]; any of them.

    None
    pageint | None

    Page number, starting at 1. A page past the last one is empty.

    None
    limitint | None

    Page size, 1 to 50. Default: 25.

    None

    Returns

    TypeDescription
    Page[Release]

    One page of releases, with genres and episodes filled in.

    Raises

    TypeDescription
    ValueError

    A filter value isn't one the API knows (it would silently return nothing or ignore the filter instead).

    TypeError

    A list filter got a single value (types="TV", years=2024) or a value of the wrong type.

    AuthRequiredError

    No valid session.

    ValidationError

    page or limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    iter_releases

    method
    iter_releases(
        *,
        genres: Sequence[int | Genre] | None = None,
        types: Sequence[ReleaseType | ValueDescription[str]] | None = None,
        years: Sequence[int] | None = None,
        search: str | None = None,
        sorting: FavoritesSorting | SortingOption | None = None,
        age_ratings: Sequence[AgeRatingValue | ValueDescription[str]] | None = None,
        limit: int | None = None,
    ) -> AsyncIterator[Release]

    Iterate over all favorite releases matching the filters, fetching page by page.

    Takes the same filters as releases(); the next page is requested only once the current one has been consumed.

    Parameters

    NameTypeDescriptionDefault
    genresSequence[int | Genre] | None

    See releases().

    None
    typesSequence[ReleaseType | ValueDescription[str]] | None

    See releases().

    None
    yearsSequence[int] | None

    See releases().

    None
    searchstr | None

    See releases().

    None
    sortingFavoritesSorting | SortingOption | None

    See releases().

    None
    age_ratingsSequence[AgeRatingValue | ValueDescription[str]] | None

    See releases().

    None
    limitint | None

    Page size, 1 to 50 — affects how many requests are made, not the result.

    None

    Returns

    TypeDescription
    AsyncIterator[Release]

    An async iterator over the releases.

    Raises

    TypeDescription
    ValueError

    Invalid filter values — raised right away, not when iteration starts.

    TypeError

    A list filter got a single value or a value of the wrong type.

    SOURCE ↗

    aniliberty.resources.FavoritesReferencesResource

    class
    FavoritesReferencesResource(http: HttpClient)

    Bases _OwnListReferencesResource

    Values the favorites filters accept, available as client.me.favorites.references.

    Unlike client.catalog.references, the lists only contain what occurs in the user's own favorites: the genres, types, years and age ratings of the releases added there. With empty favorites they are empty — except sorting(), which is always the full list.

    SOURCE ↗

    sorting

    methodasync
    sorting() -> list[SortingOption]

    Get the ways favorites can be sorted, e.g. value="CREATED_AT_DESC".

    Returns

    TypeDescription
    list[SortingOption]

    All sorting options, whatever is in favorites.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match SortingOption.

    SOURCE ↗

    genres

    methodasync
    genres() -> list[Genre]

    Get the genres of the releases in the list.

    Returns

    TypeDescription
    list[Genre]

    Genres with id and name only.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Genre.

    SOURCE ↗

    types

    methodasync
    types() -> list[ValueDescription[str]]

    Get the release types in the list, e.g. value="TV", description="ТВ".

    Returns

    TypeDescription
    list[ValueDescription[str]]

    The release types.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ValueDescription.

    SOURCE ↗

    years

    methodasync
    years() -> list[int]

    Get the release years in the list, newest first.

    Returns

    TypeDescription
    list[int]

    The years.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response wasn't a list of integers.

    SOURCE ↗

    age_ratings

    methodasync
    age_ratings() -> list[AgeRating]

    Get the age ratings in the list, e.g. value="R16_PLUS", label="16+".

    Returns

    TypeDescription
    list[AgeRating]

    The age ratings (is_adult is None here).

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match AgeRating.

    SOURCE ↗

    aniliberty.resources.CollectionsResource

    class
    CollectionsResource(http: HttpClient)

    Bases BaseResource

    The user's collections, available as client.me.collections.

    There are five collections (CollectionType): "PLANNED", "WATCHED", "WATCHING", "POSTPONED" and "ABANDONED". A release is in at most one of them: adding it to another collection moves it there. Collections are separate from favorites.

    Needs a session (client.auth.login() or AniLiberty(token=...)); without a valid one every method raises AuthRequiredError. As with favorites, the API accepts IDs of releases that don't exist, and removing a release that isn't in any collection is a no-op.

    The API also applies collection changes in the background (see docs/api-notes.md): a release removed right after being added can be back about a minute later, and removing a release can take minutes to stick. To be sure a change held, check ids() again a minute later.

    Attributes

    NameTypeDescription
    references

    Values the collections filters accept.

    SOURCE ↗

    ids

    methodasync
    ids() -> list[CollectionItem]

    Get every release in the collections, with the collection it's in.

    Returns

    TypeDescription
    list[CollectionItem]

    One item per release, in the order the releases were added.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match CollectionItem.

    SOURCE ↗

    add

    methodasync
    add(
        *releases: int | Release,
        to: CollectionType,
    ) -> list[CollectionItem]

    Put releases into a collection, moving them out of the one they were in.

    Parameters

    NameTypeDescriptionDefault
    *releasesint | Release

    Release IDs or Release objects, one or more.

    ()
    toCollectionType

    The collection, e.g. "WATCHING".

    Returns

    TypeDescription
    list[CollectionItem]

    Every release in the collections afterwards, like ids().

    Raises

    TypeDescription
    ValueError

    No releases given, or to isn't a collection type.

    TypeError

    A release is neither an int nor a Release.

    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match CollectionItem.

    SOURCE ↗

    remove

    methodasync
    remove(*releases: int | Release) -> list[CollectionItem]

    Take releases out of whatever collection they are in.

    Parameters

    NameTypeDescriptionDefault
    *releasesint | Release

    Release IDs or Release objects, one or more.

    ()

    Returns

    TypeDescription
    list[CollectionItem]

    The releases left in the collections, like ids().

    Raises

    TypeDescription
    ValueError

    No releases given.

    TypeError

    A release is neither an int nor a Release.

    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match CollectionItem.

    SOURCE ↗

    releases

    methodasync
    releases(
        collection: CollectionType,
        *,
        genres: Sequence[int | Genre] | None = None,
        types: Sequence[ReleaseType | ValueDescription[str]] | None = None,
        years: Sequence[int] | None = None,
        search: str | None = None,
        age_ratings: Sequence[AgeRatingValue | ValueDescription[str]] | None = None,
        page: int | None = None,
        limit: int | None = None,
    ) -> Page[Release]

    Get one page of the releases in a collection, matching the filters.

    Releases come most recently added first; the API offers no other order. The filters combine like the favorites' ones: all of genres, any of the types, years and age_ratings. To go through all pages, use iter_releases().

    Parameters

    NameTypeDescriptionDefault
    collectionCollectionType

    The collection, e.g. "PLANNED".

    genresSequence[int | Genre] | None

    Genre IDs (or Genre objects); a release must have all of them.

    None
    typesSequence[ReleaseType | ValueDescription[str]] | None

    Release types, e.g. ["TV", "MOVIE"]; any of them.

    None
    yearsSequence[int] | None

    Release years; any of them. A range works too: range(2020, 2026).

    None
    searchstr | None

    Text to search in names.

    None
    age_ratingsSequence[AgeRatingValue | ValueDescription[str]] | None

    Age ratings, e.g. ["R16_PLUS"]; any of them.

    None
    pageint | None

    Page number, starting at 1. A page past the last one is empty.

    None
    limitint | None

    Page size, 1 to 50. Default: 25.

    None

    Returns

    TypeDescription
    Page[Release]

    One page of releases, with genres and episodes filled in.

    Raises

    TypeDescription
    ValueError

    collection or a filter value isn't one the API knows.

    TypeError

    A list filter got a single value or a value of the wrong type.

    AuthRequiredError

    No valid session.

    ValidationError

    page or limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Release.

    SOURCE ↗

    iter_releases

    method
    iter_releases(
        collection: CollectionType,
        *,
        genres: Sequence[int | Genre] | None = None,
        types: Sequence[ReleaseType | ValueDescription[str]] | None = None,
        years: Sequence[int] | None = None,
        search: str | None = None,
        age_ratings: Sequence[AgeRatingValue | ValueDescription[str]] | None = None,
        limit: int | None = None,
    ) -> AsyncIterator[Release]

    Iterate over all releases in a collection matching the filters, page by page.

    Takes the same arguments as releases(); the next page is requested only once the current one has been consumed.

    Parameters

    NameTypeDescriptionDefault
    collectionCollectionType

    See releases().

    genresSequence[int | Genre] | None

    See releases().

    None
    typesSequence[ReleaseType | ValueDescription[str]] | None

    See releases().

    None
    yearsSequence[int] | None

    See releases().

    None
    searchstr | None

    See releases().

    None
    age_ratingsSequence[AgeRatingValue | ValueDescription[str]] | None

    See releases().

    None
    limitint | None

    Page size, 1 to 50 — affects how many requests are made, not the result.

    None

    Returns

    TypeDescription
    AsyncIterator[Release]

    An async iterator over the releases.

    Raises

    TypeDescription
    ValueError

    Invalid collection or filter values — raised right away, not when iteration starts.

    TypeError

    A list filter got a single value or a value of the wrong type.

    SOURCE ↗

    aniliberty.resources.CollectionsReferencesResource

    class
    CollectionsReferencesResource(http: HttpClient)

    Bases _OwnListReferencesResource

    Values the collections filters accept, available as client.me.collections.references.

    The lists cover all the user's collections together — the API has no per-collection variant (a type_of_collection parameter is ignored). Collections can't be sorted, so unlike favorites there is no sorting().

    SOURCE ↗

    genres

    methodasync
    genres() -> list[Genre]

    Get the genres of the releases in the list.

    Returns

    TypeDescription
    list[Genre]

    Genres with id and name only.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Genre.

    SOURCE ↗

    types

    methodasync
    types() -> list[ValueDescription[str]]

    Get the release types in the list, e.g. value="TV", description="ТВ".

    Returns

    TypeDescription
    list[ValueDescription[str]]

    The release types.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match ValueDescription.

    SOURCE ↗

    years

    methodasync
    years() -> list[int]

    Get the release years in the list, newest first.

    Returns

    TypeDescription
    list[int]

    The years.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response wasn't a list of integers.

    SOURCE ↗

    age_ratings

    methodasync
    age_ratings() -> list[AgeRating]

    Get the age ratings in the list, e.g. value="R16_PLUS", label="16+".

    Returns

    TypeDescription
    list[AgeRating]

    The age ratings (is_adult is None here).

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match AgeRating.

    SOURCE ↗

    aniliberty.resources.ViewsResource

    class
    ViewsResource(http: HttpClient)

    Bases BaseResource

    The user's watch progress and history, available as client.me.views.

    Needs a session (client.auth.login() or AniLiberty(token=...)); without a valid one every method raises AuthRequiredError.

    The API stores timecodes in two steps (see docs/api-notes.md):

    • save_timecodes() shows up in timecodes() at once, but in history(), client.episodes.timecode() and client.releases.timecodes() only about a minute later.
    • remove_timecodes() likewise takes about a minute to reach the history, and timecodes() can keep listing the deleted entries after that; deleting them once more after the minute clears them.
    SOURCE ↗

    timecodes

    methodasync
    timecodes(*, since: datetime | None = None) -> list[Timecode]

    Get the user's timecodes — the position in every episode they started.

    Parameters

    NameTypeDescriptionDefault
    sincedatetime | None

    Only timecodes saved at or after this moment. Must be timezone-aware (e.g. datetime.now(UTC) - timedelta(days=7)).

    None

    Returns

    TypeDescription
    list[Timecode]

    The timecodes, in no documented order (updating one doesn't move it).

    Raises

    TypeDescription
    ValueError

    since has no time zone.

    AuthRequiredError

    No valid session.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match Timecode.

    SOURCE ↗

    save_timecodes

    methodasync
    save_timecodes(*timecodes: Timecode) -> None

    Save the position in one or more episodes, replacing what was saved for them.

    Parameters

    NameTypeDescriptionDefault
    *timecodesTimecode

    One Timecode per episode.

    ()

    Raises

    TypeDescription
    ValueError

    No timecodes given.

    TypeError

    A value isn't a Timecode.

    AuthRequiredError

    No valid session.

    ValidationError

    An episode ID isn't a UUID. An ID of an episode that doesn't exist is accepted, and dropped later by the API.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    remove_timecodes

    methodasync
    remove_timecodes(*episodes: str | Episode | Timecode) -> None

    Delete the saved position in one or more episodes (and their history entries).

    Episodes without a timecode are skipped. See the class docs: the deletion takes about a minute, and timecodes() may need a second call after that.

    Parameters

    NameTypeDescriptionDefault
    *episodesstr | Episode | Timecode

    Episode IDs, or Episode/Timecode objects.

    ()

    Raises

    TypeDescription
    ValueError

    No episodes given.

    TypeError

    A value isn't an episode ID, Episode or Timecode.

    AuthRequiredError

    No valid session.

    ValidationError

    An episode ID isn't a UUID.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    SOURCE ↗

    history

    methodasync
    history(
        *,
        page: int | None = None,
        limit: int | None = None,
    ) -> Page[HistoryEntry]

    Get one page of the watch history — one entry per episode, most recent first.

    Parameters

    NameTypeDescriptionDefault
    pageint | None

    Page number, starting at 1. A page past the last one is empty.

    None
    limitint | None

    Page size, 1 to 50. Default: 25.

    None

    Returns

    TypeDescription
    Page[HistoryEntry]

    One page of entries, each with its episode and the episode's release.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session.

    ValidationError

    page or limit is out of range.

    APIError

    The API answered with another error.

    TransportError

    The request failed without a response.

    ResponseParseError

    The response didn't match HistoryEntry.

    SOURCE ↗

    iter_history

    method
    iter_history(*, limit: int | None = None) -> AsyncIterator[HistoryEntry]

    Iterate over the whole watch history, fetching page by page.

    Parameters

    NameTypeDescriptionDefault
    limitint | None

    Page size, 1 to 50 — affects how many requests are made, not the result.

    None

    Returns

    TypeDescription
    AsyncIterator[HistoryEntry]

    An async iterator over the entries, most recent first.

    Raises

    TypeDescription
    AuthRequiredError

    No valid session — raised once iteration starts.

    ValidationError

    limit is out of range — raised once iteration starts.

    APIError

    The API answered with another error, while iterating.

    TransportError

    A request failed without a response, while iterating.

    SOURCE ↗

    Pagination

    aniliberty.Page

    class
    Page()

    Bases AniLibertyModel, Generic[T]

    One page of results from a paginated endpoint.

    Paginated endpoints answer with {"data": [...], "meta": {"pagination": {...}}} (see docs/api-notes.md); Page flattens that into the fields below. Pages are numbered from 1. To walk through all pages, use the matching iter_*() method instead of calling the paginated one in a loop.

    Attributes

    NameTypeDescription
    itemslist[T]

    Items on this page.

    pageint

    Number of this page, starting at 1.

    limitint

    Page size that was requested (per_page in the API).

    totalint

    Number of items on all pages.

    total_pagesint

    Number of pages.

    SOURCE ↗

    has_next_page

    attributeproperty
    has_next_page: bool

    Whether there are more pages after this one.

    SOURCE ↗

    Models

    Releases

    aniliberty.Release

    class
    Release()

    Bases AniLibertyModel

    An anime release.

    Spec schema: models.anime.releases.v1.release plus the extra fields each endpoint adds. List endpoints fill only part of the extras: genres comes with every endpoint except Episode.release, background_covers with random() and get(), members/sponsors with get() and list(), episodes with get(), list() and Episode.release, latest_episode with latest() only. An extra the endpoint doesn't send is an empty list or None.

    Attributes

    NameTypeDescription
    idint

    Release ID.

    aliasstr

    URL slug, e.g. "tensei-shitara-ken-deshita-2nd-season"; get() accepts it as well as the ID.

    nameReleaseName

    Release names.

    typeAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    Release type; type.value is one of TV, ONA, WEB, OVA, OAD, MOVIE, DORAMA, SPECIAL (spec enum enums.anime.releases.release.type).

    yearint | None

    Release year.

    seasonAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    Release season; season.value is one of winter, spring, summer, autumn.

    posterImage | None

    Release poster.

    descriptionstr | None

    Synopsis, in Russian.

    notificationstr | None

    Important notice shown on the release page, if any.

    age_ratingAnnotated[AgeRating | None, EMPTY_VALUE_AS_NONE]

    Age rating.

    publish_dayAnnotated[ValueDescription[int] | None, EMPTY_VALUE_AS_NONE]

    Weekday new episodes come out on; publish_day.value is 1 (Monday) to 7 (Sunday).

    is_ongoingbool

    Whether the anime is still airing in its home country.

    is_in_productionbool

    Whether AniLibria is still dubbing it.

    is_blocked_by_geobool

    Whether it's blocked in the requester's region.

    is_blocked_by_copyrightsbool

    Whether it's blocked by the copyright holder.

    episodes_totalint | None

    Number of episodes, if known.

    average_duration_of_episodeint | None

    Average episode length, in minutes, if known.

    external_playerstr | None

    Link to an external player, if any.

    fresh_atdatetime | None

    When the release was last bumped (e.g. by a new episode).

    created_atdatetime | None

    When the release was added.

    updated_atdatetime | None

    When the release was last edited.

    added_in_users_favoritesint

    How many users added it to favorites.

    added_in_planned_collectionint

    How many users put it in their "planned" collection.

    added_in_watched_collectionint

    How many users put it in "watched".

    added_in_watching_collectionint

    How many users put it in "watching".

    added_in_postponed_collectionint

    How many users put it in "postponed".

    added_in_abandoned_collectionint

    How many users put it in "abandoned".

    shikimoriExternalRating | None

    Entry on Shikimori.

    malExternalRating | None

    Entry on MyAnimeList.

    ratingReleaseRating | None

    Score from AniLibria users.

    genreslist[Genre]

    Genres of the release.

    background_coverslist[ImageSet]

    Wide cover images for a page background.

    memberslist[ReleaseMember]

    People who worked on the release.

    sponsorslist[Sponsor]

    Sponsors shown on the release page.

    episodeslist[Episode]

    All episodes of the release, in order.

    latest_episodeEpisode | None

    The newest episode — only filled in by client.releases.latest().

    SOURCE ↗

    aniliberty.ReleaseName

    class
    ReleaseName()

    Bases AniLibertyModel

    Names of a release.

    Attributes

    NameTypeDescription
    mainstr

    The main (Russian) title.

    englishstr | None

    The English or romanized title.

    alternativestr | None

    Other titles, comma-separated, if any.

    SOURCE ↗

    aniliberty.AgeRating

    class
    AgeRating()

    Bases ValueDescription[str]

    Age rating of a release, e.g. value="R16_PLUS", label="16+".

    value is one of R0_PLUS, R6_PLUS, R12_PLUS, R16_PLUS, R18_PLUS (spec enum enums.anime.releases.release.ageRating).

    Attributes

    NameTypeDescription
    labelstr | None

    Short label, e.g. "16+".

    is_adultbool | None

    Whether the release is for adults only.

    SOURCE ↗

    aniliberty.ReleaseRating

    class
    ReleaseRating()

    Bases AniLibertyModel

    Score given to a release by AniLibria users.

    Attributes

    NameTypeDescription
    averagefloat | None

    Average score from 1 to 10, None if nobody rated it yet.

    votesint

    Number of people who rated it.

    distributiondict[int, int]

    Number of votes per score, for every score from 1 to 10.

    SOURCE ↗

    aniliberty.UserRating

    class
    UserRating()

    Bases AniLibertyModel

    The logged-in user's own score for a release, from client.releases.ratings.

    Spec schema: models.anime.releases.v1.release.rating.own.

    Attributes

    NameTypeDescription
    release_idint

    Release ID.

    scoreint | None

    The user's score, 1 to 10; None if they haven't rated the release.

    SOURCE ↗

    aniliberty.ExternalRating

    class
    ExternalRating()

    Bases AniLibertyModel

    A release's entry and score on an external site (Shikimori or MyAnimeList).

    All fields are None when the release isn't linked to that site.

    Attributes

    NameTypeDescription
    idint | None

    ID of the anime on the external site.

    urlstr | None

    Link to the anime's page there.

    ratingfloat | None

    Average score there, from 1 to 10.

    votesint | None

    Number of people who rated it there.

    SOURCE ↗

    aniliberty.ReleaseMember

    class
    ReleaseMember()

    Bases AniLibertyModel

    A person who worked on a release (voice actor, translator, timer, ...).

    Spec schema: models.anime.releases.v1.release.member.

    Attributes

    NameTypeDescription
    idstr

    Member entry ID (a UUID string).

    nicknamestr

    The member's nickname.

    roleAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    What they did. role.value is one of poster, timing, voicing, editing, decorating, translating, hevc (spec enum enums.anime.releases.release.member.role).

    userLinkedAccount | None

    Linked site account, if any (usually None).

    SOURCE ↗

    aniliberty.Sponsor

    class
    Sponsor()

    Bases AniLibertyModel

    A sponsor shown on a release page.

    Spec schema: models.anime.sponsors.v1.sponsor.

    Attributes

    NameTypeDescription
    idstr

    Sponsor ID.

    titlestr | None

    Sponsor name.

    descriptionstr | None

    Sponsor description.

    urlstr | None

    Sponsor link.

    url_titlestr | None

    Text of the sponsor link.

    SOURCE ↗

    aniliberty.Genre

    class
    Genre()

    Bases AniLibertyModel

    An anime genre.

    Spec schema: models.anime.genres.v1.genre.

    Attributes

    NameTypeDescription
    idint

    Genre ID — the value to pass to catalog genre filters.

    namestr

    Genre name, in Russian (e.g. "Экшен").

    imageImage | None

    Genre cover image.

    total_releasesint | None

    Number of releases with this genre.

    SOURCE ↗

    Episodes

    aniliberty.Episode

    class
    Episode()

    Bases AniLibertyModel

    An episode of a release, with links to its HLS streams.

    Spec schema: models.anime.releases.v1.release.episode. HLS links are absolute URLs of .m3u8 playlists on the video CDN, returned exactly as the API sends them — their query string (country, ads flags, ...) is part of the link. Downloading the video itself is out of scope for the SDK: hand the playlist to a player or to ffmpeg.

    Attributes

    NameTypeDescription
    idstr

    Episode ID (a UUID string) — pass it to client.episodes.get().

    release_idint

    ID of the release the episode belongs to.

    ordinalint | float

    Episode number. Usually an int; the spec allows fractions such as 12.5 for recap/special episodes, which come as a float.

    sort_orderint

    Position of the episode in the release's list.

    namestr | None

    Episode title, in Russian, if it has one (most don't).

    name_englishstr | None

    Episode title in English, if any.

    durationint

    Length, in seconds.

    openingAnnotated[SkipRange | None, BeforeValidator(_empty_skip_as_none)]

    Where the opening is, None if not marked.

    endingAnnotated[SkipRange | None, BeforeValidator(_empty_skip_as_none)]

    Where the ending is, None if not marked.

    previewImage | None

    Still image from the episode. Often present with all paths None.

    hls_480str | None

    HLS playlist, 480p.

    hls_720str | None

    HLS playlist, 720p.

    hls_1080str | None

    HLS playlist, 1080p — missing on some episodes.

    rutube_idstr | None

    ID of the episode on Rutube, if it's published there.

    youtube_idstr | None

    ID of the episode on YouTube, if it's published there.

    updated_atdatetime | None

    When the episode was last updated.

    releaseRelease | None

    The release with all its episodes — only filled in by client.episodes.get().

    SOURCE ↗

    aniliberty.SkipRange

    class
    SkipRange()

    Bases AniLibertyModel

    Where an opening or ending is, so a player can offer to skip it.

    The spec describes the numbers as "seconds from the start or the end of the episode"; in every live sample checked they were seconds from the start (an ending of a 1502-second episode at 1326–1502), see docs/api-notes.md.

    Attributes

    NameTypeDescription
    startint | None

    Where the segment starts, in seconds.

    stopint | None

    Where the segment ends, in seconds.

    SOURCE ↗

    Franchises

    aniliberty.Franchise

    class
    Franchise()

    Bases AniLibertyModel

    A franchise: a group of related releases (seasons, movies, spin-offs).

    Spec schema: models.anime.franchises.v1.franchise. The totals are precomputed by the API and can be missing or stale — two franchises with total_releases=None still had two releases each — so count franchise_releases when the exact number matters.

    Attributes

    NameTypeDescription
    idstr

    Franchise ID (a UUID string).

    namestr

    Name in Russian.

    name_englishstr | None

    Name in English or romanized.

    imageImage | None

    Franchise cover image (its paths can be None).

    ratingfloat | None

    Franchise rating — None for every franchise at the time of writing.

    first_yearint | None

    Year of the first release.

    last_yearint | None

    Year of the latest release.

    total_releasesint | None

    Number of releases.

    total_episodesint | None

    Number of episodes across all releases.

    total_durationstr | None

    Total watch time as human-readable Russian text, e.g. "10 часов".

    total_duration_in_secondsint | None

    Total watch time, in seconds.

    franchise_releaseslist[FranchiseRelease]

    The releases, ordered by sort_order — only filled in by client.franchises.get() and client.franchises.for_release().

    SOURCE ↗

    aniliberty.FranchiseRelease

    class
    FranchiseRelease()

    Bases AniLibertyModel

    One release of a franchise, with its place in the franchise.

    Spec schema: models.anime.franchises.v1.franchise.release.

    Attributes

    NameTypeDescription
    idstr

    ID of this release-to-franchise link (a UUID string).

    franchise_idstr

    ID of the franchise.

    release_idint

    ID of the release.

    sort_orderint

    Position of the release in the franchise, starting at 1 — in every franchise checked, the chronological order.

    releaseRelease

    The release itself (without genres and episodes; fetch it with client.releases.get() for those).

    SOURCE ↗

    Torrents

    aniliberty.Torrent

    class
    Torrent()

    Bases AniLibertyModel

    A torrent of a release.

    Spec schema: models.anime.torrents.v1.torrent.

    Attributes

    NameTypeDescription
    idint

    Torrent ID.

    hashstr

    Info hash (40 hex characters); client.torrents.get() accepts it as well as the ID, in any letter case.

    labelstr

    Full title, e.g. "One Piece - AniLiberty.TOP [WEBRip 1080p][AVC][1-24]".

    filenamestr

    Name of the .torrent file.

    descriptionstr

    Which episodes it contains, e.g. "1-24".

    magnetstr

    Magnet link.

    sizeint

    Total size of the files the torrent downloads, in bytes (not the size of the .torrent file itself).

    typeAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    Source, e.g. "WEB-DL", "BDRip" (spec enum enums.anime.torrents.torrent.type).

    qualityAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    Resolution, e.g. "1080p" (spec enum enums.anime.torrents.torrent.quality).

    codecAnnotated[TorrentCodec | None, EMPTY_VALUE_AS_NONE]

    Video codec.

    colorAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    Color depth, "8bit" or "10Bit" (spec enum enums.anime.torrents.torrent.color).

    bitrateint | None

    Bitrate in kbit/s — usually None.

    is_hardsubbool

    Whether the subtitles are burned into the video.

    seedersint

    Number of seeders.

    leechersint

    Number of leechers.

    completed_timesint

    How many times it was downloaded completely.

    sort_orderint

    Position among the release's torrents.

    created_atdatetime | None

    When the torrent was added.

    updated_atdatetime | None

    When the torrent was last updated.

    releaseRelease | None

    The release (without genres and episodes).

    torrent_memberslist[TorrentMember]

    People credited on the torrent; often empty.

    SOURCE ↗

    aniliberty.TorrentCodec

    class
    TorrentCodec()

    Bases ValueDescription[str]

    Video codec of a torrent, e.g. value="x265/HEVC", label="HEVC".

    value is one of AV1, x264/AVC, x265/HEVC, x265hq/HEVC-HQ (spec enum enums.anime.torrents.torrent.codec).

    Attributes

    NameTypeDescription
    labelstr | None

    Short label the site shows, e.g. "HEVC".

    label_colorstr | None

    Color of that label as #RRGGBB, if it has one.

    label_is_visiblebool | None

    Whether the site shows the label.

    SOURCE ↗

    aniliberty.TorrentMember

    class
    TorrentMember()

    Bases AniLibertyModel

    A person credited on a torrent (e.g. who encoded the HEVC version).

    Attributes

    NameTypeDescription
    idstr

    Member entry ID (a UUID string).

    nicknamestr

    The member's nickname.

    roleAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    What they did; role.value was "HEVC" on every torrent checked.

    external_urlstr | None

    The member's own link (e.g. a Telegram channel), if any.

    userLinkedAccount | None

    Linked site account, if any.

    SOURCE ↗

    aniliberty.TorrentFile

    class
    TorrentFile()

    Bases AniLibertyModel

    A downloaded .torrent file, from client.torrents.file().

    Attributes

    NameTypeDescription
    filenamestr | None

    The file name the API suggests (from Content-Disposition), if it sent one.

    contentbytes

    The file's bytes.

    SOURCE ↗

    Teams

    aniliberty.Team

    class
    Team()

    Bases AniLibertyModel

    An AniLibria team — one of the "leagues" the project's members are grouped into.

    Spec schema: models.teams.v1.team.

    Attributes

    NameTypeDescription
    idstr

    Team ID (a UUID string).

    titlestr

    Team name in Russian, e.g. "II Лига".

    descriptionstr | None

    Short description in Russian, e.g. "Высшая" — None for some teams.

    sort_orderint

    Position of the team in the list, from 0 (project leadership) up.

    SOURCE ↗

    aniliberty.TeamRole

    class
    TeamRole()

    Bases AniLibertyModel

    A role a team member can have: voice actor, translator, timer, ...

    Spec schema: models.teams.v1.team.role.

    Attributes

    NameTypeDescription
    idstr

    Role ID (a UUID string).

    titlestr

    Role name in Russian, e.g. "Актёр".

    colorstr | None

    The role's display color as a CSS hex string, e.g. "#339966" — None for most roles, and not normalized (both "#B523C5" and "#b523c5" occur).

    sort_orderint

    Position of the role in the list.

    SOURCE ↗

    aniliberty.TeamMember

    class
    TeamMember()

    Bases AniLibertyModel

    A member of an AniLibria team, with the team and their roles.

    Spec schema: items of responses.api.v1.teams.users (models.teams.v1.team.user with team, user and roles added).

    Attributes

    NameTypeDescription
    idstr

    Member ID (a UUID string).

    nicknamestr

    The member's nickname in the team — the same name that appears in a release's members.

    is_internbool

    Whether the member is an intern.

    is_vacationbool

    Whether the member is on vacation.

    sort_orderint

    Position of the member within their team, from 1.

    teamTeam

    The team the member belongs to.

    roleslist[TeamRole]

    The member's roles (at least one for every member at the time of writing).

    userLinkedAccount | None

    The linked site account — None for most members.

    SOURCE ↗

    Media

    aniliberty.Promotion

    class
    Promotion()

    Bases AniLibertyModel

    A promo banner from the site's front page: a featured release, a news post or an ad.

    Spec schema: models.media.promotions.v1.promotion. What a promotion links to depends on its kind: a featured release has release and no url; a news post or an ad has url (and usually url_label) and no release.

    Attributes

    NameTypeDescription
    idstr

    Promotion ID (a UUID string).

    titlestr | None

    Banner title, e.g. the release name — can be None.

    descriptionstr | None

    Banner text — can be None.

    imageImage | None

    Banner image.

    urlstr | None

    External link the banner points to (absolute), or None.

    url_labelstr | None

    Text for the link button, e.g. "Подписаться на канал", or None.

    releaseRelease | None

    The featured release (with genres, without episodes), or None.

    is_adbool

    Whether this is a paid ad rather than the site's own promo.

    has_overlaybool

    Whether the site darkens the image under the text.

    ad_eridstr | None

    Ad registry token (ERID) of an ad — None on every promotion at the time of writing.

    ad_originstr | None

    Advertiser of an ad — None on every promotion at the time of writing.

    SOURCE ↗

    aniliberty.Video

    class
    Video()

    Bases AniLibertyModel

    A video from one of AniLibria's YouTube playlists: trailers, reviews, announcements.

    Spec schema: items of responses.v1.media.videos (models.media.videos.v1.video.content with origin added).

    Attributes

    NameTypeDescription
    idint

    Video ID on the site.

    titlestr

    Video title.

    urlstr

    Link to watch the video (https://www.youtube.com/watch?v=...).

    video_idstr

    The video's YouTube ID.

    imageImage | None

    Preview image (its paths can be None).

    viewsint | None

    View count, as last synced from YouTube — can be None.

    commentsint | None

    Comment count, as last synced from YouTube — can be None.

    is_announcebool

    Whether the video is a season announcement.

    originVideoOrigin | None

    The playlist the video comes from.

    created_atdatetime | None

    When the video was published.

    updated_atdatetime | None

    When the site last synced the video's data.

    SOURCE ↗

    aniliberty.VideoOrigin

    class
    VideoOrigin()

    Bases AniLibertyModel

    Where a video comes from: a YouTube playlist the site tracks.

    Spec schema: models.media.videos.v1.video.origin.

    Attributes

    NameTypeDescription
    idstr

    Origin ID (a UUID string).

    titlestr | None

    Playlist or channel name, e.g. "AniLiberty".

    descriptionstr | None

    Playlist description in Russian.

    urlstr | None

    Playlist URL.

    typeAnnotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE]

    Source type. type.value is "YOUTUBE_PLAYLIST" — the only value in the spec (enum enums.media.videos.video.origin.type) and on every video seen.

    is_announcebool

    Whether the playlist holds season announcements.

    SOURCE ↗

    Schedule

    aniliberty.ScheduledRelease

    class
    ScheduledRelease()

    Bases AniLibertyModel

    A release in the airing schedule, with where it stands this week.

    Spec schema: models.anime.schedule.v1.releaseInSchedule. The weekday a release comes out on is release.publish_day (value 1 = Monday ... 7 = Sunday).

    Attributes

    NameTypeDescription
    releaseRelease

    The release, with genres filled in.

    published_release_episodeEpisode | None

    The episode that came out during the current week (Monday to Sunday), or None if this week's episode isn't out yet. It can come out on a different day than publish_day — e.g. a Saturday release whose episode was out on Tuesday.

    next_release_episode_numberint | None

    Number of the next episode to come out, None when no further episode is expected.

    full_season_is_releasedbool

    Whether all episodes of the season are already out.

    SOURCE ↗

    aniliberty.ScheduleNow

    class
    ScheduleNow()

    Bases AniLibertyModel

    Releases scheduled for yesterday, today and tomorrow.

    Spec schema: responses.v1.anime.schedule.now. Each list holds the releases whose publish_day is that day of the week.

    Attributes

    NameTypeDescription
    yesterdaylist[ScheduledRelease]

    Releases that came out (or were due) yesterday.

    todaylist[ScheduledRelease]

    Releases due today.

    tomorrowlist[ScheduledRelease]

    Releases due tomorrow.

    SOURCE ↗

    Catalog

    aniliberty.SortingOption

    class
    SortingOption()

    Bases ValueDescription[str]

    A way to sort a list, from client.catalog.references.sorting() or client.me.favorites.references.sorting().

    value is what the matching sorting filter takes, e.g. "FRESH_AT_DESC".

    Attributes

    NameTypeDescription
    labelstr | None

    Short name, in Russian, e.g. "Обновлены недавно".

    SOURCE ↗

    Catalog models (spec tags Аниме.Каталог and Аниме.Каталог.Справочники).

    ReleaseType

    attribute
    ReleaseType = Literal['TV', 'ONA', 'WEB', 'OVA', 'OAD', 'MOVIE', 'DORAMA', 'SPECIAL']

    Release type. Spec: enums.anime.releases.release.type.

    SOURCE ↗

    Season

    attribute
    Season = Literal['winter', 'spring', 'summer', 'autumn']

    Release season. Spec: enums.anime.releases.release.season.

    SOURCE ↗

    AgeRatingValue

    attribute
    AgeRatingValue = Literal['R0_PLUS', 'R6_PLUS', 'R12_PLUS', 'R16_PLUS', 'R18_PLUS']

    Age rating. Spec: enums.anime.releases.release.ageRating.

    SOURCE ↗

    PublishStatus

    attribute
    PublishStatus = Literal['IS_ONGOING', 'IS_NOT_ONGOING']

    Whether the anime is still airing. Spec: enums.anime.catalog.filter.publishStatus.

    SOURCE ↗

    ProductionStatus

    attribute
    ProductionStatus = Literal['IS_IN_PRODUCTION', 'IS_NOT_IN_PRODUCTION']

    Whether AniLibria is still dubbing it. Spec: enums.anime.catalog.filter.productionStatus.

    SOURCE ↗

    CatalogSorting

    attribute

    Catalog sort order. Spec: enums.anime.catalog.filter.sorting.

    SOURCE ↗

    Common

    aniliberty.ValueDescription

    class
    ValueDescription()

    Bases AniLibertyModel, Generic[T]

    A machine value with its human-readable (Russian) description.

    The API returns many enumerations this way — a release's type, season, publish day, a torrent's quality, ... — e.g. {"value": "TV", "description": "ТВ"} or {"value": 5, "description": "Пятница"} (publish_day uses weekday numbers). Fields with extra keys next to these two (age_rating's label/is_adult, a torrent codec's label_*) use subclasses.

    Attributes

    NameTypeDescription
    valueT

    The machine value — use it for comparisons and catalog filters.

    descriptionstr | None

    The value's description in Russian, for display.

    SOURCE ↗

    aniliberty.Image

    class
    Image()

    Bases ImageSet

    An image in its original format plus optimized (WebP) variants.

    Spec schema: commons.v1.models.components.image.withOptimized — used for release posters, episode previews, genre and franchise images, user avatars.

    Attributes

    NameTypeDescription
    optimizedImageSet | None

    The same image in WebP, usually smaller.

    SOURCE ↗

    aniliberty.ImageSet

    class
    ImageSet()

    Bases AniLibertyModel

    Links to one image in several sizes.

    The API sends relative paths such as /storage/releases/posters/1/abc.jpg; they resolve against the site's origin, not the /api/v1 base URL (see docs/api-notes.md). The raw paths are kept in src/preview/thumbnail, and the *_url properties return absolute URLs on the host the client talked to (or on https://aniliberty.top for a model built by hand).

    Any of the paths can be None — e.g. a user without an avatar gets an image object whose paths are all None.

    Attributes

    NameTypeDescription
    srcstr | None

    Original image path. Only posters and episode previews have it.

    previewstr | None

    Path of the medium-size image.

    thumbnailstr | None

    Path of the small image.

    SOURCE ↗

    src_url

    attributeproperty
    src_url: str | None

    Absolute URL of src.

    SOURCE ↗

    preview_url

    attributeproperty
    preview_url: str | None

    Absolute URL of preview.

    SOURCE ↗

    thumbnail_url

    attributeproperty
    thumbnail_url: str | None

    Absolute URL of thumbnail.

    SOURCE ↗

    aniliberty.LinkedAccount

    class
    LinkedAccount()

    Bases AniLibertyModel

    The site account linked to a person in the credits or in a team.

    The user of ReleaseMember, TorrentMember and TeamMember — set when the person has an account on the site, None otherwise. Spec schemas: models.teams.v1.team.user.account and the user of release/torrent members.

    Attributes

    NameTypeDescription
    idint

    User ID on the site.

    nicknamestr | None

    The account's nickname. Only team members' accounts have it (it can differ from the team nickname); for release and torrent members it's None — their own nickname is on the member.

    avatarImage | None

    The account's avatar (its paths can be None).

    SOURCE ↗

    Building blocks shared by many models: value + description pairs and images.

    EMPTY_VALUE_AS_NONE

    attribute
    EMPTY_VALUE_AS_NONE = BeforeValidator(_empty_value_as_none)

    Turns {"value": null, ...} into None for an optional ValueDescription field.

    The API sometimes sends a value + description object with both keys null — seen on season of releases in the schedule — instead of omitting it. Annotate such fields as::

    TEXT
    season: Annotated[ValueDescription[str] | None, EMPTY_VALUE_AS_NONE] = None
    SOURCE ↗

    Service

    aniliberty.ApiStatus

    class
    ApiStatus()

    Bases AniLibertyModel

    Status of the API, returned by client.app.status().

    Spec schema: responses.v1.app.status.

    Attributes

    NameTypeDescription
    is_alivebool

    Whether the API reports itself as working.

    available_api_endpointslist[str]

    Hosts the API can be reached at, e.g. ["https://aniliberty.top"] — origins without the /api/v1 suffix.

    requestRequestInfo | None

    What the API knows about the caller, if it sent that.

    SOURCE ↗

    aniliberty.RequestInfo

    class
    RequestInfo()

    Bases AniLibertyModel

    What the API knows about the client that sent the request (geolocated by IP).

    Spec schema: responses.v1.app.status → request. All fields are optional: the spec marks none of them required, and geolocation can fail.

    Attributes

    NameTypeDescription
    ipstr | None

    The client's IP address as seen by the API.

    countrystr | None

    Country name, in English (e.g. "The Netherlands").

    iso_codestr | None

    ISO 3166-1 alpha-2 country code (e.g. "NL").

    timezonestr | None

    IANA time zone name (e.g. "Europe/Amsterdam").

    SOURCE ↗

    Authentication

    aniliberty.AuthToken

    class
    AuthToken()

    Bases AniLibertyModel

    A session token, returned by the login methods of client.auth.

    Spec schemas: responses.v1.accounts.users.auth.login, responses.api.v1.accounts.otp.login, responses.v1.accounts.users.auth.social.authenticate.

    The token is an opaque string (32 characters at the time of writing) with no expiry inside; the API has no refresh endpoint. Every login creates a new, independent session, and client.auth.logout() ends only the client's own.

    Attributes

    NameTypeDescription
    tokenstr

    The session token. Keep it secret — it gives full access to the account.

    SOURCE ↗

    aniliberty.IssuedOtp

    class
    IssuedOtp()

    Bases AniLibertyModel

    A one-time code issued by client.auth.otp.issue().

    Spec schema: responses.api.v1.accounts.otp.get.

    Attributes

    NameTypeDescription
    otpOtp

    The code.

    remaining_timefloat

    Seconds until the code expires (fractional; about 300 for a new code).

    SOURCE ↗

    aniliberty.Otp

    class
    Otp()

    Bases AniLibertyModel

    A one-time code for logging in a device without typing the password.

    Spec schema: models.accounts.otp.v1.otp.

    Attributes

    NameTypeDescription
    codestr

    The code to show to the user, as a string — it can start with 0.

    device_idstr

    The device the code was issued for.

    user_idint | None

    The account that accepted the code; None until it's accepted.

    expired_atdatetime

    When the code expires.

    SOURCE ↗

    aniliberty.SocialLogin

    class
    SocialLogin()

    Bases AniLibertyModel

    Where to send the user to log in with a social network.

    Spec schema: responses.v1.accounts.users.auth.social.login.

    Attributes

    NameTypeDescription
    urlstr

    The OAuth page of the social network, to open in a browser.

    statestr

    Key to pass to client.auth.social.authenticate() once the user has logged in there.

    SOURCE ↗

    Authentication models (spec tags Аккаунты.Пользователи.Авторизация.* and Аккаунты.ОдноразовыеПароли).

    SocialProvider

    attribute
    SocialProvider = Literal['vk', 'google', 'patreon', 'discord']

    Social networks that can be used to log in.

    SOURCE ↗

    Account

    aniliberty.UserProfile

    class
    UserProfile()

    Bases AniLibertyModel

    The logged-in user's own profile, returned by client.me.profile().

    Spec schema: models.users.v1.user. email and torrents.passkey are only sent for the user's own profile.

    Attributes

    NameTypeDescription
    idint

    User ID.

    loginstr

    Login, used to log in (together with email).

    emailstr | None

    E-mail of the account.

    nicknamestr | None

    Display name; defaults to the login for a new account.

    avatarImage | None

    Avatar; all paths are None without one.

    torrentsTorrentStats | None

    Tracker passkey and traffic.

    is_bannedbool | None

    Whether the user is banned on the site.

    created_atdatetime | None

    When the account was created.

    is_with_adsbool | None

    Whether ads are shown to the user — in the spec, but not sent by the API at the time of writing (see the three flags below).

    is_with_video_adsbool | None

    Whether video ads are shown (not in the spec).

    is_with_content_adsbool | None

    Whether ads are shown among the site's content (not in the spec).

    is_with_video_ads_alwaysbool | None

    An ad flag the API doesn't document (not in the spec); it matches the isWithVideoAdsAlways parameter of the user's HLS links.

    SOURCE ↗

    aniliberty.TorrentStats

    class
    TorrentStats()

    Bases AniLibertyModel

    The account's torrent tracker data.

    Spec schema: models.users.v1.user → torrents.

    Attributes

    NameTypeDescription
    passkeystr | None

    The account's tracker passkey — the pk of .torrent and RSS links, also put into magnet links of torrents when logged in. A secret: it's left out of the model's repr.

    uploadedint | None

    Total uploaded, in bytes; None until the account has uploaded anything.

    downloadedint | None

    Total downloaded, in bytes; None until the account has downloaded anything.

    SOURCE ↗

    aniliberty.CollectionItem

    class
    CollectionItem()

    Bases AniLibertyModel

    A release in one of the user's collections, from client.me.collections.ids().

    Spec schema: responses.api.v1.accounts.users.me.collections.ids.item — the API sends a [release_id, collection] pair, e.g. [9839, "WATCHED"]; it's validated into this model (a dict with the field names works too).

    Attributes

    NameTypeDescription
    release_idint

    Release ID.

    collectionCollectionType

    The collection the release is in. A release is in one collection at most.

    SOURCE ↗

    User account models (spec tags Аккаунты.Пользователи.Мое.*).

    FavoritesSorting

    attribute

    Sort order of favorites; CREATED_AT_* is when the release was added to favorites.

    SOURCE ↗

    CollectionType

    attribute
    CollectionType = Literal['PLANNED', 'WATCHED', 'WATCHING', 'POSTPONED', 'ABANDONED']

    A user's collection: planned, watched, watching, postponed or abandoned.

    SOURCE ↗

    Watch progress

    aniliberty.Timecode

    class
    Timecode()

    Bases AniLibertyModel

    Where the user stopped in an episode, and whether they finished it.

    What client.me.views.save_timecodes() takes and client.me.views.timecodes() returns. Spec schema: responses.v1.accounts.users.me.views.timecodes.item — the API sends an [episode_id, time, is_watched] triple, which is validated into this model (a dict with the field names works too).

    Attributes

    NameTypeDescription
    release_episode_idstr

    Episode ID (a UUID string), as in Episode.id.

    timefloat

    Position in the episode, in seconds from its start (fractions allowed, not negative).

    is_watchedbool

    Whether the episode counts as watched.

    SOURCE ↗

    aniliberty.EpisodeView

    class
    EpisodeView()

    Bases AniLibertyModel

    The stored watch record of one episode.

    Returned by client.episodes.timecode() and client.releases.timecodes(). Spec schema: models.accounts.users.v1.user.view. Records are written by the API about a minute after client.me.views.save_timecodes() (see docs/api-notes.md).

    Attributes

    NameTypeDescription
    idint

    Record ID.

    release_episode_idstr

    Episode ID (a UUID string).

    timefloat

    Position in the episode, in seconds from its start.

    is_watchedbool

    Whether the episode counts as watched.

    updated_atdatetime | None

    When the record was last saved.

    user_idint | None

    The user the record belongs to.

    SOURCE ↗

    aniliberty.HistoryEntry

    class
    HistoryEntry()

    Bases EpisodeView

    An entry of the watch history, from client.me.views.history().

    Spec schema: responses.v1.accounts.users.me.views.history → data[].

    Attributes

    NameTypeDescription
    release_episodeEpisode | None

    The episode, with its release in release_episode.release.

    SOURCE ↗

    Exceptions

    aniliberty.AniLibertyError

    class
    AniLibertyError()

    Bases Exception

    Base class for all errors raised by the SDK.

    SOURCE ↗

    aniliberty.APIError

    class
    APIError(
        message: str,
        *,
        status_code: int,
        method: str,
        url: str,
        payload: Any = None,
    )

    Bases AniLibertyError

    The API responded with an error status.

    Raised as is for statuses without a dedicated subclass (e.g. 400, 405, 409); otherwise one of the subclasses below is raised, so except APIError catches all of them.

    Attributes

    NameTypeDescription
    status_code

    HTTP status code of the response.

    message

    Error message — the message field of the API's JSON body when it has one, otherwise the HTTP reason phrase.

    method

    HTTP method of the failed request.

    url

    Full URL of the failed request.

    payload

    The decoded JSON body of the response, or its text if it wasn't JSON.

    SOURCE ↗

    aniliberty.NotFoundError

    class
    NotFoundError(
        message: str,
        *,
        status_code: int,
        method: str,
        url: str,
        payload: Any = None,
    )

    Bases APIError

    The requested resource doesn't exist (404).

    The API's own message for this status is an internal Laravel lookup string such as "No query results for model [...]" — it's kept in payload, while message is a plain "Not found".

    SOURCE ↗

    aniliberty.ValidationError

    class
    ValidationError(
        message: str,
        *,
        status_code: int,
        method: str,
        url: str,
        payload: Any = None,
        errors: dict[str, list[str]] | None = None,
    )

    Bases APIError

    The API rejected the request parameters (422).

    Not to be confused with pydantic.ValidationError: this one means the request was invalid, e.g. limit above the endpoint's maximum. A response that doesn't fit the SDK's models raises ResponseParseError instead.

    Attributes

    NameTypeDescription
    errorsdict[str, list[str]]

    Messages per parameter, as the API returns them (in Russian), e.g. {"limit": ["Поле limit не может быть больше 50."]}.

    SOURCE ↗

    aniliberty.RateLimitError

    class
    RateLimitError(
        message: str,
        *,
        status_code: int,
        method: str,
        url: str,
        payload: Any = None,
        retry_after: float | None = None,
    )

    Bases APIError

    The API answered 429 Too Many Requests.

    Attributes

    NameTypeDescription
    retry_after

    The response's Retry-After value, in seconds, if it sent one.

    SOURCE ↗

    aniliberty.AuthRequiredError

    class
    AuthRequiredError(
        message: str,
        *,
        status_code: int,
        method: str,
        url: str,
        payload: Any = None,
    )

    Bases APIError

    The request needs a valid session, or the credentials were wrong (401/403 JSON).

    The API answers 403 "This action is unauthorized." to an account endpoint called without a session token, with a malformed one, or with one that was logged out or has expired — it doesn't say which. Log in again (client.auth.login()) or pass a fresh token to AniLiberty. Wrong credentials at client.auth.login() are a 401; the API's reason is in message.

    SOURCE ↗

    aniliberty.ServerError

    class
    ServerError(
        message: str,
        *,
        status_code: int,
        method: str,
        url: str,
        payload: Any = None,
    )

    Bases APIError

    The API (or the CDN in front of it) failed with a 5xx status.

    SOURCE ↗

    aniliberty.AccessBlockedError

    class
    AccessBlockedError(
        message: str,
        *,
        status_code: int,
        method: str,
        url: str,
        payload: Any = None,
    )

    Bases APIError

    Something in front of the API answered with an HTML page instead of JSON.

    The SDK always sends Accept: application/json, and with that header the API itself answers with JSON even for errors (see docs/api-notes.md). An HTML page therefore comes from the Cloudflare edge in front of the API — a bot challenge or a block of the network/IP. Resending the same request gets the same answer, so this is not retried.

    HTML on a 5xx status is not this error but ServerError: Cloudflare renders its own origin errors (502, 520, ...) as HTML pages too, and those are worth retrying.

    SOURCE ↗

    aniliberty.TransportError

    class
    TransportError(message: str, *, method: str, url: str)

    Bases AniLibertyError

    The request failed before any response arrived (connection error, timeout, ...).

    The underlying httpx exception is available as __cause__.

    Attributes

    NameTypeDescription
    method

    HTTP method of the failed request.

    url

    Full URL of the failed request.

    SOURCE ↗

    aniliberty.ResponseParseError

    class
    ResponseParseError(message: str, *, url: str, payload: Any = None)

    Bases AniLibertyError

    A successful response couldn't be decoded or didn't match the SDK's model.

    Usually means the API changed its response format. The raw data is kept so it isn't lost: payload holds the decoded JSON (or the body text if it wasn't JSON), and the original json/pydantic exception is available as __cause__.

    Attributes

    NameTypeDescription
    url

    Full URL of the request.

    payload

    Decoded JSON body, or the body text if it wasn't valid JSON.

    SOURCE ↗

    Low-level HTTP client

    Available as client.http; client.http.request(...) is the escape hatch for endpoints the SDK doesn't cover yet.

    aniliberty.HttpClient

    class
    HttpClient(
        *,
        base_url: str = DEFAULT_BASE_URL,
        timeout: float = DEFAULT_TIMEOUT,
        headers: Mapping[str, str] | None = None,
        http_client: httpx.AsyncClient | None = None,
        token: str | None = None,
        max_concurrency: int = DEFAULT_MAX_CONCURRENCY,
        request_delay: float = DEFAULT_REQUEST_DELAY,
        max_retries: int = DEFAULT_MAX_RETRIES,
        retry_base_delay: float = DEFAULT_RETRY_BASE_DELAY,
        max_retry_after: float = DEFAULT_MAX_RETRY_AFTER,
        cache_dir: str | os.PathLike[str] | None = None,
        cache_ttl: float | None = None,
        sleep: Callable[[float], Awaitable[None]] = asyncio.sleep,
        clock: Callable[[], float] = time.monotonic,
        cache_clock: Callable[[], float] = time.time,
    )

    Sends requests to the API and turns responses into JSON or SDK exceptions.

    Every resource of AniLiberty goes through this class; it is also the escape hatch for endpoints the SDK doesn't cover yet: await client.http.request("GET", "/teams/roles") returns the decoded JSON as is, with the same headers, limits, retries and error handling as the rest of the SDK.

    Request paths are relative to base_url and joined as strings, so "/app/status" with the default base URL becomes https://aniliberty.top/api/v1/app/status. Absolute URLs are not accepted.

    Being polite to the API is built in:

    • Concurrency — at most max_concurrency requests are in flight at once, however many coroutines call the client (e.g. via asyncio.gather).
    • Pacing — consecutive requests start at least request_delay seconds apart.
    • Retries — 429 is retried for any method, 5xx and network errors/timeouts only for idempotent methods (IDEMPOTENT_METHODS). The delay is the response's Retry-After when it has one, otherwise retry_base_delay doubled per attempt (1 s, 2 s, 4 s by default). Other 4xx and HTML block pages are never retried.

    With cache_dir set, request(..., cache=True) keeps the decoded JSON of successful GET responses on disk and answers repeated identical requests from there until cache_ttl passes — see request() for what is and isn't cached.

    SOURCE ↗

    base_url

    attributeproperty
    base_url: str

    The API base URL, without a trailing slash.

    SOURCE ↗

    site_url

    attributeproperty
    site_url: str

    Origin of base_url (scheme and host), e.g. https://aniliberty.top.

    Relative paths in responses, such as image paths, resolve against it.

    SOURCE ↗

    cache_dir

    attributeproperty
    cache_dir: Path | None

    The response cache directory, or None when caching is off.

    SOURCE ↗

    token

    attributeproperty
    token: str | None

    Session token sent as Authorization: Bearer <token>, or None for none.

    Set it to start or stop sending the token; it replaces an Authorization header given in headers. Public endpoints work with or without it (even with an expired one); account endpoints answer 403 without a valid one.

    SOURCE ↗

    closed

    attributeproperty
    closed: bool

    Whether aclose() has been called.

    SOURCE ↗

    aclose

    methodasync
    aclose() -> None

    Close the underlying httpx.AsyncClient, unless it was passed in by the caller.

    Safe to call more than once.

    SOURCE ↗

    url

    method
    url(path: str) -> str

    Return the full URL for an API path such as "/app/status".

    SOURCE ↗

    request

    methodasync
    request(
        method: str,
        path: str,
        *,
        params: Mapping[str, Any] | None = None,
        json: Any = None,
        with_token: bool = True,
        cache: bool = False,
        refresh: bool = False,
    ) -> Any

    Send a request and return the decoded JSON body.

    Retries and limits described on the class apply.

    A response is read from and saved to the cache only when all of these hold: the client has a cache_dir, cache=True, the method is GET, and no session token goes with the request — so nothing that depends on the logged-in user (or carries their torrent passkey) ever lands on disk. The cache key is the method, the full URL and the query parameters in sorted order. Only successful responses with a body are cached; errors are raised as usual and never stored.

    Parameters

    NameTypeDescriptionDefault
    methodstr

    HTTP method, e.g. "GET".

    pathstr

    Path relative to base_url, e.g. "/anime/releases/latest".

    paramsMapping[str, Any] | None

    Query parameters, passed to httpx as is.

    None
    jsonAny

    JSON request body.

    None
    with_tokenbool

    Whether to send token. False for the endpoints that refuse a logged-in caller (POST /accounts/otp/login answers 403 to one).

    True
    cachebool

    Whether the response may be read from and saved to the cache (see above). The SDK's own methods pass it for endpoints whose data doesn't change from call to call.

    False
    refreshbool

    Skip reading the cache and fetch from the API; the fresh response still replaces the cached one.

    False

    Returns

    TypeDescription
    Any

    The decoded JSON body (dict, list, ...), or None for an empty body.

    Raises

    TypeDescription
    APIError

    The API answered with an error status — one of its subclasses for 404, 422, 429, 401/403, 5xx and HTML pages. 429 and 5xx are raised only once retries are exhausted.

    TransportError

    The request failed without a response (network error, timeout), after retries for idempotent methods.

    ResponseParseError

    A successful response wasn't valid JSON.

    RuntimeError

    The client has been closed.

    SOURCE ↗

    request_raw

    methodasync
    request_raw(
        method: str,
        path: str,
        *,
        params: Mapping[str, Any] | None = None,
    ) -> httpx.Response

    Send a request for a non-JSON resource (a file, an XML feed) and return the response.

    Same limits, retries and error handling as request(); the body is fully read, so use stream() instead for large downloads.

    Parameters

    NameTypeDescriptionDefault
    methodstr

    HTTP method, e.g. "GET".

    pathstr

    Path relative to base_url.

    paramsMapping[str, Any] | None

    Query parameters, passed to httpx as is.

    None

    Returns

    TypeDescription
    httpx.Response

    The successful httpx.Response, with content/text and headers.

    Raises

    TypeDescription
    APIError

    The API answered with an error status — see request().

    TransportError

    The request failed without a response.

    RuntimeError

    The client has been closed.

    SOURCE ↗

    stream

    methodasync
    stream(
        method: str,
        path: str,
        *,
        params: Mapping[str, Any] | None = None,
    ) -> AsyncIterator[httpx.Response]

    Send a request and stream the response body, for downloads.

    Limits and retries apply until the response headers arrive: a 429/5xx is retried before anything is yielded, and an error status raises like request(). The body is then read by the caller (response.aiter_bytes()); the response is closed when the async with block exits.

    Parameters

    NameTypeDescriptionDefault
    methodstr

    HTTP method, e.g. "GET".

    pathstr

    Path relative to base_url.

    paramsMapping[str, Any] | None

    Query parameters, passed to httpx as is.

    None

    Yields

    TypeDescription
    AsyncIterator[httpx.Response]

    The successful httpx.Response, body not read yet.

    Raises

    TypeDescription
    APIError

    The API answered with an error status — see request().

    TransportError

    The request failed without a response.

    RuntimeError

    The client has been closed.

    SOURCE ↗