Skip to content

Komga: fixes, content filtering, and homepage settings - #6

Open
Exikle wants to merge 31 commits into
Paperback-iOS:0.9from
Exikle:0.9
Open

Komga: fixes, content filtering, and homepage settings#6
Exikle wants to merge 31 commits into
Paperback-iOS:0.9from
Exikle:0.9

Conversation

@Exikle

@Exikle Exikle commented Sep 3, 2026

Copy link
Copy Markdown

Komga extension: bug fixes, content filtering, and homepage/settings work.
Tested against a library of 86 series / 14,633 books.

Existing settings migrate rather than reset, and no declared capability
changes, so this stays on major 3.

3.1

Bug fixes, mostly long-standing:

  • Chapter titles rendered twice. The prefix strip only matched the literal
    word "chapter" plus a space, so Ch.245 and Vol.13 Ch.074.5 survived and
    showed as Ch. 245 - Ch.245. 1,645 of 14,633 books were affected.
    parseChapterTitle peels volume/chapter/separator tokens in a loop and
    recovers the volume when one was recorded.
  • Search filters were url-encoded into a JSON body, so a genre like
    Science Fiction was sent as Science%20Fiction and matched nothing.
  • getSearchFilters threw on failure despite the comment saying it must
    not, breaking Discover against an unconfigured or offline server.
  • The image interceptor could throwRequest.headers is optional and was
    non-null asserted.
  • Settings form: duplicate row ids, plaintext password field, and a
    TypeError instead of an error message on a 404 or unreachable host.
  • Dropped metadata: contentRating was pinned to EVERYONE;
    alternateTitles, readingDirection and publisher went unused.
  • Content rating on search and discover items, a Hide Adult Content setting,
    sorting options for search, shareUrl, and a connection test.
  • CI now typechecks, lints and format-checks before bundling. It previously
    published to gh-pages with nothing verifying the sources compiled.

3.2.0

Move to semver. No functional change.

3.3.0

processTitlesForUpdates: the app was calling getChapters once per tracked
series. Komga has no "changed since" condition, but /series/updated is
ordered by lastModified descending, so paging to a cutoff finds what moved in
one request — a daily run goes from 86 requests to 1. The cutoff looks an hour
further back than the last check, since a series' lastModified can trail its
books' created time by a few seconds during a library scan.

3.4.0 – 3.6.1

Genres, featured and prominent discover sections; On Deck brought under the
content filter; Chapter.creationDate.

3.7.0

Seven homepage sections were backed by five queries — two pairs ran identical
requests and differed only in card size. Presentation became a per-section
setting (Regular / Large / Hero, with Hidden replacing the visibility toggles),
and the duplicates were removed.

Labels now follow Komga's own dashboard vocabulary: On Deck, Keep Reading,
Recently Added Series, Recently Updated Series.

3.8.0

Connection status on the settings screen, checked on appearance. It no longer
trusts a bare 200 — Komga's SPA answers unknown paths with index.html, so a
base URL with a stray path previously looked like a working connection.

3.9.0

Library scope and one-shot filtering in Content Settings, applied to browsing
and search. Adds a Nearly Finished section ordered by fewest unread books,
sorted client-side because Komga ignores booksUnreadCount as a sort field.

CI switched to npm ci, caches dependencies, replaces the disabled
set-output, and validates pull requests without publishing.

3.9.1 – 3.9.2

komga.ts split into focused modules (979 → 668 lines) and utils.ts renamed
to utils/operators.ts, which had been colliding with the utils/ directory.
Root config fixes: types pointed at index.d.js, fast-xml-parser was
unused, chai was imported by the test suite but only @types/chai was
declared, and lint/format skipped the root .mjs files.

Notes

Komga silently ignores unrecognised sort fields and returns 200, so every sort
option here was verified by confirming asc and desc give different results.
titleSort is one of the ignored ones, which is why search had never actually
been sorted; releaseDate and folderName are ignored too and are left out.

Known limitations: Recently Updated Series includes metadata-only edits, as
/series/updated takes no parameter to narrow it; On Deck and Keep Reading
overlap by nature; and genre-based content rating reflects library metadata
quality rather than the content.

