@ashdev/codex-plugin-release-tsundoku 1.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @ashdev/codex-plugin-release-tsundoku
2
+
3
+ A Codex release-source plugin that announces new volume and chapter coverage
4
+ for your tracked series using a [Tsundoku](https://github.com/AshDevFr) instance's
5
+ incremental series feed. **Notification-only** — Codex does not download anything.
6
+
7
+ ## Features
8
+
9
+ - **External-ID matching by weighted voting, no title fuzzing.** Series are
10
+ matched to your Codex catalog by provider IDs (MangaBaka, AniList, MAL,
11
+ MangaUpdates, Kitsu, Shikimori, Anime-Planet, Anime News Network). Because
12
+ some providers occasionally share/merge IDs across distinct series, each
13
+ shared ID *votes*: an agreeing ID adds its weight (MangaBaka 3, AniList 2,
14
+ rest 1), a disagreeing one subtracts it, and a series matches only when
15
+ agreement wins. So a trusted disagreement (different MangaBaka IDs) overrides
16
+ a sloppy agreement (a shared MAL ID), and genuinely ambiguous ties are
17
+ skipped rather than mis-attributed.
18
+ - **Filtered feed, no stored cursor.** Each poll `POST`s your tracked series'
19
+ `provider:externalId` set to Tsundoku's filtered `/api/v1/series/feed`, so the
20
+ response contains only your series (not the whole catalog). There's no
21
+ persisted cursor — every poll re-evaluates your tracked set's current
22
+ coverage and lets Codex dedup unchanged releases. Newly tracked series are
23
+ picked up automatically and untracked ones drop out, with no bookkeeping.
24
+ - **Volume- and chapter-aware.** The feed's merged, gap-preserving coverage
25
+ spans map directly onto Codex's release model.
26
+
27
+ ## Authentication
28
+
29
+ None. The Tsundoku feed endpoint is public; you only need to point the plugin at
30
+ your instance with `baseUrl`.
31
+
32
+ ## Admin Setup
33
+
34
+ ### Adding the Plugin to Codex
35
+
36
+ Add the plugin from **Settings → Plugins** (it appears in the official plugin
37
+ gallery as "Tsundoku Releases"), or configure it manually:
38
+
39
+ - **Command:** `npx`
40
+ - **Args:**
41
+ ```
42
+ -y
43
+ @ashdev/codex-plugin-release-tsundoku
44
+ ```
45
+
46
+ Set `baseUrl` to your Tsundoku instance (e.g. `https://tsundoku.example.com`)
47
+ and save. The plugin auto-registers a single source row ("Tsundoku Releases") in
48
+ **Settings → Release tracking**, where you can disable it, change the poll
49
+ interval, or trigger an immediate poll.
50
+
51
+ ### Linking Series to Tsundoku
52
+
53
+ A series is matched whenever it carries at least one external ID that Tsundoku
54
+ also knows. Populate these by running a metadata refresh (e.g. the MangaBaka
55
+ metadata plugin) or by pasting an ID into the series' tracking panel. Supported
56
+ providers, in match-priority order:
57
+
58
+ `mangabaka`, `anilist`, `mal`, `mangaupdates`, `kitsu`, `shikimori`,
59
+ `anime_planet`, `anime_news_network`.
60
+
61
+ ## Configuration
62
+
63
+ | Field | Required | Default | Description |
64
+ | ------------------ | -------- | ------- | ------------------------------------------------------------------------ |
65
+ | `baseUrl` | yes | — | Tsundoku instance base URL. The plugin appends `/api/v1/series/feed`. |
66
+ | `defaultLanguage` | no | `en` | ISO 639-1 tag stamped on every announcement (the feed carries none). |
67
+ | `pageLimit` | no | `100` | Items per feed page (1–500). |
68
+ | `requestTimeoutMs` | no | `10000` | Per-page fetch timeout in milliseconds. |
69
+
70
+ ## How It Works
71
+
72
+ On each poll the plugin:
73
+
74
+ 1. Builds a match context from your tracked series via the host's
75
+ `releases/list_tracked`, and derives the `provider:externalId` filter set.
76
+ 2. `POST`s that filter to `/api/v1/series/feed`, paginating through the response
77
+ (cursor used only within the poll; nothing is persisted). The response is
78
+ narrowed to your tracked series.
79
+ 3. Matches each returned item to a tracked series by weighted external-ID voting
80
+ (see Features); on a confident match it records a release candidate whose
81
+ `volumes`/`chapters` mirror the item's coverage and whose confidence reflects
82
+ the vote. When several feed entries map to the same Codex series, only the
83
+ best-scoring one is recorded (ambiguous ties are skipped).
84
+ 4. Reports counters back to the host; the host applies its own threshold,
85
+ auto-ignore (for coverage you already own), and dedup.
86
+
87
+ The candidate's dedup key is the coverage high-water mark
88
+ (`tsundoku:{seriesId}:v{highestVolume}:c{highestChapter}`), so a new
89
+ announcement fires only when the frontier advances; re-delivery of the same
90
+ coverage dedups host-side. Because each poll re-evaluates the full tracked set,
91
+ **newly tracked series are backfilled on the next poll** and untracked ones stop
92
+ without any cursor bookkeeping.
93
+
94
+ If the very first feed page can't be fetched (e.g. `baseUrl` is wrong or the
95
+ instance is unreachable), the poll fails and the source shows `last_error` in
96
+ **Settings → Release tracking** rather than silently reporting "0 items". In
97
+ Docker, remember the plugin runs inside the worker container: use a URL the
98
+ container can resolve (e.g. `http://host.docker.internal:<port>`), not
99
+ `http://localhost:<port>`.
100
+
101
+ ### Limitations
102
+
103
+ - **Default language.** Tsundoku tracks official release coverage and carries no
104
+ language, so every candidate uses `defaultLanguage` (`en` unless overridden).
105
+ Per-series language preferences still gate the high-water mark host-side.
106
+ - **Full re-walk each poll.** Each poll re-fetches the current coverage of your
107
+ whole tracked set (filtered server-side, so only your series). Cheap at
108
+ typical sizes and polled a few times a day; if it ever needs to scale, an
109
+ incremental cursor could be reintroduced (with explicit invalidation on
110
+ track/untrack).
111
+ - **High-water dedup.** A filled interior gap that doesn't move the highest
112
+ volume/chapter won't re-announce.
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ # Install dependencies
118
+ npm install
119
+
120
+ # Build the plugin
121
+ npm run build
122
+
123
+ # Type check
124
+ npm run typecheck
125
+
126
+ # Run tests
127
+ npm run test
128
+
129
+ # Lint
130
+ npm run lint
131
+ ```
132
+
133
+ ## Project Structure
134
+
135
+ ```
136
+ src/
137
+ ├── index.ts # Plugin lifecycle, config, source registration, poll loop
138
+ ├── manifest.ts # Capability + config schema + supported providers
139
+ ├── fetcher.ts # Feed wire types + paginated fetchFeedPage
140
+ ├── matcher.ts # Weighted-vote external-ID matching + cross-item resolution
141
+ └── candidate.ts # Feed item → ReleaseCandidate mapping
142
+ ```
143
+
144
+ ## License
145
+
146
+ MIT