Consolidate MangaCircus app, scraper, and Compose baseline
This commit is contained in:
1 parent
cfb2d79103
commit
eef4ffa07c
103 files changed
+9316
-172
No files matched your search
@@ -0,0 +1,141 @@
|
||||
# Collections and relationships
|
||||
|
||||
Collections are a presentation layer, not a filesystem migration. Creating, reordering,
|
||||
or removing one never renames manga/chapter folders or changes reading IDs.
|
||||
|
||||
## Using the UI
|
||||
|
||||
Open **Settings → Admin → Collections & metadata** (or **Manage collections** on the dashboard).
|
||||
Create a collection, add library manga, choose each member's role and reading order,
|
||||
and save. Choose a member cover, a mosaic of up to four covers, or upload a custom
|
||||
image under 600 KB. Dashboard display can be grouped, expanded, or individual; the
|
||||
choice is remembered per browser. A collection page shows aggregate progress and
|
||||
continues the first unfinished member in the chosen order.
|
||||
|
||||
**New collection** starts an unsaved draft using an ungrouped library manga. Rename
|
||||
it, add members and choose **Save collection** (or **Save all changes**). Each manga
|
||||
can belong to one collection. If all manga are grouped, remove a member from another
|
||||
collection first; a collection's final member is retained until you delete the
|
||||
collection. Deletion never removes manga or reading progress.
|
||||
|
||||
Save feedback and errors appear above the editor. Controls are locked during a save;
|
||||
failed saves retain drafts. If another admin has changed the catalog, the stale write
|
||||
is rejected. **Reload catalog** fetches the latest revision, with confirmation before
|
||||
discarding edits; copy any wanted changes first. Unsaved JSON survives collection
|
||||
selection and must use **Validate & save JSON**, not an ordinary collection save.
|
||||
Metadata imports/research are blocked while drafts exist so they cannot silently
|
||||
replace them. Relationship edits save immediately when there is no collection draft.
|
||||
|
||||
Creation works on HTTPS, localhost and ordinary HTTP LAN/Tailscale addresses. The
|
||||
browser uses secure random bytes when its secure-context-only `randomUUID` API is
|
||||
unavailable. This does not change networking or the server's revision/validation rules.
|
||||
|
||||
Tag combinations match a single member, not tags scattered across unrelated members.
|
||||
Reading-time totals only include the current account's visible Reading Story. Collection
|
||||
publication status is ongoing if any member is ongoing, completed if all are completed,
|
||||
otherwise mixed. Collections do not control scraping or merge editions.
|
||||
|
||||
## Three distinct concepts
|
||||
|
||||
| Concept | Meaning | Owner |
|
||||
|---|---|---|
|
||||
| Relationship | Directed fact: target is a prequel/sequel/adaptation/etc. of source | Reader's Catalog Curator |
|
||||
| Collection | Which library manga share a dashboard tile | Admin decision |
|
||||
| Reading order | Recommended order within that collection | Admin decision, independent of chronology |
|
||||
|
||||
One manga belongs to at most one collection, preventing duplicate dashboard tiles and
|
||||
double-counted progress. Relationships can connect any number of works across groups.
|
||||
Only confirmed relationships appear on reader manga pages. Suggestions, rejections
|
||||
and conflicts remain visible to the admin. Removing a collection leaves its manga,
|
||||
reading history and relationships intact.
|
||||
|
||||
## Metadata and agent responsibilities
|
||||
|
||||
The metadata provider, **not the image scraper**, supplies relationships. AniList
|
||||
metadata now retains manga relationship edges in `manga.json` → `x-anilist.relations`.
|
||||
Existing metadata will not magically gain these fields: use **Research relationships**
|
||||
for a manga with a verified AniList ID, or import already-cached suggestions.
|
||||
Research uses one public metadata lookup with a 30-day cache, never image-host traffic.
|
||||
Only relationships whose two exact AniList identities already exist in the library
|
||||
become suggestions. This does not auto-enqueue missing manga or guess title matches.
|
||||
|
||||
An agent is a **catalog researcher/curator assistant**. It can read the admin JSON export,
|
||||
research public evidence, identify conflicting claims and propose relationships or
|
||||
membership edits. It should preserve rejected decisions and report uncertainty.
|
||||
It must not turn a shared-universe link into an automatic collection or an unquestioned
|
||||
reading order. No embedded LLM, API credentials, automatic research loop, or autonomous
|
||||
catalog approval is enabled in this release. That optional capability belongs behind
|
||||
this same evidence/review boundary, not inside adapters or Sentinel.
|
||||
|
||||
## Persistence and API contract
|
||||
|
||||
`data/catalog.json` is reader-owned, version 1, atomically replaced on save. Its
|
||||
revision supports optimistic concurrency; stale writes return 409. Unknown members,
|
||||
duplicate membership, invalid relationships and cover paths are rejected. Existing
|
||||
members absent from a temporary mount are retained rather than erased.
|
||||
Custom covers are content-addressed in `data/collection-covers/`. Include both in backups.
|
||||
|
||||
Authenticated reader APIs:
|
||||
|
||||
- `GET /api/collections`, `GET /api/collection?id=…` — account-specific public views.
|
||||
- `GET /api/admin/catalog` — admin export plus available manga, roles and relation types.
|
||||
- `POST /api/admin/catalog` — validated full replacement, with current `revision`.
|
||||
- `POST /api/admin/catalog/suggest` — import cached edges, with current revision.
|
||||
- `POST /api/admin/catalog/research` — `{m: libraryMangaId, revision}`; verified metadata lookup.
|
||||
- `POST /api/admin/collection-cover` — `{id, revision, base64}`; validated image upload.
|
||||
|
||||
Example proposal (use real IDs and the latest revision from the export):
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"revision": 0,
|
||||
"collections": [{
|
||||
"id": "moon-garden",
|
||||
"title": "Moon Garden Universe",
|
||||
"description": "Main story, then the prequel.",
|
||||
"grouped": true,
|
||||
"coverMode": "mosaic",
|
||||
"coverMangaId": "Moon Garden",
|
||||
"members": [
|
||||
{"mangaId": "Moon Garden", "role": "main", "readingOrder": 1},
|
||||
{"mangaId": "Before the Rain", "role": "prequel", "readingOrder": 2}
|
||||
]
|
||||
}],
|
||||
"relations": [{
|
||||
"from": "Moon Garden", "to": "Before the Rain", "type": "prequel",
|
||||
"state": "suggested", "method": "agent-research", "confidence": 0.8,
|
||||
"explanation": "Verify the publisher's chronology before accepting.",
|
||||
"evidence": [{"provider": "Publisher", "url": "https://example.com/work", "note": "Chronology"}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Provider contract: [AniList relationship connections](https://docs.anilist.co/guide/graphql/connections).
|
||||
|
||||
## Repeatable functional acceptance
|
||||
|
||||
Run `npm.cmd test`, build `docker compose build reader`, then:
|
||||
|
||||
```powershell
|
||||
# Playwright is optional developer tooling; not a deployed application dependency.
|
||||
# Set NODE_PATH to its installed package root if it is not on normal resolution paths.
|
||||
node tools/test-compose.js --collections-ui --settings-ui
|
||||
```
|
||||
|
||||
The runner creates an isolated Compose project on loopback 8180 with three synthetic
|
||||
manga, disposable accounts, paused scraper and disabled discovery. It never mounts
|
||||
the real library, DB or queue. Headless Edge (override `QA_BROWSER_CHANNEL` if needed)
|
||||
proxies a synthetic `http://manga-qa.test:8180` origin to that fixture, reproducing the
|
||||
browser restrictions of a non-localhost HTTP address without using a real tailnet
|
||||
account. Screenshots: `data/qa/collections/desktop-editor.png` and `mobile-editor.png`.
|
||||
|
||||
Coverage: creation, validation, persistence after reload, member add/remove/role/order,
|
||||
selected/mosaic/custom covers and upload limits, JSON round-trip/draft protection,
|
||||
relationship creation/rejection/duplicates/removal, cached import, missing verified
|
||||
metadata identity, desktop/mobile edits, dashboard grouping and collection reading
|
||||
order, delete/cancel, overlapping saves, transient save failure/retry, concurrent-admin
|
||||
revision conflicts, and ordinary-reader read/write denial. No external manga or
|
||||
metadata research is performed; provider lookup success remains a separate test.
|
||||
The earlier settings test checked collections navigation only; this suite exercises
|
||||
the actual collection workflow and the insecure-origin failure it missed.
|
||||
Reference in new issue
Block a user