@zenalexa/unicli 0.220.0 → 0.220.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.
Files changed (182) hide show
  1. package/AGENTS.md +10 -19
  2. package/README.md +17 -11
  3. package/README.zh-CN.md +17 -11
  4. package/dist/adapters/anilist/web.d.ts +11 -0
  5. package/dist/adapters/anilist/web.d.ts.map +1 -0
  6. package/dist/adapters/anilist/web.js +284 -0
  7. package/dist/adapters/anilist/web.js.map +1 -0
  8. package/dist/adapters/bangumi/web.d.ts +14 -0
  9. package/dist/adapters/bangumi/web.d.ts.map +1 -0
  10. package/dist/adapters/bangumi/web.js +257 -0
  11. package/dist/adapters/bangumi/web.js.map +1 -0
  12. package/dist/adapters/dlsite/web.d.ts +31 -0
  13. package/dist/adapters/dlsite/web.d.ts.map +1 -0
  14. package/dist/adapters/dlsite/web.js +455 -0
  15. package/dist/adapters/dlsite/web.js.map +1 -0
  16. package/dist/adapters/ehentai/web.d.ts +66 -0
  17. package/dist/adapters/ehentai/web.d.ts.map +1 -0
  18. package/dist/adapters/ehentai/web.js +608 -0
  19. package/dist/adapters/ehentai/web.js.map +1 -0
  20. package/dist/adapters/jikan/web.d.ts +9 -0
  21. package/dist/adapters/jikan/web.d.ts.map +1 -0
  22. package/dist/adapters/jikan/web.js +154 -0
  23. package/dist/adapters/jikan/web.js.map +1 -0
  24. package/dist/adapters/kitsu/web.d.ts +9 -0
  25. package/dist/adapters/kitsu/web.d.ts.map +1 -0
  26. package/dist/adapters/kitsu/web.js +97 -0
  27. package/dist/adapters/kitsu/web.js.map +1 -0
  28. package/dist/adapters/mangadex/web.d.ts +10 -0
  29. package/dist/adapters/mangadex/web.d.ts.map +1 -0
  30. package/dist/adapters/mangadex/web.js +188 -0
  31. package/dist/adapters/mangadex/web.js.map +1 -0
  32. package/dist/adapters/moegirl/web.d.ts +23 -0
  33. package/dist/adapters/moegirl/web.d.ts.map +1 -0
  34. package/dist/adapters/moegirl/web.js +269 -0
  35. package/dist/adapters/moegirl/web.js.map +1 -0
  36. package/dist/adapters/safebooru/web.d.ts +10 -0
  37. package/dist/adapters/safebooru/web.d.ts.map +1 -0
  38. package/dist/adapters/safebooru/web.js +120 -0
  39. package/dist/adapters/safebooru/web.js.map +1 -0
  40. package/dist/adapters/vndb/web.d.ts +10 -0
  41. package/dist/adapters/vndb/web.d.ts.map +1 -0
  42. package/dist/adapters/vndb/web.js +321 -0
  43. package/dist/adapters/vndb/web.js.map +1 -0
  44. package/dist/agents/codex-pack.d.ts +62 -0
  45. package/dist/agents/codex-pack.d.ts.map +1 -0
  46. package/dist/agents/codex-pack.js +163 -0
  47. package/dist/agents/codex-pack.js.map +1 -0
  48. package/dist/commands/agents.d.ts.map +1 -1
  49. package/dist/commands/agents.js +6 -43
  50. package/dist/commands/agents.js.map +1 -1
  51. package/dist/commands/browser/adapter.d.ts.map +1 -1
  52. package/dist/commands/browser/adapter.js +17 -3
  53. package/dist/commands/browser/adapter.js.map +1 -1
  54. package/dist/commands/describe.d.ts.map +1 -1
  55. package/dist/commands/describe.js +6 -7
  56. package/dist/commands/describe.js.map +1 -1
  57. package/dist/commands/dispatch.d.ts +1 -1
  58. package/dist/commands/dispatch.d.ts.map +1 -1
  59. package/dist/commands/dispatch.js +4 -2
  60. package/dist/commands/dispatch.js.map +1 -1
  61. package/dist/commands/mcp.d.ts +1 -1
  62. package/dist/commands/mcp.d.ts.map +1 -1
  63. package/dist/commands/mcp.js +10 -5
  64. package/dist/commands/mcp.js.map +1 -1
  65. package/dist/core/command-contract-lint.d.ts +10 -0
  66. package/dist/core/command-contract-lint.d.ts.map +1 -0
  67. package/dist/core/command-contract-lint.js +41 -0
  68. package/dist/core/command-contract-lint.js.map +1 -0
  69. package/dist/core/command-contract.d.ts +100 -0
  70. package/dist/core/command-contract.d.ts.map +1 -0
  71. package/dist/core/command-contract.js +174 -0
  72. package/dist/core/command-contract.js.map +1 -0
  73. package/dist/core/index.d.ts +2 -0
  74. package/dist/core/index.d.ts.map +1 -1
  75. package/dist/core/index.js +2 -0
  76. package/dist/core/index.js.map +1 -1
  77. package/dist/discovery/aliases.d.ts +2 -2
  78. package/dist/discovery/aliases.d.ts.map +1 -1
  79. package/dist/discovery/aliases.js +464 -6
  80. package/dist/discovery/aliases.js.map +1 -1
  81. package/dist/discovery/search.d.ts.map +1 -1
  82. package/dist/discovery/search.js +147 -2
  83. package/dist/discovery/search.js.map +1 -1
  84. package/dist/engine/args.d.ts.map +1 -1
  85. package/dist/engine/args.js +18 -1
  86. package/dist/engine/args.js.map +1 -1
  87. package/dist/engine/artifact-validation.d.ts +29 -0
  88. package/dist/engine/artifact-validation.d.ts.map +1 -0
  89. package/dist/engine/artifact-validation.js +211 -0
  90. package/dist/engine/artifact-validation.js.map +1 -0
  91. package/dist/engine/browser/diagnostics.d.ts +38 -0
  92. package/dist/engine/browser/diagnostics.d.ts.map +1 -0
  93. package/dist/engine/browser/diagnostics.js +40 -0
  94. package/dist/engine/browser/diagnostics.js.map +1 -0
  95. package/dist/engine/invoke.d.ts +1 -0
  96. package/dist/engine/invoke.d.ts.map +1 -1
  97. package/dist/engine/invoke.js +1 -0
  98. package/dist/engine/invoke.js.map +1 -1
  99. package/dist/engine/kernel/errors.d.ts +11 -0
  100. package/dist/engine/kernel/errors.d.ts.map +1 -0
  101. package/dist/engine/kernel/errors.js +15 -0
  102. package/dist/engine/kernel/errors.js.map +1 -0
  103. package/dist/engine/kernel/execute.d.ts +7 -18
  104. package/dist/engine/kernel/execute.d.ts.map +1 -1
  105. package/dist/engine/kernel/execute.js +25 -410
  106. package/dist/engine/kernel/execute.js.map +1 -1
  107. package/dist/engine/kernel/stages.d.ts +44 -0
  108. package/dist/engine/kernel/stages.d.ts.map +1 -0
  109. package/dist/engine/kernel/stages.js +428 -0
  110. package/dist/engine/kernel/stages.js.map +1 -0
  111. package/dist/engine/kernel/types.d.ts +21 -1
  112. package/dist/engine/kernel/types.d.ts.map +1 -1
  113. package/dist/engine/steps/download.d.ts +1 -0
  114. package/dist/engine/steps/download.d.ts.map +1 -1
  115. package/dist/engine/steps/download.js +10 -6
  116. package/dist/engine/steps/download.js.map +1 -1
  117. package/dist/fast-path/render.js +1 -1
  118. package/dist/fast-path/render.js.map +1 -1
  119. package/dist/manifest-compact.txt +3 -3
  120. package/dist/manifest-search.json +1 -1
  121. package/dist/manifest.json +3074 -3
  122. package/dist/mcp/handler.d.ts.map +1 -1
  123. package/dist/mcp/handler.js +11 -1
  124. package/dist/mcp/handler.js.map +1 -1
  125. package/dist/mcp/server.d.ts +1 -1
  126. package/dist/mcp/server.js +1 -1
  127. package/dist/mcp/tools.d.ts.map +1 -1
  128. package/dist/mcp/tools.js +18 -10
  129. package/dist/mcp/tools.js.map +1 -1
  130. package/dist/output/error-map.d.ts.map +1 -1
  131. package/dist/output/error-map.js +1 -1
  132. package/dist/output/error-map.js.map +1 -1
  133. package/dist/registry.d.ts.map +1 -1
  134. package/dist/registry.js +2 -1
  135. package/dist/registry.js.map +1 -1
  136. package/package.json +2 -2
  137. package/server.json +3 -3
  138. package/skills/unicli/SKILL.md +1 -1
  139. package/skills/unicli-claude-code/SKILL.md +1 -1
  140. package/skills/unicli-hermes/SKILL.md +1 -1
  141. package/src/adapters/anilist/web.test.ts +93 -0
  142. package/src/adapters/anilist/web.ts +341 -0
  143. package/src/adapters/arxiv/download.yaml +53 -0
  144. package/src/adapters/bangumi/web.test.ts +109 -0
  145. package/src/adapters/bangumi/web.ts +295 -0
  146. package/src/adapters/danbooru/artists.yaml +44 -0
  147. package/src/adapters/danbooru/comments.yaml +45 -0
  148. package/src/adapters/danbooru/detail.yaml +78 -0
  149. package/src/adapters/danbooru/download.yaml +51 -0
  150. package/src/adapters/danbooru/pools.yaml +56 -0
  151. package/src/adapters/danbooru/search.yaml +69 -0
  152. package/src/adapters/danbooru/tags.yaml +42 -0
  153. package/src/adapters/danbooru/wiki.yaml +44 -0
  154. package/src/adapters/dlsite/web.test.ts +132 -0
  155. package/src/adapters/dlsite/web.ts +557 -0
  156. package/src/adapters/ehentai/web.test.ts +157 -0
  157. package/src/adapters/ehentai/web.ts +750 -0
  158. package/src/adapters/jikan/web.test.ts +50 -0
  159. package/src/adapters/jikan/web.ts +177 -0
  160. package/src/adapters/kitsu/web.test.ts +29 -0
  161. package/src/adapters/kitsu/web.ts +109 -0
  162. package/src/adapters/konachan/detail.yaml +62 -0
  163. package/src/adapters/konachan/download.yaml +55 -0
  164. package/src/adapters/konachan/search.yaml +65 -0
  165. package/src/adapters/konachan/tags.yaml +40 -0
  166. package/src/adapters/mangadex/web.test.ts +46 -0
  167. package/src/adapters/mangadex/web.ts +210 -0
  168. package/src/adapters/moegirl/web.test.ts +87 -0
  169. package/src/adapters/moegirl/web.ts +343 -0
  170. package/src/adapters/pdf/read.yaml +49 -0
  171. package/src/adapters/pixiv/download.yaml +15 -2
  172. package/src/adapters/safebooru/detail.yaml +63 -0
  173. package/src/adapters/safebooru/download.yaml +58 -0
  174. package/src/adapters/safebooru/search.yaml +69 -0
  175. package/src/adapters/safebooru/web.test.ts +60 -0
  176. package/src/adapters/safebooru/web.ts +130 -0
  177. package/src/adapters/vndb/web.test.ts +86 -0
  178. package/src/adapters/vndb/web.ts +393 -0
  179. package/src/adapters/yandere/detail.yaml +61 -0
  180. package/src/adapters/yandere/download.yaml +56 -0
  181. package/src/adapters/yandere/search.yaml +67 -0
  182. package/src/adapters/yandere/tags.yaml +41 -0
@@ -0,0 +1,295 @@
1
+ /**
2
+ * @owner src/adapters/bangumi/web.ts
3
+ * @does Register Bangumi public search/detail commands for anime, books, games, subjects, and characters.
4
+ * @needs Bangumi public REST/search endpoints and user-agent policy.
5
+ * @feeds Chinese/Japanese ACG title, character, and visual-novel discovery.
6
+ * @breaks Bangumi legacy search endpoint or v0 subject/character schema changes can block lookup.
7
+ */
8
+
9
+ import { USER_AGENT } from "../../constants.js";
10
+ import { cli, Strategy } from "../../registry.js";
11
+
12
+ const API = "https://api.bgm.tv";
13
+
14
+ function str(value: unknown): string {
15
+ return value === undefined || value === null ? "" : String(value);
16
+ }
17
+
18
+ function required(value: unknown, label: string): string {
19
+ const text = str(value).trim();
20
+ if (!text) throw new Error(`bangumi ${label} cannot be empty.`);
21
+ return text;
22
+ }
23
+
24
+ function requireLimit(value: unknown): number {
25
+ if (value === undefined || value === null || value === "") return 10;
26
+ const n = Number(value);
27
+ if (!Number.isInteger(n) || n < 1 || n > 50) {
28
+ throw new Error("bangumi limit must be an integer in [1, 50].");
29
+ }
30
+ return n;
31
+ }
32
+
33
+ function optionalYear(value: unknown): number | undefined {
34
+ if (value === undefined || value === null || value === "") return undefined;
35
+ const n = Number(value);
36
+ if (!Number.isInteger(n) || n < 1900 || n > 2100) {
37
+ throw new Error("bangumi year must be an integer in [1900, 2100].");
38
+ }
39
+ return n;
40
+ }
41
+
42
+ const TYPE_CODES: Record<string, number> = {
43
+ book: 1,
44
+ anime: 2,
45
+ music: 3,
46
+ game: 4,
47
+ real: 6,
48
+ };
49
+ const SORTS = new Set(["match", "rank", "score", "heat"]);
50
+
51
+ function normalizeSort(value: unknown): string {
52
+ const sort = str(value || "match")
53
+ .trim()
54
+ .toLowerCase();
55
+ if (!SORTS.has(sort)) {
56
+ throw new Error("bangumi sort must be one of: match, rank, score, heat.");
57
+ }
58
+ return sort;
59
+ }
60
+
61
+ async function getJson(url: string): Promise<unknown> {
62
+ const response = await fetch(url, {
63
+ headers: {
64
+ Accept: "application/json",
65
+ "User-Agent": USER_AGENT,
66
+ },
67
+ });
68
+ if (!response.ok)
69
+ throw new Error(`bangumi request failed with HTTP ${response.status}.`);
70
+ return response.json();
71
+ }
72
+
73
+ async function postJson(url: string, body: unknown): Promise<unknown> {
74
+ const response = await fetch(url, {
75
+ method: "POST",
76
+ headers: {
77
+ Accept: "application/json",
78
+ "Content-Type": "application/json",
79
+ "User-Agent": USER_AGENT,
80
+ },
81
+ body: JSON.stringify(body),
82
+ });
83
+ if (!response.ok)
84
+ throw new Error(`bangumi request failed with HTTP ${response.status}.`);
85
+ return response.json();
86
+ }
87
+
88
+ export function mapBangumiSubjects(rows: unknown[]): Record<string, unknown>[] {
89
+ return rows.map((row, index) => {
90
+ const item = row as Record<string, unknown>;
91
+ const rating = item.rating as Record<string, unknown> | undefined;
92
+ const images = item.images as Record<string, unknown> | undefined;
93
+ return {
94
+ rank: index + 1,
95
+ id: item.id,
96
+ type: item.type,
97
+ name: str(item.name),
98
+ name_cn: str(item.name_cn),
99
+ date: str(item.air_date ?? item.date),
100
+ score: rating?.score ?? null,
101
+ rank_site: item.rank ?? null,
102
+ summary: str(item.summary).slice(0, 500),
103
+ image: str(images?.common),
104
+ url: str(item.url) || `https://bgm.tv/subject/${item.id}`,
105
+ };
106
+ });
107
+ }
108
+
109
+ export function mapBangumiSubject(
110
+ item: Record<string, unknown>,
111
+ ): Record<string, unknown> {
112
+ const rating = item.rating as Record<string, unknown> | undefined;
113
+ const images = item.images as Record<string, unknown> | undefined;
114
+ return {
115
+ id: item.id,
116
+ type: item.type,
117
+ name: str(item.name),
118
+ name_cn: str(item.name_cn),
119
+ platform: str(item.platform),
120
+ date: str(item.date),
121
+ score: rating?.score ?? null,
122
+ total_votes: rating?.total ?? null,
123
+ rank_site: item.rank ?? null,
124
+ summary: str(item.summary).slice(0, 1500),
125
+ image: str(images?.common),
126
+ url: `https://bgm.tv/subject/${item.id}`,
127
+ };
128
+ }
129
+
130
+ export function mapBangumiCharacters(
131
+ rows: unknown[],
132
+ ): Record<string, unknown>[] {
133
+ return rows.map((row, index) => {
134
+ const item = row as Record<string, unknown>;
135
+ const stat = item.stat as Record<string, unknown> | undefined;
136
+ const images = item.images as Record<string, unknown> | undefined;
137
+ return {
138
+ rank: index + 1,
139
+ id: item.id,
140
+ name: str(item.name),
141
+ gender: str(item.gender),
142
+ type: item.type ?? null,
143
+ comments: stat?.comments ?? null,
144
+ collects: stat?.collects ?? null,
145
+ summary: str(item.summary).slice(0, 700),
146
+ image: str(images?.medium ?? images?.grid),
147
+ url: `https://bgm.tv/character/${item.id}`,
148
+ };
149
+ });
150
+ }
151
+
152
+ async function searchSubject(
153
+ kind: keyof typeof TYPE_CODES,
154
+ kwargs: Record<string, unknown>,
155
+ ) {
156
+ const cap = requireLimit(kwargs.limit);
157
+ const url = new URL(`${API}/v0/search/subjects`);
158
+ url.searchParams.set("limit", String(cap));
159
+ url.searchParams.set("offset", "0");
160
+ const data = (await postJson(
161
+ url.toString(),
162
+ bangumiSubjectSearchBody(kind, kwargs),
163
+ )) as { data?: unknown[] };
164
+ const rows = mapBangumiSubjects(data.data ?? []);
165
+ if (rows.length === 0)
166
+ throw new Error(
167
+ `No Bangumi ${kind} found for "${required(kwargs.query, "query")}".`,
168
+ );
169
+ return rows;
170
+ }
171
+
172
+ export function bangumiSubjectSearchBody(
173
+ kind: keyof typeof TYPE_CODES,
174
+ kwargs: Record<string, unknown>,
175
+ ): Record<string, unknown> {
176
+ const year = optionalYear(kwargs.year);
177
+ const sort = normalizeSort(kwargs.sort);
178
+ const filter: Record<string, unknown> = { type: [TYPE_CODES[kind]] };
179
+ if (year !== undefined) {
180
+ filter.air_date = [`>=${year}-01-01`, `<${year + 1}-01-01`];
181
+ }
182
+ return {
183
+ keyword: required(kwargs.query, "query"),
184
+ ...(sort === "match" ? {} : { sort }),
185
+ filter,
186
+ };
187
+ }
188
+
189
+ async function searchCharacters(kwargs: Record<string, unknown>) {
190
+ const query = required(kwargs.query, "query");
191
+ const cap = requireLimit(kwargs.limit);
192
+ const url = new URL(`${API}/v0/search/characters`);
193
+ url.searchParams.set("limit", String(cap));
194
+ const data = (await postJson(url.toString(), {
195
+ keyword: query,
196
+ filter: {},
197
+ })) as { data?: unknown[] };
198
+ const rows = mapBangumiCharacters(data.data ?? []);
199
+ if (rows.length === 0)
200
+ throw new Error(`No Bangumi characters found for "${query}".`);
201
+ return rows;
202
+ }
203
+
204
+ const SEARCH_ARGS = [
205
+ { name: "query", type: "str" as const, required: true, positional: true },
206
+ { name: "limit", type: "int" as const, default: 10 },
207
+ { name: "year", type: "int" as const },
208
+ {
209
+ name: "sort",
210
+ type: "str" as const,
211
+ default: "match",
212
+ choices: ["match", "rank", "score", "heat"],
213
+ description: "match, rank, score, heat",
214
+ },
215
+ ];
216
+ const CHARACTER_ARGS = SEARCH_ARGS.filter(
217
+ (arg) => arg.name === "query" || arg.name === "limit",
218
+ );
219
+ const SUBJECT_COLUMNS = [
220
+ "rank",
221
+ "id",
222
+ "type",
223
+ "name",
224
+ "name_cn",
225
+ "date",
226
+ "score",
227
+ "rank_site",
228
+ "url",
229
+ ];
230
+ const CHARACTER_COLUMNS = [
231
+ "rank",
232
+ "id",
233
+ "name",
234
+ "gender",
235
+ "comments",
236
+ "collects",
237
+ "summary",
238
+ "url",
239
+ ];
240
+
241
+ for (const name of ["anime", "book", "game"] as const) {
242
+ cli({
243
+ site: "bangumi",
244
+ name,
245
+ description: `Search Bangumi ${name} subjects by Japanese title, Chinese title, romaji, or alias`,
246
+ domain: "bgm.tv",
247
+ strategy: Strategy.PUBLIC,
248
+ browser: false,
249
+ args: SEARCH_ARGS,
250
+ columns: SUBJECT_COLUMNS,
251
+ func: async (_page, kwargs) => searchSubject(name, kwargs),
252
+ });
253
+ }
254
+
255
+ cli({
256
+ site: "bangumi",
257
+ name: "characters",
258
+ description:
259
+ "Search Bangumi characters by Japanese name, Chinese name, romaji, or alias",
260
+ domain: "bgm.tv",
261
+ strategy: Strategy.PUBLIC,
262
+ browser: false,
263
+ args: CHARACTER_ARGS,
264
+ columns: CHARACTER_COLUMNS,
265
+ func: async (_page, kwargs) => searchCharacters(kwargs),
266
+ });
267
+
268
+ cli({
269
+ site: "bangumi",
270
+ name: "subject",
271
+ description: "Get Bangumi subject details by subject id",
272
+ domain: "bgm.tv",
273
+ strategy: Strategy.PUBLIC,
274
+ browser: false,
275
+ args: [{ name: "id", type: "str", required: true, positional: true }],
276
+ columns: [
277
+ "id",
278
+ "type",
279
+ "name",
280
+ "name_cn",
281
+ "platform",
282
+ "date",
283
+ "score",
284
+ "rank_site",
285
+ "summary",
286
+ "url",
287
+ ],
288
+ func: async (_page, kwargs) => [
289
+ mapBangumiSubject(
290
+ (await getJson(
291
+ `${API}/v0/subjects/${required(kwargs.id, "id")}`,
292
+ )) as Record<string, unknown>,
293
+ ),
294
+ ],
295
+ });
@@ -0,0 +1,44 @@
1
+ site: danbooru
2
+ name: artists
3
+ description: Search Danbooru artist records
4
+ domain: danbooru.donmai.us
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ query:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Artist name
14
+ limit:
15
+ type: int
16
+ default: 20
17
+ description: Number of artists to return
18
+
19
+ pipeline:
20
+ - fetch:
21
+ url: "https://danbooru.donmai.us/artists.json"
22
+ params:
23
+ "search[name]": "${{ args.query }}"
24
+ limit: "${{ args.limit }}"
25
+
26
+ - map:
27
+ rank: "${{ index + 1 }}"
28
+ id: "${{ item.id }}"
29
+ name: "${{ item.name }}"
30
+ group_name: "${{ item.group_name }}"
31
+ other_names: "${{ Array.isArray(item.other_names) ? item.other_names.join(', ') : '' }}"
32
+ is_banned: "${{ item.is_banned }}"
33
+ is_deleted: "${{ item.is_deleted }}"
34
+ url: "${{ 'https://danbooru.donmai.us/artists/' + item.id }}"
35
+
36
+ columns: [rank, id, name, group_name, other_names, is_banned, is_deleted, url]
37
+
38
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
39
+ capabilities: ["http.fetch"]
40
+ minimum_capability: http.fetch
41
+ trust: public
42
+ confidentiality: public
43
+ quarantine: false
44
+ schema_version: v2
@@ -0,0 +1,45 @@
1
+ site: danbooru
2
+ name: comments
3
+ description: List Danbooru comments for a post
4
+ domain: danbooru.donmai.us
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ post_id:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Danbooru post ID
14
+ x-unicli-kind: id
15
+ limit:
16
+ type: int
17
+ default: 20
18
+ description: Number of comments to return
19
+
20
+ pipeline:
21
+ - fetch:
22
+ url: "https://danbooru.donmai.us/comments.json"
23
+ params:
24
+ "search[post_id]": "${{ args.post_id }}"
25
+ limit: "${{ args.limit }}"
26
+
27
+ - map:
28
+ rank: "${{ index + 1 }}"
29
+ id: "${{ item.id }}"
30
+ post_id: "${{ item.post_id }}"
31
+ creator_id: "${{ item.creator_id }}"
32
+ score: "${{ item.score }}"
33
+ body: "${{ String(item.body || '').replace(/\\s+/g, ' ').trim() }}"
34
+ created_at: "${{ item.created_at }}"
35
+ is_deleted: "${{ item.is_deleted }}"
36
+
37
+ columns: [rank, id, post_id, creator_id, score, body, created_at, is_deleted]
38
+
39
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
40
+ capabilities: ["http.fetch"]
41
+ minimum_capability: http.fetch
42
+ trust: public
43
+ confidentiality: public
44
+ quarantine: false
45
+ schema_version: v2
@@ -0,0 +1,78 @@
1
+ site: danbooru
2
+ name: detail
3
+ description: Get Danbooru post details by ID
4
+ domain: danbooru.donmai.us
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ id:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Danbooru post ID
14
+ x-unicli-kind: id
15
+
16
+ pipeline:
17
+ - fetch:
18
+ url: "https://danbooru.donmai.us/posts/${{ args.id }}.json"
19
+
20
+ - map:
21
+ id: "${{ item.id }}"
22
+ rating: "${{ item.rating }}"
23
+ score: "${{ item.score }}"
24
+ up_score: "${{ item.up_score }}"
25
+ down_score: "${{ item.down_score }}"
26
+ fav_count: "${{ item.fav_count }}"
27
+ file_ext: "${{ item.file_ext }}"
28
+ file_size: "${{ item.file_size }}"
29
+ image_width: "${{ item.image_width }}"
30
+ image_height: "${{ item.image_height }}"
31
+ artist_tags: "${{ item.tag_string_artist }}"
32
+ character_tags: "${{ item.tag_string_character }}"
33
+ copyright_tags: "${{ item.tag_string_copyright }}"
34
+ general_tags: "${{ item.tag_string_general }}"
35
+ meta_tags: "${{ item.tag_string_meta }}"
36
+ source: "${{ item.source }}"
37
+ preview_url: "${{ item.preview_file_url }}"
38
+ large_url: "${{ item.large_file_url }}"
39
+ file_url: "${{ item.file_url }}"
40
+ pixiv_id: "${{ item.pixiv_id }}"
41
+ parent_id: "${{ item.parent_id }}"
42
+ has_children: "${{ item.has_children }}"
43
+ url: "${{ 'https://danbooru.donmai.us/posts/' + item.id }}"
44
+
45
+ columns:
46
+ [
47
+ id,
48
+ rating,
49
+ score,
50
+ up_score,
51
+ down_score,
52
+ fav_count,
53
+ file_ext,
54
+ file_size,
55
+ image_width,
56
+ image_height,
57
+ artist_tags,
58
+ character_tags,
59
+ copyright_tags,
60
+ general_tags,
61
+ meta_tags,
62
+ source,
63
+ preview_url,
64
+ large_url,
65
+ file_url,
66
+ pixiv_id,
67
+ parent_id,
68
+ has_children,
69
+ url,
70
+ ]
71
+
72
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
73
+ capabilities: ["http.fetch"]
74
+ minimum_capability: http.fetch
75
+ trust: public
76
+ confidentiality: public
77
+ quarantine: false
78
+ schema_version: v2
@@ -0,0 +1,51 @@
1
+ site: danbooru
2
+ name: download
3
+ description: Download original media for a Danbooru post
4
+ domain: danbooru.donmai.us
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ id:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Danbooru post ID
14
+ x-unicli-kind: id
15
+ output:
16
+ type: str
17
+ default: "./danbooru-downloads"
18
+ description: Output directory
19
+ x-unicli-kind: path
20
+
21
+ pipeline:
22
+ - fetch:
23
+ url: "https://danbooru.donmai.us/posts/${{ args.id }}.json"
24
+
25
+ - map:
26
+ id: "${{ item.id }}"
27
+ rating: "${{ item.rating }}"
28
+ tags: "${{ item.tag_string }}"
29
+ file_ext: "${{ item.file_ext }}"
30
+ file_url: "${{ item.file_url }}"
31
+ url: "${{ 'https://danbooru.donmai.us/posts/' + item.id }}"
32
+
33
+ - assert:
34
+ condition: "data.length > 0 && data[0].file_url"
35
+ message: "Danbooru post has no public downloadable file URL."
36
+
37
+ - download:
38
+ url: "${{ item.file_url }}"
39
+ dir: "${{ args.output }}"
40
+ filename: "${{ item.id }}.${{ item.file_ext | default('bin') }}"
41
+ type: auto
42
+
43
+ columns: [id, rating, tags, file_url, url, _download]
44
+
45
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
46
+ capabilities: ["http.fetch", "http.download"]
47
+ minimum_capability: http.download
48
+ trust: public
49
+ confidentiality: public
50
+ quarantine: false
51
+ schema_version: v2
@@ -0,0 +1,56 @@
1
+ site: danbooru
2
+ name: pools
3
+ description: Search Danbooru pools
4
+ domain: danbooru.donmai.us
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ query:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Pool name wildcard query
14
+ limit:
15
+ type: int
16
+ default: 20
17
+ description: Number of pools to return
18
+
19
+ pipeline:
20
+ - fetch:
21
+ url: "https://danbooru.donmai.us/pools.json"
22
+ params:
23
+ "search[name_matches]": "${{ args.query }}"
24
+ limit: "${{ args.limit }}"
25
+
26
+ - map:
27
+ rank: "${{ index + 1 }}"
28
+ id: "${{ item.id }}"
29
+ name: "${{ item.name }}"
30
+ category: "${{ item.category }}"
31
+ post_count: "${{ item.post_count }}"
32
+ description: "${{ String(item.description || '').replace(/\\s+/g, ' ').slice(0, 250) }}"
33
+ is_active: "${{ item.is_active }}"
34
+ is_deleted: "${{ item.is_deleted }}"
35
+ url: "${{ 'https://danbooru.donmai.us/pools/' + item.id }}"
36
+
37
+ columns:
38
+ [
39
+ rank,
40
+ id,
41
+ name,
42
+ category,
43
+ post_count,
44
+ description,
45
+ is_active,
46
+ is_deleted,
47
+ url,
48
+ ]
49
+
50
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
51
+ capabilities: ["http.fetch"]
52
+ minimum_capability: http.fetch
53
+ trust: public
54
+ confidentiality: public
55
+ quarantine: false
56
+ schema_version: v2
@@ -0,0 +1,69 @@
1
+ site: danbooru
2
+ name: search
3
+ description: Search Danbooru posts by tag query
4
+ domain: danbooru.donmai.us
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ tags:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Danbooru tag query, for example "landscape rating:g"
14
+ limit:
15
+ type: int
16
+ default: 20
17
+ description: Number of posts to return
18
+ page:
19
+ type: int
20
+ default: 1
21
+ description: Page number
22
+
23
+ pipeline:
24
+ - fetch:
25
+ url: "https://danbooru.donmai.us/posts.json"
26
+ params:
27
+ tags: "${{ args.tags }}"
28
+ limit: "${{ args.limit }}"
29
+ page: "${{ args.page }}"
30
+
31
+ - map:
32
+ rank: "${{ index + 1 }}"
33
+ id: "${{ item.id }}"
34
+ rating: "${{ item.rating }}"
35
+ score: "${{ item.score }}"
36
+ fav_count: "${{ item.fav_count }}"
37
+ file_ext: "${{ item.file_ext }}"
38
+ image_width: "${{ item.image_width }}"
39
+ image_height: "${{ item.image_height }}"
40
+ tags: "${{ item.tag_string }}"
41
+ source: "${{ item.source }}"
42
+ preview_url: "${{ item.preview_file_url }}"
43
+ file_url: "${{ item.file_url }}"
44
+ url: "${{ 'https://danbooru.donmai.us/posts/' + item.id }}"
45
+
46
+ columns:
47
+ [
48
+ rank,
49
+ id,
50
+ rating,
51
+ score,
52
+ fav_count,
53
+ file_ext,
54
+ image_width,
55
+ image_height,
56
+ tags,
57
+ source,
58
+ preview_url,
59
+ file_url,
60
+ url,
61
+ ]
62
+
63
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
64
+ capabilities: ["http.fetch"]
65
+ minimum_capability: http.fetch
66
+ trust: public
67
+ confidentiality: public
68
+ quarantine: false
69
+ schema_version: v2
@@ -0,0 +1,42 @@
1
+ site: danbooru
2
+ name: tags
3
+ description: Search Danbooru tags
4
+ domain: danbooru.donmai.us
5
+ type: web-api
6
+ strategy: public
7
+
8
+ args:
9
+ query:
10
+ type: str
11
+ required: true
12
+ positional: true
13
+ description: Tag name prefix or wildcard query
14
+ limit:
15
+ type: int
16
+ default: 20
17
+ description: Number of tags to return
18
+
19
+ pipeline:
20
+ - fetch:
21
+ url: "https://danbooru.donmai.us/tags.json"
22
+ params:
23
+ "search[name_matches]": "${{ args.query }}"
24
+ limit: "${{ args.limit }}"
25
+
26
+ - map:
27
+ rank: "${{ index + 1 }}"
28
+ id: "${{ item.id }}"
29
+ name: "${{ item.name }}"
30
+ post_count: "${{ item.post_count }}"
31
+ category: "${{ item.category }}"
32
+ is_deprecated: "${{ item.is_deprecated }}"
33
+
34
+ columns: [rank, id, name, post_count, category, is_deprecated]
35
+
36
+ # schema-v2 metadata — injected by `unicli migrate schema-v2`
37
+ capabilities: ["http.fetch"]
38
+ minimum_capability: http.fetch
39
+ trust: public
40
+ confidentiality: public
41
+ quarantine: false
42
+ schema_version: v2