-
Notifications
You must be signed in to change notification settings - Fork 0
API
Interactive schema:
docs/openapi.json(auto-exported viapython scripts/export_openapi.py, viewable with any OpenAPI viewer).
All endpoints (except /healthz, /login, /logout) require an
authenticated session cookie. Unauthenticated /api/* requests receive
401 {"detail":"Authentication required"}. The SPA obtains the cookie via
POST /login.
-
Base URL:
http://<host>:8001 -
Content-Type:
application/json(exceptPOST /login, which isapplication/x-www-form-urlencoded). -
Auth: session cookie
galleryvault_session(HttpOnly, SameSite=lax). -
HTTP Basic Auth: Supported only on
GET /api/opds. Fixed usernamegalleryvault(not an ExHentai account); password is the web login password. Missing or invalid credentials on this endpoint return401 UnauthorizedwithWWW-Authenticate: Basic realm="GalleryVault OPDS". Session cookies remain valid. All other/api/*routes (including CBZ export) are cookie-only.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /healthz |
no |
{status: "ok"} — liveness probe (used by the compose healthcheck). 503 when the database is unreachable. |
| GET | /metrics |
no | Prometheus-text request counters (gv_http_requests_total, gv_http_errors_total). |
| GET | /api/auth/session |
yes |
{authenticated, auth_required, must_change_password} or 401. |
| GET | /api/onboarding/status |
yes |
{password_default, exhentai_configured, library_count} — setup progress for the first-run wizard. |
| GET | /login |
no | Returns the HTML login page. Redirects to / if already authenticated. |
| POST | /login |
no | Form field password. Success sets the session cookie and redirects (303) to /; failure redirects to /login?error=1. |
| POST | /logout |
no | Clears the session cookie, redirects to /login. |
| POST | /api/auth/change-password |
yes | JSON {current, new}. 403 if current is wrong; 204 on success. Persists the new hash in the DB so it survives restarts. |
Login is password-only. must_change_password is true when login is
required and no password hash is configured anywhere (the built-in default
p1a2s3s4 is in effect); the SPA shows a banner that links to Settings.
Turning off login entirely is done by disabling auth_required in Settings (or
AUTH_REQUIRED=false in the environment), which lets the API through without a
session —
in that mode must_change_password is always false.
Example login (curl):
curl -c cookies.txt -X POST http://localhost:8001/login \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'password=YOUR_PASSWORD'Subsequent calls pass the cookie:
curl -b cookies.txt http://localhost:8001/api/settingsFor the OPDS feed, HTTP Basic authentication is also accepted (username galleryvault, password is the web login password):
curl -H "Authorization: Basic <base64(galleryvault:password)>" http://localhost:8001/api/opds| Method | Path | Description |
|---|---|---|
| GET | /api/settings |
Public subset of effective settings (library roots, proxies, quality, favorites, tag-sync, Telegram status, …). |
| POST | /api/settings |
Persist settings. See SettingsRequest fields below. |
| GET | /api/settings/cookie-health |
Probe or return current ExHentai Cookie login status ({state, detail, checked_at}). Cached ~10 min then re-probes. States: ok, not_logged_in (session expired), no_exhentai_access (lacks ExHentai access, e.g. empty/blank 200 on exhentai.org or Sad Panda; distinct from expired session), or failed (network/site error, or empty 200 on e-hentai.org / unknown hosts). |
| POST | /api/settings/exhentai/test |
Validate the configured ExHentai cookies. Returns {status, message}. |
| POST | /api/telegram/test |
Send a test message to every configured telegram_chat_ids. Returns {ok, results}. 422 if the bot token or chat IDs are missing. |
POST /api/settings body (all fields optional):
{
"library_roots": ["/library"],
"exhentai_base_url": "https://exhentai.org",
"exhentai_cookies": {"ipb_member_id": "...", "ipb_pass_hash": "...", "igneous": "..."},
"http_proxy": null, "socks5_proxy": null,
"download_root": "/downloads",
"download_concurrency": 2,
"page_concurrency": 4,
"download_quality": "resample",
"download_title": "japanese",
"archive_quality": "resample",
"favorites_archive_enabled": false,
"favorites_archive_max_pages": 0,
"archive_fallback_pages": true,
"use_hah": false,
"image_download_timeout_seconds": 120,
"image_slow_warmup_seconds": 30,
"image_min_speed_kb_s": 10,
"title_display": "japanese",
"favorites_categories": [0, 5],
"download_favorites_enabled": false,
"favorites_poll_interval_minutes": 720,
"auto_sync_tags": true,
"tag_sync_interval_seconds": 1.5,
"tag_sync_concurrency": 4,
"telegram_bot_token": null,
"telegram_chat_ids": ["12345"],
"telegram_allowed_user_ids": [67890],
"telegram_notify_level": "summary",
"telegram_notify_lang": "zh",
"duplicate_policy": "keep_first",
"archive_roots": ["/archive1", "/archive2"],
"auto_archive_downloads": false,
"archive_delete_source": false,
"auth_required": true,
"tag_translation_update_interval_minutes": 720,
"trusted_proxies": ["192.168.1.0/24"],
"favorites": [
{"favcat": 0, "enabled": true, "mode": "incremental", "poll_interval_minutes": 720}
]
}Sending favorites updates every folder row (enable flag, mode, interval) and
derives favorites_categories from the enabled ones.
telegram_notify_level controls download notifications: summary (default,
buffers terminal download events into a single digest flushed when the queue is
idle), immediate (one message per event, the legacy behaviour),
failures_only (only final failures), or off (no automatic notifications).
telegram_notify_lang selects the language of Telegram notification copy:
zh (default) or en. All notifications (downloads, library scans, favorites
checks and the Telegram bot replies) share this language; gallery titles are
always shown untranslated.
duplicate_policy decides how the library scan resolves a gallery (same gid)
found under more than one scan root: keep_first (default, the already-stored
copy wins), prefer_more_pages, prefer_newer, prefer_larger,
prefer_smaller, or manual (never auto-resolve — everything is reported for
manual cleanup on the Duplicate copies page). All duplicates are recorded in
duplicate_records regardless of policy.
archive_roots specifies multiple cold storage directories (multi-line string in Settings UI).
When cold archiving is triggered, the archiver queries remaining free disk space across all
configured roots via statvfs and automatically balances archives onto the mount point with the
most available space. Cold CBZ archives strictly enforce English/ASCII canonical filenames
(gid-gallery.title.cbz) to guarantee cross-filesystem and network backup compatibility.
| Method | Path | Description |
|---|---|---|
| GET | /api/eh/search |
Live ExHentai listing. Query: q, category (f_cats 0–1023 or comma-separated names), min_rating (f_srdd), next (gid-ts cursor), list (search default / popular / watched / toplist), tl for toplist (11 yesterday / 12 month / 13 year / 15 all-time). Paths: /, /popular, /watched, /toplist.php?tl=. Never page=N. Response {state, items, next, list, tl}. state is ok / empty / not_logged_in / no_exhentai_access / challenge / rate_limited. A 302 through remoteapi.php is challenge, not cookie failure. Items include local badges from galleries + favorite_items. EH payload cached 90s (errors 15s) keyed by query+list+cursor. Download/favorite reuse POST /api/downloads and POST /api/favorites/add (not POST /api/galleries/{id}/favorite). |
| Method | Path | Description |
|---|---|---|
| POST | /api/downloads |
Enqueue a gallery. Body: {gid, token, title, mode, max_pages?, quality?}. max_pages (int) requests a partial/sample download — only the first N pages are fetched; it is persisted and honored by the background worker. quality (resample/original) overrides the global download_quality for this task (page-by-page original downloads ignore H@H and fetch full-size images). Returns 202 {id, gid, status}. |
| GET | /api/downloads |
List tasks. Query: page, page_size (≤500), status (pending/downloading/success/failed/cancelled). Items include current_page/total_pages progress, retry_count/max_retries, and archive_fallback (boolean indicating whether an archive task fell back to page-by-page H@H mode). Backed by the idx_download_tasks_status_id composite index to eliminate N+1 filesystem checks. |
| POST | /api/downloads/{task_id}/cancel |
Cancel a pending or downloading task. An in-flight download is interrupted (page writes stop, partial files are removed; the worker does not write to disk upon cancellation). Cancel latency is bounded by the page-progress ticks (at most one in-flight page finishes first). |
| POST | /api/downloads/{task_id}/retry |
Re-queue a failed/cancelled/successful task ({id, status:pending}). Retries are otherwise automatic: transient failures re-queue with an exponential backoff up to max_retries (default 10), and a periodic sweep re-activates failed tasks that still have budget left. |
| DELETE | /api/downloads/{task_id} |
204 – permanently remove a download task and its attempt log. |
| POST | /api/downloads/clear-success |
Remove every task with status=success. Returns {deleted}. Does not delete ingested gallery files. |
| POST | /api/downloads/batch |
202 — batch enqueue downloads. Body: {items: [{gid, token, title?, mode?, quality?, max_pages?}], mode?, quality?, max_pages?}. |
curl -b cookies.txt -X POST http://localhost:8001/api/downloads \
-H 'Content-Type: application/json' \
-d '{"gid": 12345, "token": "abcdef", "title": "Example", "mode": "full"}'| Method | Path | Description |
|---|---|---|
| GET | /api/favorites/categories |
The ten favorite folders with enabled/mode, plus cloud_count (live folder size from the favorites page header), local_count/local_size (galleries already local, from favorite_items), cloud_size (exact: local real size + fetched sizes of missing galleries, with an average estimate for the unfetched tail). |
| POST | /api/favorites/categories |
Body {favcat, enabled?, mode?} to update one folder. |
| POST | /api/favorites/sync-categories |
Refresh folder names from ExHentai. Alias: POST /api/favorites/fetch-categories. |
| POST | /api/favorites/{favcat}/check |
202 – scan folder favcat. A disabled folder runs check-only (monitor_only): records items and sizes but never downloads; an enabled folder downloads missing galleries per its mode. Mode semantics: incremental enqueues only gids never recorded in favorite_items (new additions), monitor_only never downloads, and force skips the recorded-set filter and enqueues every folder gallery not already in the local library (galleries table). Both incremental and force skip already-local galleries. |
| POST | /api/favorites/check |
202 – generic favorites check. Body: {favcat?: int} (defaults to 0). Equivalent entrypoint to {favcat}/check. |
| POST | /api/favorites/check-all |
202 – check every configured folder at once (spawns one check per favcat). |
| GET | /api/favorites/check-status |
Per-folder check progress: {running, categories: {favcat: {running, done, total, error}}, last_error}. done/total track the cursor walk. |
| POST | /api/favorites/compute-sizes |
202 – fetch sizes for missing galleries in the background so cloud_size becomes exact. |
| GET | /api/favorites/metadata-status |
Favorites metadata sync/apply worker status (running, stage (sync/apply), done, total, applied, last_error). |
| GET | /api/favorites/cover |
Query gid + token. Fetches and caches a remote gallery cover (served as the image bytes); 404 when the gallery has no usable cover. |
| GET | /api/favorites/{favcat}/items |
Paginated folder galleries (page, page_size, optional state = all/local/cloud, q = title search, order_by = last_seen_desc/first_seen_desc/posted_at_desc/title_asc/file_size_desc). Each row: favcat, gid, token, title, url, first_seen_at, state (local/cloud), and when the gallery is local (not expunged/trashed) gallery_id, category, page_count, cover_url, file_size, tags. Cloud-only galleries get cached metadata (file_size, title_jpn, tags, category) and cover_url=/api/favorites/cover?gid=&token= (disk-first; the check/download-missing pass warms /gv-cache/remote-covers/{gid}.img). The items response does not download covers. |
| POST | /api/favorites/download-missing |
202 – spawns a per-folder _favorite_size_sync pass that downloads cover files for every gallery in the folder missing a cover on disk (using the thumb URL captured from the favorites listing). |
| POST | /api/favorites/remove |
Body {gids: [...], delete_local?: bool}. Remove galleries from ExHentai favorites (all folders, favorites.php ddact=delete like SXJ, chunked 25/batch with per-gid retry). Local favorite_items (and delete_local on-disk deletes) apply only to cloud-successful gids; cloud_failed keep their local rows. Returns {cloud_ok, cloud_removed, cloud_failed, local_removed, deleted_local_galleries, failed_deletions}. |
| POST | /api/favorites/move |
Body {gids: [...], target_favcat: int} (0-9). Move galleries to another ExHentai favorite folder (favorites.php ddact=favX, chunked 25/batch with per-gid retry) and update local favorite_items. Returns {gids, target_favcat, cloud_ok, cloud_moved, cloud_failed, local_moved}. |
| POST | /api/favorites/add |
Body {gid?: int, token?: str, target_favcat: int, note?: str, items?: [{gid, token, title, note}]}. Add galleries to an ExHentai favorite folder (gallerypopups.php?act=addfav; EH move — one gid one folder). Local write only for successful_gids, then migrate out of other local folders. Auth/network failure does not mark already-succeeded gids as failed. Returns {gids, target_favcat, cloud_ok, cloud_added, cloud_failed, successful_gids, local_added}. |
| POST | /api/favorites/note |
Body {gid, note, token?, favcat?}. Update favnote via the same addfav channel; local favorite_items.note is written only after cloud success. |
| POST | /api/favorites/download-selected |
Body {gids: [...], archive?: bool, quality?: string} — 202; enqueues each selected cloud-only favorite gid ({queued, skipped}). Trashed/expunged local copies count as cloud (can re-download). quality applies to page-by-page as well as archive; archive: true uses the ExHentai zip channel. Alias: POST /api/favorites/download-batch. |
| POST | /api/favorites/duplicates/scan |
202 – background scan grouping favorite items into duplicate sets (same normalized title + same artist). |
| GET | /api/favorites/duplicates/status |
Scan progress (stage, done, total) and result groups (key, artist, items: [{favcat, gid, token, title, url, gallery_id, file_size, posted_at, first_seen_at, title_jpn, cover_data, tags}]), group_count, item_count, plus ignored (previously hidden groups, restorable). Cloud items are enriched via the batched gdata API (cover, size, posted date, tags); local items' posted dates are persisted onto galleries.posted_at. |
| POST | /api/favorites/duplicates/ignore |
Body {key, title?, gids?} – hide a duplicate group from every later scan. |
| DELETE | /api/favorites/duplicates/ignore?key= |
Restore a previously ignored group. |
| POST | /api/favorites/duplicates/unignore |
Body {key, title?, gids?} – restore an ignored duplicate group (alias for DELETE ignore). |
| GET | /api/favorites/duplicates/ignored |
List the currently ignored duplicate groups (each with key, title, items) so they can be restored. |
| POST | /api/favorites/sync |
Force refresh and sync favorites metadata cache. |
| GET | /api/galleries/{identifier}/favorite |
Which favorite folders a gallery is in: {gid, favorite: bool, favcats: [...]}. |
ExHentai's official archive download: the server packs the whole gallery into a
zip (charging GP) and the client streams it on a single connection, which is
much faster than per-page H@H fetches for large galleries. Tasks created with
archive: true (or mode containing "archive") run through this channel.
The executor reads archiver.php, requests the zip once (the returned URL is
persisted under .gv-{gid}/.archive.json so retries resume with a Range request
instead of re-charging GP), streams it, unzips, renames images by page order and
writes the same .ehviewer/.galleryvault.json metadata as a page-by-page
download. Insufficient GP fails the task immediately without retries.
| Method | Path | Description |
|---|---|---|
| POST | /api/archives/preview |
Body {gids: [...]}. Read-only, does not charge GP: returns {funds, items:[{gid, title, resample_cost, resample_size, original_cost, original_size, resample_available, original_available}]}. Tokens are resolved from favorite_items (and gallery-update rows); unavailable gids are omitted and per-gallery fetch failures carry an error field. |
The scheduled favorites check uses the archive channel when
favorites_archive_enabled is on: galleries whose filecount exceeds
favorites_archive_max_pages (0 = all) are archived with the archive_quality
tier, the rest download page-by-page.
| Method | Path | Description |
|---|---|---|
| GET | /api/updates |
Re-upload tracking. Query page, page_size, state (active = everything except ignored, all, pending, downloading, failed, ignored). Each row: id, gallery_id (old local copy), old_gid, new_gid, title, favcat, favcat_name, status (pending/downloading/failed/ignored), error_message, detected_at, updated_at, cover_url. Detection: a local gallery whose gid is not in any favorite folder but whose normalized title matches a favorite item. If the new gid is already in the local library, detection finalizes immediately (deletes the old copy; ignored rows are left alone). An in-flight download for the new gid is pinned onto the update row (downloading) so the finalize loop can close it. |
| POST | /api/updates/scan |
202 – trigger a detection scan now (also runs automatically after every favorites check). |
| GET | /api/updates/status |
Detection/update status: {detecting, last_detected_at, last_run, last_error, last_found, counts: {pending, downloading, failed, ignored}}. |
| POST | /api/updates/update |
Body {ids: [...], archive?: bool, quality?: string} – 202; enqueues the new-gid download for each pending row ({started, skipped}). If the new gid is already local, the old copy is deleted immediately (no extra download). archive: true uses the ExHentai archive channel with the given quality tier. When the download finishes, the old local copy is deleted (files + row, cascade-deleting the update row). |
| POST | /api/updates/ignore |
Body {ids: [...]} – mark rows ignored (hidden from the active list, restorable). |
| POST | /api/updates/unignore |
Body {ids: [...]} – restore ignored rows to pending. |
| GET | /api/updates/ignored |
Paginated list of currently ignored rows (for the restore page). |
| POST | /api/updates/delete |
Body {ids: [...]} – permanently remove failed/ignored update records (200 {deleted}). |
| Method | Path | Description |
|---|---|---|
| GET | /api/galleries |
Search/browse. Query: page, page_size, q, tags (csv ns:name), tag_mode (and/or), tag_match (exact/fuzzy), category (including __not_fav__), read_status, min_rating, page_min/page_max, size_min/size_max (bytes, coalesce(storage_size,file_size)), posted_from/posted_to, uploader (substring), image_quality (original/resample), min_local_rating (1–5), list_id. Language shortcuts are ordinary language: tags. |
| GET | /api/galleries/categories |
Count of galleries per category dictionary, including __not_fav__ and total all. |
| GET | /api/galleries/trash |
Paginated list of soft-deleted galleries in trash. Query: page, page_size. (200 JSON, 422). |
| GET | /api/galleries/expunged |
Paginated list of expunged/banned galleries on EH. Query: page, page_size. (200 JSON, 422). |
| GET | /api/galleries/random |
{id} of a random non-expunged gallery (404 when empty). |
| GET | /api/galleries/{identifier}/next |
{id} of the next non-expunged gallery (ascending by id) — used by the reader to advance past the last page (404 when none). |
| GET | /api/galleries/{identifier} |
Metadata including local_rating / local_note, page list, tags (local: custom tags live alongside EH tags), spider_info, and eh_url. |
| PATCH | /api/galleries/{identifier}/local |
Body {local_rating?, local_note?, local_tags?}. Local stars 1–5 (null clears), note, and local: tags only — does not replace ExHentai tags. |
| GET | /api/galleries/{identifier}/lists |
Local lists that contain this gallery. |
| POST | /api/galleries/{identifier}/download-original |
202 – enqueue download for original quality. Body {archive?: bool} (supports page-by-page or ExHentai archive channel). Superseded resampled copy is removed upon completion. |
| POST | /api/galleries/{identifier}/redownload |
202 – enqueue redownloading this gallery (mode=gallery or gallery_archive; does not follow replacement chains). |
| POST | /api/galleries/{identifier}/favorite |
Body {favcat: int} (0-9). Modify the ExHentai favorite category folder for this gallery. |
| POST | /api/galleries/{identifier}/read |
Mark gallery as read (upsert_progress to last page). |
| DELETE | /api/galleries/{identifier} |
Remove a gallery (cascades to pages, tag links, progress, history). Query delete_files=true also deletes on-disk files; partial disk deletion failure returns 500 with failed_paths and deleted_paths in the body, removing deleted copy paths from DB and keeping the row if residual copies remain. |
| POST | /api/galleries/delete-bulk |
Body {ids: [...], delete_files?: bool}. Bulk remove galleries by id; delete_files also deletes on-disk files, keeping each row whose files failed to delete. Returns {deleted, failed_deletions}. Ids are processed in 500-row batches to stay under asyncpg's parameter limit. |
| POST | /api/galleries/delete-filtered |
Body {q?, category?, tags?, tag_mode?, tag_match?, delete_files?}. Remove every gallery matching the current library filter (same semantics as GET /api/galleries). The backend pages the filter and deletes in 500-row batches, so the client never sends a huge id list. Returns {deleted, matched, failed_deletions}. When matched exceeds 5000 the request is rejected with 409 (refine the filter or delete in batches). |
| POST | /api/galleries/expunged/redownload |
Body {ids: [...]}. Batch queue re-download for expunged galleries. (200 JSON, 422). |
| POST | /api/galleries/restore |
Body {ids: [...]}. Restore galleries from trash. (200 JSON, 422). |
| POST | /api/galleries/purge |
Body {ids: [...]}. Permanently purge soft-deleted galleries and disk files. (200 JSON, 422). |
| GET | /api/galleries/{identifier}/export.cbz |
Download the gallery as a CBZ (requires session cookie authentication). An on-disk .cbz is streamed with FileResponse; a directory gallery is packed in page order (ZIP_STORED) to a tempfile. Member paths must resolve inside the gallery directory (zip-slip → 400); missing files → 404. Records an export-cbz task log. |
| GET | /api/galleries/{identifier}/pages/{page_index} |
Stream one page image (image/jpeg/image/png/…). |
| GET | /api/galleries/{identifier}/pages/{page_index}/meta |
Probe animation properties ({animated: bool, duration_ms: int}). GIF/WebP frame delays are summed from a binary scan without decoding bitmaps (duration_ms capped at 120000); Pillow fallback is at most 50 frames. Used by the adaptive reader slideshow. |
| GET | /api/galleries/{identifier}/thumb/{page_index} |
Serve a cached static JPEG thumbnail for a page (generated on first access into /gv-cache/thumbs, Cache-Control + ETag). |
| GET | /api/galleries/{identifier}/progress |
Reading progress (current_page, total_pages). |
| PUT | /api/galleries/{identifier}/progress |
Body {current_page, total_pages} – records progress and history. |
| POST | /api/galleries/{identifier}/progress |
Body {current_page, total_pages} – records progress and history (equivalent alias to PUT). (200 JSON, 422). |
| DELETE | /api/galleries/{identifier}/progress |
Clear reading progress and that gallery's history row (204). Removes it from Continue Reading / History. |
| DELETE | /api/galleries/progress |
Clear / reset reading progress for all galleries (204). Requires query parameter confirm=true (400 Bad Request if omitted) to prevent accidental bulk wipe. |
| POST | /api/galleries/{identifier}/sync-tags |
Sync tags from ExHentai. |
| GET | /api/galleries/integrity |
Paged list of galleries with integrity issues (page, page_size ≤ 500). Does not trigger a scan. Returns {total, page, page_size, magic_scan, items: [{id, gid, title, page_count, actual_pages, file_count, cover_url, storage_path, tags}]} where magic_scan contains {running, started_at, completed_at, scanned, total, corrupt}. |
| POST | /api/galleries/integrity/scan |
202 – trigger background file integrity magic header scan (JPEG/PNG/WebP/GIF magic header and 4/8-digit zero padding). If globally paused, returns 200 {"status": "paused", "detail": "Global paused: integrity scan is disabled"} without spawning. If already running, avoids duplicate spawn. Returns current magic_scan summary (running, started_at, completed_at, scanned, total, corrupt). |
Galleries with integrity anomalies (missing pages or corrupted magic headers) can be incrementally repaired via POST /api/galleries/{identifier}/redownload. The downloader re-fetches only the corrupt or missing pages while preserving valid images and existing metadata, saving upstream bandwidth and IP quota.
Example:
curl -b cookies.txt 'http://localhost:8001/api/galleries?q=myth&page=1&page_size=24'| Method | Path | Description |
|---|---|---|
| GET | /api/history |
Reading history (page, page_size). |
| DELETE | /api/history |
Clear history (204). Requires query parameter confirm=true (400 Bad Request if omitted) to prevent accidental clearing. |
| GET | /api/tags/search |
Search local tags. Query q, page, page_size (≤500), namespace. Items include display (Chinese translation when available) and usage_count. With zh=1, q is matched against Chinese translations (for the tag-autocomplete in the search box). |
| GET | /api/tags/search/status |
Tag translation auto-update status (entries, last, last_error, source, interval_minutes). |
| POST | /api/tags/search/reload |
202 – download the latest EhTagTranslation release (db.text.json) and reload translations now. |
Tag translations are updated in the backend from the latest
EhTagTranslation/Database
release (the same source ehsyringe uses). A background task runs every
TAG_TRANSLATION_UPDATE_INTERVAL_MINUTES (default 720, 0 disables); a manual
refresh is available via the button in Settings. Markdown icon syntax
() embedded in translations is stripped for display.
| Method | Path | Description |
|---|---|---|
| GET | /api/series |
Paged list of series groups: {items, total, page, page_size}. Query: page (default 1), page_size (default 25), show_all (0 = doujinshi/manga only, 1 = all categories). Members include local galleries and cloud-only favorites with cover_url (/api/favorites/cover). |
| POST | /api/series |
Body {name} — create a manual series group (201). |
| GET | /api/series/{series_id} |
Series group details and member list (404 if missing). |
| PATCH | /api/series/{series_id} |
Body {name} — rename a series group. |
| DELETE | /api/series/{series_id} |
Delete a series group and its memberships. |
| POST | /api/series/{series_id}/items |
Body {gallery_ids: [...], gids: [...]} — add local galleries or gids to the series. |
| POST | /api/series/{series_id}/items/remove |
Body {gallery_ids: [...], gids: [...]} — remove local galleries or gids from the series. |
| GET | /api/series/{series_id}/cloud-candidates |
Query q (optional) — search cloud favorites candidates eligible to add to this series. |
| POST | /api/series/{series_id}/cloud-items |
Body {gids: [...]} — add cloud-only favorite members to the series. |
| POST | /api/series/{series_id}/cloud-items/remove |
Body {gids: [...]} — remove cloud members from the series. |
| POST | /api/series/rebuild |
Trigger full series re-clustering and rebuild; logs progress to task history as series-rebuild. |
| Method | Path | Description |
|---|---|---|
| GET | /api/pause |
Get current global background tasks pause status: {paused: bool}. |
| POST | /api/pause |
Set or toggle global pause status. Body: {paused: bool}. (200 JSON, 422). |
| GET | /api/archive |
Get cold archive status and statistics. (200 JSON). |
| POST | /api/archive |
Trigger cold archive packaging/migration run. (202 JSON, 422). |
| POST | /api/system/purge-archived-sources |
200 — Safe background purge of unpacked source directories in the downloads directory when a corresponding CBZ archive already exists in cold storage (archive_roots). Actively skips galleries with pending or downloading tasks, verifies matching GIDs across cold and hot locations, safely deletes the source folder on disk, decrements physical disk usage, and records a task in #/logs. Returns {deleted_dirs: int, reclaimed_bytes: int, active_skipped: int}. |
| Method | Path | Description |
|---|---|---|
| GET | /api/scan |
Current scan status (running, started_at, completed_at, scanned, persisted, success, errors, expunged, duplicates, duplicate_gids, last). |
| POST | /api/scan |
202 – trigger a library scan. |
| GET | /api/scan/duplicates |
Duplicate-copy groups found by the last scan. Each group (gid, status (open/dismissed), policy, winner_path) lists every physical copy with path, key, gallery_id, storage_type, title, page_count, file_size, posted_at, is_current, tags. |
| POST | /api/scan/duplicates/{gid}/resolve |
Body {path, delete_others?} – make path the stored copy (the gallery row is re-pointed at it). With delete_others=true the other copies are deleted from disk (paths must be inside the scan roots and listed in the group) and the group is dropped. |
| POST | /api/scan/duplicates/{gid}/dismiss |
Hide a duplicate group (survives rescans until the copies actually change). |
| POST | /api/scan/duplicates/{gid}/restore |
Bring a dismissed group back. |
| GET | /api/scan/duplicates/thumb/{key} |
Lazily-generated JPEG cover thumbnail for one copy (cached under /gv-cache/thumbs/dup/{key}/0.jpg). Invalid keys (.., slashes, or absolute paths) return 404. |
| GET | /api/library/duplicates/cross-gid |
Cached cross-GID duplicate clusters grouped by normalized title and artist. Returns {ready: bool, count: int, groups: [{key, artist, items: [{gid, token, title, title_jpn, display_title, gallery_id, storage_path, cover_url, cloud, ...}]}]}. Groups combine both local galleries and cloud-only favorite entries with tags, category, and page count. Clustering strips convention/group prefixes (e.g. (C100), [Group]) and matches artist namespaces and normalized title strings with multi-tier scoring. |
| POST | /api/library/duplicates/cross-gid/refresh |
Trigger an immediate background re-clustering of cross-GID duplicate candidates and return refreshed results. (Note: ignoring a cross-GID group reuses POST /api/favorites/duplicates/ignore). |
| GET | /api/tag-sync/status |
Background tag-sync worker status (running, queued, total, processed, succeeded, failed, retries, interval, last_error, category_refreshed, category_refresh_running). |
| POST | /api/tag-sync/start |
202 – re-queue every gallery still needing a tag sync for a manual full run. |
| POST | /api/tag-sync/refresh-categories |
202 – run a one-time category backfill: galleries in the generic bucket that have ExHentai coordinates but were never category-refreshed are re-fetched and classified; galleries 404 on ExHentai are moved to deleted. Status is visible via category_refreshed/category_refresh_running on /api/tag-sync/status. |
| POST | /api/tag-sync/repair-categories |
200 — Fast local category self-healing: synchronizes category fields in the local database directly from existing .galleryvault.json or cached metadata, instantly repairing galleries mistakenly lumped into Misc or Other without external network requests to ExHentai. Returns {repaired: int, total: int}. |
| GET | /api/thumbs/status |
Thumbnail generation worker status (running, queued, processed, succeeded, failed, total, last_error). |
| POST | /api/thumbs/generate |
202 – queue every gallery missing a cover thumbnail for background generation. |
| GET | /api/notifications |
In-app notification ring (maxlen 100): {items, unread_count}. Loaded from cache/notifications.json on startup and rewritten on change; write failures are logged only. |
| POST | /api/notifications/read |
Mark every item read; returns the same shape as GET with unread_count: 0. |
| POST | /api/notifications/clear |
Clear every notification; returns {items: [], unread_count: 0}. |
| GET | /api/opds |
OPDS Atom catalog of recently ingested galleries (supports session cookie or HTTP Basic auth). Acquisition links GET /api/galleries/{id}/export.cbz. |
| GET | /api/system/storage |
Library / downloads / cache usage. Missing roots report bytes: 0 (not 500). largest is the top 10 by DB storage_size. |
| GET | /api/saved-searches |
Named library filters stored in user_settings.saved_searches (get+merge, max 30). |
| POST | /api/saved-searches |
Body {name, query}. |
| DELETE | /api/saved-searches/{search_id} |
Remove one saved search. |
| GET | /api/lists |
Local lists (id, name, count). Independent of ExHentai. |
| POST | /api/lists |
Body {name}. |
| GET | /api/lists/{list_id} |
Metadata and gallery items for a specific local list. (200 JSON, 404, 422). |
| PATCH | /api/lists/{list_id} |
Rename. |
| DELETE | /api/lists/{list_id} |
Delete list and memberships. |
| POST | /api/lists/{list_id}/items |
Body {gallery_ids} — add (CBZ without gid allowed). |
| POST | /api/lists/{list_id}/items/remove |
Body {gallery_ids}. |
| GET | /api/quota |
Cached GP + Image Limit. JSON {gp, image_limit: {current, limit}, image_limits, checked_at, error, cached}. |
| GET | /api/logs |
Aggregated activity log: {running: [...], finished: [...]}. running lists the live background tasks (each with task (scan/tag-sync/thumbs/metadata), started_at, done, total, stage, cancellable); finished is the latest-first history of completed tasks with task, started_at, completed_at, status (success/failed/cancelled), reason, done, total. |
| POST | /api/logs/{task}/cancel |
202 – request cancellation of a running background task (scan, tag-sync, thumbs, metadata). The worker stops at the next safe point; the queue is drained for queue-based tasks. |
| GET | /api/system/logs |
In-memory diagnostic ring buffer (query: min_level (default INFO), limit (default 100, ≤500), search (text search)). Items include timestamp, level, logger, message, and extras (request id, worker context); secrets are masked. httpx 2xx/3xx access lines (including Telegram getUpdates long-poll) are omitted; 4xx/5xx and business logs are kept. |
| POST | /api/system/logs/level |
Body {level} (DEBUG/INFO/WARNING/ERROR) — change the process log level without restart. |
| DELETE | /api/system/logs |
Clear the in-memory ring buffer (200 {"status":"cleared"}). |
| GET | /api/system/logs/download |
Download galleryvault.log (rotated file under configured log root plus recent memory lines) as an attachment. The file path must resolve within the log root (403 if out-of-bounds, 404 if missing). |
-
400/422– invalid input (validation). -
401– authentication required (API routes). -
404– resource not found. -
409– conflict (e.g. download already queued). -
502– upstream ExHentai request failed (error detail is scrubbed of cookies/tokens). -
503– a backend service (downloader, database) is unavailable.