Files
MangaCircus/docs/library-format.md
MaashkinoozheandClaude Opus 5.5 7b52da2f9c Official release dates from MangaUpdates; peers lower ecosystem dates (BL-0048, BL-0049)
- scraper/metadata/release-dates.js fills x-dates.release from MangaUpdates
  publisher releases. MangaUpdates also lists English back-catalog re-releases,
  so a date is kept only inside the AniList original run, when no scanlation or
  source upload is more than 4 days earlier, and when it fits the release order
  (no lower chapter dated after a higher one). Seasons that restart chapter
  numbers are skipped; dates this job wrote and no longer accepts are withdrawn.
- The scraper refreshes ongoing series nightly before its update checks, only
  while no download runs, and bumps the publish revision so the reader reconciles.
- tools/release-dates.js for a whole library (dry run default). Production dry
  run: 728 chapters dated (Kagurabachi, JJK, Chainsaw Man, One Piece from 928).
- Peer sync: chapters this library already has adopt a peer's earlier ecosystem
  date (job result ecosystemLowered); the peer listing now carries dates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 08:47:36 +02:00

19 KiB
Raw Permalink Blame History

The file system library

MangaCircus has no import database for content. The library folder is the source of truth. The app reads it in place (read-only access is enough), and anything that can write files can add manga: a scraper, a sync tool, or a person with a file browser.

This document is the format specification. import.md describes how the app reads it, and scraper-guide.md is a rule book for tools that write it.

Goals of the format

Goal How the format meets it
Readable without the app Plain image files in plain folders. Zero-padded numbers make alphabetical order equal reading order in every file browser and picture viewer
Fast, simple indexing Everything the app needs is in folder names, file names and a few small JSON files. Image contents are never decoded. Only the first bytes of each image are read once to learn its size
Self-describing Page roles (cover, double page, extra, rotated) are visible in the file name, not hidden in a database
Several sources One manga can list several upstream sources. One chapter can exist in several versions side by side
Room for irregular releases In-between chapters (10.5), extra chapters and extra books outside the numbering

Layout

Always three folder levels, Manga / Book / Chapter, with the pages inside the chapter folder:

<library>/
  <Manga>/                          manga folder; its name is the manga's id
    manga.json                      optional
    cover.<ext>                     optional
    <Book>/                         e.g. "v01 Spring"
      book.json                     optional
      cover.<ext>                   optional
      <Chapter>/                    e.g. "c0001 The Seed" (one folder per chapter version)
        chapter.json                recommended
        0001_cover.<ext>
        0002.<ext>
        ...

Series without volumes (most webtoons, ongoing magazine series) still need the book level. Use a single v01, or seasons (s01 Season 1).

Anything else is ignored:

  • files at the library root,
  • files other than manga.json / cover.* in a manga folder,
  • files other than book.json / cover.* in a book folder,
  • non-image files and sub-folders inside a chapter folder,
  • every file or folder whose name starts with ..

Manga folders

  • Any name that does not start with .. The folder name is the manga's id: URLs, reading progress, follows, votes and the "already seen chapters" list all refer to it. Renaming the folder turns it into a new manga for the app.
  • The displayed title comes from manga.json → title, and falls back to the folder name.
  • Manga without a single readable chapter are left out (and logged).

Book and chapter folder names

