Roadmap¶
Ordered by what is expensive to retrofit rather than by what is most visible. Schema and identity decisions come first, because everything else hangs off them; the reader is the next user-facing milestone.
Already in place¶
Worth stating plainly, since these are the usual "do this before you have users" list and they are done:
- Stable internal identity. Comics are keyed by an integer primary key that has nothing to do with the file path. Path is a uniqueness constraint, not a key, so moving or renaming a file does not orphan covers, reading state or search entries.
- Per-user data is genuinely per-user. Reading progress, favourites, ratings and notes live on rows each account owns outright — one person's data cannot overwrite another's.
- Roles. Administrator versus standard account, enforced by a dependency on every admin route and covered by tests.
- Vetted password hashing and session handling. scrypt with a per-password salt, sliding session expiry, hashed session and reset tokens, and throttled sign-in attempts.
- Duplicate detection at import. A size-plus-partial-hash fingerprint, computed during the existing import pass.
- Automated database backups. Verified
VACUUM INTOsnapshots on a schedule, with rotation, an admin page and a CLI command. - The full ComicInfo.xml field set. All 38 plain-valued elements are parsed,
stored and editable,
AgeRatingandMangaamong them — the two that future content filtering and the reader's page direction depend on. - Portable identity. Every comic carries a UUID alongside its rowid, so a library can be exported, merged or synced without primary keys colliding.
- Full-file integrity hashing. SHA-256 over whole files, on demand rather
than at import, with a
verifycommand that reports drift. - ComicInfo write-back and CBZ conversion. Stored metadata can be written into the archive, so the files describe themselves to any other reader, and CBR/CB7/CBT can be repacked as CBZ with every image copied byte for byte.
- Relocation.
relocaterewrites stored paths after comics move on disk, so a reorganised library keeps its reading state, ratings and edits instead of being re-imported onto fresh rows. - A metadata source interface, wired all the way through. Comic Vine is no
longer the only option: sources sit behind
app/providers/base.pywith the matching rules shared between them, Metron is implemented, and both are reachable from Settings, the match screen and bulk match. - Falling through sources in order, and filling gaps between them. Auto-
match tries each configured source in the order set on the Settings page.
The first confident result decides the match; later confident sources fill
in whatever it left empty — Comic Vine has no
genre, Metron has nolocations— without ever overwriting a field the primary already found. Stops once nothing is left to fill. The order belongs to the account, never the server, since different libraries suit different sources first. - Skins. A skin is a colour palette and nothing else — no layout, no typography, no images — which keeps it a block of values that cannot break the application, only change how it looks. Administrator-only and server-wide: one is active at a time and everybody sees it, signed in or not. Every skin has to define both a light and a dark palette, since light/dark stays a personal, per-account choice layered on top — a skin missing one half would leave every account on that mode half-themed, without the admin who installed it ever seeing the problem. Two sample palettes, Newsprint and Midnight, come seeded; adding your own is checked against WCAG AA before it can be saved. Manage skins from Admin → Skins; the full authoring contract is in Skins.
- Disk usage for every import and upload location. Reported per physical
device rather than per path, since
LONGBOX_UPLOAD_DIRdefaults insideLONGBOX_DATA_DIRand browse roots are frequently on the same disk as either — showing free space against every configured path would otherwise double- or triple-count the same number under different labels. A missing path, an unreadable one, or one behind a slow network mount gets its own error rather than raising or hanging the page, since each lookup runs with its own timeout. On Admin → Server, next to the data directory, the upload directory and every browse root; on Settings, for your own upload folder. - Container handling from
comiccore/ebookcore, not four separate copies. CBZ/CBR/CB7/CBT sniffing, backend selection, entry listing and page ordering were written three times over — here, in comic-converter and in metacomic — and running the three side by side over one corpus found real divergence between them, including the CB7 bug fixed here in #15, live in two of the three at once.app/archives.py's four container classes are now adapters over comiccore'sopen_archive, layered over a lower-levelebookcorepackage that two apps not yet built, Flyleaf and Bindery, will depend on directly for non-comic containers. Only the container stage exists, soapp/metadata.py,app/matching.pyandapp/providers/are untouched.
Two concrete gains, confirmed rather than assumed: RarComic and
SevenZipComic used to wrap exactly one library (rarfile, py7zr) with
no fallback if it couldn't open a file; the shared backend chain tries
several per format in order (RAR: 7-Zip, then rarfile, then unrar, then
bsdtar; 7z: py7zr, then the 7-Zip CLI). And detection now sniffs TAR by
its ustar magic bytes — the old hand-rolled sniff here never checked that
at all, so a mislabelled CBT used to fail outright; a fixture in
tests/test_import.py proves it now opens correctly. ACBF reading order
(comiccore v0.3.0) is included too: any container that carries an ACBF
document gets its declared page order rather than a natural-sort guess,
with zero extra code on this side. Not included: recognising a standalone
.acbf file, or a directory of loose images, as an importable thing in its
own right — both are real capabilities in the underlying packages, neither
is wired into the scanner yet, and each is its own roadmap item below.
comiccore/ebookcore are vendored under vendor/ rather than a
pip install git+... dependency — both are private repos, and this
project's own docker-compose.yml builds the image locally, which would
otherwise mean every self-hoster's docker build needing read access to
someone else's private repos. See vendor/README.md for the exact commits
vendored and how to refresh them. The bar this adoption itself was held to,
and comic-converter's own: the existing format fixtures and assertions
pass unedited.
The decision the reader was built on¶
Pages as derived assets. Settled and implemented before the reader was written, because it determined the reader's shape rather than its implementation.
The reader asks "what is the URL for page 14?", never "stream me page 14". On a single server those look equivalent; at any distance they are not, because a thirty-page issue is 20–60 MB and no amount of application tuning makes that arrive quickly from the other side of the world. Serving bytes through the application also means no caching, and re-rendering a PDF page every time somebody turns back.
On a home server the implementation stays boring: extract pages on first read into a local cache and serve them like covers. The interface is what matters — it is what allows a CDN to be put in front later without touching the reader.
See Architecture for the full analysis, including what it would take to run this as a service, and why the catalogue model question that used to sit here has been settled in favour of what is already built.
The reader¶
Built, and documented in full under Reading and OPDS — keys, fit modes, how pages are served, and why EPUB is turned away.
After the reader¶
- OPDS feed, so Panels, Chunky and similar can browse the library over the network rather than only through this UI.
- Stop tagging a collected edition as a single issue. A file named
Series v02is a trade paperback, but where the source files each paperback as its own one-issue series — which Comic Vine does at least as often as it numbers the volumes as issues — Longbox reads thev02as issue #2 and tags the second single issue instead. It does so at the series name's confidence, which is high enough for bulk match to apply it without asking, and ComicInfo write-back then puts it in the file. The first fix is to refuse the confident match when the number came from a volume marker and collections are present; actually picking the right collection is a larger job.tests/test_collected_editions.pyrecords the current behaviour. - Collections and reading lists — user-defined, as
(user_id, comic_id)records. - EPUB reading — the one advertised format that imports but cannot be read, since it is reflowable text rather than page images. A reflowable spine rendered in an iframe, not the page-image reader above, and worth a real design pass before starting rather than bolting onto the existing one.
- Image skins — wallpaper and a custom logo, as a separate and much larger job than colour skins: the app ships no image assets at all today, even the favicon is an inline SVG, so this means new upload, storage, serving and cache-invalidation surface rather than a stylesheet change.
- Folder-of-images import — a directory of loose page images treated as
one comic.
ebookcorecan already open one; nothing here scans for one, since a folder as a single importable unit is a new shape for the scanner and the library row, not a container-layer change. - Standalone ACBF documents as an importable format — today only a zip
carrying an embedded ACBF document opens (as a CBZ that happens to read in
the right order); a bare
.acbffile is not, since it is not a container archive at all and needs its own handler. - Watchlists, alerting when a new issue of a followed series is imported.
- Reading statistics, built on the progress data the reader will produce.
- Content filtering by age rating, which needs the
AgeRatingfield above and the access mapping from the sharing work. - Recommendations and shared lists or activity feeds, if this ever becomes more than a household server.
- File organisation and renaming tools, now that the metadata is complete enough to rename from.
- Casting or a mobile app.
Not planned yet, but worth noting¶
Two-factor authentication. Longbox assumes a trusted network; if it is ever exposed directly to the internet rather than sitting behind a VPN or authenticating proxy, this moves up the list sharply.