Servers
Inkshelf reads books from three kinds of servers: Komga (comics and manga), Mealie (recipes)
and Calibre-Web (ebooks). The app never talks to any of them directly. It talks to one
interface, servers.Server, and each kind of server has a package that turns its own API into
that interface. A fourth kind is one more package.
The Server interface and its model
internal/servers says, once for every kind, what the app needs:
Series,Libraries,Books(seriesID),InProgress: the lists.OpenBook(thenPages.Page(n)) for a book of page images,OpenFilefor a book fetched whole (a CBZ or an EPUB),FirstPage: downloading.SeriesThumbnail,BookThumbnail: the small covers.SetProgress,ClearProgress: the reading progress (see progress-sync.md).
The model is neutral: it holds only what the app shows or needs, whatever the server calls it.
| Type | What it is |
|---|---|
Series | a series of books, with its counts of books read and in progress |
Book | a book of a series; its Number (may be 2.5) orders it; its Format says how it is downloaded |
Library | a group of series (Komga's "Manga", "Comics"), for the series list's filter |
Format | PageImages (fetched page by page), ComicArchive (a CBZ, fetched whole), Reflowable (an EPUB, fetched whole) |
ReadingProgress | the last page read and whether the book is completed |
Direction | LeftToRight or RightToLeft, or "" when the server does not say |
Kind | a kind of server's name, logo and Words ("category"/"recipe" for Mealie) |
Config | one entry of config.json |
servers also sorts titles for French and English readers (SortKey, CompareTitles, Fold):
leading articles and accents left out, numbers compared as numbers through
naturalorder.
What a server cannot do
A server that cannot do something returns an error wrapping errors.ErrUnsupported, and the app
does without: no library filter, or no progress on the server. Never return an empty list for
"not supported": the app would believe there is nothing.
The three kinds
| Kind | Package | API | Login | Notes |
|---|---|---|---|---|
| Komga | servers/komga | REST, JSON | API key in X-API-Key | Books are PageImages: pages fetched one by one, as originals when JPEG/PNG/WebP, else converted to PNG by Komga (pageConversions). Keeps progress and libraries. |
| Mealie | servers/mealie | REST, JSON | API token as Authorization: Bearer | One series per category, one book per recipe. servers/mealie/recipepages lays a recipe out and draws its pages as images. No progress, no libraries. |
| Calibre-Web | servers/calibreweb | OPDS (Atom feeds) | HTTP Basic auth | Reads the whole catalog. A book is an EPUB (Reflowable) or a CBZ (ComicArchive), the EPUB preferred. No progress, no libraries. |
Each package has a Client (the API, call by call) and a Server (the servers.Server built on
it), made with NewServer(config, httpClient), and a kind.go declaring its Kind, whose logo
is a logo.png embedded with //go:embed (made from icons/*.svg by make icons).
Shared pieces
- servers/httpclient:
Newmakes the one HTTP client the app sends every request through (more idle connections than Go's default, a one-minute timeout).Do,GetJSONandGetBytessend a request a server built and turn a non-2xx answer into a*StatusError, so callers can tell a 404 or a 401 from a network error witherrors.As. Calibre-Web downloads files through a copy of the client without the timeout. - servers/registry:
OpenerandKindOfswitch onConfig.Type("komga"or empty,"mealie","calibreweb"). Only the programs in cmd import it. This is what keepsappandshelffrom knowing any one server (see architecture.md).
Fakes
| Fake | For |
|---|---|
| servers/servertest | Fake, a Server in memory whose content is its fields. It records the pages asked for and the progress sent. For the tests of shelf and progress. |
| servers/komga/fakekomga, servers/mealie/fakemealie, servers/calibreweb/fakecalibreweb | NewHandler(): an http.Handler that answers like the real server, with fixed generated content (series, pages, recipes and photos, EPUBs and CBZs). The emulator runs them (-fake, -fake-type), and their content never changes, so screenshots compare byte for byte. |
The fakes speak HTTP, so the real client code runs against them: a change to the Komga client shows in the emulator's shots.
Adding a kind of server
- Create
internal/servers/<kind>/with adoc.gothat explains the server and its API, aClient, and aServerthat implementsservers.Server. Return errors wrappingerrors.ErrUnsupportedfor what it cannot do. Send requests throughhttpclient.Do,GetJSONorGetBytes. - Add
kind.go: itsKind(name,Words, embeddedlogo.png). Drawicons/<kind>.svgand add a rule for itslogo.pngto theiconstarget of the Makefile. - Add a case for its
typein both switches of servers/registry. - Write tests against an
httptestserver, askomga/client_test.godoes. - Write
internal/servers/<kind>/fake<kind>/with aNewHandler(), add it tofakeHandlersin cmd/emulator/main.go, and write a script in emulator/scripts. If the script needs-fake-type <kind>, add it toscript-flagsin the Makefile, thenmake baselineonce its shots look right. - Document the
config.jsonentry in usage.md. - Run
make verifyand the dependency check of architecture.md.