@clearmist-labs/comic-archive-handler 1.0.0
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/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/index.d.mts +584 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2157 -0
- package/dist/index.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/docs/API.md +809 -0
- package/package.json +82 -0
- package/schemas/ComicInfo v2.1.xsd +127 -0
- package/schemas/LICENSE-MetronInfo.txt +504 -0
- package/schemas/MetronInfo v1.1.xsd +355 -0
package/docs/API.md
ADDED
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Package: `@clearmist-labs/comic-archive-handler`
|
|
4
|
+
|
|
5
|
+
The package is ESM-only and requires Node.js 18 or newer. Archive inputs are either a filesystem path or a `Buffer`. Functions that produce archives return a `Buffer` by default. Pass an `output` path or writable stream to stream the result and receive `void` instead.
|
|
6
|
+
|
|
7
|
+
Most runtime functions are available as named exports and as properties of the default export. Type-only exports are available to TypeScript consumers but are not runtime properties of the default export.
|
|
8
|
+
|
|
9
|
+
## Archive detection
|
|
10
|
+
|
|
11
|
+
### `detectArchiveType(input)`
|
|
12
|
+
|
|
13
|
+
Detects an archive from its magic bytes and returns `'zip'`, `'rar'`, `'tar'`, `'asar'`, `'7z'`, `'ace'`, or `'unknown'`.
|
|
14
|
+
|
|
15
|
+
**Options**
|
|
16
|
+
|
|
17
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
18
|
+
|
|
19
|
+
**Example**
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import { detectArchiveType } from '@clearmist-labs/comic-archive-handler';
|
|
23
|
+
|
|
24
|
+
const type = await detectArchiveType('/books/example.cbz');
|
|
25
|
+
console.log(type); // 'zip'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### `isZip(input)`, `isRar(input)`, `isTar(input)`, `isAsar(input)`, `is7z(input)`, `isAce(input)`
|
|
29
|
+
|
|
30
|
+
Convenience predicates that detect an archive and return whether it matches the named format. `isAce` detects ACE (`.cba`) archives, but every actual ACE operation (read, write, or conversion) throws `UnsupportedOperationError` — see [`UnsupportedOperationError`](#unsupportedoperationerror).
|
|
31
|
+
|
|
32
|
+
**Options**
|
|
33
|
+
|
|
34
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
35
|
+
|
|
36
|
+
**Example**
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import { isZip } from '@clearmist-labs/comic-archive-handler';
|
|
40
|
+
|
|
41
|
+
if (await isZip(comicBuffer)) {
|
|
42
|
+
console.log('This is a CBZ-style ZIP archive.');
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Archive operations
|
|
47
|
+
|
|
48
|
+
### `convertArchive(input, targetType, options?)`
|
|
49
|
+
|
|
50
|
+
Converts an archive to `zip`, `rar`, `tar`, `asar`, or `7z`. Entries are copied as-is unless image conversion is requested. Writing RAR is unsupported and throws `UnsupportedOperationError`. ACE is not supported in either direction: converting an ACE archive to another format, or converting to `'ace'`, both throw `UnsupportedOperationError`.
|
|
51
|
+
|
|
52
|
+
**Options**
|
|
53
|
+
|
|
54
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
55
|
+
- `targetType: 'zip' | 'rar' | 'tar' | 'asar' | '7z' | 'ace'` - The output container format.
|
|
56
|
+
- `options.tempDir?: string` - Directory for staging operations that need real files, notably ASAR writes and all 7z operations.
|
|
57
|
+
- `options.output?: string | Writable` - Output file path or writable stream. Without it, the function returns `Promise<Buffer>`.
|
|
58
|
+
- `options.image?: { format: ImageOutputFormat; options?: ImageConvertOptions }` - Re-encode image entries while converting the archive.
|
|
59
|
+
- `options.image.format` - `'webp'`, `'jpg'`, or `'png'`.
|
|
60
|
+
- `options.image.options` - Image format options described under [`convertImageBuffer`](#convertimagebuffer).
|
|
61
|
+
|
|
62
|
+
**Example**
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
import { convertArchive } from '@clearmist-labs/comic-archive-handler';
|
|
66
|
+
|
|
67
|
+
const output = await convertArchive('/books/example.cbr', 'zip', {
|
|
68
|
+
output: '/books/example.cbz',
|
|
69
|
+
image: { format: 'webp', options: { webp: { quality: 92 } } },
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `listArchiveFiles(input)`
|
|
74
|
+
|
|
75
|
+
Lists every entry path in an archive, in archive iteration order. It returns paths only; entry contents are not read.
|
|
76
|
+
|
|
77
|
+
**Options**
|
|
78
|
+
|
|
79
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
80
|
+
|
|
81
|
+
**Example**
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
const files = await cah.listArchiveFiles(comicBuffer);
|
|
85
|
+
console.log(files); // ['P00001.jpg', 'ComicInfo.xml', ...]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `renameArchiveImagesSequentially(input, options?)`
|
|
89
|
+
|
|
90
|
+
Renames image entries in natural-sort order to `P#####.<extension>`, while leaving other entries unchanged.
|
|
91
|
+
|
|
92
|
+
**Options**
|
|
93
|
+
|
|
94
|
+
- `options.start?: number` - First page number. Defaults to `1`.
|
|
95
|
+
- `options.pad?: number` - Number of digits in the page number. Defaults to `5`.
|
|
96
|
+
- `options.tempDir?: string` - Temporary staging directory for ASAR or 7z operations.
|
|
97
|
+
- `options.output?: string | Writable` - Output destination. Without it, returns a `Buffer`.
|
|
98
|
+
|
|
99
|
+
**Example**
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const renamed = await cah.renameArchiveImagesSequentially(comicBuffer, {
|
|
103
|
+
start: 0,
|
|
104
|
+
pad: 4,
|
|
105
|
+
});
|
|
106
|
+
// Images become P0000.jpg, P0001.jpg, ...
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `extractArchive(input, destDir, options?)`
|
|
110
|
+
|
|
111
|
+
Extracts every entry in an archive to real files under `destDir`, preserving relative paths. `destDir` is created if it doesn't exist. Throws `ArchiveFormatError` for an undetectable format, or `UnsupportedOperationError` for ACE.
|
|
112
|
+
|
|
113
|
+
**Options**
|
|
114
|
+
|
|
115
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
116
|
+
- `destDir: string` - Destination directory entries are written under.
|
|
117
|
+
- `options.tempDir?: string` - Temporary staging directory for 7z reads.
|
|
118
|
+
|
|
119
|
+
**Example**
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
const files = await cah.extractArchive('/books/example.cbz', '/tmp/extracted');
|
|
123
|
+
console.log(files); // ['ComicInfo.xml', 'P00001.jpg', ...]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `stripNonEssentialFiles(input, options?)`
|
|
127
|
+
|
|
128
|
+
Keeps image files and XML files, removing other archive entries such as thumbnails or desktop metadata.
|
|
129
|
+
|
|
130
|
+
**Options**
|
|
131
|
+
|
|
132
|
+
- `options.extraKeepExtensions?: string[]` - Additional extensions to preserve, without leading dots. Matching is case-insensitive.
|
|
133
|
+
- `options.tempDir?: string` - Temporary staging directory for ASAR or 7z operations.
|
|
134
|
+
- `options.output?: string | Writable` - Output destination. Without it, returns a `Buffer`.
|
|
135
|
+
|
|
136
|
+
**Example**
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const cleaned = await cah.stripNonEssentialFiles(comicBuffer, {
|
|
140
|
+
extraKeepExtensions: ['json'],
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Metadata
|
|
145
|
+
|
|
146
|
+
`ComicMetadata` is the canonical metadata shape, covering every field defined by both bundled schemas (`schemas/ComicInfo v2.1.xsd` and `schemas/MetronInfo v1.1.xsd`) — see [`schemaVersions.ts`](../src/metadata/schemaVersions.ts) if those XSDs are ever upgraded. Conversion between ComicInfo.xml and MetronInfo.xml is intentionally lossy when a field has no equivalent in the target schema; the complete field mapping is maintained in `src/metadata/schema.ts`.
|
|
147
|
+
|
|
148
|
+
List fields that MetronInfo represents as an element with an optional `id` attribute (`genres`, `tags`, `characters`, `teams`, `locations`, `stories`, `reprints`) accept either a plain string or a `{ name, id? }` object (see [`MetronResource`](#types)); the `id` is preserved on round-trip through MetronInfo.xml and dropped when writing ComicInfo.xml, which has no equivalent concept.
|
|
149
|
+
|
|
150
|
+
### `metadataToComicInfoXml(metadata)`
|
|
151
|
+
|
|
152
|
+
Serializes canonical metadata to a ComicInfo.xml document string. Elements are written in the exact order required by the schema's `xs:sequence`.
|
|
153
|
+
|
|
154
|
+
**Options**
|
|
155
|
+
|
|
156
|
+
- `metadata: ComicMetadata` - Canonical metadata. In addition to the common fields (`title`, `series`, `number`, `summary`, `genres`, `tags`, `credits`, `pages`), this writes ComicInfo-specific fields: `blackAndWhite`, `manga`, `alternateSeries`, `alternateNumber`, `alternateCount`, `scanInformation`, `seriesGroup`, `mainCharacterOrTeam`, and `review`.
|
|
157
|
+
|
|
158
|
+
**Example**
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
const xml = cah.metadataToComicInfoXml({
|
|
162
|
+
title: 'The Example',
|
|
163
|
+
series: 'Examples',
|
|
164
|
+
number: '1',
|
|
165
|
+
genres: ['Adventure'],
|
|
166
|
+
credits: [{ name: 'Jane Doe', role: 'Writer' }],
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `comicInfoXmlToMetadata(xml)`
|
|
171
|
+
|
|
172
|
+
Parses a ComicInfo.xml string or `Buffer` into canonical metadata.
|
|
173
|
+
|
|
174
|
+
**Options**
|
|
175
|
+
|
|
176
|
+
- `xml: string | Buffer` - ComicInfo.xml content.
|
|
177
|
+
|
|
178
|
+
**Example**
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
const metadata = cah.comicInfoXmlToMetadata(xml);
|
|
182
|
+
console.log(metadata.series);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `metadataToMetronInfoXml(metadata)`
|
|
186
|
+
|
|
187
|
+
Serializes canonical metadata to a MetronInfo.xml document string.
|
|
188
|
+
|
|
189
|
+
**Options**
|
|
190
|
+
|
|
191
|
+
- `metadata: ComicMetadata` - Canonical metadata. In addition to the common fields, MetronInfo supports fields with no ComicInfo equivalent: `identifiers` (external database links), `publisherId`, `imprintId`, `seriesId`, `seriesLang`, `seriesStartYear`, `seriesIssueCount`, `seriesVolumeCount`, `seriesAlternativeNames`, `mangaVolume`, `collectionTitle`, `stories`, `prices`, `universes`, `reprints`, `communityRatingCount`, `gtinUpc`, and `lastModified`. Credits accept optional `creatorId`/`roleId`, and story arcs accept an optional `id`.
|
|
192
|
+
|
|
193
|
+
**Example**
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
const xml = cah.metadataToMetronInfoXml({
|
|
197
|
+
series: 'Examples',
|
|
198
|
+
seriesSort: 'Examples',
|
|
199
|
+
number: '1',
|
|
200
|
+
coverDate: { year: 2026, month: 9, day: 12 },
|
|
201
|
+
identifiers: [{ source: 'Metron', value: '12345', primary: true }],
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `metronInfoXmlToMetadata(xml)`
|
|
206
|
+
|
|
207
|
+
Parses a MetronInfo.xml string or `Buffer` into canonical metadata.
|
|
208
|
+
|
|
209
|
+
**Options**
|
|
210
|
+
|
|
211
|
+
- `xml: string | Buffer` - MetronInfo.xml content.
|
|
212
|
+
|
|
213
|
+
**Example**
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
const metadata = cah.metronInfoXmlToMetadata(metronXml);
|
|
217
|
+
console.log(metadata.coverDate);
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### `metadataToXml(metadata, schema)` and `xmlToMetadata(xml, schema)`
|
|
221
|
+
|
|
222
|
+
Schema-selecting wrappers around the format-specific metadata converters.
|
|
223
|
+
|
|
224
|
+
**Options**
|
|
225
|
+
|
|
226
|
+
- `metadata: ComicMetadata` or `xml: string | Buffer` - Metadata object or XML document.
|
|
227
|
+
- `schema: 'ComicInfo' | 'MetronInfo'` - XML schema to write or parse.
|
|
228
|
+
|
|
229
|
+
**Example**
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
const xml = cah.metadataToXml({ title: 'Example' }, 'ComicInfo');
|
|
233
|
+
const metadata = cah.xmlToMetadata(xml, 'ComicInfo');
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### `validateMetadataXml(xml, schema)`
|
|
237
|
+
|
|
238
|
+
Validates an XML document against the bundled XSD for `'ComicInfo'` or `'MetronInfo'`, using [`xmllint-wasm`](https://www.npmjs.com/package/xmllint-wasm) (a WASM build of libxml2 — no native or Java dependency). Returns `{ valid, issues }`; `issues` is empty when the document conforms, otherwise it has one entry per schema violation with the offending element/attribute in `message` and, where available, a `line` number.
|
|
239
|
+
|
|
240
|
+
Because the validator implements XSD 1.0, it cannot check the two `<xs:assert>` business rules in MetronInfo.xml v1.1 (at most one primary `URL`, at most one primary `ID`); everything else in both schemas is enforced.
|
|
241
|
+
|
|
242
|
+
**Options**
|
|
243
|
+
|
|
244
|
+
- `xml: string | Buffer` - XML document to validate.
|
|
245
|
+
- `schema: 'ComicInfo' | 'MetronInfo'` - Schema to validate against.
|
|
246
|
+
|
|
247
|
+
**Example**
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
const xml = cah.metadataToComicInfoXml({ title: 'The Example' });
|
|
251
|
+
const { valid, issues } = await cah.validateMetadataXml(xml, 'ComicInfo');
|
|
252
|
+
|
|
253
|
+
if (!valid) {
|
|
254
|
+
// Find out why your schema is invalid.
|
|
255
|
+
for (const issue of issues) {
|
|
256
|
+
console.log(issue.line, issue.message);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `hasComicMetadata(input)`
|
|
262
|
+
|
|
263
|
+
Checks for a root-level or nested `ComicInfo.xml` or `MetronInfo.xml` entry without parsing it. Returns `{ present, schema?, path? }`.
|
|
264
|
+
|
|
265
|
+
**Options**
|
|
266
|
+
|
|
267
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
268
|
+
|
|
269
|
+
**Example**
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
const result = await cah.hasComicMetadata(comicBuffer);
|
|
273
|
+
if (result.present) {
|
|
274
|
+
console.log(result.schema, result.path);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### `readArchiveMetadata(input)`
|
|
279
|
+
|
|
280
|
+
Finds and parses the first recognized metadata entry. Returns `{ schema, metadata }`, or `null` when no metadata file is present.
|
|
281
|
+
|
|
282
|
+
**Options**
|
|
283
|
+
|
|
284
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
285
|
+
|
|
286
|
+
**Example**
|
|
287
|
+
|
|
288
|
+
```js
|
|
289
|
+
const result = await cah.readArchiveMetadata(comicBuffer);
|
|
290
|
+
console.log(result?.metadata.title);
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### `addMetadataToArchive(input, metadata, schema, options?)`
|
|
294
|
+
|
|
295
|
+
Adds a `ComicInfo.xml` or `MetronInfo.xml` entry to an archive. Existing metadata is rejected unless `overwrite: true` is supplied.
|
|
296
|
+
|
|
297
|
+
**Options**
|
|
298
|
+
|
|
299
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
300
|
+
- `metadata: ComicMetadata` - Metadata to serialize.
|
|
301
|
+
- `schema: 'ComicInfo' | 'MetronInfo'` - Metadata file to create or replace.
|
|
302
|
+
- `options.overwrite?: boolean` - Replace an existing matching file. Defaults to `false`.
|
|
303
|
+
- `options.tempDir?: string` - Temporary staging directory for ASAR or 7z operations.
|
|
304
|
+
- `options.output?: string | Writable` - Output destination. Without it, returns a `Buffer`.
|
|
305
|
+
|
|
306
|
+
**Example**
|
|
307
|
+
|
|
308
|
+
```js
|
|
309
|
+
const withMetadata = await cah.addMetadataToArchive(
|
|
310
|
+
comicBuffer,
|
|
311
|
+
{
|
|
312
|
+
title: 'The Example',
|
|
313
|
+
series: 'Examples',
|
|
314
|
+
number: '1',
|
|
315
|
+
},
|
|
316
|
+
'ComicInfo',
|
|
317
|
+
{ overwrite: true },
|
|
318
|
+
);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Image conversion and detection
|
|
322
|
+
|
|
323
|
+
### `convertImageBuffer(image, format, options?)`
|
|
324
|
+
|
|
325
|
+
Re-encodes an image `Buffer` as WebP, JPEG, or PNG.
|
|
326
|
+
|
|
327
|
+
**Options**
|
|
328
|
+
|
|
329
|
+
- `image: Buffer` - Input image bytes.
|
|
330
|
+
- `format: 'webp' | 'jpg' | 'png'` - Output format.
|
|
331
|
+
- `options.webp.quality?: number` - WebP quality. Defaults to `92`.
|
|
332
|
+
- `options.webp.effort?: number` - WebP compression effort from `0` to `6`. Defaults to `6`.
|
|
333
|
+
- `options.webp.smartSubsample?: boolean` - Use sharp YUV subsampling. Defaults to `true`.
|
|
334
|
+
- `options.jpeg.quality?: number` - JPEG quality. Defaults to `90`.
|
|
335
|
+
- `options.png.quality?: number` - PNG palette quality; only effective when `palette` is `true`.
|
|
336
|
+
- `options.png.compressionLevel?: number` - PNG zlib compression level from `0` to `9`.
|
|
337
|
+
- `options.png.palette?: boolean` - Enable lossy palette quantization.
|
|
338
|
+
|
|
339
|
+
**Example**
|
|
340
|
+
|
|
341
|
+
```js
|
|
342
|
+
const webp = await cah.convertImageBuffer(jpegBytes, 'webp', {
|
|
343
|
+
webp: { quality: 90, effort: 6, smartSubsample: true },
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### `convertArchiveImages(input, format, options?)`
|
|
348
|
+
|
|
349
|
+
Re-encodes image entries in an archive and updates their extensions. Non-image entries are copied unchanged.
|
|
350
|
+
|
|
351
|
+
**Options**
|
|
352
|
+
|
|
353
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
354
|
+
- `format: 'webp' | 'jpg' | 'png'` - Output image format.
|
|
355
|
+
- `options.webp`, `options.jpeg`, `options.png` - Format-specific options listed under [`convertImageBuffer`](#convertimagebuffer).
|
|
356
|
+
- `options.tempDir?: string` - Temporary staging directory for ASAR or 7z operations.
|
|
357
|
+
- `options.output?: string | Writable` - Output destination. Without it, returns a `Buffer`.
|
|
358
|
+
|
|
359
|
+
**Example**
|
|
360
|
+
|
|
361
|
+
```js
|
|
362
|
+
const webpArchive = await cah.convertArchiveImages(comicBuffer, 'webp', {
|
|
363
|
+
webp: { quality: 92 },
|
|
364
|
+
output: '/books/example-webp.cbz',
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### `IMAGE_EXTENSIONS`
|
|
369
|
+
|
|
370
|
+
Readonly list of recognized image extensions: `jpg`, `jpeg`, `png`, `gif`, `webp`, `bmp`, `tiff`, and `tif`.
|
|
371
|
+
|
|
372
|
+
**Options**
|
|
373
|
+
|
|
374
|
+
This constant has no options.
|
|
375
|
+
|
|
376
|
+
**Example**
|
|
377
|
+
|
|
378
|
+
```js
|
|
379
|
+
console.log(cah.IMAGE_EXTENSIONS.includes('webp')); // true
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### `getExtension(entryPath)`
|
|
383
|
+
|
|
384
|
+
Returns the lower-case extension from an archive entry path, without the leading dot. Returns an empty string when there is no extension.
|
|
385
|
+
|
|
386
|
+
**Options**
|
|
387
|
+
|
|
388
|
+
- `entryPath: string` - Archive entry path.
|
|
389
|
+
|
|
390
|
+
**Example**
|
|
391
|
+
|
|
392
|
+
```js
|
|
393
|
+
console.log(cah.getExtension('pages/P00001.JPEG')); // 'jpeg'
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### `isImagePath(entryPath)`
|
|
397
|
+
|
|
398
|
+
Returns whether an archive entry path has an extension in `IMAGE_EXTENSIONS`.
|
|
399
|
+
|
|
400
|
+
**Options**
|
|
401
|
+
|
|
402
|
+
- `entryPath: string` - Archive entry path.
|
|
403
|
+
|
|
404
|
+
**Example**
|
|
405
|
+
|
|
406
|
+
```js
|
|
407
|
+
if (cah.isImagePath('P00001.jpg')) console.log('page image');
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## Perceptual hashing
|
|
411
|
+
|
|
412
|
+
### `computeImagePHash(image)`
|
|
413
|
+
|
|
414
|
+
Computes a standard 64-bit DCT perceptual hash from an image `Buffer`. The result is a `bigint`.
|
|
415
|
+
|
|
416
|
+
**Options**
|
|
417
|
+
|
|
418
|
+
- `image: Buffer` - Input image bytes.
|
|
419
|
+
|
|
420
|
+
**Example**
|
|
421
|
+
|
|
422
|
+
```js
|
|
423
|
+
const hash = await cah.computeImagePHash(imageBytes);
|
|
424
|
+
const hex = cah.phashToHex(hash);
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### `computeArchiveImagePHash(input, entryPath)`
|
|
428
|
+
|
|
429
|
+
Reads one archive entry and computes its image perceptual hash. Throws `MetadataNotFoundError` when the entry does not exist.
|
|
430
|
+
|
|
431
|
+
**Options**
|
|
432
|
+
|
|
433
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
434
|
+
- `entryPath: string` - Exact archive entry path.
|
|
435
|
+
|
|
436
|
+
**Example**
|
|
437
|
+
|
|
438
|
+
```js
|
|
439
|
+
const hash = await cah.computeArchiveImagePHash(comicBuffer, 'P00001.jpg');
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### `phashToHex(hash)`
|
|
443
|
+
|
|
444
|
+
Formats a 64-bit perceptual hash as a zero-padded 16-character hexadecimal string.
|
|
445
|
+
|
|
446
|
+
**Options**
|
|
447
|
+
|
|
448
|
+
- `hash: bigint` - Perceptual hash returned by `computeImagePHash` or `computeArchiveImagePHash`.
|
|
449
|
+
|
|
450
|
+
**Example**
|
|
451
|
+
|
|
452
|
+
```js
|
|
453
|
+
console.log(cah.phashToHex(15n)); // '000000000000000f'
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### `hammingDistance(a, b)`
|
|
457
|
+
|
|
458
|
+
Counts the differing bits between two perceptual hashes. Lower values indicate greater similarity.
|
|
459
|
+
|
|
460
|
+
**Options**
|
|
461
|
+
|
|
462
|
+
- `a: bigint` - First 64-bit hash.
|
|
463
|
+
- `b: bigint` - Second 64-bit hash.
|
|
464
|
+
|
|
465
|
+
**Example**
|
|
466
|
+
|
|
467
|
+
```js
|
|
468
|
+
const distance = cah.hammingDistance(hashA, hashB);
|
|
469
|
+
if (distance <= 5) console.log('Likely similar pages');
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## SHA-256 hashing
|
|
473
|
+
|
|
474
|
+
### `sha256ArchiveEntry(input, entryPath)`
|
|
475
|
+
|
|
476
|
+
Returns the lower-case SHA-256 digest of one archive entry's uncompressed content. Throws `MetadataNotFoundError` when the entry does not exist.
|
|
477
|
+
|
|
478
|
+
**Options**
|
|
479
|
+
|
|
480
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
481
|
+
- `entryPath: string` - Exact archive entry path.
|
|
482
|
+
|
|
483
|
+
**Example**
|
|
484
|
+
|
|
485
|
+
```js
|
|
486
|
+
const digest = await cah.sha256ArchiveEntry(comicBuffer, 'P00001.jpg');
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### `sha256Archive(input)`
|
|
490
|
+
|
|
491
|
+
Returns the lower-case SHA-256 digest of the entire archive. For ASAR, only the content region is hashed, excluding the header and file index.
|
|
492
|
+
|
|
493
|
+
**Options**
|
|
494
|
+
|
|
495
|
+
- `input: ArchiveInput` - A filesystem path or archive `Buffer`.
|
|
496
|
+
|
|
497
|
+
**Example**
|
|
498
|
+
|
|
499
|
+
```js
|
|
500
|
+
const digest = await cah.sha256Archive('/books/example.cbz');
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Benchmarking
|
|
504
|
+
|
|
505
|
+
### `benchmarkArchive(filePath, options?)`
|
|
506
|
+
|
|
507
|
+
Extracts a comic archive into the report's `source/` subdirectory, validates it contains image files, then generates one archive per writable container format (`zip`, `tar`, `asar`, `7z`) times benchmarked page image format (`webp`, `png`, `jpg` by default, or a subset via `options.imageFormats`) — 12 variants in total by default, each containing only the original's XML and image entries, always re-encoded from the untouched source pages (never from a previously-converted variant). Benchmarks each variant's average archive-creation time (packaging only — images are re-encoded once per format before timing starts) and average random single-entry read time, writes the generated archives and a markdown report to a timestamped subdirectory, and returns the results.
|
|
508
|
+
|
|
509
|
+
Throws `ArchiveFormatError` (undetectable format) or `UnsupportedOperationError` (ACE) if `filePath` isn't extractable, `NoImagesFoundError` if the archive has no image files, `FilesystemAccessError` if `filePath` doesn't exist, and `RangeError` if `options.imageFormats` is empty or names an unsupported format.
|
|
510
|
+
|
|
511
|
+
**Options**
|
|
512
|
+
|
|
513
|
+
- `filePath: string` - Filesystem path to the source archive.
|
|
514
|
+
- `options.tempDir?: string` - Staging directory for extraction and any asar/7z operations. Defaults to a fresh directory under the OS temp dir.
|
|
515
|
+
- `options.reportsDir?: string` - Directory the dated report subdirectory is created under. Defaults to `./reports` (relative to `process.cwd()`).
|
|
516
|
+
- `options.creationIterations?: number` - Timed creation runs to average per variant. Defaults to `3`, minimum `1`.
|
|
517
|
+
- `options.seekSamples?: number` - Random single-entry reads to average per variant. Defaults to `10`, minimum `1`.
|
|
518
|
+
- `options.imageFormats?: ImageOutputFormat[]` - Image formats to benchmark. Defaults to all of `BENCHMARK_IMAGE_FORMATS` (`webp`, `png`, `jpg`). Must be non-empty and only name supported formats.
|
|
519
|
+
- `options.image?: ImageConvertOptions` - Image encode options (`webp`, `jpeg`, `png`) applied uniformly across every generated variant.
|
|
520
|
+
|
|
521
|
+
**Example**
|
|
522
|
+
|
|
523
|
+
```js
|
|
524
|
+
const result = await cah.benchmarkArchive('/books/example.cbz', {
|
|
525
|
+
reportsDir: './reports',
|
|
526
|
+
creationIterations: 5,
|
|
527
|
+
seekSamples: 20,
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
console.log(result.reportPath); // ./reports/2026-09-12T19-47-00Z/report.md
|
|
531
|
+
for (const variant of result.variants) {
|
|
532
|
+
console.log(variant.archiveType, variant.imageFormat, variant.fileSizeBytes, variant.avgSeekMs);
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### `renderBenchmarkReportMarkdown(result)`
|
|
537
|
+
|
|
538
|
+
Renders a `BenchmarkArchiveResult` (as returned by `benchmarkArchive`) to the markdown report text, including the results table and the ranked summary sections. `benchmarkArchive` calls this internally to write `report.md`; exported for callers that want the markdown without re-running the benchmark (e.g. to regenerate a report from a saved result).
|
|
539
|
+
|
|
540
|
+
**Options**
|
|
541
|
+
|
|
542
|
+
- `result: BenchmarkArchiveResult`
|
|
543
|
+
|
|
544
|
+
**Example**
|
|
545
|
+
|
|
546
|
+
```js
|
|
547
|
+
const markdown = cah.renderBenchmarkReportMarkdown(result);
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### `WRITABLE_ARCHIVE_TYPES`, `BENCHMARK_IMAGE_FORMATS`, `ARCHIVE_TYPE_EXTENSIONS`
|
|
551
|
+
|
|
552
|
+
Constants describing the combinations `benchmarkArchive` generates: the writable container formats (`'zip' | 'tar' | 'asar' | '7z'`), the benchmarked image formats (`'webp' | 'png' | 'jpg'`), and the conventional comic-archive extension for each container format (`{ zip: 'cbz', tar: 'cbt', asar: 'cbas', '7z': 'cb7' }`).
|
|
553
|
+
|
|
554
|
+
**Options**
|
|
555
|
+
|
|
556
|
+
These constants have no options.
|
|
557
|
+
|
|
558
|
+
**Example**
|
|
559
|
+
|
|
560
|
+
```js
|
|
561
|
+
console.log(cah.WRITABLE_ARCHIVE_TYPES); // ['zip', 'tar', 'asar', '7z']
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Types
|
|
565
|
+
|
|
566
|
+
The following types are exported for TypeScript consumers.
|
|
567
|
+
|
|
568
|
+
- `ArchiveType` - `'zip' | 'rar' | 'tar' | 'asar' | '7z' | 'ace' | 'unknown'`.
|
|
569
|
+
- `ArchiveInput` - `Buffer | string`.
|
|
570
|
+
- `MetadataSchema` - `'ComicInfo' | 'MetronInfo'`.
|
|
571
|
+
- `ImageOutputFormat` - `'webp' | 'jpg' | 'png'`.
|
|
572
|
+
- `ArchiveWriteOptions` - Shared `tempDir?` and `output?` options.
|
|
573
|
+
- `ConvertArchiveOptions` - Archive write options plus `image?: { format, options? }`.
|
|
574
|
+
- `AddMetadataOptions` - Archive write options plus `overwrite?: boolean`.
|
|
575
|
+
- `RenameOptions` - Archive write options plus `start?: number` and `pad?: number`.
|
|
576
|
+
- `StripOptions` - Archive write options plus `extraKeepExtensions?: string[]`.
|
|
577
|
+
- `ImageConvertOptions` - `webp?`, `jpeg?`, and `png?` format option groups.
|
|
578
|
+
- `WebpOptions` - `quality?`, `effort?`, and `smartSubsample?`.
|
|
579
|
+
- `JpegOptions` - `quality?`.
|
|
580
|
+
- `PngOptions` - `quality?`, `compressionLevel?`, and `palette?`.
|
|
581
|
+
- `ComicMetadata` - Canonical metadata object; see the metadata section above.
|
|
582
|
+
- `ComicCredit` - `{ name: string; role: string; creatorId?: string; roleId?: string }`. The `*Id` fields are MetronInfo-only (`Creator`/`Role` `id` attributes).
|
|
583
|
+
- `ComicStoryArc` - `{ name: string; number?: number; id?: string }`. `id` is MetronInfo-only (`Arc` `id` attribute).
|
|
584
|
+
- `ComicPage` - Page index and optional page attributes such as `type`, `doublePage`, dimensions, `key`, and `bookmark`.
|
|
585
|
+
- `ComicDate` - Optional numeric `year`, `month`, and `day`.
|
|
586
|
+
- `ComicInfoCreditRole` - One of the roles in `COMIC_INFO_CREDIT_ROLES`.
|
|
587
|
+
- `MetronResource` - `{ name: string; id?: string }`. Used for MetronInfo list items (`genres`, `tags`, `characters`, `teams`, `locations`, `stories`, `reprints`) that carry an optional database `id`; a plain `string` is accepted anywhere this type is expected.
|
|
588
|
+
- `ComicAlternativeName` - `MetronResource & { lang?: string }`. Used for `seriesAlternativeNames`.
|
|
589
|
+
- `ComicUniverse` - `{ name: string; designation?: string; id?: string }`. Used for `universes`.
|
|
590
|
+
- `ComicPrice` - `{ amount: number; country: string }`. Used for `prices`.
|
|
591
|
+
- `ComicUrl` - `{ url: string; primary?: boolean }`. Used for `web` entries when a MetronInfo `primary` flag is needed; a plain `string` is accepted too.
|
|
592
|
+
- `ComicIdentifier` - `{ source: string; value: string; primary?: boolean }`. Used for `identifiers` (MetronInfo `IDS > ID`).
|
|
593
|
+
- `MetadataValidationResult` - `{ valid: boolean; issues: MetadataValidationIssue[] }`, returned by `validateMetadataXml`.
|
|
594
|
+
- `MetadataValidationIssue` - `{ message: string; line?: number }`.
|
|
595
|
+
- `ExtractArchiveOptions` - `{ tempDir?: string }`.
|
|
596
|
+
- `WritableArchiveType` - `'zip' | 'tar' | 'asar' | '7z'`.
|
|
597
|
+
- `BenchmarkArchiveOptions` - `{ tempDir?, reportsDir?, creationIterations?, seekSamples?, imageFormats?, image? }`; see [`benchmarkArchive`](#benchmarkarchivefilepath-options).
|
|
598
|
+
- `BenchmarkVariantResult` - One generated variant's stats: `{ archiveType, imageFormat, fileName, filePath, fileSizeBytes, pageCount, avgImageSizeBytes, avgCreationMs, avgSeekMs }`. `avgImageSizeBytes` (average size of one converted page) depends only on `imageFormat`, not `archiveType`.
|
|
599
|
+
- `BenchmarkArchiveResult` - `{ sourcePath, generatedAt, reportDir, reportPath, sourceDir, archivesDir, variants: BenchmarkVariantResult[] }`, returned by `benchmarkArchive`. `sourceDir` holds every entry extracted from the source archive, for inspection alongside the generated `archivesDir` and `reportPath`.
|
|
600
|
+
|
|
601
|
+
**Example**
|
|
602
|
+
|
|
603
|
+
```ts
|
|
604
|
+
import type { ComicMetadata, ConvertArchiveOptions } from '@clearmist-labs/comic-archive-handler';
|
|
605
|
+
|
|
606
|
+
const metadata: ComicMetadata = { title: 'The Example', number: '1' };
|
|
607
|
+
const options: ConvertArchiveOptions = { output: './example.cbz' };
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
## Metadata helpers
|
|
611
|
+
|
|
612
|
+
### `COMIC_INFO_CREDIT_ROLES`
|
|
613
|
+
|
|
614
|
+
Readonly list of ComicInfo credit role names supported by the converter: `Writer`, `Penciller`, `Inker`, `Colorist`, `Letterer`, `CoverArtist`, `Editor`, and `Translator`.
|
|
615
|
+
|
|
616
|
+
**Options**
|
|
617
|
+
|
|
618
|
+
This constant has no options.
|
|
619
|
+
|
|
620
|
+
**Example**
|
|
621
|
+
|
|
622
|
+
```js
|
|
623
|
+
for (const role of cah.COMIC_INFO_CREDIT_ROLES) console.log(role);
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### `splitCommaList(value?)` and `joinCommaList(values?)`
|
|
627
|
+
|
|
628
|
+
Small helpers for converting between comma-separated strings and string arrays. Empty input produces `undefined`.
|
|
629
|
+
|
|
630
|
+
**Options**
|
|
631
|
+
|
|
632
|
+
- `splitCommaList(value?: string)` - Comma-separated text; whitespace around items is trimmed.
|
|
633
|
+
- `joinCommaList(values?: string[])` - Values to join with a comma and a space.
|
|
634
|
+
|
|
635
|
+
**Example**
|
|
636
|
+
|
|
637
|
+
```js
|
|
638
|
+
const values = cah.splitCommaList('Writer, Penciller');
|
|
639
|
+
const text = cah.joinCommaList(values);
|
|
640
|
+
console.log(text); // 'Writer, Penciller'
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### `resourceName(item)` and `resourceId(item)`
|
|
644
|
+
|
|
645
|
+
Helpers for reading a `string | MetronResource` list item (see [`MetronResource`](#types)) without a type check at every call site.
|
|
646
|
+
|
|
647
|
+
**Options**
|
|
648
|
+
|
|
649
|
+
- `resourceName(item: string | MetronResource)` - Returns the plain text value.
|
|
650
|
+
- `resourceId(item: string | MetronResource)` - Returns the MetronInfo `id` attribute, or `undefined` for a plain string.
|
|
651
|
+
|
|
652
|
+
**Example**
|
|
653
|
+
|
|
654
|
+
```js
|
|
655
|
+
const genre = { name: 'Action', id: 'genre-1' };
|
|
656
|
+
console.log(cah.resourceName(genre), cah.resourceId(genre)); // 'Action' 'genre-1'
|
|
657
|
+
console.log(cah.resourceName('Adventure'), cah.resourceId('Adventure')); // 'Adventure' undefined
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### `joinResourceNames(values?)`
|
|
661
|
+
|
|
662
|
+
Like `joinCommaList`, but accepts `(string | MetronResource)[]` and joins the plain names, discarding any `id`. Used internally to write ComicInfo's comma-separated `Genre`/`Tags`/`Characters`/`Teams`/`Locations` fields from list values that may carry MetronInfo `id` attributes.
|
|
663
|
+
|
|
664
|
+
**Options**
|
|
665
|
+
|
|
666
|
+
- `values?: (string | MetronResource)[]`
|
|
667
|
+
|
|
668
|
+
**Example**
|
|
669
|
+
|
|
670
|
+
```js
|
|
671
|
+
console.log(cah.joinResourceNames([{ name: 'Action', id: 'genre-1' }, 'Adventure'])); // 'Action, Adventure'
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
### Schema reference enums
|
|
675
|
+
|
|
676
|
+
Readonly value lists for the enumerated (`xs:enumeration`) types in each XSD. These are reference data, not enforced constraints — fields such as `ageRating` remain plain `string` so unrecognized or future values still round-trip.
|
|
677
|
+
|
|
678
|
+
- `COMIC_INFO_YES_NO_VALUES` - `'Unknown' | 'No' | 'Yes'` (ComicInfo `BlackAndWhite`).
|
|
679
|
+
- `COMIC_INFO_MANGA_VALUES` - `'Unknown' | 'No' | 'Yes' | 'YesAndRightToLeft'` (ComicInfo `Manga`).
|
|
680
|
+
- `COMIC_INFO_AGE_RATING_VALUES` - ComicInfo `AgeRating` values, e.g. `'Everyone'`, `'Teen'`, `'Mature 17+'`.
|
|
681
|
+
- `COMIC_INFO_PAGE_TYPE_VALUES` - ComicInfo `Pages > Page` `Type` attribute values, e.g. `'FrontCover'`, `'Story'`, `'BackCover'`.
|
|
682
|
+
- `METRON_FORMAT_VALUES` - MetronInfo `Series > Format` values, e.g. `'Single Issue'`, `'Trade Paperback'`.
|
|
683
|
+
- `METRON_INFORMATION_SOURCE_VALUES` - MetronInfo `IDS > ID` `source` attribute values, e.g. `'Comic Vine'`, `'Metron'`.
|
|
684
|
+
- `METRON_ROLE_VALUES` - MetronInfo `Credits > Credit > Roles > Role` values (a larger set than `COMIC_INFO_CREDIT_ROLES`).
|
|
685
|
+
- `METRON_AGE_RATING_VALUES` - MetronInfo `AgeRating` values — a different set from ComicInfo's.
|
|
686
|
+
|
|
687
|
+
**Example**
|
|
688
|
+
|
|
689
|
+
```js
|
|
690
|
+
if (!cah.METRON_ROLE_VALUES.includes(role)) console.warn(`Non-standard MetronInfo role: ${role}`);
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
## Errors
|
|
694
|
+
|
|
695
|
+
All custom errors extend `Error` and can be matched with `instanceof`.
|
|
696
|
+
|
|
697
|
+
### `UnsupportedOperationError`
|
|
698
|
+
|
|
699
|
+
The requested operation is not supported, such as writing a RAR archive, or reading, writing, or converting an ACE archive at all.
|
|
700
|
+
|
|
701
|
+
**Options**
|
|
702
|
+
|
|
703
|
+
- `message: string` - Error message.
|
|
704
|
+
|
|
705
|
+
**Example**
|
|
706
|
+
|
|
707
|
+
```js
|
|
708
|
+
try {
|
|
709
|
+
await cah.convertArchive(input, 'rar');
|
|
710
|
+
} catch (error) {
|
|
711
|
+
if (error instanceof cah.UnsupportedOperationError) console.log('RAR writing is unavailable');
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
try {
|
|
715
|
+
await cah.convertArchive(cbaInput, 'zip');
|
|
716
|
+
} catch (error) {
|
|
717
|
+
if (error instanceof cah.UnsupportedOperationError) console.log('ACE is unsupported entirely');
|
|
718
|
+
}
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### `ArchiveFormatError`
|
|
722
|
+
|
|
723
|
+
The archive is invalid, undetectable, or conflicts with an operation such as adding duplicate metadata without overwrite enabled.
|
|
724
|
+
|
|
725
|
+
**Options**
|
|
726
|
+
|
|
727
|
+
- `message: string` - Error message.
|
|
728
|
+
|
|
729
|
+
**Example**
|
|
730
|
+
|
|
731
|
+
```js
|
|
732
|
+
try {
|
|
733
|
+
await cah.addMetadataToArchive(input, metadata, 'ComicInfo');
|
|
734
|
+
} catch (error) {
|
|
735
|
+
if (error instanceof cah.ArchiveFormatError) console.log(error.message);
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
### `MetadataNotFoundError`
|
|
740
|
+
|
|
741
|
+
The requested metadata or archive entry was not found.
|
|
742
|
+
|
|
743
|
+
**Options**
|
|
744
|
+
|
|
745
|
+
- `message: string` - Error message.
|
|
746
|
+
|
|
747
|
+
**Example**
|
|
748
|
+
|
|
749
|
+
```js
|
|
750
|
+
try {
|
|
751
|
+
await cah.sha256ArchiveEntry(input, 'missing.jpg');
|
|
752
|
+
} catch (error) {
|
|
753
|
+
if (error instanceof cah.MetadataNotFoundError) console.log('Entry missing');
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
### `FilesystemAccessError`
|
|
758
|
+
|
|
759
|
+
A required temporary or output filesystem operation could not be completed.
|
|
760
|
+
|
|
761
|
+
**Options**
|
|
762
|
+
|
|
763
|
+
- `message: string` - Error message.
|
|
764
|
+
|
|
765
|
+
**Example**
|
|
766
|
+
|
|
767
|
+
```js
|
|
768
|
+
try {
|
|
769
|
+
await cah.convertArchive(input, 'asar', { tempDir: '/unwritable/path' });
|
|
770
|
+
} catch (error) {
|
|
771
|
+
if (error instanceof cah.FilesystemAccessError) console.log(error.message);
|
|
772
|
+
}
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
### `SevenZipUnavailableError`
|
|
776
|
+
|
|
777
|
+
The bundled 7-Zip executable could not be found or started for a 7z operation.
|
|
778
|
+
|
|
779
|
+
**Options**
|
|
780
|
+
|
|
781
|
+
- `message: string` - Error message.
|
|
782
|
+
|
|
783
|
+
**Example**
|
|
784
|
+
|
|
785
|
+
```js
|
|
786
|
+
try {
|
|
787
|
+
await cah.convertArchive(input, '7z');
|
|
788
|
+
} catch (error) {
|
|
789
|
+
if (error instanceof cah.SevenZipUnavailableError) console.log('7z is unavailable');
|
|
790
|
+
}
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### `NoImagesFoundError`
|
|
794
|
+
|
|
795
|
+
`benchmarkArchive` found no image entries in the source archive.
|
|
796
|
+
|
|
797
|
+
**Options**
|
|
798
|
+
|
|
799
|
+
- `message: string` - Error message.
|
|
800
|
+
|
|
801
|
+
**Example**
|
|
802
|
+
|
|
803
|
+
```js
|
|
804
|
+
try {
|
|
805
|
+
await cah.benchmarkArchive('/books/metadata-only.cbz');
|
|
806
|
+
} catch (error) {
|
|
807
|
+
if (error instanceof cah.NoImagesFoundError) console.log('No pages to benchmark');
|
|
808
|
+
}
|
|
809
|
+
```
|