The sdk under src/Komga/sdk is regenerated by openapi-ts, so formatting it
only creates churn on the next run.
Request.headers is optional, so the non-null assertions threw a TypeError
on any request that arrived without headers.
The old prefix strip only matched the literal word "chapter" followed by a
space, so abbreviated and volume-prefixed titles were left intact and shown
twice, e.g. "Ch. 245 - Ch.245" or "Ch. 74.5 - Vol.13 Ch.074.5". Across the
test library that was 1645 of 14633 books.

parseChapterTitle peels leading volume/chapter/separator tokens in a loop,
which also covers doubled prefixes like "Chapter 10 - Chapter 10", and pulls
out the volume when the source recorded one. Volume placeholders (TBD, TBA)
are stripped without setting a volume. Drops the hardcoded volume: 0 and the
file size that was being passed as langCode.
contentRating was pinned to EVERYONE, so 18+ series were labelled safe.
Komga exposes a minimum age in metadata.ageRating. alternateTitles and
readingDirection were also available and unused; readingDirection is what
the reader needs to pick long strip mode.
The comment above it already said this must not throw, but each of the four
lookups threw on failure, so an unconfigured or offline server broke the
homepage. They now fall back to empty option lists and run in parallel.
These go into a JSON body, not a query string, so a genre like "Science
Fiction" was sent as "Science%20Fiction" and matched nothing. Any tag or
genre containing a space or ampersand returned no results.
These files predated the prettier config and were never run through it.
Formatting only, no behaviour change.
The workflow published to gh-pages without anything verifying the sources
compiled.
contentRating was only set on the manga details page, so search results and
every carousel item shipped it unset and the app had nothing to filter on.

Komga libraries rarely populate ageRating — every series in the test library
has it null — so the rating now falls back to genres when it is absent.
Books carry no genres, which leaves On Deck unrated.
- both navigation rows shared the id 'authentication'
- the password field rendered in plaintext, InputRow has isSecureEntry
- only a 400 carries `violations`, so a 404 or an unreachable host threw a
  TypeError instead of reporting the real problem
- "Hompage" typo in the section header
Adds a Content Settings form with a Hide Adult Content switch and an editable
genre list, loaded from the server's own genres. Search and Continue Reading
exclude them server-side through the search condition; Recently Added and
Recently Updated have no condition parameter so they are filtered client-side.
On Deck cannot be filtered as Komga exposes no genres on books.

Recently Added and Recently Updated also get show/hide toggles, which the
other two sections already had.
Reports the result inline with a styled label instead of requiring a blind
submit, and reuses the same status messages as submission.
Row ids may only contain alphanumerics or `._-@()[]%?#+=/&:`, so genres with
a space such as `martial arts` were rejected at render. Encoded with base64
the same way the search filters already build their genre and tag ids; the
stored setting stays as plain genre names.
Komga records external links per series (AniList, MangaDex, MangaUpdates and
others) but MangaInfo.shareUrl was left unset, so the app's share action had
no target. Picks the first match from a preference order, falling back to
whatever link exists. Also surfaces totalBookCount, which is the intended
length of the series as opposed to the books actually present.
Search was pinned to a hardcoded sort behind a dead `orderResultsAlphabetically`
local. That sort was also broken: Komga ignores an unrecognised sort field
instead of erroring, and `titleSort` is one of them, so results were never
actually ordered by title. The working field is `metadata.titleSort`.

Every option was checked against a live server by confirming asc and desc
return different results; `releaseDate` and `folderName` are ignored the same
way and are deliberately left out.
Versions were major.minor; other Paperback extensions use full semver, so
match that. Major stays at 3: nothing since the 3.0 tracker release breaks
compatibility, the new settings keys are additive and no capability was
dropped. Minor goes to 2 rather than 1 because several different builds
already shipped as 3.1, and 3.1.0 could compare equal to those.
The app was calling getChapters once per tracked series on every update run,
86 requests for this library. Komga has no "changed since" search condition,
but /series/updated is ordered by lastModified descending, so paging until the
cutoff identifies everything that moved in one or two requests and the rest is
marked skippable.

