@storyteller-platform/audiobook 0.1.0 → 0.3.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/entry.cjs CHANGED
@@ -21,34 +21,127 @@ __export(entry_exports, {
21
21
  AudiobookEntry: () => AudiobookEntry
22
22
  });
23
23
  module.exports = __toCommonJS(entry_exports);
24
- var import_zip = require("@zip.js/zip.js");
25
- var import_node_taglib_sharp = require("node-taglib-sharp");
26
- var import_base = require("./base.cjs");
27
- var import_Uint8ArrayFileAbstraction = require("./taglib/Uint8ArrayFileAbstraction.cjs");
28
- class AudiobookEntry extends import_base.BaseAudiobookEntry {
29
- constructor(entry) {
30
- super();
31
- this.entry = entry;
32
- this.filename = entry.filename;
33
- }
34
- filename;
35
- file = null;
36
- async readData() {
37
- if ("data" in this.entry) {
38
- return this.entry.data;
39
- }
40
- const writer = new import_zip.Uint8ArrayWriter();
41
- return await this.entry.getData(writer);
42
- }
43
- async getData() {
44
- const file = await this.getFile();
45
- return file.fileAbstraction.data;
46
- }
47
- async createFile() {
48
- const data = await this.readData();
49
- return import_node_taglib_sharp.File.createFromAbstraction(
50
- new import_Uint8ArrayFileAbstraction.Uint8ArrayFileAbstraction(this.filename, data)
51
- );
24
+ var import_ffmpeg = require("./ffmpeg.cjs");
25
+ var import_mime = require("./mime.cjs");
26
+ function splitNames(names) {
27
+ if (!names) return [];
28
+ return names.split(/[;,/]/).map((name) => name.trim());
29
+ }
30
+ class AudiobookEntry {
31
+ constructor(filename) {
32
+ this.filename = filename;
33
+ }
34
+ info = null;
35
+ async getInfo() {
36
+ this.info ??= await (0, import_ffmpeg.getTrackMetadata)(this.filename);
37
+ return this.info;
38
+ }
39
+ async getMetadataTags() {
40
+ const info = await this.getInfo();
41
+ return info.tags;
42
+ }
43
+ async getTitle() {
44
+ const tags = await this.getMetadataTags();
45
+ return tags.album ?? tags.title ?? null;
46
+ }
47
+ async setTitle(title) {
48
+ const tags = await this.getMetadataTags();
49
+ tags.album = title;
50
+ }
51
+ async getTrackTitle() {
52
+ const tags = await this.getMetadataTags();
53
+ return tags.title ?? null;
54
+ }
55
+ async setTrackTitle(trackTitle) {
56
+ const tags = await this.getMetadataTags();
57
+ tags.title = trackTitle;
58
+ }
59
+ async getSubtitle() {
60
+ const tags = await this.getMetadataTags();
61
+ return tags.subtitle ?? null;
62
+ }
63
+ async setSubtitle(subtitle) {
64
+ const tags = await this.getMetadataTags();
65
+ tags.subtitle = subtitle;
66
+ }
67
+ async getDescription() {
68
+ const tags = await this.getMetadataTags();
69
+ return tags.description ?? tags.comment ?? null;
70
+ }
71
+ async setDescription(description) {
72
+ const tags = await this.getMetadataTags();
73
+ tags.description = description;
74
+ tags.comment = description;
75
+ }
76
+ async getAuthors() {
77
+ const tags = await this.getMetadataTags();
78
+ return splitNames(tags.albumArtist ?? tags.artist);
79
+ }
80
+ async setAuthors(authors) {
81
+ const tags = await this.getMetadataTags();
82
+ tags.artist = authors.join(";");
83
+ tags.albumArtist = authors.join(";");
84
+ }
85
+ async getNarrators() {
86
+ const tags = await this.getMetadataTags();
87
+ return splitNames(tags.composer ?? tags.performer);
88
+ }
89
+ async setNarrators(narrators) {
90
+ const tags = await this.getMetadataTags();
91
+ tags.composer = narrators.join(";");
92
+ tags.performer = narrators.join(";");
93
+ }
94
+ async getCoverArt() {
95
+ const info = await this.getInfo();
96
+ return info.attachedPic || null;
97
+ }
98
+ async setCoverArt(picture) {
99
+ const info = await this.getInfo();
100
+ info.attachedPic = picture;
101
+ }
102
+ async getPublisher() {
103
+ const tags = await this.getMetadataTags();
104
+ return tags.publisher ?? null;
105
+ }
106
+ async setPublisher(publisher) {
107
+ const tags = await this.getMetadataTags();
108
+ tags.publisher = publisher;
109
+ }
110
+ async getReleaseDate() {
111
+ const tags = await this.getMetadataTags();
112
+ return tags.date ? new Date(tags.date) : null;
113
+ }
114
+ async setReleaseDate(date) {
115
+ const tags = await this.getMetadataTags();
116
+ tags.date = `${date.getDate().toString().padStart(2, "0")}-${date.getMonth().toString().padStart(2, "0")}-${date.getFullYear()}`;
117
+ }
118
+ async getDuration() {
119
+ const info = await this.getInfo();
120
+ return info.duration;
121
+ }
122
+ async getResource() {
123
+ const duration = await this.getDuration();
124
+ const title = await this.getTrackTitle() ?? null;
125
+ const type = (0, import_mime.lookupAudioMime)(this.filename);
126
+ return {
127
+ filename: this.filename,
128
+ title,
129
+ type,
130
+ duration,
131
+ bitrate: null
132
+ };
133
+ }
134
+ async getChapters() {
135
+ const info = await this.getInfo();
136
+ return info.chapters.map((chapter) => ({
137
+ filename: this.filename,
138
+ title: chapter.title ?? null,
139
+ start: chapter.startTime
140
+ }));
141
+ }
142
+ async saveAndClose() {
143
+ const info = await this.getInfo();
144
+ await (0, import_ffmpeg.writeTrackMetadata)(this.filename, info.tags, info.attachedPic);
52
145
  }
53
146
  }
54
147
  // Annotate the CommonJS export names for ESM import in node:
package/dist/entry.d.cts CHANGED
@@ -1,19 +1,38 @@
1
- import { Entry } from '@zip.js/zip.js';
2
- import { File } from 'node-taglib-sharp';
3
- import { BaseAudiobookEntry } from './base.cjs';
1
+ import { TrackInfo, AttachedPic } from './ffmpeg.cjs';
2
+ import { AudiobookResource, AudiobookChapter } from './resources.cjs';
4
3
 
5
4
  interface Uint8ArrayEntry {
6
5
  filename: string;
7
6
  data: Uint8Array;
8
7
  }
9
- declare class AudiobookEntry extends BaseAudiobookEntry {
10
- protected entry: Uint8ArrayEntry | Entry;
8
+ declare class AudiobookEntry {
11
9
  filename: string;
12
- protected file: File | null;
13
- protected readData(): Promise<Uint8Array<ArrayBufferLike>>;
14
- getData(): Promise<Uint8Array<ArrayBufferLike>>;
15
- createFile(): Promise<File>;
16
- constructor(entry: Uint8ArrayEntry | Entry);
10
+ private info;
11
+ constructor(filename: string);
12
+ getInfo(): Promise<TrackInfo>;
13
+ getMetadataTags(): Promise<TrackInfo["tags"]>;
14
+ getTitle(): Promise<string | null>;
15
+ setTitle(title: string): Promise<void>;
16
+ getTrackTitle(): Promise<string | null>;
17
+ setTrackTitle(trackTitle: string): Promise<void>;
18
+ getSubtitle(): Promise<string | null>;
19
+ setSubtitle(subtitle: string): Promise<void>;
20
+ getDescription(): Promise<string | null>;
21
+ setDescription(description: string): Promise<void>;
22
+ getAuthors(): Promise<string[]>;
23
+ setAuthors(authors: string[]): Promise<void>;
24
+ getNarrators(): Promise<string[]>;
25
+ setNarrators(narrators: string[]): Promise<void>;
26
+ getCoverArt(): Promise<AttachedPic | null>;
27
+ setCoverArt(picture: AttachedPic): Promise<void>;
28
+ getPublisher(): Promise<string | null>;
29
+ setPublisher(publisher: string): Promise<void>;
30
+ getReleaseDate(): Promise<Date | null>;
31
+ setReleaseDate(date: Date): Promise<void>;
32
+ getDuration(): Promise<number>;
33
+ getResource(): Promise<AudiobookResource>;
34
+ getChapters(): Promise<AudiobookChapter[]>;
35
+ saveAndClose(): Promise<void>;
17
36
  }
18
37
 
19
38
  export { AudiobookEntry, type Uint8ArrayEntry };
package/dist/entry.d.ts CHANGED
@@ -1,19 +1,38 @@
1
- import { Entry } from '@zip.js/zip.js';
2
- import { File } from 'node-taglib-sharp';
3
- import { BaseAudiobookEntry } from './base.js';
1
+ import { TrackInfo, AttachedPic } from './ffmpeg.js';
2
+ import { AudiobookResource, AudiobookChapter } from './resources.js';
4
3
 
5
4
  interface Uint8ArrayEntry {
6
5
  filename: string;
7
6
  data: Uint8Array;
8
7
  }
9
- declare class AudiobookEntry extends BaseAudiobookEntry {
10
- protected entry: Uint8ArrayEntry | Entry;
8
+ declare class AudiobookEntry {
11
9
  filename: string;
12
- protected file: File | null;
13
- protected readData(): Promise<Uint8Array<ArrayBufferLike>>;
14
- getData(): Promise<Uint8Array<ArrayBufferLike>>;
15
- createFile(): Promise<File>;
16
- constructor(entry: Uint8ArrayEntry | Entry);
10
+ private info;
11
+ constructor(filename: string);
12
+ getInfo(): Promise<TrackInfo>;
13
+ getMetadataTags(): Promise<TrackInfo["tags"]>;
14
+ getTitle(): Promise<string | null>;
15
+ setTitle(title: string): Promise<void>;
16
+ getTrackTitle(): Promise<string | null>;
17
+ setTrackTitle(trackTitle: string): Promise<void>;
18
+ getSubtitle(): Promise<string | null>;
19
+ setSubtitle(subtitle: string): Promise<void>;
20
+ getDescription(): Promise<string | null>;
21
+ setDescription(description: string): Promise<void>;
22
+ getAuthors(): Promise<string[]>;
23
+ setAuthors(authors: string[]): Promise<void>;
24
+ getNarrators(): Promise<string[]>;
25
+ setNarrators(narrators: string[]): Promise<void>;
26
+ getCoverArt(): Promise<AttachedPic | null>;
27
+ setCoverArt(picture: AttachedPic): Promise<void>;
28
+ getPublisher(): Promise<string | null>;
29
+ setPublisher(publisher: string): Promise<void>;
30
+ getReleaseDate(): Promise<Date | null>;
31
+ setReleaseDate(date: Date): Promise<void>;
32
+ getDuration(): Promise<number>;
33
+ getResource(): Promise<AudiobookResource>;
34
+ getChapters(): Promise<AudiobookChapter[]>;
35
+ saveAndClose(): Promise<void>;
17
36
  }
18
37
 
19
38
  export { AudiobookEntry, type Uint8ArrayEntry };
package/dist/entry.js CHANGED
@@ -1,31 +1,127 @@
1
- import { Uint8ArrayWriter } from "@zip.js/zip.js";
2
- import { File } from "node-taglib-sharp";
3
- import { BaseAudiobookEntry } from "./base.js";
4
- import { Uint8ArrayFileAbstraction } from "./taglib/Uint8ArrayFileAbstraction.js";
5
- class AudiobookEntry extends BaseAudiobookEntry {
6
- constructor(entry) {
7
- super();
8
- this.entry = entry;
9
- this.filename = entry.filename;
10
- }
11
- filename;
12
- file = null;
13
- async readData() {
14
- if ("data" in this.entry) {
15
- return this.entry.data;
16
- }
17
- const writer = new Uint8ArrayWriter();
18
- return await this.entry.getData(writer);
19
- }
20
- async getData() {
21
- const file = await this.getFile();
22
- return file.fileAbstraction.data;
23
- }
24
- async createFile() {
25
- const data = await this.readData();
26
- return File.createFromAbstraction(
27
- new Uint8ArrayFileAbstraction(this.filename, data)
28
- );
1
+ import {
2
+ getTrackMetadata,
3
+ writeTrackMetadata
4
+ } from "./ffmpeg.js";
5
+ import { lookupAudioMime } from "./mime.js";
6
+ function splitNames(names) {
7
+ if (!names) return [];
8
+ return names.split(/[;,/]/).map((name) => name.trim());
9
+ }
10
+ class AudiobookEntry {
11
+ constructor(filename) {
12
+ this.filename = filename;
13
+ }
14
+ info = null;
15
+ async getInfo() {
16
+ this.info ??= await getTrackMetadata(this.filename);
17
+ return this.info;
18
+ }
19
+ async getMetadataTags() {
20
+ const info = await this.getInfo();
21
+ return info.tags;
22
+ }
23
+ async getTitle() {
24
+ const tags = await this.getMetadataTags();
25
+ return tags.album ?? tags.title ?? null;
26
+ }
27
+ async setTitle(title) {
28
+ const tags = await this.getMetadataTags();
29
+ tags.album = title;
30
+ }
31
+ async getTrackTitle() {
32
+ const tags = await this.getMetadataTags();
33
+ return tags.title ?? null;
34
+ }
35
+ async setTrackTitle(trackTitle) {
36
+ const tags = await this.getMetadataTags();
37
+ tags.title = trackTitle;
38
+ }
39
+ async getSubtitle() {
40
+ const tags = await this.getMetadataTags();
41
+ return tags.subtitle ?? null;
42
+ }
43
+ async setSubtitle(subtitle) {
44
+ const tags = await this.getMetadataTags();
45
+ tags.subtitle = subtitle;
46
+ }
47
+ async getDescription() {
48
+ const tags = await this.getMetadataTags();
49
+ return tags.description ?? tags.comment ?? null;
50
+ }
51
+ async setDescription(description) {
52
+ const tags = await this.getMetadataTags();
53
+ tags.description = description;
54
+ tags.comment = description;
55
+ }
56
+ async getAuthors() {
57
+ const tags = await this.getMetadataTags();
58
+ return splitNames(tags.albumArtist ?? tags.artist);
59
+ }
60
+ async setAuthors(authors) {
61
+ const tags = await this.getMetadataTags();
62
+ tags.artist = authors.join(";");
63
+ tags.albumArtist = authors.join(";");
64
+ }
65
+ async getNarrators() {
66
+ const tags = await this.getMetadataTags();
67
+ return splitNames(tags.composer ?? tags.performer);
68
+ }
69
+ async setNarrators(narrators) {
70
+ const tags = await this.getMetadataTags();
71
+ tags.composer = narrators.join(";");
72
+ tags.performer = narrators.join(";");
73
+ }
74
+ async getCoverArt() {
75
+ const info = await this.getInfo();
76
+ return info.attachedPic || null;
77
+ }
78
+ async setCoverArt(picture) {
79
+ const info = await this.getInfo();
80
+ info.attachedPic = picture;
81
+ }
82
+ async getPublisher() {
83
+ const tags = await this.getMetadataTags();
84
+ return tags.publisher ?? null;
85
+ }
86
+ async setPublisher(publisher) {
87
+ const tags = await this.getMetadataTags();
88
+ tags.publisher = publisher;
89
+ }
90
+ async getReleaseDate() {
91
+ const tags = await this.getMetadataTags();
92
+ return tags.date ? new Date(tags.date) : null;
93
+ }
94
+ async setReleaseDate(date) {
95
+ const tags = await this.getMetadataTags();
96
+ tags.date = `${date.getDate().toString().padStart(2, "0")}-${date.getMonth().toString().padStart(2, "0")}-${date.getFullYear()}`;
97
+ }
98
+ async getDuration() {
99
+ const info = await this.getInfo();
100
+ return info.duration;
101
+ }
102
+ async getResource() {
103
+ const duration = await this.getDuration();
104
+ const title = await this.getTrackTitle() ?? null;
105
+ const type = lookupAudioMime(this.filename);
106
+ return {
107
+ filename: this.filename,
108
+ title,
109
+ type,
110
+ duration,
111
+ bitrate: null
112
+ };
113
+ }
114
+ async getChapters() {
115
+ const info = await this.getInfo();
116
+ return info.chapters.map((chapter) => ({
117
+ filename: this.filename,
118
+ title: chapter.title ?? null,
119
+ start: chapter.startTime
120
+ }));
121
+ }
122
+ async saveAndClose() {
123
+ const info = await this.getInfo();
124
+ await writeTrackMetadata(this.filename, info.tags, info.attachedPic);
29
125
  }
30
126
  }
31
127
  export {
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var ffmpeg_exports = {};
20
+ __export(ffmpeg_exports, {
21
+ escapeQuotes: () => escapeQuotes,
22
+ getTrackMetadata: () => getTrackMetadata,
23
+ getTrackMetadataFromBuffer: () => getTrackMetadataFromBuffer,
24
+ quotePath: () => quotePath,
25
+ writeTrackMetadata: () => writeTrackMetadata
26
+ });
27
+ module.exports = __toCommonJS(ffmpeg_exports);
28
+ var import_node_child_process = require("node:child_process");
29
+ var import_node_crypto = require("node:crypto");
30
+ var import_node_fs = require("node:fs");
31
+ var import_promises = require("node:fs/promises");
32
+ var import_node_os = require("node:os");
33
+ var import_node_path = require("node:path");
34
+ var import_node_util = require("node:util");
35
+ var import_path = require("@storyteller-platform/path");
36
+ const execPromise = (0, import_node_util.promisify)(import_node_child_process.exec);
37
+ const execFilePromise = (0, import_node_util.promisify)(import_node_child_process.execFile);
38
+ async function execCmd(command) {
39
+ let stdout = "";
40
+ let stderr = "";
41
+ try {
42
+ ;
43
+ ({ stdout, stderr } = await execPromise(command));
44
+ return stdout;
45
+ } catch (error) {
46
+ console.error(error);
47
+ console.warn(stdout);
48
+ throw new Error(stderr);
49
+ }
50
+ }
51
+ async function execCmdBuffer(command, args) {
52
+ var _a, _b;
53
+ try {
54
+ const { stdout } = await execFilePromise(command, args, {
55
+ encoding: "buffer",
56
+ maxBuffer: 10 * 1024 * 1024,
57
+ cwd: process.cwd()
58
+ });
59
+ return stdout;
60
+ } catch (error) {
61
+ console.error(error);
62
+ if (error && typeof error === "object" && "stdout" in error) {
63
+ console.warn((_a = error.stdout) == null ? void 0 : _a.toString());
64
+ throw new Error((_b = error.stdout) == null ? void 0 : _b.toString());
65
+ }
66
+ throw error;
67
+ }
68
+ }
69
+ function escapeQuotes(input) {
70
+ return input.replaceAll(/"/g, '\\"');
71
+ }
72
+ function quotePath(path) {
73
+ return `"${escapeQuotes(path)}"`;
74
+ }
75
+ function lookup(codecName) {
76
+ switch (codecName) {
77
+ case "mjpeg":
78
+ case "jpeg": {
79
+ return "image/jpeg";
80
+ }
81
+ case "bmp": {
82
+ return "image/bmp";
83
+ }
84
+ case "png": {
85
+ return "image/png";
86
+ }
87
+ case "gif": {
88
+ return "image/gif";
89
+ }
90
+ }
91
+ }
92
+ async function getTrackMetadata(path) {
93
+ const stdout = await execCmd(
94
+ `ffprobe -i ${quotePath(path)} -v quiet -show_format -show_chapters -show_streams -output_format json`
95
+ );
96
+ const { chapters, streams, format, duration, bit_rate } = JSON.parse(
97
+ stdout
98
+ );
99
+ const attachedPicStream = streams.find(
100
+ (stream) => !!stream.disposition.attached_pic
101
+ );
102
+ const attachedPic = attachedPicStream && {
103
+ name: attachedPicStream.tags.title,
104
+ mimeType: lookup(attachedPicStream.codec_name),
105
+ kind: attachedPicStream.tags.comment === "Cover (front)" ? "coverFront" : attachedPicStream.tags.comment === "Cover (back)" ? "coverBack" : "unknown",
106
+ description: attachedPicStream.tags.comment,
107
+ data: await execCmdBuffer("ffmpeg", [
108
+ "-nostdin",
109
+ "-i",
110
+ path,
111
+ "-map",
112
+ "0:v",
113
+ "-c:v",
114
+ "copy",
115
+ "-vframes",
116
+ "1",
117
+ "-f",
118
+ "image2",
119
+ "-update",
120
+ "1",
121
+ "pipe:1"
122
+ ])
123
+ };
124
+ return {
125
+ duration: parseFloat(duration),
126
+ bitRate: bit_rate !== void 0 ? parseFloat(bit_rate) : bit_rate,
127
+ tags: {
128
+ title: format.tags.title ?? format.tags.Title,
129
+ subtitle: format.tags.subtitle ?? format.tags.Subtitle,
130
+ date: format.tags.date ?? format.tags.Date,
131
+ album: format.tags.album ?? format.tags.Album,
132
+ albumArtist: format.tags.album_artist ?? format.tags.Album_Artist,
133
+ artist: format.tags.artist ?? format.tags.Artist,
134
+ performer: format.tags.performer ?? format.tags.Performer,
135
+ composer: format.tags.composer ?? format.tags.Composer,
136
+ comment: format.tags.comment ?? format.tags.Comment,
137
+ description: format.tags.description ?? format.tags.Description,
138
+ publisher: format.tags.publisher ?? format.tags.Publisher
139
+ },
140
+ chapters: chapters.map(
141
+ (chapter) => ({
142
+ id: chapter.id,
143
+ startTime: parseFloat(chapter.start_time),
144
+ endTime: parseFloat(chapter.end_time),
145
+ title: chapter.tags.title
146
+ })
147
+ ),
148
+ attachedPic
149
+ };
150
+ }
151
+ async function writeTrackMetadata(path, metadata, attachedPic) {
152
+ const metadataArgs = [];
153
+ if (metadata.title) {
154
+ metadataArgs.push(`-metadata title="${escapeQuotes(metadata.title)}"`);
155
+ }
156
+ if (metadata.subtitle) {
157
+ metadataArgs.push(`-metadata subtitle="${escapeQuotes(metadata.subtitle)}"`);
158
+ }
159
+ if (metadata.date) {
160
+ metadataArgs.push(`-metadata date="${escapeQuotes(metadata.date)}"`);
161
+ }
162
+ if (metadata.album) {
163
+ metadataArgs.push(`-metadata album="${escapeQuotes(metadata.album)}"`);
164
+ }
165
+ if (metadata.albumArtist) {
166
+ metadataArgs.push(
167
+ `-metadata album_artist="${escapeQuotes(metadata.albumArtist)}"`
168
+ );
169
+ }
170
+ if (metadata.artist) {
171
+ metadataArgs.push(`-metadata artist="${escapeQuotes(metadata.artist)}"`);
172
+ }
173
+ if (metadata.performer) {
174
+ metadataArgs.push(
175
+ `-metadata performer="${escapeQuotes(metadata.performer)}"`
176
+ );
177
+ }
178
+ if (metadata.composer) {
179
+ metadataArgs.push(`-metadata composer="${escapeQuotes(metadata.composer)}"`);
180
+ }
181
+ if (metadata.comment) {
182
+ metadataArgs.push(`-metadata comment="${escapeQuotes(metadata.comment)}"`);
183
+ }
184
+ if (metadata.description) {
185
+ metadataArgs.push(
186
+ `-metadata description="${escapeQuotes(metadata.description)}"`
187
+ );
188
+ }
189
+ if (metadata.publisher) {
190
+ metadataArgs.push(
191
+ `-metadata publisher="${escapeQuotes(metadata.publisher)}"`
192
+ );
193
+ }
194
+ const ext = (0, import_path.extname)(path);
195
+ const tmpPath = (0, import_node_path.join)(
196
+ (0, import_node_os.tmpdir)(),
197
+ `storyteller-platform-audiobook-${(0, import_node_crypto.randomUUID)()}${ext}`
198
+ );
199
+ let picPath = null;
200
+ try {
201
+ if (attachedPic) {
202
+ const imageExt = attachedPic.mimeType.split("/")[1];
203
+ picPath = (0, import_node_path.join)(
204
+ (0, import_node_os.tmpdir)(),
205
+ `storyteller-platform-audiobook-${(0, import_node_crypto.randomUUID)()}.${imageExt}`
206
+ );
207
+ await (0, import_promises.writeFile)(picPath, attachedPic.data);
208
+ metadataArgs.push(`-disposition:v:0 attached_pic`);
209
+ if (attachedPic.name) {
210
+ metadataArgs.push(
211
+ `-metadata:s:v title="${escapeQuotes(attachedPic.name)}"`
212
+ );
213
+ }
214
+ if (attachedPic.kind !== "unknown") {
215
+ metadataArgs.push(
216
+ `-metadata:s:v comment="${attachedPic.kind === "coverFront" ? "Cover (front)" : "Cover (back)"}"`
217
+ );
218
+ }
219
+ }
220
+ const cmd = `ffmpeg -i "${path}" ${metadataArgs.join(" ")} -codec copy "${tmpPath}"`;
221
+ await execCmd(cmd);
222
+ await (0, import_promises.cp)(tmpPath, path, { force: true });
223
+ } finally {
224
+ try {
225
+ await (0, import_promises.rm)(tmpPath);
226
+ } catch (error) {
227
+ console.warn(`Failed to clean up temporary file ${tmpPath}:`, error);
228
+ }
229
+ if (picPath) {
230
+ try {
231
+ await (0, import_promises.rm)(picPath);
232
+ } catch (error) {
233
+ console.warn(`Failed to clean up temporary file ${picPath}:`, error);
234
+ }
235
+ }
236
+ }
237
+ }
238
+ async function getTrackMetadataFromBuffer(buffer) {
239
+ const tempFile = (0, import_node_path.join)((0, import_node_os.tmpdir)(), `ffprobe-${(0, import_node_crypto.randomUUID)()}`);
240
+ try {
241
+ (0, import_node_fs.writeFileSync)(tempFile, buffer);
242
+ const result = await getTrackMetadata(tempFile);
243
+ return result;
244
+ } finally {
245
+ try {
246
+ await (0, import_promises.rm)(tempFile);
247
+ } catch (error) {
248
+ console.warn(`Failed to clean up temporary file ${tempFile}:`, error);
249
+ }
250
+ }
251
+ }
252
+ // Annotate the CommonJS export names for ESM import in node:
253
+ 0 && (module.exports = {
254
+ escapeQuotes,
255
+ getTrackMetadata,
256
+ getTrackMetadataFromBuffer,
257
+ quotePath,
258
+ writeTrackMetadata
259
+ });