LIBSOCIAL-CORE

    libsocial-core API reference

    Текст страницы — оригинал из репозитория SDK, на английском. Исходный файл ↗

    import libsocial. Most users get these through ranobelib or mangalib, which re-export the models and exceptions — see libsocial-core.

    Building blocks

    libsocial.Site

    класс
    Site(id: int, origin: str)

    One lib.social site, as the shared api.cdnlibs.org API tells them apart.

    The core knows no site by itself: each site package defines its own (ranobelib.me is Site(id=3, origin="https://ranobelib.me"), mangalib.me id=1) and passes it in.

    Атрибуты

    ИмяТипОписание
    idint

    The site's id — sent as the Site-Id header on title/chapter requests and as site_id[] on catalog listings, and used to filter the network-wide /constants lists (genres, types, statuses) down to this site. Without the right one, the API answers 404 for titles that exist (see docs/api-notes.md).

    originstr

    The site's web origin, e.g. "https://ranobelib.me" — sent as Origin/Referer so requests look like they come from the site's own pages (see docs/api-notes.md, section "WAF 403"), and the base for site-relative URLs.

    ИСХОДНИК ↗

    libsocial.TitleClient

    класс
    TitleClient(
        url: str,
        *,
        cache_dir: str | Path | None = None,
        cache_ttl: float | None = None,
        verbosity: Verbosity = False,
        headers: Mapping[str, str] | None = None,
    )

    Базовые классы Generic[TitleT, ChapterT, VolumeT]

    One title on one lib.social site: metadata, chapters, translations, bulk download.

    Not used directly: a site package subclasses it, setting the class attributes below — which site it talks to, and which models a title, a chapter (with the site's own content fields) and a volume are validated as — and adds whatever is specific to the site's content (export, size estimates, ...).

    Атрибуты

    ИмяТипОписание
    siteSite

    The site the title is on. Set by the subclass.

    title_modeltype[TitleT]

    The model get_info() returns. Set by the subclass.

    chapter_modeltype[ChapterT]

    The model chapters are validated as — both table-of-contents entries and fetched chapters, so its content fields must be optional. Set by the subclass.

    volume_modeltype[VolumeT]

    The model chapters are grouped into, Volume[chapter_model] or a subclass of it. Set by the subclass.

    default_cache_dirPath

    cache_dir used when none is passed.

    info_fieldstuple[str, ...]

    Extra fields[] get_info() requests on top of the API's default response (see docs/api-notes.md for what each adds).

    ИСХОДНИК ↗

    slug_url

    атрибутсвойство
    slug_url: str

    The title's {id}--{slug} identifier.

    ИСХОДНИК ↗

    aclose

    методasync
    aclose() -> None

    Close the underlying HTTP client.

    ИСХОДНИК ↗

    download_title

    методasync
    download_title(
        *,
        branch_id: int | None = None,
        translation_index: int | None = None,
        chapter_delay: float = 0.0,
        on_chapter: Callable[[int, int], None] | None = None,
        max_rate_limit_retries: int = DEFAULT_RATE_LIMIT_RETRIES,
    ) -> list[VolumeT]

    Download every chapter of the title, across all its volumes.

    Fetches the chapter list once, then every chapter's content, sequentially, in the API's own order — same pacing/retry/backoff as any other chapter fetch (see ApiClient), plus chapter_delay on top if given.

    Resolving translations is checked for the whole title up front, not chapter by chapter: if any chapters have more than one translation, this fetches nothing and raises MultipleTitleTranslationsError listing all of them at once (unless branch_id or translation_index resolves every one of them), rather than failing partway through a long download on the first ambiguous chapter.

    A long title's sequential download can outlast ApiClient's own retry budget for 429s (tuned for a single one-off request, not hundreds/thousands of sequential fetches — see docs/api-notes.md). On top of that, each chapter fetch that still comes back rate-limited is retried again here, up to max_rate_limit_retries times, with its own (coarser) backoff. If a chapter fetch still fails after that — from rate limiting or anything else — the chapters already downloaded are not discarded: they're attached to a raised DownloadTitleInterruptedError instead of being lost.

    Параметры

    ИмяТипОписаниеПо умолчанию
    branch_idint | None

    Translation to use for every chapter that has more than one — same meaning as get_chapter(..., branch_id=...), applied title-wide. Chapters with only one translation are unaffected. Mutually exclusive with translation_index.

    None
    translation_indexint | None

    Alternative to branch_id: pick the translation at this position in each ambiguous chapter's branches list (0 for the first, and so on) — useful when ambiguous chapters don't share one branch_id across the title. Mutually exclusive with branch_id.

    None
    chapter_delayfloat

    Extra delay, in seconds, after fetching each chapter, on top of ApiClient's own per-request pacing — to be gentler on the API during a large bulk download. Defaults to no extra delay.

    0.0
    on_chapterCallable[[int, int], None] | None

    Called with (completed, total) after each chapter is fetched, if given — a programmatic progress hook for callers that aren't printing to a console (e.g. a web app polling/streaming download progress to a browser), who would otherwise have no way to observe anything before this whole await returns. Independent of verbosity: both can be set at once, each drives its own output.

    None
    max_rate_limit_retriesint

    How many extra times to retry a single chapter fetch that comes back rate-limited, on top of ApiClient's own retries, before giving up on the whole download — see above. Each retry honors the API's Retry-After if it sent one, otherwise waits with its own capped exponential backoff. Set to 0 to disable this extra layer and let a persistent RateLimitError end the download immediately (still wrapped in DownloadTitleInterruptedError with whatever was already fetched).

    DEFAULT_RATE_LIMIT_RETRIES

    Исключения

    ТипОписание
    ValueError

    If both branch_id and translation_index are given.

    MultipleTitleTranslationsError

    If the title has one or more chapters with more than one translation that branch_id/translation_index didn't resolve.

    DownloadTitleInterruptedError

    If fetching a chapter fails unrecoverably partway through the download — carries every chapter already fetched.

    ИСХОДНИК ↗

    get_chapter

    методasync
    get_chapter(
        volume: int,
        number: str,
        *,
        branch_id: int | None = None,
        refresh: bool = False,
    ) -> ChapterT

    Fetch a single chapter, including its content.

    When a chapter has more than one team's translation and branch_id isn't given, this raises MultipleTranslationsError rather than guessing — see get_translations() and docs/api-notes.md for why the API's own default (when branch_id is omitted) isn't relied on. Doing this check costs an extra request to fetch the chapter list, only when branch_id isn't already given.

    Параметры

    ИмяТипОписаниеПо умолчанию
    volumeint

    The chapter's volume number.

    numberstr

    The chapter number, as returned by the API — may contain a decimal (e.g. "51.6").

    branch_idint | None

    Which translation to fetch, as returned by get_translations(). Only required when the chapter has more than one.

    None
    refreshbool

    Bypass the disk cache and re-fetch from the API even if a cached response is on hand.

    False

    Исключения

    ТипОписание
    MultipleTranslationsError

    If the chapter has more than one translation and branch_id wasn't given.

    ИСХОДНИК ↗

    get_chapters

    методasync
    get_chapters(chapters: list[tuple[int, str]]) -> list[ChapterT]

    Fetch several chapters, including their content.

    Fetches the chapter list once, shared across all requested chapters (also used to detect chapters with more than one translation — see get_chapter), then each chapter individually and sequentially, in the order given.

    Параметры

    ИмяТипОписание
    chapterslist[tuple[int, str]]

    A list of (volume, number) pairs identifying each chapter.

    Исключения

    ТипОписание
    ChapterNotFoundError

    If any requested chapter doesn't exist.

    MultipleTranslationsError

    If any requested chapter has more than one translation.

    ИСХОДНИК ↗

    get_info

    методasync
    get_info(*, refresh: bool = False) -> TitleT

    Fetch title metadata: names, cover, summary, genres, tags, authors, teams.

    Параметры

    ИмяТипОписаниеПо умолчанию
    refreshbool

    Bypass the disk cache and re-fetch from the API even if a cached response is on hand.

    False
    ИСХОДНИК ↗

    get_table_of_contents

    методasync
    get_table_of_contents(*, refresh: bool = False) -> list[VolumeT]

    Fetch the title's volumes and chapter names/numbers, without chapter content.

    Параметры

    ИмяТипОписаниеПо умолчанию
    refreshbool

    Bypass the disk cache and re-fetch from the API even if a cached response is on hand — needed to pick up newly published chapters, since otherwise the cached chapter list would keep being reused.

    False
    ИСХОДНИК ↗

    get_translations

    методasync
    get_translations(volume: int, number: str) -> list[ChapterBranch]

    Fetch the available translations (branches) for a chapter.

    Параметры

    ИмяТипОписание
    volumeint

    The chapter's volume number.

    numberstr

    The chapter number.

    Исключения

    ТипОписание
    ChapterNotFoundError

    If no chapter exists for this volume/number.

    ИСХОДНИК ↗

    get_volume

    методasync
    get_volume(volume: int) -> VolumeT

    Fetch a whole volume: all its chapters, each including content.

    The API has no bulk "volume content" endpoint (see docs/api-notes.md), so this fetches the chapter list once to find which numbers belong to the volume, then fetches each of those chapters individually and sequentially.

    Параметры

    ИмяТипОписание
    volumeint

    The volume number.

    Исключения

    ТипОписание
    VolumeNotFoundError

    If the title has no chapters for this volume.

    MultipleTranslationsError

    If any of the volume's chapters has more than one translation.

    ИСХОДНИК ↗

    get_volumes

    методasync
    get_volumes(volumes: list[int]) -> list[VolumeT]

    Fetch several whole volumes, each including chapter content.

    Fetches the chapter list once, shared across all requested volumes, then each chapter individually and sequentially — same approach as get_volume, applied to more than one volume without re-fetching the chapter list per volume.

    Параметры

    ИмяТипОписание
    volumeslist[int]

    The volume numbers to fetch.

    Исключения

    ТипОписание
    VolumeNotFoundError

    If the title has no chapters for one of the volumes.

    MultipleTranslationsError

    If any of the volumes' chapters has more than one translation.

    ИСХОДНИК ↗

    libsocial.Catalog

    класс
    Catalog(
        *,
        cache_dir: str | Path | None = None,
        cache_ttl: float | None = None,
        headers: Mapping[str, str] | None = None,
    )

    Базовые классы Generic[TitleT]

    Listing/searching one site's catalog.

    Not used directly: a site package subclasses it, setting site and page_model (and optionally default_cache_dir) as class attributes — so its own Catalog keeps a plain constructor and returns its own page and Title models.

    Атрибуты

    ИмяТипОписание
    siteSite

    The site whose catalog is listed. Set by the subclass.

    page_modeltype[CatalogPage[TitleT]]

    The model a listing page is validated as — CatalogPage[ItsTitle] or a subclass of it, which also decides the model of its items. Set by the subclass.

    default_cache_dirPath

    cache_dir used when none is passed.

    ИСХОДНИК ↗

    aclose

    методasync
    aclose() -> None

    Close the underlying HTTP client.

    ИСХОДНИК ↗

    list_countries

    методasync
    list_countries(*, refresh: bool = False) -> list[Country]

    Fetch the full list of catalog countries (id → name), for use as filter options with list_titles(countries=...).

    Mirrors list_genres() exactly, down to the network-wide, not-site-scoped shape of the underlying endpoint (GET /api/constants?fields[]=types — the API's own name for what this SDK calls "country" is "type", see docs/api-notes.md and Country's docstring). This filters that down to entries tagged for site before returning, same as list_genres() does for genres.

    Параметры

    ИмяТипОписаниеПо умолчанию
    refreshbool

    Bypass the disk cache and re-fetch from the API even if the country list was already cached.

    False

    Возвращает

    ТипОписание
    list[Country]

    Every Country (id, name) that applies to site.

    ИСХОДНИК ↗

    list_genres

    методasync
    list_genres(*, refresh: bool = False) -> list[Genre]

    Fetch the full list of catalog genres (id → name), for use as filter options with list_titles(genres=[...]).

    The underlying endpoint (GET /api/constants?fields[]=genres) has no site-scoping parameter — it returns the full genre list shared across the whole lib.social network in one shot, each tagged with the site ids it applies to (see docs/api-notes.md). This filters that down to genres tagged for site before returning, so callers don't get filter options that could never match any of the site's titles (e.g. "Детское", tagged only for site 5, on ranobelib.me).

    Параметры

    ИмяТипОписаниеПо умолчанию
    refreshbool

    Bypass the disk cache and re-fetch from the API even if the genre list was already cached.

    False

    Возвращает

    ТипОписание
    list[Genre]

    Every Genre (id, name, adult flag) that applies to site.

    ИСХОДНИК ↗

    list_statuses

    методasync
    list_statuses(*, refresh: bool = False) -> list[Label]

    Fetch the full list of title statuses (id → label), for use as filter options with list_titles(statuses=...).

    Mirrors list_genres()/list_countries(): the underlying endpoint (GET /api/constants?fields[]=status) isn't site-scoped, so entries not tagged for site are dropped before returning (see docs/api-notes.md). Returns the same Label model as Title.status, so a title's status can be compared against these directly.

    Параметры

    ИмяТипОписаниеПо умолчанию
    refreshbool

    Bypass the disk cache and re-fetch from the API even if the status list was already cached.

    False

    Возвращает

    ТипОписание
    list[Label]

    Every status (Label: id, label) that applies to site.

    ИСХОДНИК ↗

    list_titles

    методasync
    list_titles(
        *,
        page: int = 1,
        per_page: int = 30,
        query: str | None = None,
        genres: list[int] | None = None,
        tags: list[int] | None = None,
        statuses: list[int] | None = None,
        countries: list[int] | None = None,
        min_chapters: int | None = None,
        max_chapters: int | None = None,
        sort: str = DEFAULT_SORT,
        refresh: bool = False,
    ) -> CatalogPage[TitleT]

    Fetch one page of catalog listing/search results.

    Параметры

    ИмяТипОписаниеПо умолчанию
    pageint

    1-based page number.

    1
    per_pageint

    How many titles per page. Must be between MIN_PER_PAGE (10) and MAX_PER_PAGE (60) — the API's own limits (see docs/api-notes.md); anything outside that range raises here instead of a confusing 422 from the API.

    30
    querystr | None

    Free-text search term, matched against name/rus_name/eng_name. None (the default) or an empty string lists without searching.

    None
    genreslist[int] | None

    Genre ids to filter by. A title must have all of them, not just one (AND, not OR — see docs/api-notes.md). Not validated against list_genres() before sending — an unrecognized id just matches nothing rather than erroring.

    None
    tagslist[int] | None

    Tag.ids (from Title.tags) to filter by. Same AND semantics as genres — a title must have all of them, not just any one (confirmed against the live API, see docs/api-notes.md; not the OR some might expect from tags being more numerous/specific than genres). Also not validated before sending, same as genres.

    None
    statuseslist[int] | None

    Title.status.ids to filter by (e.g. ongoing, completed). A title matches if its own status is any of these (OR, same as countries — a title only has one status, see docs/api-notes.md). None or an empty list applies no status filter. Ids come from list_statuses().

    None
    countrieslist[int] | None

    Country.ids to filter by. A title matches if its own country is any of these (OR, not AND like genres/tags — a title only has one country of origin, so requiring all of them could never match past the first, see docs/api-notes.md). None or an empty list applies no country filter. Ids come from list_countries(). Sent on the wire as the API's types[] parameter, not a country/countries[] one — see docs/api-notes.md for why (Country mirrors the API's own "type" concept, which isn't strictly limited to literal countries).

    None
    min_chaptersint | None

    Only titles with at least this many chapters (inclusive). Counts the same number as Title.chapter_count (uploaded chapters). None (the default) applies no lower bound.

    None
    max_chaptersint | None

    Only titles with at most this many chapters (inclusive). None (the default) applies no upper bound.

    None
    sortstr

    Sort order. Despite the name, this is sent as the API's sort_by parameter — an actual sort parameter exists but is silently ignored by the API (see docs/api-notes.md). Known accepted values: "name", "created_at", "views", "chap_count", "last_chapter_at" (the default), "rate_avg", "random" — this list isn't guaranteed exhaustive.

    DEFAULT_SORT
    refreshbool

    Bypass the disk cache and re-fetch from the API even if this exact combination of parameters was already cached.

    False

    Возвращает

    ТипОписание
    CatalogPage[TitleT]

    A page_model page: the matching titles plus whether a next page exists.

    Исключения

    ТипОписание
    ValueError

    If page is less than 1, per_page is outside MIN_PER_PAGE..MAX_PER_PAGE, min_chapters/max_chapters is negative, or min_chapters is greater than max_chapters.

    ИСХОДНИК ↗

    libsocial.ApiClient

    класс
    ApiClient(
        site: Site,
        *,
        base_url: str = API_BASE_URL,
        timeout: float = 15.0,
        transport: httpx.AsyncBaseTransport | None = None,
        headers: Mapping[str, str] | None = None,
        max_concurrency: int = 5,
        request_delay: float | None = None,
        max_retries: int = 3,
        retry_base_delay: float = 1.0,
        sleep: Callable[[float], Awaitable[None]] = asyncio.sleep,
        pacer: Pacer | None = None,
    )

    Thin async wrapper around the undocumented api.cdnlibs.org JSON API, for one site.

    The API is shared by the whole lib.social network; site scopes requests to one site (Site-Id header, site_id[] on catalog listings — see docs/api-notes.md).

    Bounds concurrency with a semaphore, paces request starts with a small fixed delay, and retries 429/5xx responses with exponential backoff — see docs/api-notes.md for why a manual implementation was chosen over a dependency like tenacity for this. Pacing is shared, by default, with every other ApiClient in the process talking to the same host (see Pacer), so clients for different sites don't add up their request rates.

    Every request carries browser-like Origin/Referer/User-Agent headers on top of the API-specific Site-Id/Accept: without a non-empty Referer, the DDoS-Guard edge in front of the API answers every request with an HTML 403 page (see docs/api-notes.md, section "WAF 403").

    Атрибуты

    ИмяТипОписание
    headers_hintstr

    Where AccessBlockedError's message suggests passing different headers. A site package's subclass names its own entry points here.

    ИСХОДНИК ↗

    aclose

    методasync
    aclose() -> None

    Close the underlying HTTP connection pool.

    ИСХОДНИК ↗

    get_chapter

    методasync
    get_chapter(
        slug_url: str,
        *,
        number: str,
        volume: str,
        branch_id: int | None = None,
    ) -> dict[str, Any]

    Fetch a single chapter's metadata and content.

    Параметры

    ИмяТипОписаниеПо умолчанию
    slug_urlstr

    The title's {id}--{slug} identifier.

    numberstr

    The chapter number, as returned by the API (may contain a decimal).

    volumestr

    The volume number.

    branch_idint | None

    Which translation team's branch to fetch, for chapters with more than one. Defaults to whichever branch the API picks (see docs/api-notes.md — this is not simply the first branch listed).

    None

    Возвращает

    ТипОписание
    dict[str, Any]

    The data object from the API response.

    ИСХОДНИК ↗

    get_chapters

    методasync
    get_chapters(slug_url: str) -> list[dict[str, Any]]

    Fetch the raw chapter list for slug_url (single request, no pagination).

    Параметры

    ИмяТипОписание
    slug_urlstr

    The title's {id}--{slug} identifier.

    Возвращает

    ТипОписание
    list[dict[str, Any]]

    The data array from the API response.

    ИСХОДНИК ↗

    get_title

    методasync
    get_title(
        slug_url: str,
        *,
        fields: list[str] | None = None,
    ) -> dict[str, Any]

    Fetch raw title metadata for slug_url.

    Параметры

    ИмяТипОписаниеПо умолчанию
    slug_urlstr

    The title's {id}--{slug} identifier.

    fieldslist[str] | None

    Extra fields[] values to request from the API.

    None

    Возвращает

    ТипОписание
    dict[str, Any]

    The data object from the API response.

    ИСХОДНИК ↗

    list_countries

    методasync
    list_countries() -> list[dict[str, Any]]

    Fetch the raw country/type list shared across the whole lib.social network.

    Same shape and caveats as list_genres: not site-scoped by any request parameter, returns every "type" known to the network at once, each tagged with the site ids it applies to via site_ids. Named list_countries/fields[]=types mismatch is deliberate — see Catalog.list_countries and docs/api-notes.md for why this reuses the API's own "type" concept rather than the unrelated fields[]=countries field.

    Возвращает

    ТипОписание
    list[dict[str, Any]]

    The raw data.types array from the API response, unfiltered by site.

    ИСХОДНИК ↗

    list_genres

    методasync
    list_genres() -> list[dict[str, Any]]

    Fetch the raw genre list shared across the whole lib.social network.

    Unlike list_titles, this endpoint isn't site-scoped by any request parameter (confirmed: a Site-Id header changes nothing about the response) — it returns every genre known to the network at once, each tagged with the site ids it applies to via a site_ids field. Callers must filter to their site themselves (see Catalog.list_genres, which drops e.g. site-5-only genres like "Детское" when listing for ranobelib.me).

    Возвращает

    ТипОписание
    list[dict[str, Any]]

    The raw data.genres array from the API response, unfiltered by site.

    ИСХОДНИК ↗

    list_statuses

    методasync
    list_statuses() -> list[dict[str, Any]]

    Fetch the raw title status list shared across the whole lib.social network.

    Same shape and caveats as list_genres: not site-scoped by any request parameter, returns every status known to the network at once, each tagged with the site ids it applies to via site_ids.

    Возвращает

    ТипОписание
    list[dict[str, Any]]

    The raw data.status array from the API response, unfiltered by site.

    ИСХОДНИК ↗

    list_titles

    методasync
    list_titles(
        *,
        page: int,
        per_page: int,
        query: str | None,
        genres: list[int] | None,
        tags: list[int] | None,
        statuses: list[int] | None,
        countries: list[int] | None,
        min_chapters: int | None,
        max_chapters: int | None,
        sort: str,
    ) -> dict[str, Any]

    Fetch one page of the catalog listing/search results.

    Unlike the other ApiClient methods, this returns the whole response body, not just data — callers need meta.has_next_page too, and there's no total count to derive it from otherwise (see docs/api-notes.md).

    Параметры

    ИмяТипОписание
    pageint

    1-based page number.

    per_pageint

    Page size. The API only accepts 10..60 (validated by the caller, see Catalog.list_titles); values outside that raise here via a 422.

    querystr | None

    Free-text search term, matched against name/rus_name/eng_name. None or empty omits the q parameter entirely.

    genreslist[int] | None

    Genre ids to filter by. A title must have all of them (AND, not OR — see docs/api-notes.md), not just any one.

    tagslist[int] | None

    Tag ids to filter by. Same AND semantics as genres — a title must have all of them, not just any one (see docs/api-notes.md).

    statuseslist[int] | None

    Title.status.ids to filter by. A title matches if its own status is any of these (OR, like countries — a title only has one status, see docs/api-notes.md). Sent on the wire as repeated status[] parameters.

    countrieslist[int] | None

    Country.ids to filter by. A title matches if its own country is any of these (OR, not AND like genres/tags — a title only has one country, confirmed against the live API, see docs/api-notes.md). Sent on the wire as repeated types[] parameters — the API's own name for this concept is "type", not "country" (see docs/api-notes.md).

    min_chaptersint | None

    Minimum chapter count, inclusive. Sent as chap_count_min.

    max_chaptersint | None

    Maximum chapter count, inclusive. Sent as chap_count_max.

    sortstr

    Forwarded as the API's sort_by parameter, not sort — the API silently ignores an actual sort parameter (see docs/api-notes.md); this mismatch between the SDK's public keyword and the wire parameter name is deliberate, not a typo.

    Возвращает

    ТипОписание
    dict[str, Any]

    The full JSON response: {"data": [...], "meta": {...}, "links": {...}}.

    ИСХОДНИК ↗

    libsocial.Pacer

    класс
    Pacer(*, clock: Callable[[], float] = time.monotonic)

    Spaces out request starts: each one at least its delay after the previous one.

    Every ApiClient talking to the same host shares one Pacer by default (see shared_pacer()), so two clients for different sites in one process — say ranobelib and mangalib, both on api.cdnlibs.org — pace against each other instead of each sending at its own full rate and doubling the load on the API between them.

    Works by reservation: a caller takes the next free start slot under a plain threading.Lock, held only for that bookkeeping and never across an await, then sleeps until the slot outside of it. So a single Pacer is safe to share across event loops and threads, which an asyncio.Lock (bound to the loop that first used it) is not.

    ИСХОДНИК ↗

    wait

    методasync
    wait(delay: float, *, sleep: Callable[[float], Awaitable[None]]) -> None

    Wait for this request's start slot, and reserve delay seconds after it.

    Параметры

    ИмяТипОписание
    delayfloat

    Minimum time, in seconds, before the next request (from any client sharing this pacer) may start after this one. Each caller passes its own, so clients with different delays can share a pacer.

    sleepCallable[[float], Awaitable[None]]

    Sleep function to wait with — the calling client's, so tests that inject a fake one don't actually wait.

    ИСХОДНИК ↗

    libsocial.shared_pacer

    функция
    shared_pacer(host: str) -> Pacer

    The process-wide Pacer for host, created on first use.

    Параметры

    ИмяТипОписание
    hoststr

    The API host, e.g. "api.cdnlibs.org" — matched case-insensitively.

    ИСХОДНИК ↗

    libsocial.PlannedChapter

    класс
    PlannedChapter()

    Базовые классы NamedTuple

    A chapter a bulk operation will fetch, with the translation to fetch it in.

    Атрибуты

    ИмяТипОписание
    volumestr

    The chapter's volume number.

    numberstr

    The chapter number.

    branch_idint | None

    The branch to request, or None to let the API pick — only for chapters with a single translation, where there's nothing to pick between.

    ИСХОДНИК ↗

    Models

    libsocial.Title

    класс
    Title()

    Базовые классы BaseModel

    Metadata for a single title.

    ИСХОДНИК ↗

    chapter_count

    атрибут
    chapter_count: int | None = None

    Number of uploaded chapters. Only TitleClient.get_info() fills it in: the catalog listing endpoint doesn't return it (see docs/api-notes.md), so it's None on Catalog.list_titles() items.

    ИСХОДНИК ↗

    last_chapter_at

    атрибут
    last_chapter_at: datetime | None = Field(default=None, alias='last_item_at')

    When the most recent chapter was published — the value the catalog's sort="last_chapter_at" orders by. Only TitleClient.get_info() fills it in: the catalog listing endpoint doesn't return it (see docs/api-notes.md), so it's None on Catalog.list_titles() items, and on a title with no chapters yet.

    ИСХОДНИК ↗

    libsocial.Chapter

    класс
    Chapter()

    Базовые классы BaseModel

    A chapter: volume, number, name and available translations — not its content.

    index/item_number/branches_count/branches come from the chapter-list endpoint (see TitleClient.get_table_of_contents()) and are None/empty when a Chapter instead comes from fetching a single chapter, which has a different response shape (see docs/api-notes.md). What the chapter contains is site-specific: a site package subclasses this with its content fields and parsing.

    ИСХОДНИК ↗

    libsocial.ChapterBranch

    класс
    ChapterBranch()

    Базовые классы BaseModel

    A single team's (or solo uploader's) translation of a chapter.

    A chapter has more than one branch when several teams have translated it independently; see Chapter.branches_count.

    ИСХОДНИК ↗

    libsocial.Volume

    класс
    Volume()

    Базовые классы BaseModel, Generic[ChapterT]

    A volume: its number and the chapters it contains, in title order.

    Generic over the chapter model — a site package uses Volume[ItsChapter] (or a subclass of it) so its chapters keep their content fields.

    ИСХОДНИК ↗

    libsocial.CatalogPage

    класс
    CatalogPage()

    Базовые классы BaseModel, Generic[TitleT]

    One page of catalog listing/search results, as returned by Catalog.list_titles().

    items reuses the title model as-is — a catalog list item has every field Title requires, the ones it doesn't send (genres, summary, chapter_count, last_chapter_at, ...) just come back at their defaults, same as any other partially-populated Title (see docs/api-notes.md).

    ИСХОДНИК ↗

    Exceptions

    libsocial.LibSocialError

    класс
    LibSocialError()

    Базовые классы Exception

    Base class for all errors raised by libsocial-core and the site SDKs built on it.

    ИСХОДНИК ↗

    libsocial.TitleNotFoundError

    класс
    TitleNotFoundError(slug_url: str)

    Базовые классы LibSocialError

    Raised when a title cannot be found on the site.

    Атрибуты

    ИмяТипОписание
    slug_url

    The title's {id}--{slug} identifier that couldn't be found.

    ИСХОДНИК ↗

    libsocial.ChapterNotFoundError

    класс
    ChapterNotFoundError(slug_url: str, *, volume: str, number: str)

    Базовые классы LibSocialError

    Raised when a chapter cannot be found for a given volume/number.

    Атрибуты

    ИмяТипОписание
    slug_url

    The title's {id}--{slug} identifier.

    volume

    The volume number that was requested.

    number

    The chapter number that was requested.

    ИСХОДНИК ↗

    libsocial.VolumeNotFoundError

    класс
    VolumeNotFoundError(slug_url: str, *, volume: str)

    Базовые классы LibSocialError

    Raised when a title has no chapters for a given volume number.

    Атрибуты

    ИмяТипОписание
    slug_url

    The title's {id}--{slug} identifier.

    volume

    The volume number that was requested.

    ИСХОДНИК ↗

    libsocial.MultipleTranslationsError

    класс
    MultipleTranslationsError(
        slug_url: str,
        *,
        volume: str,
        number: str,
        branches: list[ChapterBranch],
    )

    Базовые классы LibSocialError

    Raised when a chapter has more than one team's translation and none was selected.

    The SDK does not guess a default: the API's own default when branch_id is omitted is not simply the first branch listed, and the actual rule it uses isn't documented (see docs/api-notes.md) — returning it silently would be unpredictable. Call TitleClient.get_translations() to list the available branches, then pass one's branch_id explicitly.

    Атрибуты

    ИмяТипОписание
    slug_url

    The title's {id}--{slug} identifier.

    volume

    The chapter's volume number.

    number

    The chapter number.

    branches

    The chapter's available translations, as returned by TitleClient.get_translations().

    ИСХОДНИК ↗

    libsocial.MultipleTitleTranslationsError

    класс
    MultipleTitleTranslationsError(
        slug_url: str,
        *,
        chapters: list[AmbiguousChapter],
    )

    Базовые классы LibSocialError

    Raised by download_title() when one or more chapters have an unresolved translation.

    Unlike MultipleTranslationsError (one chapter), this can list several: a title-wide bulk download checks every chapter up front rather than stopping at the first ambiguous one, so a caller doesn't have to fix one chapter, rerun, hit the next, and rerun again. Pass branch_id or translation_index to download_title() to resolve them, or fetch the listed chapters individually with get_chapter(..., branch_id=...).

    Атрибуты

    ИмяТипОписание
    slug_url

    The title's {id}--{slug} identifier.

    chapters

    The ambiguous chapters and their available translations.

    ИСХОДНИК ↗

    libsocial.AuthRequiredError

    класс
    AuthRequiredError(url: str)

    Базовые классы LibSocialError

    Raised when the requested content requires authorization (paid or early access).

    Атрибуты

    ИмяТипОписание
    url

    The request URL that returned 403.

    ИСХОДНИК ↗

    libsocial.AccessBlockedError

    класс
    AccessBlockedError(
        url: str,
        *,
        headers_hint: str = DEFAULT_HEADERS_HINT,
    )

    Базовые классы LibSocialError

    Raised when the site's edge protection rejects a request before it reaches the API.

    Not an authorization problem, unlike AuthRequiredError: the DDoS-Guard edge in front of api.cdnlibs.org answers with an HTML 403 page (the API's own 403 is JSON) when it doesn't like the request — so far, a missing or empty Referer (see docs/api-notes.md, section "WAF 403"). The SDK already sends browser-like headers, so seeing this means either the edge changed its rules — check for an SDK update, or send what it now expects via headers= on the title client/Catalog (or ApiClient, if using it directly) — or the edge is blocking the network/IP itself, which no header fixes. Not retried: resending the same request gets the same answer.

    Атрибуты

    ИмяТипОписание
    url

    The request URL that returned 403.

    ИСХОДНИК ↗

    libsocial.RateLimitError

    класс
    RateLimitError(retry_after: float | None = None)

    Базовые классы LibSocialError

    Raised when the API responds with 429 Too Many Requests.

    Атрибуты

    ИмяТипОписание
    retry_after

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

    ИСХОДНИК ↗

    libsocial.DownloadTitleInterruptedError

    класс
    DownloadTitleInterruptedError(
        slug_url: str,
        *,
        volumes: Sequence[Volume[Any]],
        completed: int,
        total: int,
    )

    Базовые классы LibSocialError

    Raised by download_title() when a chapter fetch fails unrecoverably partway through.

    download_title() fetches hundreds-to-thousands of chapters sequentially for a long title, which can outlast even the extra, bulk-specific rate-limit retry budget described in download_title()'s max_rate_limit_retries — or hit some other unrecoverable error (e.g. AuthRequiredError on a chapter that turns out to need a paywall/auth token). Rather than letting that error propagate on its own and silently discard every chapter already fetched, it's wrapped in this exception instead, carrying what was already downloaded — so a caller can keep that partial result, or simply call download_title() again: the SDK's disk cache means already-fetched chapters aren't re-requested, so a retry resumes close to where this one stopped rather than restarting the whole title. See docs/api-notes.md's "Rate limiting и retry" section and issue #41.

    Атрибуты

    ИмяТипОписание
    slug_url

    The title's {id}--{slug} identifier.

    volumeslist[Volume[Any]]

    Chapters fetched before the failure, grouped into volumes the same way a successful download_title() call would return them.

    completed

    How many chapters were fetched before the failure.

    total

    How many chapters download_title() had planned to fetch in total.

    ИСХОДНИК ↗

    Cache maintenance

    libsocial.cache_stats

    функция
    cache_stats(cache_dir: str | Path = DEFAULT_CACHE_DIR) -> CacheStats

    Count the entries in a disk cache and their total size on disk.

    Only files the cache itself writes are counted, not anything else that happens to be in the directory.

    Параметры

    ИмяТипОписаниеПо умолчанию
    cache_dirstr | Path

    The cache directory — the same cache_dir passed to TitleClient / Catalog (default: DEFAULT_CACHE_DIR, .libsocial_cache in the current directory; a site package may use its own default).

    DEFAULT_CACHE_DIR

    Возвращает

    ТипОписание
    CacheStats

    The number of entries and their total size in bytes. A missing directory is an empty

    CacheStats

    cache, CacheStats(entries=0, bytes=0), not an error.

    ИСХОДНИК ↗

    libsocial.clear_cache

    функция
    clear_cache(
        cache_dir: str | Path = DEFAULT_CACHE_DIR,
        *,
        older_than: float | None = None,
    ) -> CacheStats

    Remove entries from a disk cache.

    Only files the cache itself writes are removed — anything else in the directory, and the directory itself, is left alone. Safe to call while clients are using the same cache: an entry being read as it's removed is just a cache miss, and one written after it's removed is simply recreated.

    Параметры

    ИмяТипОписаниеПо умолчанию
    cache_dirstr | Path

    The cache directory — see cache_stats().

    DEFAULT_CACHE_DIR
    older_thanfloat | None

    If given, only remove entries written more than this many seconds ago (the same age check cache_ttl uses), plus any corrupted entry, which the cache can't serve anyway. None (the default) removes every entry.

    None

    Возвращает

    ТипОписание
    CacheStats

    The number of entries removed and the bytes they took. A missing directory removes

    CacheStats

    nothing, CacheStats(entries=0, bytes=0), rather than raising.

    Исключения

    ТипОписание
    ValueError

    If older_than is negative.

    ИСХОДНИК ↗

    libsocial.CacheStats

    класс
    CacheStats(entries: int, bytes: int)

    How many cache entries there are (or were removed), and their total size on disk.

    ИСХОДНИК ↗