II — RANOBELIB-PYTHON-SDK · RANOBELIB

    ranobelib API reference

    Entry point

    ranobelib.RanobeLib

    class
    RanobeLib(
        url: str,
        *,
        cache_dir: str | Path | None = None,
        cache_ttl: float | None = None,
        verbosity: Verbosity = False,
        headers: Mapping[str, str] | None = None,
    )

    Bases TitleClient[Title, Chapter, Volume]

    Entry point for interacting with a single ranobelib.me title.

    SOURCE ↗

    slug_url

    attributeproperty
    slug_url: str

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

    SOURCE ↗

    aclose

    methodasync
    aclose() -> None

    Close the underlying HTTP client.

    SOURCE ↗

    download_title

    methodasync
    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.

    Parameters

    NameTypeDescriptionDefault
    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

    Raises

    TypeDescription
    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.

    SOURCE ↗

    estimate_title_size

    methodasync
    estimate_title_size(
        *,
        sample_size: int = DEFAULT_SAMPLE_SIZE,
        average_image_size: int = DEFAULT_AVERAGE_IMAGE_SIZE,
        branch_id: int | None = None,
        translation_index: int | None = None,
    ) -> int

    Estimate the whole title's download size in bytes, from a sample of its chapters.

    Fetches the chapter list (cheap, cached), then the content of up to sample_size chapters spread evenly across the title — not just the first ones, since early and late chapters can differ in length — and extrapolates the sample's average sizing.chapter_size() across the title's total chapter count. This trades exactness for cost: an exact total requires fetching every chapter's content, the same cost as download_title() itself, which defeats the purpose of checking "is this worth downloading" before paying that cost.

    Sampled chapters go through the same disk cache as any other chapter fetch, so a later download_title() call doesn't refetch them.

    Parameters

    NameTypeDescriptionDefault
    sample_sizeint

    How many chapters to sample. Clamped to the number of chapters the title actually has (or that resolve to a usable translation, see below) if that's fewer.

    DEFAULT_SAMPLE_SIZE
    average_image_sizeint

    Forwarded to sizing.chapter_size() for each sampled chapter.

    DEFAULT_AVERAGE_IMAGE_SIZE
    branch_idint | None

    Same meaning as download_title(branch_id=...) — which translation to sample for chapters that have more than one. Mutually exclusive with translation_index.

    None
    translation_indexint | None

    Same meaning as download_title(translation_index=...). Mutually exclusive with branch_id.

    None

    Returns

    TypeDescription
    int

    The estimated size in bytes, or 0 if the title has no chapters, or none of

    int

    its chapters have a translation branch_id/translation_index resolves.

    Raises

    TypeDescription
    ValueError

    If both branch_id and translation_index are given, or if sample_size is less than 1.

    SOURCE ↗

    export

    methodasync
    export(chapters: list[Chapter], *, fmt: str, path: str | Path) -> Path

    Export chapters to a file.

    Parameters

    NameTypeDescription
    chapterslist[Chapter]

    The chapters to include, in the order they should appear.

    pathstr | Path

    Where to write the exported file.

    fmtstr

    Export format — a key of ranobelib.exporters.EXPORTERS (currently: "txt", "fb2", "epub", and "pdf" where WeasyPrint's native libraries are available).

    Raises

    TypeDescription
    ValueError

    If fmt isn't a registered export format.

    SOURCE ↗

    get_chapter

    methodasync
    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.

    Parameters

    NameTypeDescriptionDefault
    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

    Raises

    TypeDescription
    MultipleTranslationsError

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

    SOURCE ↗

    get_chapters

    methodasync
    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.

    Parameters

    NameTypeDescription
    chapterslist[tuple[int, str]]

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

    Raises

    TypeDescription
    ChapterNotFoundError

    If any requested chapter doesn't exist.

    MultipleTranslationsError

    If any requested chapter has more than one translation.

    SOURCE ↗

    get_info

    methodasync
    get_info(*, refresh: bool = False) -> TitleT

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

    Parameters

    NameTypeDescriptionDefault
    refreshbool

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

    False
    SOURCE ↗

    get_table_of_contents

    methodasync
    get_table_of_contents(*, refresh: bool = False) -> list[VolumeT]

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

    Parameters

    NameTypeDescriptionDefault
    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
    SOURCE ↗

    get_translations

    methodasync
    get_translations(volume: int, number: str) -> list[ChapterBranch]

    Fetch the available translations (branches) for a chapter.

    Parameters

    NameTypeDescription
    volumeint

    The chapter's volume number.

    numberstr

    The chapter number.

    Raises

    TypeDescription
    ChapterNotFoundError

    If no chapter exists for this volume/number.

    SOURCE ↗

    get_volume

    methodasync
    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.

    Parameters

    NameTypeDescription
    volumeint

    The volume number.

    Raises

    TypeDescription
    VolumeNotFoundError

    If the title has no chapters for this volume.

    MultipleTranslationsError

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

    SOURCE ↗

    get_volumes

    methodasync
    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.

    Parameters

    NameTypeDescription
    volumeslist[int]

    The volume numbers to fetch.

    Raises

    TypeDescription
    VolumeNotFoundError

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

    MultipleTranslationsError

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

    SOURCE ↗

    ranobelib.Catalog

    class
    Catalog(
        *,
        cache_dir: str | Path | None = None,
        cache_ttl: float | None = None,
        headers: Mapping[str, str] | None = None,
    )

    Bases _CoreCatalog[Title]

    Entry point for listing/searching the ranobelib.me catalog.

    Lists titles (list_titles()) and the filter options for them (list_genres(), list_countries(), list_statuses()), each filtered to ranobelib.me.

    SOURCE ↗

    aclose

    methodasync
    aclose() -> None

    Close the underlying HTTP client.

    SOURCE ↗

    list_countries

    methodasync
    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.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

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

    False

    Returns

    TypeDescription
    list[Country]

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

    SOURCE ↗

    list_genres

    methodasync
    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).

    Parameters

    NameTypeDescriptionDefault
    refreshbool

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

    False

    Returns

    TypeDescription
    list[Genre]

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

    SOURCE ↗

    list_statuses

    methodasync
    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.

    Parameters

    NameTypeDescriptionDefault
    refreshbool

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

    False

    Returns

    TypeDescription
    list[Label]

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

    SOURCE ↗

    list_titles

    methodasync
    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.

    Parameters

    NameTypeDescriptionDefault
    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

    Returns

    TypeDescription
    CatalogPage[TitleT]

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

    Raises

    TypeDescription
    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.

    SOURCE ↗

    Models

    ranobelib.CatalogPage

    class
    CatalogPage()

    Bases _core.CatalogPage[Title]

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

    items reuses Title 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).

    SOURCE ↗

    ranobelib.Title

    class
    Title()

    Bases _core.Title

    Metadata for a single title (novel).

    SOURCE ↗

    base_names

    attributeproperty
    base_names: list[str]

    The title's names without the format suffix ranobelib.me appends to them.

    rus_name, eng_name, name and other_names, in that order, each with trailing publication-format markers removed — "Старшая школа D×D (Новелла)" → "Старшая школа D×D", likewise (Novel), (LN), (WN), (Light Novel), (Ранобэ), [квест], ... (stacked ones too, (LN) (Novel)). Other bracketed tails — a year, (18+), (Фанфик), [LitRPG], a franchise name — distinguish one title from another and are kept. Whitespace is trimmed; empty names and duplicates (keeping the first) are dropped. Nothing else is normalized (case, punctuation, ×): that's up to whatever compares the names.

    The original fields are left as they are. On Catalog.list_titles() items other_names is empty (the listing doesn't return it), so it contributes nothing.

    SOURCE ↗

    chapter_count

    attribute
    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.

    SOURCE ↗

    last_chapter_at

    attribute
    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.

    SOURCE ↗

    ranobelib.Chapter

    class
    Chapter()

    Bases _core.Chapter

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

    index/item_number/branches_count/branches come from the chapter-list endpoint (see RanobeLib.get_table_of_contents()) and are None/empty when a Chapter instead comes from fetching a single chapter's content, which has a different response shape (see docs/api-notes.md). content is the reverse: only populated by the single-chapter endpoint.

    footnotes holds the chapter's translator footnotes (see Footnote), which are removed from content rather than kept in both places — empty when the chapter has none, or when content isn't fetched.

    SOURCE ↗

    ranobelib.Footnote

    class
    Footnote()

    Bases BaseModel

    A translator footnote (term explanation, translation note) taken out of a chapter.

    The API has no structured footnotes (see docs/api-notes.md): they're ordinary paragraphs/list items whose text starts with ↑, so that arrow is what the SDK detects them by — and strips from content. There's no marker linking a footnote back to a point in the text: translators mark the reference with an unnumbered * at best, which the text also uses for scene breaks and censored words, so Chapter.footnotes's order (the order they appear in the source) is the only link there is.

    SOURCE ↗

    content

    attribute
    content: str

    Sanitized inline HTML (same tag vocabulary as Chapter.content), without the arrow and without a wrapping <p>.

    SOURCE ↗

    ranobelib.Volume

    class
    Volume()

    Bases _core.Volume[Chapter]

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

    SOURCE ↗

    ranobelib.ChapterBranch

    class
    ChapterBranch()

    Bases 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.

    SOURCE ↗

    ranobelib.Genre

    class
    Genre()

    Bases BaseModel

    A genre tag (e.g. Fantasy, Romance).

    SOURCE ↗

    ranobelib.Country

    class
    Country()

    Bases BaseModel

    A title's country/region of origin.

    Maps onto what the raw API itself calls "type" (Title's raw type field; the catalog filter's types[] parameter; GET /api/constants?fields[]=types) — not a country/countries[]/fields[]=countries concept, which turned out to be something unrelated (see docs/api-notes.md). The values aren't all literal countries: on ranobelib.me, Japan, Korea and China sit next to original English-language work, original web novel and fanfiction, all surfaced as-is.

    SOURCE ↗

    ranobelib.Label

    class
    Label()

    Bases BaseModel

    A small {id, label} enum value used throughout the API (status, age rating, ...).

    SOURCE ↗

    Exceptions

    ranobelib.RanobeLibError

    attribute
    RanobeLibError = LibSocialError

    Base class for all errors raised by the SDK (libsocial.LibSocialError).

    SOURCE ↗

    ranobelib.TitleNotFoundError

    class
    TitleNotFoundError(slug_url: str)

    Bases LibSocialError

    Raised when a title cannot be found on the site.

    Attributes

    NameTypeDescription
    slug_url

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

    SOURCE ↗

    ranobelib.ChapterNotFoundError

    class
    ChapterNotFoundError(slug_url: str, *, volume: str, number: str)

    Bases LibSocialError

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

    Attributes

    NameTypeDescription
    slug_url

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

    volume

    The volume number that was requested.

    number

    The chapter number that was requested.

    SOURCE ↗

    ranobelib.VolumeNotFoundError

    class
    VolumeNotFoundError(slug_url: str, *, volume: str)

    Bases LibSocialError

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

    Attributes

    NameTypeDescription
    slug_url

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

    volume

    The volume number that was requested.

    SOURCE ↗

    ranobelib.MultipleTranslationsError

    class
    MultipleTranslationsError(
        slug_url: str,
        *,
        volume: str,
        number: str,
        branches: list[ChapterBranch],
    )

    Bases 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.

    Attributes

    NameTypeDescription
    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().

    SOURCE ↗

    ranobelib.MultipleTitleTranslationsError

    class
    MultipleTitleTranslationsError(
        slug_url: str,
        *,
        chapters: list[AmbiguousChapter],
    )

    Bases 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=...).

    Attributes

    NameTypeDescription
    slug_url

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

    chapters

    The ambiguous chapters and their available translations.

    SOURCE ↗

    ranobelib.AuthRequiredError

    class
    AuthRequiredError(url: str)

    Bases LibSocialError

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

    Attributes

    NameTypeDescription
    url

    The request URL that returned 403.

    SOURCE ↗

    ranobelib.AccessBlockedError

    class
    AccessBlockedError(
        url: str,
        *,
        headers_hint: str = DEFAULT_HEADERS_HINT,
    )

    Bases 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.

    Attributes

    NameTypeDescription
    url

    The request URL that returned 403.

    SOURCE ↗

    ranobelib.RateLimitError

    class
    RateLimitError(retry_after: float | None = None)

    Bases LibSocialError

    Raised when the API responds with 429 Too Many Requests.

    Attributes

    NameTypeDescription
    retry_after

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

    SOURCE ↗

    ranobelib.DownloadTitleInterruptedError

    class
    DownloadTitleInterruptedError(
        slug_url: str,
        *,
        volumes: Sequence[Volume[Any]],
        completed: int,
        total: int,
    )

    Bases 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.

    Attributes

    NameTypeDescription
    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.

    SOURCE ↗

    Cache maintenance

    ranobelib.cache_stats

    function
    cache_stats(cache_dir: str | Path = DEFAULT_CACHE_DIR) -> CacheStats

    Count the entries in an SDK 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.

    Parameters

    NameTypeDescriptionDefault
    cache_dirstr | Path

    The cache directory — the same cache_dir passed to RanobeLib / Catalog (default: theirs, .ranobelib_cache in the current directory).

    DEFAULT_CACHE_DIR

    Returns

    TypeDescription
    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.

    SOURCE ↗

    ranobelib.clear_cache

    function
    clear_cache(
        cache_dir: str | Path = DEFAULT_CACHE_DIR,
        *,
        older_than: float | None = None,
    ) -> CacheStats

    Remove entries from an SDK 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.

    Parameters

    NameTypeDescriptionDefault
    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

    Returns

    TypeDescription
    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.

    Raises

    TypeDescription
    ValueError

    If older_than is negative.

    SOURCE ↗

    ranobelib.CacheStats

    class
    CacheStats(entries: int, bytes: int)

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

    SOURCE ↗

    Sizing

    ranobelib.chapter_size

    function
    chapter_size(
        chapter: Chapter,
        *,
        average_image_size: int = DEFAULT_AVERAGE_IMAGE_SIZE,
    ) -> int

    Estimate a fetched chapter's size in bytes: exact text plus assumed-average images.

    Parameters

    NameTypeDescriptionDefault
    chapterChapter

    A chapter with content already fetched (e.g. via get_chapter()).

    average_image_sizeint

    Assumed bytes per <img> referenced in the chapter's content.

    DEFAULT_AVERAGE_IMAGE_SIZE

    Raises

    TypeDescription
    ValueError

    If chapter.content is None (not yet fetched — see Chapter's docstring for when that happens).

    SOURCE ↗

    ranobelib.volume_size

    function
    volume_size(
        volume: Volume,
        *,
        average_image_size: int = DEFAULT_AVERAGE_IMAGE_SIZE,
    ) -> int

    Estimate a fetched volume's size in bytes: the sum of its chapters' chapter_size().

    Parameters

    NameTypeDescriptionDefault
    volumeVolume

    A volume whose chapters have content already fetched (e.g. via get_volume()).

    average_image_sizeint

    Forwarded to chapter_size() for each chapter.

    DEFAULT_AVERAGE_IMAGE_SIZE

    Raises

    TypeDescription
    ValueError

    If any chapter in volume.chapters has no content — see chapter_size().

    SOURCE ↗

    Chapter content

    ranobelib.chapter_image_urls

    function
    chapter_image_urls(chapter: Chapter) -> list[str]

    List the image URLs a fetched chapter references.

    Parameters

    NameTypeDescription
    chapterChapter

    A chapter with content already fetched (e.g. via get_chapter()).

    Returns

    TypeDescription
    list[str]

    Every <img src> in chapter.content and then chapter.footnotes, in

    list[str]

    document order, each URL listed once (its first occurrence).

    Raises

    TypeDescription
    ValueError

    If chapter.content is None (not yet fetched — see Chapter's docstring for when that happens).

    SOURCE ↗

    ranobelib.rewrite_image_urls

    function
    rewrite_image_urls(
        chapter: Chapter,
        replace: Callable[[str], str],
    ) -> Chapter

    Return a copy of a fetched chapter with every <img src> passed through replace.

    Parameters

    NameTypeDescription
    chapterChapter

    A chapter with content already fetched (e.g. via get_chapter()).

    replaceCallable[[str], str]

    Called with each image URL (once per <img>, so possibly more than once for the same URL); its result becomes the new src, HTML-escaped as an attribute value. An <img> without a src is left as it is.

    Returns

    TypeDescription
    Chapter

    A new Chapter whose content and footnotes have their <img src>

    Chapter

    rewritten; every other field is unchanged, and chapter itself is not modified.

    Raises

    TypeDescription
    ValueError

    If chapter.content is None — see chapter_image_urls().

    SOURCE ↗

    ranobelib.chapter_paragraphs

    function
    chapter_paragraphs(chapter: Chapter) -> list[str]

    A fetched chapter's top-level blocks as plain text, in document order.

    Entry i is the text of the i-th top-level element of chapter.content — the same position a browser gives it among the rendered content's element children — so a paragraph index counted in the DOM can be used to look it up directly.

    Parameters

    NameTypeDescription
    chapterChapter

    A chapter with content already fetched (e.g. via get_chapter()).

    Returns

    TypeDescription
    list[str]

    One string per top-level element: its text with entities decoded and whitespace

    list[str]

    collapsed to single spaces ("" for a block without text, such as <hr /> or

    list[str]

    an image-only paragraph). chapter.footnotes aren't included — they're not part

    list[str]

    of content (see Chapter.footnotes).

    Raises

    TypeDescription
    ValueError

    If chapter.content is None (not yet fetched — see Chapter's docstring for when that happens).

    SOURCE ↗

    Exporters

    Not re-exported from the top-level ranobelib package — import from ranobelib.exporters. Adding a new export format is a new module in ranobelib/exporters/ implementing Exporter and decorated with @register; no changes needed elsewhere.

    ranobelib.exporters.Exporter

    class
    Exporter()

    Bases Protocol

    Renders a title's chapters to a single file in some format.

    SOURCE ↗

    format

    attribute
    format: str

    The registry key this exporter is selected by, e.g. "txt".

    SOURCE ↗

    export

    methodasync
    export(
        title: Title,
        chapters: list[Chapter],
        output_path: Path,
        *,
        on_chapter: Callable[[], None] | None = None,
        headers: Mapping[str, str] | None = None,
    ) -> Path

    Write chapters (in the given order) to output_path.

    async since embedding illustrations (epub, pdf) requires downloading them — the SDK is async-only throughout (see CLAUDE.md), so this can't drop to a sync HTTP call. txt/fb2 do no I/O and just don't await anything in their bodies.

    Parameters

    NameTypeDescriptionDefault
    titleTitle

    The chapters' parent title, for metadata (name, authors, ...).

    chapterslist[Chapter]

    The chapters to include, in the order they should appear.

    output_pathPath

    Where to write the exported file.

    on_chapterCallable[[], None] | None

    Called once per chapter processed, if given — drives RanobeLib.export()'s progress bar (see CLAUDE.md's roadmap step 23). For epub/pdf this covers the per-chapter embedding step, not the earlier illustration-download step, which isn't itself progress-reported.

    None
    headersMapping[str, str] | None

    Extra HTTP headers for any downloads the exporter makes (epub/pdf illustrations), merged over the SDK's browser-like defaults — RanobeLib.export() passes its own headers= through here. Exporters that make no requests (txt, fb2) accept and ignore it.

    None

    Returns

    TypeDescription
    Path

    output_path, once the file has been written.

    SOURCE ↗

    ranobelib.exporters.register

    function
    register(exporter: ExporterT) -> ExporterT

    Class decorator: register exporter under its format key.

    SOURCE ↗

    Registered exporters, keyed by Exporter.format.