Skip to content

Komga-compatible API with two-way progress sync - #130

Merged
OrbitMPGH merged 13 commits into
devfrom
feat/komga-api
Oct 20, 2026
Merged

OrbitMPGH merged 13 commits into
devfrom
feat/komga-api

Conversation

@OrbitMPGH

Copy link
Copy Markdown
Owner

Closes #124.

What it does

Maki now answers Komga's API under /komga, so apps that only speak Komga can use it as their server: the Mihon Komga extension, Mihon's Komga tracker, Tachimanga. The address to enter in the app is http://host:8990/komga.

  • Mapping. A root folder is a library, a series with at least one downloaded chapter is a series, and a downloaded chapter is a book. Everything reads through the token owner's DataScope, so root-folder grants apply exactly as they do in the app and over OPDS.
  • Auth. The credential is the user's existing OPDS token. Apps can send it as X-API-Key, or as the password in HTTP Basic (any username). After either one, Maki sets a Maki.Komga session cookie. Mihon's tracker shares the source's cookie jar and sends nothing else, so two-way sync depends on that cookie. Bad credentials get a 401, not OPDS's 404, because the extension only sends Basic after a 401. A Full API key does not open the API. The cookie is never accepted for a POST.
  • Off by default. A new per-user komga.enabled switch sits in the OPDS card. It only takes effect while OPDS is also on, since it shares the same token. When the token was just generated, the card also shows the server address and the API key to copy.
  • Progress, both directions. Writes come from PATCH books/{id}/read-progress and from the tracker's read-progress/tachiyomi endpoints (v1 and v2). All of them go through ReaderService, so stats, scrobbling, Kavita push-back and incognito behave as they do in the built-in reader. If the tracker moves forward by exactly one chapter, that counts as a real read. If it jumps several chapters, they are bulk-marked silently (MarkReadAsync), so binding the tracker to a series you already read in Mihon doesn't show up in Rewind as one day of reading. Fetching a page does not count as progress here, unlike OPDS.
  • numberSort (KomgaNumbering) is what Mihon treats as the chapter number and syncs on. It never decreases along ChapterOrder. Copies of the same chapter in different languages are treated as one chapter.

Every DTO field is always written, including nulls. kotlinx.serialization rejects a missing key even when the property is nullable.

Also in here

  • fix(storybook): the scrobble settings mock was missing the three MangaUpdates fields, which broke tsc -b on dev.
  • New .claude/rules/komga.md, plus entries in the README, docs/features.md and the release checklist (7.4a).
  • I ran lingui extract for the new strings. I didn't translate anything.

Testing

  • KomgaControllerTests (12): header, Basic and cookie auth, cookie refused on POST, both switches required, Full key refused, root-folder visibility, book order, page numbering, the PATCH round trip, tracker step vs jump, v1 tracker update.
  • KomgaNumberingTests (6), plus the authorization inventory and all OPDS tests, all passing.
  • npx tsc -b, oxlint and check:secure-context are clean.
  • Live run against a scratch copy of .devconfig on :8992 using curl in the extension's request shapes: 401 without a key; libraries, series and books lists; pages and page images; thumbnails; users/me; Basic auth setting the cookie; cookie-only GET and PUT working and POST refused; a one-chapter tracker PUT stored as a real read (BulkMarked 0); a jump stored as bulk-marked; a PATCH storing page 3.

Not tested in a real Mihon or Tachimanga install. Release checklist item 7.4a covers that.

🤖 Generated with Claude Code

OrbitMPGH and others added 13 commits October 20, 2026 09:54
The MangaUpdates scrobbler added three fields to ScrobbleSettings, and the
Storybook fixture never got them, so `tsc -b` failed on dev.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apps that only speak Komga (the Mihon Komga extension and tracker,
Tachimanga) can now use Maki as their server at http://host:port/komga.

- Libraries are root folders, series are series with something downloaded,
  books are downloaded chapters. Everything reads through the token owner's
  data scope, so root-folder grants apply as they do in the app and OPDS.
- The credential is the user's OPDS token, sent as X-API-Key, as a Basic
  password, or through the session cookie issued after either. The Mihon
  tracker only has the cookie. Both opds.enabled and the new komga.enabled
  have to be on; it is off by default.
- Reading progress goes through ReaderService: PATCH books/{id}/read-progress
  and the tracker's tachiyomi endpoints (v1 and v2). One newly read chapter
  from the tracker is a read; a jump of several is a silent bulk mark, so
  binding the tracker does not land in Rewind as one day's reading.
- Settings: a Komga switch in the OPDS card, with the server address and,
  when the token was just generated, the API key to paste.

Closes #124

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The API-key handler stays out of /komga, where X-API-Key carries the
  OPDS token: it logged one failure line and ran one lookup per page.
- Tracker summaries and v1 updates count chapters, not language copies,
  so a bilingual series can complete and lastBookRead maps to chapters.
- A one-chapter tracker update falls back to the next copy, then to a
  silent mark, when the first copy's archive cannot be opened.
- The plain series browse pages in SQL and loads only the page's rows;
  filter lists project just their column.
- Unpaged book lists are capped at the max page size, and next/previous
  builds the neighbour from the series it already placed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm 11.21 on CI refuses `npm ci` because the optional wasm runtime that
oxc's wasm32 binding pulls in names @emnapi/runtime without the lockfile
holding an entry for it. Regenerated with that npm; no versions changed,
only the missing entry and npm's peer flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The field was relabelled "Paste series URL or id…" with the MangaUpdates
scrobbler, and the story still looked for the old text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two runs on this branch failed different stories each time (nine, then
six, none touched here), all passing locally in isolation and in a full
run. In the CI log, MSW misses requests such as /api/v1/tags and
/api/v1/auth/me, which reach Vite's proxy and fail the running story.
Retries only apply when CI is set; a broken story still fails all three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	frontend/vitest.config.ts
StatsPage > Admin Reader Picker and SeriesDetailPage > Pending Torrent
Proposal failed on every CI run of PR 130 and passed locally. Both wait
for admin-only UI, which needs /initialize.json, then /auth/me, then
its own query, and on a CI runner that chain outlasts Testing Library's
default one-second timeout. Delaying the /auth/me mock by 900ms locally
fails exactly those two stories; with a 5s timeout they pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`Notification.extend(...)` in theme.ts is Mantine's component, not the
browser API, but /\bNotification\./ matched it and failed the step. The
rule now matches only the browser API's own members (permission,
requestPermission, maxActions) and `new Notification(`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI extracts with --clean and fails on any diff, so the #~ entries left by
earlier plain extracts (strings since removed from the source) failed the
catalogue step once the Storybook step stopped failing first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Komga clients send ?convert=jpeg or png for a page whose media type they
cannot decode, and the API accepted and ignored it. With pages now stored as
WebP or AVIF that matters: the page is re-encoded at full size unless its
bytes already are that format, an unknown value is a 400 as on Komga, and a
page no permitted decoder reads is a 415. The ETag carries the format, and
the book thumbnail's first-page fallback is always JPEG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ga-api

# Conflicts:
#	.claude/rules/storybook.md
…t in the Komga card story

Both branches added the MangaUpdates fields to scrobbleSettings, so the
merge kept two copies and tsc -b failed with TS1117. The Komga API card
story now waits for its content like the rest of the suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@OrbitMPGH
OrbitMPGH merged commit 62a3446 into dev Oct 20, 2026
2 checks passed
@OrbitMPGH
OrbitMPGH deleted the feat/komga-api branch October 20, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant