@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 +121 -28
- package/dist/entry.d.cts +29 -10
- package/dist/entry.d.ts +29 -10
- package/dist/entry.js +124 -28
- package/dist/ffmpeg.cjs +259 -0
- package/dist/ffmpeg.d.cts +67 -0
- package/dist/ffmpeg.d.ts +67 -0
- package/dist/ffmpeg.js +231 -0
- package/dist/index.cjs +275 -33
- package/dist/index.d.cts +62 -11
- package/dist/index.d.ts +62 -11
- package/dist/index.js +263 -33
- package/dist/mime.cjs +112 -0
- package/dist/mime.d.cts +40 -0
- package/dist/mime.d.ts +40 -0
- package/dist/mime.js +75 -0
- package/dist/resources.cjs +16 -0
- package/dist/resources.d.cts +14 -0
- package/dist/resources.d.ts +14 -0
- package/dist/resources.js +0 -0
- package/package.json +23 -29
- package/dist/base.cjs +0 -248
- package/dist/base.d.cts +0 -67
- package/dist/base.d.ts +0 -67
- package/dist/base.js +0 -223
- package/dist/node/entry.cjs +0 -76
- package/dist/node/entry.d.cts +0 -18
- package/dist/node/entry.d.ts +0 -18
- package/dist/node/entry.js +0 -52
- package/dist/node/index.cjs +0 -107
- package/dist/node/index.d.cts +0 -17
- package/dist/node/index.d.ts +0 -17
- package/dist/node/index.js +0 -89
- package/dist/taglib/Uint8ArrayFileAbstraction.cjs +0 -128
- package/dist/taglib/Uint8ArrayFileAbstraction.d.cts +0 -26
- package/dist/taglib/Uint8ArrayFileAbstraction.d.ts +0 -26
- package/dist/taglib/Uint8ArrayFileAbstraction.js +0 -106
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
|
|
25
|
-
var
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.filename =
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
async
|
|
44
|
-
const
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
async
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
10
|
-
protected entry: Uint8ArrayEntry | Entry;
|
|
8
|
+
declare class AudiobookEntry {
|
|
11
9
|
filename: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
10
|
-
protected entry: Uint8ArrayEntry | Entry;
|
|
8
|
+
declare class AudiobookEntry {
|
|
11
9
|
filename: string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
filename
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 {
|
package/dist/ffmpeg.cjs
ADDED
|
@@ -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
|
+
});
|