<letters>[ ]<number>[<separator><title>][ [<source>]]
Part Rule Examples
letters Only letters (any case), optionally followed by one space. Free choice and just a hint for humans, except x, which means "extra" v, c, s, vol, Ch , Chapter , x
number Digits, optionally one decimal part. It decides the order and the identity. Zero-pad it so file browsers sort it right (4 digits for chapters, 2 for books) 0001, 12, 0010.5
separator The number must be followed by the end of the name, a space, _, -, [ or (. Use a space: _ sorts after ., which breaks the order of decimal chapters in file browsers , -
title Anything. Used as the title when the JSON file has none. Leading -, : or . are trimmed The Seed
[source] Optional, at the very end. Names the version (scan group, site) when chapter.json has no source [MangaDex]

Valid: v01, v01 Spring, c0001 The Seed, c0002 Roots [MangaDex], c0002.5 Tea Time, x01 Character Sheets, Vol 3, Chapter 12 - Title, c0010.5[Official].

Ignored, with a warning in the log:

  • c0012a: a letter right after the number.
  • Ch.12: a dot is not a letter.
  • Specials: no number.
  • - c0001: the name must start with the key.

The key

The additive unnumbered MangaDex chapter form is special-mangadex-<chapter-uuid> Pilot [MangaDex · Group]. It is accepted only for chapters, never books. Its logical key is the complete lower-case prefix/UUID; chapter.json supplies special:true, label (Pilot/One-shot/Special), sourceId and nullable sourceNumber. The reader shows that label with no fabricated number. Such chapters sort after numbered entries and are not inherently skippable extras. Different source UUIDs remain distinct releases; no content equivalence is guessed. Numeric IDs and source-version folders are unchanged. Older reader releases ignore these new folders but retain them on disk; upgrade again to restore visibility.

Whole-volume WeebCentral releases use the analogous special-weebcentral-volume-<26-character-release-id> chapter key. They live in the matching volume book and carry releaseKind:"volume", volume, sourceId, and a Volume N label in chapter.json. This keeps an entire volume distinct from a numbered chapter in the same book. The default skip-any download policy also skips a whole-volume release if that book already contains numbered chapters; add-source allows the extra source edition explicitly.

The number is normalised: leading zeros and trailing decimal zeros are dropped (0010.50 → 10.5), and an x prefix is kept. The result is the key:

Folder Key
v01 Spring 1
c0002.5 Tea Time 2.5
x01 Artbook x1
  • Book keys must be unique within a manga. A second folder with the same book number is ignored and logged.
  • Chapter id = <book key>/<chapter key>, for example 1/2.5 or x1/x1. Moving a chapter to another book, or renumbering it, therefore makes it a different chapter: reading progress and votes stay behind, and followers are told about a "new" chapter.
  • Because the key is a number, c0010.1 and c0010.10 are the same key (10.1). Avoid trailing zeros in decimals.

Order

Inside a manga, books sort by (regular before extra, number). Inside a book, chapters sort the same way. The reading order of the whole manga is books in order, each with its chapters in order:

v01: c0001, c0002, c0002.5, x01, x02   <- extras of book 1 come after its regular chapters
v02: c0003, c0004
x01: x01                               <- extra books come after all regular books

Extras

Something "extra" can be skipped while reading (user setting Skip extra pages and extra chapters) and gets an "extra" badge in the chapter list:

  • A chapter whose letters are exactly x (x01 Character Sheets) is extra. It sits outside the continuous numbering and is listed after the regular chapters of its book.
  • Every chapter in an extra book (x01 Specials) is extra.
  • "extra": true in chapter.json or book.json marks something as extra without moving it. Use it for an in-order omake such as c0010.5.

Chapter versions

Several chapter folders in the same book with the same chapter key are versions of one chapter, for example two scan groups or an official release plus a fan translation:

c0002 Roots [MangaDex]/
c0002 Roots [Official]/
  • The version id is the folder name. Votes and reading progress refer to it, so don't rename version folders.
  • Users vote versions up or down. The chapter opens in the version with the highest total. Ties go to the higher "priority" in chapter.json (default 0), then to the first folder name in sort order.
  • Users can still open any other version from the chapter list or the reader's version picker.
  • Each version has its own chapter.json, page count and page tags. When versions have different titles, the chapter title comes from the first version (in folder order) that has one.
  • Versions without any image are ignored.

Pages

A chapter folder contains its pages as plain images: jpg, jpeg, png, webp, gif, avif or bmp (extension case doesn't matter).

<number>[_<tag>]...<.ext>
  • Order is the natural sort order of the file names. Zero-pad the number (0001 … 9999) so every picture viewer agrees. For more than 9999 pages, use 5 digits throughout.
  • Tags follow the number, each introduced by _, and are case-insensitive: 0012_double.jpg, 0030_extra.png, 0007_double_rot90.jpg.
Tag Meaning Effect in the reader
cover Chapter cover / title page. It is also the chapter's image: the fallback cover for the book or manga when those have none Stands alone in Book mode. Can be skipped (Skip chapter cover pages)
double One image showing two facing pages (a spread) Stands alone in Book mode. In Flow mode it has the height of a single page, so it is twice as wide, but never wider than the screen
extra Extra content: translator notes, credits, recruitment pages, ads Can be skipped (Skip extra pages…)
rot90, rot180, rot270 The image is stored turned. The viewer turns it clockwise by this angle Width and height swap for rot90 / rot270
  • A page whose (rotated) image is wider than tall counts as a double page even without the tag. Tag it anyway; it documents the intent.
  • Unknown tags are ignored, so tools may add their own (0005_color.jpg) without breaking anything.
  • A file name without a leading number (page-a.jpg) is still a page. It is just sorted by name, which you rarely want.

Covers

Level Cover Fallback
Manga (library tile, manga page) cover.<ext> in the manga folder cover of the first book
Book cover.<ext> in the book folder first cover-tagged page (else the first page) of the first chapter's first version
Chapter first page tagged cover first page

Covers are served at their original size. A cover around 600×900 px keeps the library page light.

Metadata files

UTF-8 JSON (a BOM is tolerated). All fields are optional, and unknown fields are kept and ignored. A broken file is logged and treated as empty, so the folder still works with its name-based defaults.

manga.json

Field Type Meaning
title string Display title (default: folder name)
altTitles string[] Other titles
authors string[] Authors / artists
status string Free text, e.g. ongoing, completed, hiatus
year number First release year
tags string[] Genres / tags
description string Synopsis (line breaks are kept)
direction "rtl" | "ltr" | "ttb" How the pages are read. rtl (the default): manga, right to left. ltr: comics, printed manhwa. ttb: webtoon, one vertical strip cut into slices (aliases vertical, webtoon)
directionOverride same values Explicit manual choice. Use this even when choosing rtl, which is also the fallback. May be set at manga, book, or chapter level
x-reading-direction object Generated provenance: { "version": 1, "value": "ttb", "source": "detected" } or source: "fallback", with optional numeric evidence. The scraper owns this field; it is not a confidence score
sources {name, url}[] Upstream sources the manga was scraped from. Several are allowed
x-anilist object AniList work ID and linked metadata. The ID identifies a work, not necessarily a particular pilot or edition
x-metadata.imported object Last imported values for title, alternative titles, authors, status, year, tags and description. The scraper updates a displayed field only when it is empty or still equals its previous imported value; edits in legacy files without this snapshot are preserved
x-scraper.autoUpdate boolean Optional explicit admin choice. When absent, status: "ongoing" enables nightly checks and other statuses disable them

The whole file is sent to logged-in users as part of the manga page. Don't put secrets in it (scraper tokens, cookies).

An AniList refresh with a different ID is stopped for review before chapter publication. Existing files that already contain x-scraper.autoUpdate keep that choice; the scraper does not guess whether an older value was automatic or manual. New files leave the choice absent so the default can follow status changes.

book.json

Field Type Meaning
title string Book title (default: title part of the folder name)
direction as above Overrides the manga's direction for this book
extra boolean Marks all its chapters as extra without renaming the folder
Field Type Meaning
title string Chapter title without "Chapter N" (default: title part of the folder name)
source {name, url} or string Where this version came from (default: [source] in the folder name)
added ISO date/time string When this version entered this library, used for "recently updated" (default: the folder's modification time). The scraper keeps it when it replaces a version
x-dates object Other chapter dates, written by the scraper (see below)
extra boolean Marks the chapter as extra without moving it
priority number Tie-breaker between versions with equal votes. Higher wins (default 0)
direction as above Overrides book/manga direction, e.g. a colour webtoon special inside a printed series

Chapter dates (x-dates)

"added": "2026-10-17T15:04:03.424Z",
"x-dates": {
  "version": 1,
  "source": { "at": "2026-10-18T15:06:05.348Z", "name": "WeebCentral", "batch": false },
  "release": { "at": "2023-09-17", "precision": "day", "source": "MangaUpdates · Viz" },
  "ecosystem": { "at": "2026-10-17T15:04:03.424Z", "via": "this server" }
}
  • source: when the source site uploaded this version (WeebCentral's full chapter list, MangaDex publishAt). batch: true means it arrived with 3 or more other uploads less than 10 minutes apart, which is a site import, not a release.

  • release: the official public release (precision day, month or year). It is filled from MangaUpdates releases by publishers such as Viz, MANGA Plus or Webtoon (scraper/metadata/release-dates.js). Those lists include English re-releases of old chapters, so a date is kept only when all of these hold:

    • it lies within the work's original run on AniList;
    • no scanlation or source upload shows the chapter more than 4 days earlier;
    • it fits the release order: walking down from the newest chapter, a lower chapter can't be dated more than 2 days after a higher one.

    Series whose chapter numbers restart in a later book are skipped.

    • The scraper refreshes ongoing series nightly before its update checks, but only while no download is running.
    • node tools/release-dates.js <library> covers a whole library: a dry run by default, then --apply --writers-stopped.
    • A date that this job wrote and no longer accepts is removed. Release dates from any other source stay.
  • ecosystem: when the chapter first entered any Manga Circus library. Peer transfers carry it, and the earliest known date always wins. A peer's claim can't be earlier than the source upload or later than now.

The chapter list shows one release date per chapter, choosing the first tier that has a date: release → source unless batch (shown as ≈) → ecosystem (◎) → added (↓). Each tier takes the earliest value across the chapter's versions. Existing libraries get these fields from node tools/backfill-chapter-dates.js <library>: a dry run by default, then --apply --writers-stopped and a deep Rescan.

Manual directions have priority at chapter → book → manga. A directionOverride or a plain direction without matching generated provenance is manual. Otherwise, the scanner checks the version's existing image dimensions for coherent tall strips and uses ttb when they are present. A prior generated vertical series direction applies to chapters with insufficient shape evidence. The final fallback is rtl; this does not classify a work as Japanese or prove its reading order. A direct edit to a generated direction becomes manual when it differs from its provenance. Existing plain directions are treated as manual because old scraper defaults and human edits cannot be distinguished safely. Set directionOverride to make a lasting choice, or explicitly review and migrate a legacy entry. Image files and chapter IDs do not change when direction metadata changes.

Picture-viewer compatibility

  • Alphabetical order of chapter folders and page files equals reading order, as long as numbers are zero-padded and separated from titles by a space.
  • In-between chapters sort correctly: c0010 Title < c0010.5 Omake < c0011 (a space sorts before .).
  • Extras (x…) sort after the numbered folders. That is also where the app lists them.
  • The one thing a plain viewer can't do is apply rot… tags.

Examples

Manga with everything

Moon Garden/
  manga.json  cover.png
  v01 Spring/
    book.json  cover.png
    c0001 The Seed/            chapter.json  0001_cover.png 0002.png … 0006_double.png … 0012.png
    c0002 Roots [MangaDex]/    chapter.json  0001_cover.png … 0014.png
    c0002 Roots [Official]/    chapter.json  0001_cover.png … 0011.png     <- second version of ch. 2
    c0002.5 Tea Time/          chapter.json  0001.png … 0004.png           <- in-between chapter
    x01 Character Sheets/      chapter.json  0001_extra.png …              <- extra chapter
  v02 Summer/
    c0003 The Tide/            … 0009_rot90.png … 0014_extra.png 0015_extra.png
  x01 Specials/
    x01 Artbook/               …                                           <- extra book

Webtoon (manga.json: "direction": "ttb")

Skyline Tower/
  v01 Season 1/
    c0001 Episode 1/   0001.png … 0008.png      <- slices of one strip, same width, no gaps

Bare minimum (no JSON at all: titles and ids come from the folder names)

Blank Slate/
  v01/
    c0001/  0001.png 0002.png …

One-shot: a manga with one book and one chapter (Oneshot Title/v01/c0001/…), or an extra book inside the related series (x01 Oneshots/x01 Title/).

Renaming: what survives

Change Effect
Title part of a book folder (v01 Spring → v01 Early Spring) Nothing lost
Title part of a chapter folder (c0003 Tide → c0003 The Tide) Same chapter (the key is unchanged), so progress and notifications are fine. The full folder name is the version id, though, so votes for that version are lost, and saved progress is re-mapped proportionally
manga.json / chapter.json content Picked up on the next scan. Nothing lost
Manga folder name New manga: progress, follows, votes and seen-chapters are gone
Book number, chapter number, or moving a chapter to another book New chapter id: progress/votes stay with the old id, and followers are notified again
Version folder name Votes for that version are lost. Progress falls back to the best version at a proportional page
Page files replaced in place (same file names) Cached image sizes may be stale until an admin runs Full rescan