@clearmist-labs/comic-archive-handler 1.5.0 → 1.7.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/dist/index.d.mts +55 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +131 -35
- package/dist/index.mjs.map +1 -1
- package/docs/API.md +64 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -30,6 +30,12 @@ export interface ImageConvertOptions {
|
|
|
30
30
|
jpeg?: JpegOptions;
|
|
31
31
|
png?: PngOptions;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* How many images an archive operation converts at once. A function is called
|
|
35
|
+
* again before each conversion starts, so the limit can change while an archive
|
|
36
|
+
* is being processed. Values below 1 are treated as 1.
|
|
37
|
+
*/
|
|
38
|
+
export type ImageConcurrency = number | (() => number);
|
|
33
39
|
/** Shared by every archive-producing operation. */
|
|
34
40
|
export interface ArchiveWriteOptions {
|
|
35
41
|
/**
|
|
@@ -49,6 +55,8 @@ export interface ConvertArchiveOptions extends ArchiveWriteOptions {
|
|
|
49
55
|
image?: {
|
|
50
56
|
format: ImageOutputFormat;
|
|
51
57
|
options?: ImageConvertOptions;
|
|
58
|
+
/** How many images to convert at once. Defaults to `1`. */
|
|
59
|
+
concurrency?: ImageConcurrency;
|
|
52
60
|
};
|
|
53
61
|
/**
|
|
54
62
|
* Replaces the source's embedded comic metadata in the output. The source's
|
|
@@ -469,7 +477,21 @@ export declare function removeComicMetadata(input: ArchiveInput, schema: Metadat
|
|
|
469
477
|
//#endregion
|
|
470
478
|
//#region src/images/convert.d.ts
|
|
471
479
|
export declare function convertImageBuffer(image: Buffer, format: ImageOutputFormat, options?: ImageConvertOptions): Promise<Buffer>;
|
|
472
|
-
export declare function convertArchiveImages(input: ArchiveInput, format: ImageOutputFormat, options?: ImageConvertOptions & ArchiveWriteOptions
|
|
480
|
+
export declare function convertArchiveImages(input: ArchiveInput, format: ImageOutputFormat, options?: ImageConvertOptions & ArchiveWriteOptions & {
|
|
481
|
+
concurrency?: ImageConcurrency;
|
|
482
|
+
}): Promise<Buffer | void>;
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/images/concurrency.d.ts
|
|
485
|
+
/**
|
|
486
|
+
* Sets how many libvips threads process each image, and returns the value now
|
|
487
|
+
* in effect. The setting is process-wide and applies to every image operation
|
|
488
|
+
* this package performs (conversion, dimensions, perceptual hashing). Callers
|
|
489
|
+
* that already parallelize across images (one worker thread per core, say)
|
|
490
|
+
* should pass `1` so each image doesn't also fan out across every core.
|
|
491
|
+
* sharp's default is the CPU core count, except on glibc Linux without
|
|
492
|
+
* jemalloc, where it is already `1`.
|
|
493
|
+
*/
|
|
494
|
+
export declare function setImageConcurrency(threads: number): number;
|
|
473
495
|
//#endregion
|
|
474
496
|
//#region src/images/phash.d.ts
|
|
475
497
|
/**
|
|
@@ -489,7 +511,13 @@ export interface ImageDimensions {
|
|
|
489
511
|
width: number;
|
|
490
512
|
height: number;
|
|
491
513
|
}
|
|
492
|
-
|
|
514
|
+
export interface ImageInfo extends ImageDimensions {
|
|
515
|
+
/** The format detected from the image's contents (not its file extension), such as `jpeg`, `png`, `webp`, or `bmp`. */
|
|
516
|
+
type: string;
|
|
517
|
+
}
|
|
518
|
+
/** Reads an image's format and pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
|
|
519
|
+
export declare function readImageInfo(buffer: Buffer): Promise<ImageInfo | null>;
|
|
520
|
+
/** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
|
|
493
521
|
export declare function readImageDimensions(buffer: Buffer): Promise<ImageDimensions | null>;
|
|
494
522
|
//#endregion
|
|
495
523
|
//#region src/images/isImage.d.ts
|
|
@@ -552,6 +580,27 @@ export interface ReadArchiveEntriesResult {
|
|
|
552
580
|
*/
|
|
553
581
|
export declare function readArchiveEntries(input: ArchiveInput): AsyncGenerator<ReadArchiveEntriesResult>;
|
|
554
582
|
//#endregion
|
|
583
|
+
//#region src/readArchiveImageInfo.d.ts
|
|
584
|
+
export interface ArchiveImageInfo {
|
|
585
|
+
/** The entry's file name, without its directory. */
|
|
586
|
+
name: string;
|
|
587
|
+
/** The entry's full path inside the archive. */
|
|
588
|
+
path: string;
|
|
589
|
+
/** Pixel width, or `null` when the entry's contents aren't a readable image. */
|
|
590
|
+
width: number | null;
|
|
591
|
+
/** Pixel height, or `null` when the entry's contents aren't a readable image. */
|
|
592
|
+
height: number | null;
|
|
593
|
+
/** The format detected from the entry's contents (not its extension), or `null` when the contents aren't a readable image. */
|
|
594
|
+
type: string | null;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Returns the name, path, dimensions, and format of every image entry in an
|
|
598
|
+
* archive, in archive iteration order. Image entries are identified by
|
|
599
|
+
* extension (see `isImagePath`); non-image entries are skipped without being
|
|
600
|
+
* read. Everything is gathered in a single pass over the archive.
|
|
601
|
+
*/
|
|
602
|
+
export declare function readArchiveImageInfo(input: ArchiveInput): Promise<ArchiveImageInfo[]>;
|
|
603
|
+
//#endregion
|
|
555
604
|
//#region src/removeArchiveEntry.d.ts
|
|
556
605
|
export declare function removeArchiveEntry(input: ArchiveInput, entryPath: string, options?: RemoveEntryOptions): Promise<Buffer | void>;
|
|
557
606
|
//#endregion
|
|
@@ -582,12 +631,15 @@ export declare function renderBenchmarkReportMarkdown(result: BenchmarkArchiveRe
|
|
|
582
631
|
declare const comicArchiveHandler: {
|
|
583
632
|
convertImageBuffer: typeof convertImageBuffer;
|
|
584
633
|
convertArchiveImages: typeof convertArchiveImages;
|
|
634
|
+
setImageConcurrency: typeof setImageConcurrency;
|
|
585
635
|
computeImagePHash: typeof computeImagePHash;
|
|
586
636
|
phashToHex: typeof phashToHex;
|
|
587
637
|
computeArchiveImagePHash: typeof computeArchiveImagePHash;
|
|
588
638
|
hammingDistance: typeof hammingDistance;
|
|
589
639
|
IMAGE_EXTENSIONS: readonly ["jpg", "jpeg", "png", "gif", "webp", "bmp", "tiff", "tif"];
|
|
590
640
|
isImagePath: typeof isImagePath;
|
|
641
|
+
readImageDimensions: typeof readImageDimensions;
|
|
642
|
+
readImageInfo: typeof readImageInfo;
|
|
591
643
|
sha256ArchiveEntry: typeof sha256ArchiveEntry;
|
|
592
644
|
sha256Archive: typeof sha256Archive;
|
|
593
645
|
renameArchiveImagesSequentially: typeof renameArchiveImagesSequentially;
|
|
@@ -595,6 +647,7 @@ declare const comicArchiveHandler: {
|
|
|
595
647
|
listArchiveFiles: typeof listArchiveFiles;
|
|
596
648
|
readArchiveEntry: typeof readArchiveEntry;
|
|
597
649
|
readArchiveEntries: typeof readArchiveEntries;
|
|
650
|
+
readArchiveImageInfo: typeof readArchiveImageInfo;
|
|
598
651
|
removeArchiveEntry: typeof removeArchiveEntry;
|
|
599
652
|
benchmarkArchive: typeof benchmarkArchive;
|
|
600
653
|
renderBenchmarkReportMarkdown: typeof renderBenchmarkReportMarkdown;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/benchmark/types.ts","../src/errors.ts","../src/detect.ts","../src/convertArchive.ts","../src/extractArchive.ts","../src/metadata/comicInfo.ts","../src/metadata/metronInfo.ts","../src/metadata/validate.ts","../src/metadata/schema.ts","../src/metadata/index.ts","../src/images/convert.ts","../src/images/phash.ts","../src/images/dimensions.ts","../src/images/isImage.ts","../src/hashing/sha256.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/readArchiveEntry.ts","../src/readArchiveEntries.ts","../src/removeArchiveEntry.ts","../src/benchmark/benchmarkArchive.ts","../src/benchmark/report.ts","../src/index.ts"],"mappings":";;YAEY;;YAGA,eAAe;YAEf;YAEA;iBAEK;;EAEf;;EAEA;;EAEA;;iBAGe;;EAEf;;iBAGe;;EAEf;;EAEA;;EAEA;;iBAGe;EACf,OAAO;EACP,OAAO;EACP,MAAM
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/benchmark/types.ts","../src/errors.ts","../src/detect.ts","../src/convertArchive.ts","../src/extractArchive.ts","../src/metadata/comicInfo.ts","../src/metadata/metronInfo.ts","../src/metadata/validate.ts","../src/metadata/schema.ts","../src/metadata/index.ts","../src/images/convert.ts","../src/images/concurrency.ts","../src/images/phash.ts","../src/images/dimensions.ts","../src/images/isImage.ts","../src/hashing/sha256.ts","../src/rename.ts","../src/strip.ts","../src/listFiles.ts","../src/readArchiveEntry.ts","../src/readArchiveEntries.ts","../src/readArchiveImageInfo.ts","../src/removeArchiveEntry.ts","../src/benchmark/benchmarkArchive.ts","../src/benchmark/report.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -814,6 +814,43 @@ function getAdapter(type) {
|
|
|
814
814
|
return adapter;
|
|
815
815
|
}
|
|
816
816
|
//#endregion
|
|
817
|
+
//#region src/internal/convertEntriesConcurrently.ts
|
|
818
|
+
function resolveLimit(concurrency) {
|
|
819
|
+
const value = typeof concurrency === "function" ? concurrency() : concurrency;
|
|
820
|
+
return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : 1;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Maps source entries to write entries, running up to `concurrency` image
|
|
824
|
+
* conversions at once while preserving entry order. Each image's bytes are
|
|
825
|
+
* read before the source iterator advances, so adapters whose entry streams
|
|
826
|
+
* are only valid until the next entry keep working. A pass-through entry is
|
|
827
|
+
* yielded only after every earlier conversion has been yielded, and before the
|
|
828
|
+
* source advances, since its content is read lazily by the writer. A function
|
|
829
|
+
* `concurrency` is re-read before each new conversion starts, so the limit can
|
|
830
|
+
* follow the caller's load while the archive is being converted.
|
|
831
|
+
*/
|
|
832
|
+
async function* convertEntriesConcurrently(source, shouldConvert, convert, passThrough, concurrency = 1) {
|
|
833
|
+
const pending = [];
|
|
834
|
+
try {
|
|
835
|
+
for await (const entry of source) {
|
|
836
|
+
if (shouldConvert(entry)) {
|
|
837
|
+
const task = convert(entry, await streamToBuffer(entry.openReadStream()));
|
|
838
|
+
task.catch(() => void 0);
|
|
839
|
+
pending.push(task);
|
|
840
|
+
while (pending.length >= resolveLimit(concurrency)) yield await pending.shift();
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
const output = passThrough(entry);
|
|
844
|
+
if (!output) continue;
|
|
845
|
+
while (pending.length > 0) yield await pending.shift();
|
|
846
|
+
yield output;
|
|
847
|
+
}
|
|
848
|
+
while (pending.length > 0) yield await pending.shift();
|
|
849
|
+
} finally {
|
|
850
|
+
await Promise.allSettled(pending);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
//#endregion
|
|
817
854
|
//#region src/images/isImage.ts
|
|
818
855
|
const IMAGE_EXTENSIONS = [
|
|
819
856
|
"jpg",
|
|
@@ -862,22 +899,20 @@ function replaceExtension$1(entryPath, format) {
|
|
|
862
899
|
}
|
|
863
900
|
async function convertArchiveImages(input, format, options = {}) {
|
|
864
901
|
const adapter = getAdapter(await detectArchiveType(input));
|
|
865
|
-
const { tempDir, output, ...imageOptions } = options;
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
content: Readable.from(converted)
|
|
873
|
-
};
|
|
874
|
-
} else yield {
|
|
875
|
-
path: entry.path,
|
|
876
|
-
size: entry.size,
|
|
877
|
-
content: entry.openReadStream()
|
|
902
|
+
const { tempDir, output, concurrency, ...imageOptions } = options;
|
|
903
|
+
const entries = convertEntriesConcurrently(adapter.listEntries(input, { tempDir }), (entry) => isImagePath(entry.path) && getExtension(entry.path) !== (format === "jpg" ? "jpg" : format), async (entry, buffer) => {
|
|
904
|
+
const converted = await convertImageBuffer(buffer, format, imageOptions);
|
|
905
|
+
return {
|
|
906
|
+
path: replaceExtension$1(entry.path, format),
|
|
907
|
+
size: converted.length,
|
|
908
|
+
content: Readable.from(converted)
|
|
878
909
|
};
|
|
879
|
-
}
|
|
880
|
-
|
|
910
|
+
}, (entry) => ({
|
|
911
|
+
path: entry.path,
|
|
912
|
+
size: entry.size,
|
|
913
|
+
content: entry.openReadStream()
|
|
914
|
+
}), concurrency);
|
|
915
|
+
return withOutput(output, (destination) => adapter.write(entries, destination, { tempDir }));
|
|
881
916
|
}
|
|
882
917
|
//#endregion
|
|
883
918
|
//#region src/removeArchiveEntry.ts
|
|
@@ -1900,21 +1935,19 @@ async function convertArchive(input, targetType, options = {}) {
|
|
|
1900
1935
|
const image = options.image;
|
|
1901
1936
|
const metadata = options.metadata;
|
|
1902
1937
|
async function* entries() {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
content: Readable.from(converted)
|
|
1911
|
-
};
|
|
1912
|
-
} else yield {
|
|
1913
|
-
path: entry.path,
|
|
1914
|
-
size: entry.size,
|
|
1915
|
-
content: entry.openReadStream()
|
|
1938
|
+
const imageExtension = image?.format === "jpg" ? "jpg" : image?.format;
|
|
1939
|
+
yield* convertEntriesConcurrently(sourceAdapter.listEntries(input, { tempDir: options.tempDir }), (entry) => Boolean(image) && isImagePath(entry.path) && getExtension(entry.path) !== imageExtension, async (entry, buffer) => {
|
|
1940
|
+
const converted = await convertImageBuffer(buffer, image.format, image.options);
|
|
1941
|
+
return {
|
|
1942
|
+
path: replaceExtension(entry.path, imageExtension),
|
|
1943
|
+
size: converted.length,
|
|
1944
|
+
content: Readable.from(converted)
|
|
1916
1945
|
};
|
|
1917
|
-
}
|
|
1946
|
+
}, (entry) => metadata && METADATA_FILE_NAMES.has(entry.path.split("/").pop() ?? "") ? void 0 : {
|
|
1947
|
+
path: entry.path,
|
|
1948
|
+
size: entry.size,
|
|
1949
|
+
content: entry.openReadStream()
|
|
1950
|
+
}, image?.concurrency);
|
|
1918
1951
|
if (metadata && targetType !== "asar") for (const schema of Object.keys(metadata)) {
|
|
1919
1952
|
const value = metadata[schema];
|
|
1920
1953
|
if (!value) continue;
|
|
@@ -1953,6 +1986,20 @@ async function extractArchive(input, destDir, options = {}) {
|
|
|
1953
1986
|
return written;
|
|
1954
1987
|
}
|
|
1955
1988
|
//#endregion
|
|
1989
|
+
//#region src/images/concurrency.ts
|
|
1990
|
+
/**
|
|
1991
|
+
* Sets how many libvips threads process each image, and returns the value now
|
|
1992
|
+
* in effect. The setting is process-wide and applies to every image operation
|
|
1993
|
+
* this package performs (conversion, dimensions, perceptual hashing). Callers
|
|
1994
|
+
* that already parallelize across images (one worker thread per core, say)
|
|
1995
|
+
* should pass `1` so each image doesn't also fan out across every core.
|
|
1996
|
+
* sharp's default is the CPU core count, except on glibc Linux without
|
|
1997
|
+
* jemalloc, where it is already `1`.
|
|
1998
|
+
*/
|
|
1999
|
+
function setImageConcurrency(threads) {
|
|
2000
|
+
return sharp.concurrency(threads);
|
|
2001
|
+
}
|
|
2002
|
+
//#endregion
|
|
1956
2003
|
//#region src/images/phash.ts
|
|
1957
2004
|
const HASH_SIZE = 32;
|
|
1958
2005
|
const BLOCK_SIZE = 8;
|
|
@@ -2023,18 +2070,39 @@ async function computeArchiveImagePHash(input, entryPath) {
|
|
|
2023
2070
|
}
|
|
2024
2071
|
//#endregion
|
|
2025
2072
|
//#region src/images/dimensions.ts
|
|
2026
|
-
|
|
2027
|
-
|
|
2073
|
+
const BMP_HEADER_SIZE = 26;
|
|
2074
|
+
/** `sharp` can't read BMP, so read a BMP's dimensions straight from its `BITMAPINFOHEADER`. Height is negative for top-down bitmaps. */
|
|
2075
|
+
function readBmpInfo(buffer) {
|
|
2076
|
+
if (buffer.length < BMP_HEADER_SIZE || buffer.toString("latin1", 0, 2) !== "BM") return null;
|
|
2077
|
+
const width = buffer.readInt32LE(18);
|
|
2078
|
+
const height = Math.abs(buffer.readInt32LE(22));
|
|
2079
|
+
return width > 0 && height > 0 ? {
|
|
2080
|
+
width,
|
|
2081
|
+
height,
|
|
2082
|
+
type: "bmp"
|
|
2083
|
+
} : null;
|
|
2084
|
+
}
|
|
2085
|
+
/** Reads an image's format and pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
|
|
2086
|
+
async function readImageInfo(buffer) {
|
|
2028
2087
|
try {
|
|
2029
|
-
const { width, height } = await sharp(buffer).metadata();
|
|
2088
|
+
const { width, height, format } = await sharp(buffer).metadata();
|
|
2030
2089
|
return width !== void 0 && height !== void 0 ? {
|
|
2031
2090
|
width,
|
|
2032
|
-
height
|
|
2091
|
+
height,
|
|
2092
|
+
type: format
|
|
2033
2093
|
} : null;
|
|
2034
2094
|
} catch {
|
|
2035
|
-
return
|
|
2095
|
+
return readBmpInfo(buffer);
|
|
2036
2096
|
}
|
|
2037
2097
|
}
|
|
2098
|
+
/** Reads an image's pixel dimensions from its header without decoding it; `null` when the buffer isn't a readable image. */
|
|
2099
|
+
async function readImageDimensions(buffer) {
|
|
2100
|
+
const info = await readImageInfo(buffer);
|
|
2101
|
+
return info ? {
|
|
2102
|
+
width: info.width,
|
|
2103
|
+
height: info.height
|
|
2104
|
+
} : null;
|
|
2105
|
+
}
|
|
2038
2106
|
//#endregion
|
|
2039
2107
|
//#region src/hashing/asarContent.ts
|
|
2040
2108
|
/**
|
|
@@ -2190,6 +2258,30 @@ async function* readArchiveEntries(input) {
|
|
|
2190
2258
|
}
|
|
2191
2259
|
}
|
|
2192
2260
|
//#endregion
|
|
2261
|
+
//#region src/readArchiveImageInfo.ts
|
|
2262
|
+
/**
|
|
2263
|
+
* Returns the name, path, dimensions, and format of every image entry in an
|
|
2264
|
+
* archive, in archive iteration order. Image entries are identified by
|
|
2265
|
+
* extension (see `isImagePath`); non-image entries are skipped without being
|
|
2266
|
+
* read. Everything is gathered in a single pass over the archive.
|
|
2267
|
+
*/
|
|
2268
|
+
async function readArchiveImageInfo(input) {
|
|
2269
|
+
const adapter = getAdapter(await detectArchiveType(input));
|
|
2270
|
+
const images = [];
|
|
2271
|
+
for await (const entry of adapter.listEntries(input)) {
|
|
2272
|
+
if (!isImagePath(entry.path)) continue;
|
|
2273
|
+
const info = await readImageInfo(await streamToBuffer(entry.openReadStream()));
|
|
2274
|
+
images.push({
|
|
2275
|
+
name: path.posix.basename(entry.path.replaceAll("\\", "/")),
|
|
2276
|
+
path: entry.path,
|
|
2277
|
+
width: info?.width ?? null,
|
|
2278
|
+
height: info?.height ?? null,
|
|
2279
|
+
type: info?.type ?? null
|
|
2280
|
+
});
|
|
2281
|
+
}
|
|
2282
|
+
return images;
|
|
2283
|
+
}
|
|
2284
|
+
//#endregion
|
|
2193
2285
|
//#region src/benchmark/timing.ts
|
|
2194
2286
|
/** Runs `run` `iterations` times and returns the average duration in milliseconds. */
|
|
2195
2287
|
async function averageDuration(iterations, run) {
|
|
@@ -2422,12 +2514,15 @@ const comicArchiveHandler = {
|
|
|
2422
2514
|
...metadata_exports,
|
|
2423
2515
|
convertImageBuffer,
|
|
2424
2516
|
convertArchiveImages,
|
|
2517
|
+
setImageConcurrency,
|
|
2425
2518
|
computeImagePHash,
|
|
2426
2519
|
phashToHex,
|
|
2427
2520
|
computeArchiveImagePHash,
|
|
2428
2521
|
hammingDistance,
|
|
2429
2522
|
IMAGE_EXTENSIONS,
|
|
2430
2523
|
isImagePath,
|
|
2524
|
+
readImageDimensions,
|
|
2525
|
+
readImageInfo,
|
|
2431
2526
|
sha256ArchiveEntry,
|
|
2432
2527
|
sha256Archive,
|
|
2433
2528
|
renameArchiveImagesSequentially,
|
|
@@ -2435,6 +2530,7 @@ const comicArchiveHandler = {
|
|
|
2435
2530
|
listArchiveFiles,
|
|
2436
2531
|
readArchiveEntry,
|
|
2437
2532
|
readArchiveEntries,
|
|
2533
|
+
readArchiveImageInfo,
|
|
2438
2534
|
removeArchiveEntry,
|
|
2439
2535
|
benchmarkArchive,
|
|
2440
2536
|
renderBenchmarkReportMarkdown,
|
|
@@ -2443,6 +2539,6 @@ const comicArchiveHandler = {
|
|
|
2443
2539
|
ARCHIVE_TYPE_EXTENSIONS
|
|
2444
2540
|
};
|
|
2445
2541
|
//#endregion
|
|
2446
|
-
export { ARCHIVE_TYPE_EXTENSIONS, ArchiveFormatError, BENCHMARK_IMAGE_FORMATS, COMIC_INFO_AGE_RATING_VALUES, COMIC_INFO_CREDIT_ROLES, COMIC_INFO_MANGA_VALUES, COMIC_INFO_PAGE_TYPE_VALUES, COMIC_INFO_YES_NO_VALUES, FilesystemAccessError, IMAGE_EXTENSIONS, METRON_AGE_RATING_VALUES, METRON_FORMAT_VALUES, METRON_INFORMATION_SOURCE_VALUES, METRON_ROLE_VALUES, MetadataNotFoundError, NoImagesFoundError, SevenZipUnavailableError, UnsupportedOperationError, WRITABLE_ARCHIVE_TYPES, addMetadataToArchive, benchmarkArchive, comicInfoXmlToMetadata, computeArchiveImagePHash, computeImagePHash, convertArchive, convertArchiveImages, convertImageBuffer, comicArchiveHandler as default, detectArchiveType, extractArchive, getExtension, hammingDistance, hasComicMetadata, is7z, isAce, isAsar, isImagePath, isRar, isTar, isZip, joinCommaList, joinResourceNames, listArchiveFiles, metadataToComicInfoXml, metadataToMetronInfoXml, metadataToXml, metronInfoXmlToMetadata, phashToHex, readArchiveEntries, readArchiveEntry, readArchiveMetadata, readImageDimensions, removeArchiveEntry, removeComicMetadata, renameArchiveImagesSequentially, renderBenchmarkReportMarkdown, resourceId, resourceName, sha256Archive, sha256ArchiveEntry, splitCommaList, stripNonEssentialFiles, validateMetadataXml, xmlToMetadata };
|
|
2542
|
+
export { ARCHIVE_TYPE_EXTENSIONS, ArchiveFormatError, BENCHMARK_IMAGE_FORMATS, COMIC_INFO_AGE_RATING_VALUES, COMIC_INFO_CREDIT_ROLES, COMIC_INFO_MANGA_VALUES, COMIC_INFO_PAGE_TYPE_VALUES, COMIC_INFO_YES_NO_VALUES, FilesystemAccessError, IMAGE_EXTENSIONS, METRON_AGE_RATING_VALUES, METRON_FORMAT_VALUES, METRON_INFORMATION_SOURCE_VALUES, METRON_ROLE_VALUES, MetadataNotFoundError, NoImagesFoundError, SevenZipUnavailableError, UnsupportedOperationError, WRITABLE_ARCHIVE_TYPES, addMetadataToArchive, benchmarkArchive, comicInfoXmlToMetadata, computeArchiveImagePHash, computeImagePHash, convertArchive, convertArchiveImages, convertImageBuffer, comicArchiveHandler as default, detectArchiveType, extractArchive, getExtension, hammingDistance, hasComicMetadata, is7z, isAce, isAsar, isImagePath, isRar, isTar, isZip, joinCommaList, joinResourceNames, listArchiveFiles, metadataToComicInfoXml, metadataToMetronInfoXml, metadataToXml, metronInfoXmlToMetadata, phashToHex, readArchiveEntries, readArchiveEntry, readArchiveImageInfo, readArchiveMetadata, readImageDimensions, readImageInfo, removeArchiveEntry, removeComicMetadata, renameArchiveImagesSequentially, renderBenchmarkReportMarkdown, resourceId, resourceName, setImageConcurrency, sha256Archive, sha256ArchiveEntry, splitCommaList, stripNonEssentialFiles, validateMetadataXml, xmlToMetadata };
|
|
2447
2543
|
|
|
2448
2544
|
//# sourceMappingURL=index.mjs.map
|