@storyteller-platform/audiobook 0.3.0 → 0.3.4
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/chunk-BIEQXUOY.js +50 -0
- package/dist/entry.cjs +6 -1
- package/dist/entry.d.cts +1 -0
- package/dist/entry.d.ts +1 -0
- package/dist/entry.js +7 -1
- package/dist/ffmpeg.cjs +13 -10
- package/dist/ffmpeg.d.cts +2 -2
- package/dist/ffmpeg.d.ts +2 -2
- package/dist/ffmpeg.js +14 -10
- package/dist/index.cjs +169 -44
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +120 -46
- package/dist/mime.js +1 -0
- package/package.json +6 -4
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
2
|
+
var __typeError = (msg) => {
|
|
3
|
+
throw TypeError(msg);
|
|
4
|
+
};
|
|
5
|
+
var __using = (stack, value, async) => {
|
|
6
|
+
if (value != null) {
|
|
7
|
+
if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
|
|
8
|
+
var dispose, inner;
|
|
9
|
+
if (async) dispose = value[__knownSymbol("asyncDispose")];
|
|
10
|
+
if (dispose === void 0) {
|
|
11
|
+
dispose = value[__knownSymbol("dispose")];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") __typeError("Object not disposable");
|
|
15
|
+
if (inner) dispose = function() {
|
|
16
|
+
try {
|
|
17
|
+
inner.call(this);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return Promise.reject(e);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
stack.push([async, dispose, value]);
|
|
23
|
+
} else if (async) {
|
|
24
|
+
stack.push([async]);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
var __callDispose = (stack, error, hasError) => {
|
|
29
|
+
var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
|
|
30
|
+
return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
|
|
31
|
+
};
|
|
32
|
+
var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
|
|
33
|
+
var next = (it) => {
|
|
34
|
+
while (it = stack.pop()) {
|
|
35
|
+
try {
|
|
36
|
+
var result = it[1] && it[1].call(it[2]);
|
|
37
|
+
if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
|
|
38
|
+
} catch (e) {
|
|
39
|
+
fail(e);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (hasError) throw error;
|
|
43
|
+
};
|
|
44
|
+
return next();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
__using,
|
|
49
|
+
__callDispose
|
|
50
|
+
};
|
package/dist/entry.cjs
CHANGED
|
@@ -119,8 +119,13 @@ class AudiobookEntry {
|
|
|
119
119
|
const info = await this.getInfo();
|
|
120
120
|
return info.duration;
|
|
121
121
|
}
|
|
122
|
+
async getBitRate() {
|
|
123
|
+
const info = await this.getInfo();
|
|
124
|
+
return info.bitRate;
|
|
125
|
+
}
|
|
122
126
|
async getResource() {
|
|
123
127
|
const duration = await this.getDuration();
|
|
128
|
+
const bitrate = await this.getBitRate() ?? null;
|
|
124
129
|
const title = await this.getTrackTitle() ?? null;
|
|
125
130
|
const type = (0, import_mime.lookupAudioMime)(this.filename);
|
|
126
131
|
return {
|
|
@@ -128,7 +133,7 @@ class AudiobookEntry {
|
|
|
128
133
|
title,
|
|
129
134
|
type,
|
|
130
135
|
duration,
|
|
131
|
-
bitrate
|
|
136
|
+
bitrate
|
|
132
137
|
};
|
|
133
138
|
}
|
|
134
139
|
async getChapters() {
|
package/dist/entry.d.cts
CHANGED
|
@@ -30,6 +30,7 @@ declare class AudiobookEntry {
|
|
|
30
30
|
getReleaseDate(): Promise<Date | null>;
|
|
31
31
|
setReleaseDate(date: Date): Promise<void>;
|
|
32
32
|
getDuration(): Promise<number>;
|
|
33
|
+
getBitRate(): Promise<number | undefined>;
|
|
33
34
|
getResource(): Promise<AudiobookResource>;
|
|
34
35
|
getChapters(): Promise<AudiobookChapter[]>;
|
|
35
36
|
saveAndClose(): Promise<void>;
|
package/dist/entry.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ declare class AudiobookEntry {
|
|
|
30
30
|
getReleaseDate(): Promise<Date | null>;
|
|
31
31
|
setReleaseDate(date: Date): Promise<void>;
|
|
32
32
|
getDuration(): Promise<number>;
|
|
33
|
+
getBitRate(): Promise<number | undefined>;
|
|
33
34
|
getResource(): Promise<AudiobookResource>;
|
|
34
35
|
getChapters(): Promise<AudiobookChapter[]>;
|
|
35
36
|
saveAndClose(): Promise<void>;
|
package/dist/entry.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./chunk-BIEQXUOY.js";
|
|
1
2
|
import {
|
|
2
3
|
getTrackMetadata,
|
|
3
4
|
writeTrackMetadata
|
|
@@ -99,8 +100,13 @@ class AudiobookEntry {
|
|
|
99
100
|
const info = await this.getInfo();
|
|
100
101
|
return info.duration;
|
|
101
102
|
}
|
|
103
|
+
async getBitRate() {
|
|
104
|
+
const info = await this.getInfo();
|
|
105
|
+
return info.bitRate;
|
|
106
|
+
}
|
|
102
107
|
async getResource() {
|
|
103
108
|
const duration = await this.getDuration();
|
|
109
|
+
const bitrate = await this.getBitRate() ?? null;
|
|
104
110
|
const title = await this.getTrackTitle() ?? null;
|
|
105
111
|
const type = lookupAudioMime(this.filename);
|
|
106
112
|
return {
|
|
@@ -108,7 +114,7 @@ class AudiobookEntry {
|
|
|
108
114
|
title,
|
|
109
115
|
type,
|
|
110
116
|
duration,
|
|
111
|
-
bitrate
|
|
117
|
+
bitrate
|
|
112
118
|
};
|
|
113
119
|
}
|
|
114
120
|
async getChapters() {
|
package/dist/ffmpeg.cjs
CHANGED
|
@@ -90,20 +90,19 @@ function lookup(codecName) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
async function getTrackMetadata(path) {
|
|
93
|
+
var _a, _b;
|
|
93
94
|
const stdout = await execCmd(
|
|
94
95
|
`ffprobe -i ${quotePath(path)} -v quiet -show_format -show_chapters -show_streams -output_format json`
|
|
95
96
|
);
|
|
96
|
-
const { chapters, streams, format
|
|
97
|
-
stdout
|
|
98
|
-
);
|
|
97
|
+
const { chapters, streams, format } = JSON.parse(stdout);
|
|
99
98
|
const attachedPicStream = streams.find(
|
|
100
99
|
(stream) => !!stream.disposition.attached_pic
|
|
101
100
|
);
|
|
102
101
|
const attachedPic = attachedPicStream && {
|
|
103
|
-
name: attachedPicStream.tags.title,
|
|
102
|
+
name: (_a = attachedPicStream.tags) == null ? void 0 : _a.title,
|
|
104
103
|
mimeType: lookup(attachedPicStream.codec_name),
|
|
105
|
-
kind:
|
|
106
|
-
description: attachedPicStream.tags.comment,
|
|
104
|
+
kind: "coverFront",
|
|
105
|
+
description: (_b = attachedPicStream.tags) == null ? void 0 : _b.comment,
|
|
107
106
|
data: await execCmdBuffer("ffmpeg", [
|
|
108
107
|
"-nostdin",
|
|
109
108
|
"-i",
|
|
@@ -122,8 +121,8 @@ async function getTrackMetadata(path) {
|
|
|
122
121
|
])
|
|
123
122
|
};
|
|
124
123
|
return {
|
|
125
|
-
duration: parseFloat(duration),
|
|
126
|
-
bitRate: bit_rate !== void 0 ? parseFloat(bit_rate) : bit_rate,
|
|
124
|
+
duration: parseFloat(format.duration),
|
|
125
|
+
bitRate: format.bit_rate !== void 0 ? parseFloat(format.bit_rate) : format.bit_rate,
|
|
127
126
|
tags: {
|
|
128
127
|
title: format.tags.title ?? format.tags.Title,
|
|
129
128
|
subtitle: format.tags.subtitle ?? format.tags.Subtitle,
|
|
@@ -149,6 +148,7 @@ async function getTrackMetadata(path) {
|
|
|
149
148
|
};
|
|
150
149
|
}
|
|
151
150
|
async function writeTrackMetadata(path, metadata, attachedPic) {
|
|
151
|
+
const args = [];
|
|
152
152
|
const metadataArgs = [];
|
|
153
153
|
if (metadata.title) {
|
|
154
154
|
metadataArgs.push(`-metadata title="${escapeQuotes(metadata.title)}"`);
|
|
@@ -205,7 +205,9 @@ async function writeTrackMetadata(path, metadata, attachedPic) {
|
|
|
205
205
|
`storyteller-platform-audiobook-${(0, import_node_crypto.randomUUID)()}.${imageExt}`
|
|
206
206
|
);
|
|
207
207
|
await (0, import_promises.writeFile)(picPath, attachedPic.data);
|
|
208
|
-
|
|
208
|
+
args.push(`-i ${quotePath(picPath)}`);
|
|
209
|
+
args.push(`-map 0:a -map 1:v`);
|
|
210
|
+
args.push(`-disposition:v:0 attached_pic`);
|
|
209
211
|
if (attachedPic.name) {
|
|
210
212
|
metadataArgs.push(
|
|
211
213
|
`-metadata:s:v title="${escapeQuotes(attachedPic.name)}"`
|
|
@@ -217,7 +219,8 @@ async function writeTrackMetadata(path, metadata, attachedPic) {
|
|
|
217
219
|
);
|
|
218
220
|
}
|
|
219
221
|
}
|
|
220
|
-
|
|
222
|
+
args.push(...metadataArgs);
|
|
223
|
+
const cmd = `ffmpeg -i ${quotePath(path)} ${args.join(" ")} -codec copy "${tmpPath}"`;
|
|
221
224
|
await execCmd(cmd);
|
|
222
225
|
await (0, import_promises.cp)(tmpPath, path, { force: true });
|
|
223
226
|
} finally {
|
package/dist/ffmpeg.d.cts
CHANGED
|
@@ -11,8 +11,8 @@ type AttachedPic = {
|
|
|
11
11
|
data: Uint8Array;
|
|
12
12
|
mimeType: string;
|
|
13
13
|
kind: "coverFront" | "coverBack" | "unknown";
|
|
14
|
-
name?: string;
|
|
15
|
-
description?: string;
|
|
14
|
+
name?: string | undefined;
|
|
15
|
+
description?: string | undefined;
|
|
16
16
|
};
|
|
17
17
|
declare function getTrackMetadata(path: string): Promise<{
|
|
18
18
|
duration: number;
|
package/dist/ffmpeg.d.ts
CHANGED
|
@@ -11,8 +11,8 @@ type AttachedPic = {
|
|
|
11
11
|
data: Uint8Array;
|
|
12
12
|
mimeType: string;
|
|
13
13
|
kind: "coverFront" | "coverBack" | "unknown";
|
|
14
|
-
name?: string;
|
|
15
|
-
description?: string;
|
|
14
|
+
name?: string | undefined;
|
|
15
|
+
description?: string | undefined;
|
|
16
16
|
};
|
|
17
17
|
declare function getTrackMetadata(path: string): Promise<{
|
|
18
18
|
duration: number;
|
package/dist/ffmpeg.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./chunk-BIEQXUOY.js";
|
|
1
2
|
import { exec, execFile } from "node:child_process";
|
|
2
3
|
import { randomUUID } from "node:crypto";
|
|
3
4
|
import { writeFileSync } from "node:fs";
|
|
@@ -63,20 +64,19 @@ function lookup(codecName) {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
async function getTrackMetadata(path) {
|
|
67
|
+
var _a, _b;
|
|
66
68
|
const stdout = await execCmd(
|
|
67
69
|
`ffprobe -i ${quotePath(path)} -v quiet -show_format -show_chapters -show_streams -output_format json`
|
|
68
70
|
);
|
|
69
|
-
const { chapters, streams, format
|
|
70
|
-
stdout
|
|
71
|
-
);
|
|
71
|
+
const { chapters, streams, format } = JSON.parse(stdout);
|
|
72
72
|
const attachedPicStream = streams.find(
|
|
73
73
|
(stream) => !!stream.disposition.attached_pic
|
|
74
74
|
);
|
|
75
75
|
const attachedPic = attachedPicStream && {
|
|
76
|
-
name: attachedPicStream.tags.title,
|
|
76
|
+
name: (_a = attachedPicStream.tags) == null ? void 0 : _a.title,
|
|
77
77
|
mimeType: lookup(attachedPicStream.codec_name),
|
|
78
|
-
kind:
|
|
79
|
-
description: attachedPicStream.tags.comment,
|
|
78
|
+
kind: "coverFront",
|
|
79
|
+
description: (_b = attachedPicStream.tags) == null ? void 0 : _b.comment,
|
|
80
80
|
data: await execCmdBuffer("ffmpeg", [
|
|
81
81
|
"-nostdin",
|
|
82
82
|
"-i",
|
|
@@ -95,8 +95,8 @@ async function getTrackMetadata(path) {
|
|
|
95
95
|
])
|
|
96
96
|
};
|
|
97
97
|
return {
|
|
98
|
-
duration: parseFloat(duration),
|
|
99
|
-
bitRate: bit_rate !== void 0 ? parseFloat(bit_rate) : bit_rate,
|
|
98
|
+
duration: parseFloat(format.duration),
|
|
99
|
+
bitRate: format.bit_rate !== void 0 ? parseFloat(format.bit_rate) : format.bit_rate,
|
|
100
100
|
tags: {
|
|
101
101
|
title: format.tags.title ?? format.tags.Title,
|
|
102
102
|
subtitle: format.tags.subtitle ?? format.tags.Subtitle,
|
|
@@ -122,6 +122,7 @@ async function getTrackMetadata(path) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
async function writeTrackMetadata(path, metadata, attachedPic) {
|
|
125
|
+
const args = [];
|
|
125
126
|
const metadataArgs = [];
|
|
126
127
|
if (metadata.title) {
|
|
127
128
|
metadataArgs.push(`-metadata title="${escapeQuotes(metadata.title)}"`);
|
|
@@ -178,7 +179,9 @@ async function writeTrackMetadata(path, metadata, attachedPic) {
|
|
|
178
179
|
`storyteller-platform-audiobook-${randomUUID()}.${imageExt}`
|
|
179
180
|
);
|
|
180
181
|
await writeFile(picPath, attachedPic.data);
|
|
181
|
-
|
|
182
|
+
args.push(`-i ${quotePath(picPath)}`);
|
|
183
|
+
args.push(`-map 0:a -map 1:v`);
|
|
184
|
+
args.push(`-disposition:v:0 attached_pic`);
|
|
182
185
|
if (attachedPic.name) {
|
|
183
186
|
metadataArgs.push(
|
|
184
187
|
`-metadata:s:v title="${escapeQuotes(attachedPic.name)}"`
|
|
@@ -190,7 +193,8 @@ async function writeTrackMetadata(path, metadata, attachedPic) {
|
|
|
190
193
|
);
|
|
191
194
|
}
|
|
192
195
|
}
|
|
193
|
-
|
|
196
|
+
args.push(...metadataArgs);
|
|
197
|
+
const cmd = `ffmpeg -i ${quotePath(path)} ${args.join(" ")} -codec copy "${tmpPath}"`;
|
|
194
198
|
await execCmd(cmd);
|
|
195
199
|
await cp(tmpPath, path, { force: true });
|
|
196
200
|
} finally {
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
9
|
+
var __typeError = (msg) => {
|
|
10
|
+
throw TypeError(msg);
|
|
11
|
+
};
|
|
6
12
|
var __export = (target, all) => {
|
|
7
13
|
for (var name in all)
|
|
8
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -15,7 +21,56 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
21
|
}
|
|
16
22
|
return to;
|
|
17
23
|
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
18
32
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
var __using = (stack, value, async) => {
|
|
34
|
+
if (value != null) {
|
|
35
|
+
if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
|
|
36
|
+
var dispose, inner;
|
|
37
|
+
if (async) dispose = value[__knownSymbol("asyncDispose")];
|
|
38
|
+
if (dispose === void 0) {
|
|
39
|
+
dispose = value[__knownSymbol("dispose")];
|
|
40
|
+
if (async) inner = dispose;
|
|
41
|
+
}
|
|
42
|
+
if (typeof dispose !== "function") __typeError("Object not disposable");
|
|
43
|
+
if (inner) dispose = function() {
|
|
44
|
+
try {
|
|
45
|
+
inner.call(this);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
return Promise.reject(e);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
stack.push([async, dispose, value]);
|
|
51
|
+
} else if (async) {
|
|
52
|
+
stack.push([async]);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
};
|
|
56
|
+
var __callDispose = (stack, error, hasError) => {
|
|
57
|
+
var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
|
|
58
|
+
return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
|
|
59
|
+
};
|
|
60
|
+
var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
|
|
61
|
+
var next = (it) => {
|
|
62
|
+
while (it = stack.pop()) {
|
|
63
|
+
try {
|
|
64
|
+
var result = it[1] && it[1].call(it[2]);
|
|
65
|
+
if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
|
|
66
|
+
} catch (e) {
|
|
67
|
+
fail(e);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (hasError) throw error;
|
|
71
|
+
};
|
|
72
|
+
return next();
|
|
73
|
+
};
|
|
19
74
|
var index_exports = {};
|
|
20
75
|
__export(index_exports, {
|
|
21
76
|
AAC_FILE_EXTENSIONS: () => AAC_FILE_EXTENSIONS,
|
|
@@ -37,12 +92,30 @@ var import_node_crypto = require("node:crypto");
|
|
|
37
92
|
var import_node_fs = require("node:fs");
|
|
38
93
|
var import_promises = require("node:fs/promises");
|
|
39
94
|
var import_node_os = require("node:os");
|
|
40
|
-
var
|
|
41
|
-
var import_fslib = require("@yarnpkg/fslib");
|
|
42
|
-
var import_libzip = require("@yarnpkg/libzip");
|
|
95
|
+
var util = __toESM(require("node:util"), 1);
|
|
43
96
|
var import_mime_types = require("mime-types");
|
|
97
|
+
var import_yauzl = __toESM(require("yauzl"), 1);
|
|
98
|
+
var import_yazl = require("yazl");
|
|
44
99
|
var import_path = require("@storyteller-platform/path");
|
|
45
100
|
var import_entry = require("./entry.cjs");
|
|
101
|
+
function promisify(api) {
|
|
102
|
+
return function(arg, options) {
|
|
103
|
+
return new Promise(function(resolve, reject) {
|
|
104
|
+
api(arg, options, function(err, response) {
|
|
105
|
+
if (err) {
|
|
106
|
+
reject(err);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
resolve(response);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const unzipFromPath = promisify(
|
|
115
|
+
(arg, options, callback) => {
|
|
116
|
+
import_yauzl.default.open(arg, options, callback);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
46
119
|
const COVER_IMAGE_FILE_EXTENSIONS = [
|
|
47
120
|
".jpeg",
|
|
48
121
|
".jpg",
|
|
@@ -75,7 +148,7 @@ class Audiobook {
|
|
|
75
148
|
metadata = {};
|
|
76
149
|
inputs;
|
|
77
150
|
entries = [];
|
|
78
|
-
|
|
151
|
+
extractPath = void 0;
|
|
79
152
|
isZip;
|
|
80
153
|
constructor(...inputs) {
|
|
81
154
|
this.inputs = inputs;
|
|
@@ -89,32 +162,56 @@ class Audiobook {
|
|
|
89
162
|
}
|
|
90
163
|
async getEntries() {
|
|
91
164
|
if (this.isZip) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
await (0, import_promises.mkdir)(this.tmpDir, { recursive: true });
|
|
99
|
-
const zipFs = new import_libzip.ZipFS(first);
|
|
100
|
-
const entries = zipFs.getAllFiles();
|
|
101
|
-
for (const entry of entries) {
|
|
102
|
-
await (0, import_promises.mkdir)((0, import_path.join)(this.tmpDir, (0, import_path.dirname)(entry)), { recursive: true });
|
|
103
|
-
await (0, import_promises2.pipeline)(
|
|
104
|
-
zipFs.createReadStream(entry),
|
|
105
|
-
(0, import_node_fs.createWriteStream)((0, import_path.join)(this.tmpDir, entry))
|
|
165
|
+
var _stack = [];
|
|
166
|
+
try {
|
|
167
|
+
const [first] = this.inputs;
|
|
168
|
+
const extractPath = (0, import_path.join)(
|
|
169
|
+
(0, import_node_os.tmpdir)(),
|
|
170
|
+
`storyteller-platform-audiobook-${(0, import_node_crypto.randomUUID)()}`
|
|
106
171
|
);
|
|
172
|
+
this.extractPath = extractPath;
|
|
173
|
+
await (0, import_promises.mkdir)(this.extractPath, { recursive: true });
|
|
174
|
+
const zipfile = await unzipFromPath(first, { lazyEntries: true });
|
|
175
|
+
const stack = __using(_stack, new DisposableStack());
|
|
176
|
+
stack.defer(() => {
|
|
177
|
+
zipfile.close();
|
|
178
|
+
});
|
|
179
|
+
const entries = [];
|
|
180
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
181
|
+
zipfile.on("end", () => {
|
|
182
|
+
resolve();
|
|
183
|
+
});
|
|
184
|
+
const openReadStream = util.promisify(
|
|
185
|
+
zipfile.openReadStream.bind(zipfile)
|
|
186
|
+
);
|
|
187
|
+
zipfile.readEntry();
|
|
188
|
+
zipfile.on("entry", async (entry) => {
|
|
189
|
+
if (entry.fileName.endsWith("/")) {
|
|
190
|
+
zipfile.readEntry();
|
|
191
|
+
} else {
|
|
192
|
+
entries.push(entry.fileName);
|
|
193
|
+
const readStream = await openReadStream(entry);
|
|
194
|
+
readStream.on("end", function() {
|
|
195
|
+
zipfile.readEntry();
|
|
196
|
+
});
|
|
197
|
+
const writePath = (0, import_path.join)(extractPath, entry.fileName);
|
|
198
|
+
await (0, import_promises.mkdir)((0, import_path.dirname)(writePath), { recursive: true });
|
|
199
|
+
readStream.pipe((0, import_node_fs.createWriteStream)(writePath));
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
await promise;
|
|
203
|
+
return entries.filter(
|
|
204
|
+
(entry) => AUDIO_FILE_EXTENSIONS.includes(
|
|
205
|
+
(0, import_path.extname)(entry)
|
|
206
|
+
)
|
|
207
|
+
).map((entry) => new import_entry.AudiobookEntry((0, import_path.join)(extractPath, entry)));
|
|
208
|
+
} catch (_) {
|
|
209
|
+
var _error = _, _hasError = true;
|
|
210
|
+
} finally {
|
|
211
|
+
__callDispose(_stack, _error, _hasError);
|
|
107
212
|
}
|
|
108
|
-
zipFs.discardAndClose();
|
|
109
|
-
return entries.filter(
|
|
110
|
-
(entry) => AUDIO_FILE_EXTENSIONS.includes(
|
|
111
|
-
(0, import_path.extname)(entry)
|
|
112
|
-
)
|
|
113
|
-
).map((entry) => new import_entry.AudiobookEntry((0, import_path.join)(tmpDir, entry)));
|
|
114
213
|
} else {
|
|
115
|
-
return this.inputs.map(
|
|
116
|
-
(input) => new import_entry.AudiobookEntry(input)
|
|
117
|
-
);
|
|
214
|
+
return this.inputs.map((input) => new import_entry.AudiobookEntry(input));
|
|
118
215
|
}
|
|
119
216
|
}
|
|
120
217
|
async getFirstValue(getter) {
|
|
@@ -252,30 +349,58 @@ class Audiobook {
|
|
|
252
349
|
for (const entry of this.entries) {
|
|
253
350
|
await entry.saveAndClose();
|
|
254
351
|
}
|
|
255
|
-
if (this.isZip) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
await zipFs.mkdirPromise(
|
|
262
|
-
import_fslib.ppath.join(import_fslib.PortablePath.root, (0, import_path.dirname)(entry)),
|
|
263
|
-
{ recursive: true }
|
|
264
|
-
);
|
|
265
|
-
await (0, import_promises2.pipeline)(
|
|
266
|
-
(0, import_node_fs.createReadStream)((0, import_path.join)(tmpDir, entry)),
|
|
267
|
-
zipFs.createWriteStream(
|
|
268
|
-
import_fslib.ppath.join(import_fslib.PortablePath.root, entry)
|
|
269
|
-
)
|
|
352
|
+
if (this.isZip && this.extractPath) {
|
|
353
|
+
var _stack = [];
|
|
354
|
+
try {
|
|
355
|
+
const tmpArchivePath = (0, import_path.join)(
|
|
356
|
+
(0, import_node_os.tmpdir)(),
|
|
357
|
+
`storyteller-platform-epub-${(0, import_node_crypto.randomUUID)()}`
|
|
270
358
|
);
|
|
359
|
+
const [first] = this.inputs;
|
|
360
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
361
|
+
const zipfile = new import_yazl.ZipFile();
|
|
362
|
+
const writeStream = (0, import_node_fs.createWriteStream)(tmpArchivePath);
|
|
363
|
+
writeStream.on("close", () => {
|
|
364
|
+
resolve();
|
|
365
|
+
});
|
|
366
|
+
const stack = __using(_stack, new AsyncDisposableStack(), true);
|
|
367
|
+
stack.defer(async () => {
|
|
368
|
+
writeStream.close();
|
|
369
|
+
await (0, import_promises.rm)(tmpArchivePath, { force: true });
|
|
370
|
+
});
|
|
371
|
+
zipfile.outputStream.pipe(writeStream);
|
|
372
|
+
const entries = await (0, import_promises.readdir)(this.extractPath, {
|
|
373
|
+
recursive: true,
|
|
374
|
+
withFileTypes: true
|
|
375
|
+
});
|
|
376
|
+
for (const entry of entries) {
|
|
377
|
+
if (entry.isDirectory()) continue;
|
|
378
|
+
zipfile.addFile(
|
|
379
|
+
(0, import_path.join)(entry.parentPath, entry.name),
|
|
380
|
+
(0, import_path.join)(entry.parentPath, entry.name).replace(
|
|
381
|
+
`${this.extractPath}/`,
|
|
382
|
+
""
|
|
383
|
+
)
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
zipfile.end();
|
|
387
|
+
await promise;
|
|
388
|
+
await (0, import_promises.cp)(tmpArchivePath, first);
|
|
389
|
+
return;
|
|
390
|
+
} catch (_) {
|
|
391
|
+
var _error = _, _hasError = true;
|
|
392
|
+
} finally {
|
|
393
|
+
var _promise = __callDispose(_stack, _error, _hasError);
|
|
394
|
+
_promise && await _promise;
|
|
271
395
|
}
|
|
272
|
-
zipFs.saveAndClose();
|
|
273
|
-
return;
|
|
274
396
|
}
|
|
275
397
|
}
|
|
276
398
|
discardAndClose() {
|
|
277
399
|
this.inputs = [""];
|
|
278
400
|
this.entries = [];
|
|
401
|
+
if (this.extractPath) {
|
|
402
|
+
(0, import_node_fs.rmSync)(this.extractPath, { recursive: true });
|
|
403
|
+
}
|
|
279
404
|
}
|
|
280
405
|
[Symbol.dispose]() {
|
|
281
406
|
this.discardAndClose();
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__callDispose,
|
|
3
|
+
__using
|
|
4
|
+
} from "./chunk-BIEQXUOY.js";
|
|
1
5
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
import { mkdir, readFile, readdir } from "node:fs/promises";
|
|
6
|
+
import { createWriteStream, rmSync } from "node:fs";
|
|
7
|
+
import { cp, mkdir, readFile, readdir, rm } from "node:fs/promises";
|
|
4
8
|
import { tmpdir } from "node:os";
|
|
5
|
-
import
|
|
6
|
-
import { PortablePath, ppath } from "@yarnpkg/fslib";
|
|
7
|
-
import { ZipFS } from "@yarnpkg/libzip";
|
|
9
|
+
import * as util from "node:util";
|
|
8
10
|
import { lookup } from "mime-types";
|
|
11
|
+
import yauzl from "yauzl";
|
|
12
|
+
import { ZipFile } from "yazl";
|
|
9
13
|
import { basename, dirname, extname, join } from "@storyteller-platform/path";
|
|
10
14
|
import { AudiobookEntry } from "./entry.js";
|
|
15
|
+
function promisify(api) {
|
|
16
|
+
return function(arg, options) {
|
|
17
|
+
return new Promise(function(resolve, reject) {
|
|
18
|
+
api(arg, options, function(err, response) {
|
|
19
|
+
if (err) {
|
|
20
|
+
reject(err);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
resolve(response);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const unzipFromPath = promisify(
|
|
29
|
+
(arg, options, callback) => {
|
|
30
|
+
yauzl.open(arg, options, callback);
|
|
31
|
+
}
|
|
32
|
+
);
|
|
11
33
|
const COVER_IMAGE_FILE_EXTENSIONS = [
|
|
12
34
|
".jpeg",
|
|
13
35
|
".jpg",
|
|
@@ -40,7 +62,7 @@ class Audiobook {
|
|
|
40
62
|
metadata = {};
|
|
41
63
|
inputs;
|
|
42
64
|
entries = [];
|
|
43
|
-
|
|
65
|
+
extractPath = void 0;
|
|
44
66
|
isZip;
|
|
45
67
|
constructor(...inputs) {
|
|
46
68
|
this.inputs = inputs;
|
|
@@ -54,32 +76,56 @@ class Audiobook {
|
|
|
54
76
|
}
|
|
55
77
|
async getEntries() {
|
|
56
78
|
if (this.isZip) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
var _stack = [];
|
|
80
|
+
try {
|
|
81
|
+
const [first] = this.inputs;
|
|
82
|
+
const extractPath = join(
|
|
83
|
+
tmpdir(),
|
|
84
|
+
`storyteller-platform-audiobook-${randomUUID()}`
|
|
85
|
+
);
|
|
86
|
+
this.extractPath = extractPath;
|
|
87
|
+
await mkdir(this.extractPath, { recursive: true });
|
|
88
|
+
const zipfile = await unzipFromPath(first, { lazyEntries: true });
|
|
89
|
+
const stack = __using(_stack, new DisposableStack());
|
|
90
|
+
stack.defer(() => {
|
|
91
|
+
zipfile.close();
|
|
92
|
+
});
|
|
93
|
+
const entries = [];
|
|
94
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
95
|
+
zipfile.on("end", () => {
|
|
96
|
+
resolve();
|
|
97
|
+
});
|
|
98
|
+
const openReadStream = util.promisify(
|
|
99
|
+
zipfile.openReadStream.bind(zipfile)
|
|
71
100
|
);
|
|
101
|
+
zipfile.readEntry();
|
|
102
|
+
zipfile.on("entry", async (entry) => {
|
|
103
|
+
if (entry.fileName.endsWith("/")) {
|
|
104
|
+
zipfile.readEntry();
|
|
105
|
+
} else {
|
|
106
|
+
entries.push(entry.fileName);
|
|
107
|
+
const readStream = await openReadStream(entry);
|
|
108
|
+
readStream.on("end", function() {
|
|
109
|
+
zipfile.readEntry();
|
|
110
|
+
});
|
|
111
|
+
const writePath = join(extractPath, entry.fileName);
|
|
112
|
+
await mkdir(dirname(writePath), { recursive: true });
|
|
113
|
+
readStream.pipe(createWriteStream(writePath));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
await promise;
|
|
117
|
+
return entries.filter(
|
|
118
|
+
(entry) => AUDIO_FILE_EXTENSIONS.includes(
|
|
119
|
+
extname(entry)
|
|
120
|
+
)
|
|
121
|
+
).map((entry) => new AudiobookEntry(join(extractPath, entry)));
|
|
122
|
+
} catch (_) {
|
|
123
|
+
var _error = _, _hasError = true;
|
|
124
|
+
} finally {
|
|
125
|
+
__callDispose(_stack, _error, _hasError);
|
|
72
126
|
}
|
|
73
|
-
zipFs.discardAndClose();
|
|
74
|
-
return entries.filter(
|
|
75
|
-
(entry) => AUDIO_FILE_EXTENSIONS.includes(
|
|
76
|
-
extname(entry)
|
|
77
|
-
)
|
|
78
|
-
).map((entry) => new AudiobookEntry(join(tmpDir, entry)));
|
|
79
127
|
} else {
|
|
80
|
-
return this.inputs.map(
|
|
81
|
-
(input) => new AudiobookEntry(input)
|
|
82
|
-
);
|
|
128
|
+
return this.inputs.map((input) => new AudiobookEntry(input));
|
|
83
129
|
}
|
|
84
130
|
}
|
|
85
131
|
async getFirstValue(getter) {
|
|
@@ -217,30 +263,58 @@ class Audiobook {
|
|
|
217
263
|
for (const entry of this.entries) {
|
|
218
264
|
await entry.saveAndClose();
|
|
219
265
|
}
|
|
220
|
-
if (this.isZip) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
await zipFs.mkdirPromise(
|
|
227
|
-
ppath.join(PortablePath.root, dirname(entry)),
|
|
228
|
-
{ recursive: true }
|
|
229
|
-
);
|
|
230
|
-
await pipeline(
|
|
231
|
-
createReadStream(join(tmpDir, entry)),
|
|
232
|
-
zipFs.createWriteStream(
|
|
233
|
-
ppath.join(PortablePath.root, entry)
|
|
234
|
-
)
|
|
266
|
+
if (this.isZip && this.extractPath) {
|
|
267
|
+
var _stack = [];
|
|
268
|
+
try {
|
|
269
|
+
const tmpArchivePath = join(
|
|
270
|
+
tmpdir(),
|
|
271
|
+
`storyteller-platform-epub-${randomUUID()}`
|
|
235
272
|
);
|
|
273
|
+
const [first] = this.inputs;
|
|
274
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
275
|
+
const zipfile = new ZipFile();
|
|
276
|
+
const writeStream = createWriteStream(tmpArchivePath);
|
|
277
|
+
writeStream.on("close", () => {
|
|
278
|
+
resolve();
|
|
279
|
+
});
|
|
280
|
+
const stack = __using(_stack, new AsyncDisposableStack(), true);
|
|
281
|
+
stack.defer(async () => {
|
|
282
|
+
writeStream.close();
|
|
283
|
+
await rm(tmpArchivePath, { force: true });
|
|
284
|
+
});
|
|
285
|
+
zipfile.outputStream.pipe(writeStream);
|
|
286
|
+
const entries = await readdir(this.extractPath, {
|
|
287
|
+
recursive: true,
|
|
288
|
+
withFileTypes: true
|
|
289
|
+
});
|
|
290
|
+
for (const entry of entries) {
|
|
291
|
+
if (entry.isDirectory()) continue;
|
|
292
|
+
zipfile.addFile(
|
|
293
|
+
join(entry.parentPath, entry.name),
|
|
294
|
+
join(entry.parentPath, entry.name).replace(
|
|
295
|
+
`${this.extractPath}/`,
|
|
296
|
+
""
|
|
297
|
+
)
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
zipfile.end();
|
|
301
|
+
await promise;
|
|
302
|
+
await cp(tmpArchivePath, first);
|
|
303
|
+
return;
|
|
304
|
+
} catch (_) {
|
|
305
|
+
var _error = _, _hasError = true;
|
|
306
|
+
} finally {
|
|
307
|
+
var _promise = __callDispose(_stack, _error, _hasError);
|
|
308
|
+
_promise && await _promise;
|
|
236
309
|
}
|
|
237
|
-
zipFs.saveAndClose();
|
|
238
|
-
return;
|
|
239
310
|
}
|
|
240
311
|
}
|
|
241
312
|
discardAndClose() {
|
|
242
313
|
this.inputs = [""];
|
|
243
314
|
this.entries = [];
|
|
315
|
+
if (this.extractPath) {
|
|
316
|
+
rmSync(this.extractPath, { recursive: true });
|
|
317
|
+
}
|
|
244
318
|
}
|
|
245
319
|
[Symbol.dispose]() {
|
|
246
320
|
this.discardAndClose();
|
package/dist/mime.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyteller-platform/audiobook",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@storyteller-platform/tsup": "^0.1.0",
|
|
34
34
|
"@tsconfig/strictest": "^2.0.5",
|
|
35
|
+
"@types/yazl": "^3",
|
|
35
36
|
"markdown-toc": "^1.2.0",
|
|
36
37
|
"remark-toc": "^9.0.0",
|
|
37
38
|
"tsup": "^8.5.0",
|
|
@@ -46,10 +47,11 @@
|
|
|
46
47
|
"@storyteller-platform/path": "^0.1.1",
|
|
47
48
|
"@types/mime-types": "^2",
|
|
48
49
|
"@types/node": "^24.0.0",
|
|
49
|
-
"@
|
|
50
|
-
"@yarnpkg/libzip": "^3.2.2",
|
|
50
|
+
"@types/yauzl": "^2.10.3",
|
|
51
51
|
"mime-types": "^3.0.1",
|
|
52
|
-
"music-metadata": "^11.9.0"
|
|
52
|
+
"music-metadata": "^11.9.0",
|
|
53
|
+
"yauzl": "^3.2.0",
|
|
54
|
+
"yazl": "^3.3.1"
|
|
53
55
|
},
|
|
54
56
|
"publishConfig": {
|
|
55
57
|
"access": "public",
|