A series' lastModified can trail its books' created time by a few seconds when
both are written during a single library scan, so the cutoff looks an hour
further back than the app's last check. Checked against every series in a
14.6k book library: a daily run drops from 86 requests to 27 with nothing
missed. A failed request returns without setting any priority, since marking
titles skipped on incomplete information would swallow real updates.
Adds a Genres section using DiscoverSectionType.genres, which was one of four
section types the extension never used. Each tile carries a search query in
the shape getSearchResults already reads, so tapping one runs a normal genre
search. Hidden genres are left out of the grid when Hide Adult Content is on,
and the section has a toggle like the others.
Uses the last two unused DiscoverSectionType values. Featured shows recently
added series with cover, status, publisher, summary and book counts; the
prominent carousel shows in-progress series with read progress.

Both overlap sections that already exist, so they default to off rather than
crowding the homepage on upgrade, and the toggle subtitles say so.
On Deck was the one section the content filter could not reach, since Komga
puts genres on series and that endpoint returns books. Fetching the hidden
series once and filtering by membership covers it in a single extra request,
and only when the filter is enabled. The settings footer no longer claims the
section is unfilterable.

Chapter.creationDate now carries the book's created time, which is when it
entered the library rather than when it was published.
"Featured" described the card style rather than what the section shows. The
id and settings key stay as they are so existing toggle state survives.
Seven homepage sections were backed by five queries: Featured and Recently
Added both called /series/new, and the prominent carousel and Continue
Reading both called /series/list with the same condition and sort.
Presentation is now a property of each section rather than a reason to
duplicate it, chosen per section as Regular, Large or Hero, with Hidden
replacing the old visibility toggles.

Labels follow Komga's own dashboard vocabulary so they read the way users
already know them from the web UI: Continue Reading becomes Keep Reading,
and the two series sections name themselves as such.

Existing settings migrate rather than reset. An old visibility boolean maps
to Regular or Hidden, and a duplicate section that was switched on becomes
its owner's style.
The status was only visible after pressing Test Connection a level down. The
root settings screen now checks on appearance and shows the result, with the
server URL as the Server Settings subtitle.

The check moves into a shared helper that Test Connection and form submission
both use, replacing two copies of the same error handling. It also no longer
trusts a bare 200: Komga's SPA answers unknown paths with index.html, so a
base URL with a stray path looked like a successful connection and reported
"Connected as undefined".
Adds a library picker and a one-shot toggle to Content Settings, applied to
browsing and search alike. The discover endpoints take them as query params
while /series/list takes conditions, so both paths are covered.

Nearly Finished orders series by how few unread books remain. Komga ignores
booksUnreadCount as a sort field, so the ordering happens client side over
the in-progress set, which is bounded by what the user is actually reading.
- npm ci rather than npm install, since a lockfile is committed
- cache the npm download directory
- set-output was disabled by GitHub in 2023; write to $GITHUB_OUTPUT
- run on pull_request as well, with bundling and deployment guarded to
  pushes so a PR is checked without publishing
komga.ts had grown to 979 lines, most of it not the extension interface.
Moves out discover section building, content rating, query scoping and the
shared formatting helpers, leaving 668 lines of interface implementation.

Also renames utils.ts to utils/operators.ts, since a file and a directory
sharing the name made imports ambiguous. No behaviour change.
- types pointed at index.d.js rather than index.d.ts
- fast-xml-parser was declared but imported nowhere
- src/tests/suite.ts imports chai directly, but only @types/chai was
  declared; it was resolving by hoisting from @paperback/toolchain
- lint and format only covered src, so the root .mjs config files were
  never checked, and openapi-ts.config.mjs had drifted out of style
- dropped a commented-out import from the eslint config
@Exikle

Exikle commented Sep 3, 2026

Copy link
Copy Markdown
Author

#4 this is done predating my changes.

@Exikle

Exikle commented Sep 3, 2026

Copy link
Copy Markdown
Author

#3 as well seems to be fixed with my changes on top of paper. shows up in my testing.

getChapters sets an empty title for a chapter with no name of its own, which
is the common case. The progress form fell back with ??, so it only caught
null and undefined and showed blank rows for 92% of chapters.
Reverts the pull_request trigger; keeps npm ci, dependency caching and the
$GITHUB_OUTPUT change.
config.ts repeated the same seven genres that content_rating.ts already
defines, so the two could drift. The default hide list now derives from them.
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