@storyteller-platform/audiobook 0.3.3 → 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 +3 -5
- package/dist/ffmpeg.js +4 -5
- 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
|
@@ -94,9 +94,7 @@ async function getTrackMetadata(path) {
|
|
|
94
94
|
const stdout = await execCmd(
|
|
95
95
|
`ffprobe -i ${quotePath(path)} -v quiet -show_format -show_chapters -show_streams -output_format json`
|
|
96
96
|
);
|
|
97
|
-
const { chapters, streams, format
|
|
98
|
-
stdout
|
|
99
|
-
);
|
|
97
|
+
const { chapters, streams, format } = JSON.parse(stdout);
|
|
100
98
|
const attachedPicStream = streams.find(
|
|
101
99
|
(stream) => !!stream.disposition.attached_pic
|
|
102
100
|
);
|
|
@@ -123,8 +121,8 @@ async function getTrackMetadata(path) {
|
|
|
123
121
|
])
|
|
124
122
|
};
|
|
125
123
|
return {
|
|
126
|
-
duration: parseFloat(duration),
|
|
127
|
-
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,
|
|
128
126
|
tags: {
|
|
129
127
|
title: format.tags.title ?? format.tags.Title,
|
|
130
128
|
subtitle: format.tags.subtitle ?? format.tags.Subtitle,
|
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";
|
|
@@ -67,9 +68,7 @@ async function getTrackMetadata(path) {
|
|
|
67
68
|
const stdout = await execCmd(
|
|
68
69
|
`ffprobe -i ${quotePath(path)} -v quiet -show_format -show_chapters -show_streams -output_format json`
|
|
69
70
|
);
|
|
70
|
-
const { chapters, streams, format
|
|
71
|
-
stdout
|
|
72
|
-
);
|
|
71
|
+
const { chapters, streams, format } = JSON.parse(stdout);
|
|
73
72
|
const attachedPicStream = streams.find(
|
|
74
73
|
(stream) => !!stream.disposition.attached_pic
|
|
75
74
|
);
|
|
@@ -96,8 +95,8 @@ async function getTrackMetadata(path) {
|
|
|
96
95
|
])
|
|
97
96
|
};
|
|
98
97
|
return {
|
|
99
|
-
duration: parseFloat(duration),
|
|
100
|
-
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,
|
|
101
100
|
tags: {
|
|
102
101
|
title: format.tags.title ?? format.tags.Title,
|
|
103
102
|
subtitle: format.tags.subtitle ?? format.tags.Subtitle,
|
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",
|