@thinkable-labs/audio-enhancer 1.0.0 → 2.0.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/enhancer.d.ts +19 -4
- package/dist/enhancer.js +67 -77
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/enhancer.d.ts
CHANGED
|
@@ -6,14 +6,23 @@ export interface EnhanceOptions {
|
|
|
6
6
|
export interface EnhanceItem {
|
|
7
7
|
videoPath: string;
|
|
8
8
|
voiceId: string;
|
|
9
|
+
/** Produce the isolated→voice-change variant. Default: true. */
|
|
10
|
+
withIsolation?: boolean;
|
|
11
|
+
/** Produce the direct (skip isolation) voice-change variant. Default: true. */
|
|
12
|
+
withoutIsolation?: boolean;
|
|
9
13
|
}
|
|
14
|
+
export type Variant = "isolated" | "direct";
|
|
10
15
|
export type ItemStatus = "pending" | "extracting" | "isolating" | "voice-changing" | "success" | "error";
|
|
16
|
+
export interface VariantResult {
|
|
17
|
+
variant: Variant;
|
|
18
|
+
outputPath: string;
|
|
19
|
+
status: "success" | "error";
|
|
20
|
+
error?: string;
|
|
21
|
+
}
|
|
11
22
|
export interface ItemResult {
|
|
12
23
|
item: EnhanceItem;
|
|
13
24
|
videoName: string;
|
|
14
|
-
|
|
15
|
-
status: ItemStatus;
|
|
16
|
-
error?: string;
|
|
25
|
+
outputs: VariantResult[];
|
|
17
26
|
}
|
|
18
27
|
export interface BatchEnhanceResult {
|
|
19
28
|
results: ItemResult[];
|
|
@@ -25,10 +34,16 @@ export type ProgressCallback = (update: {
|
|
|
25
34
|
current: number;
|
|
26
35
|
total: number;
|
|
27
36
|
videoName: string;
|
|
37
|
+
variant?: Variant;
|
|
28
38
|
status: ItemStatus;
|
|
29
39
|
outputPath?: string;
|
|
30
40
|
error?: string;
|
|
31
41
|
}) => void;
|
|
32
|
-
export declare function enhanceVideo(item: EnhanceItem, options: EnhanceOptions
|
|
42
|
+
export declare function enhanceVideo(item: EnhanceItem, options: EnhanceOptions, onProgress?: (update: {
|
|
43
|
+
variant?: Variant;
|
|
44
|
+
status: ItemStatus;
|
|
45
|
+
outputPath?: string;
|
|
46
|
+
error?: string;
|
|
47
|
+
}) => void): Promise<ItemResult>;
|
|
33
48
|
export declare function enhanceBatch(items: EnhanceItem[], options: EnhanceOptions, onProgress?: ProgressCallback): Promise<BatchEnhanceResult>;
|
|
34
49
|
//# sourceMappingURL=enhancer.d.ts.map
|
package/dist/enhancer.js
CHANGED
|
@@ -39,41 +39,65 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const extractor_1 = require("./extractor");
|
|
41
41
|
const elevenlabs_1 = require("./elevenlabs");
|
|
42
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
43
|
+
function resolveVariants(item) {
|
|
44
|
+
const withIsolation = item.withIsolation ?? true;
|
|
45
|
+
const withoutIsolation = item.withoutIsolation ?? true;
|
|
46
|
+
const variants = [];
|
|
47
|
+
if (withIsolation)
|
|
48
|
+
variants.push("isolated");
|
|
49
|
+
if (withoutIsolation)
|
|
50
|
+
variants.push("direct");
|
|
51
|
+
return variants;
|
|
52
|
+
}
|
|
53
|
+
function variantOutputPath(outputDir, videoName, variant) {
|
|
54
|
+
const suffix = variant === "isolated" ? "isolated" : "direct";
|
|
55
|
+
return path.join(outputDir, `${videoName}-${suffix}.mp3`);
|
|
56
|
+
}
|
|
42
57
|
// ─── Enhance a single video ────────────────────────────────────────────────
|
|
43
|
-
async function enhanceVideo(item, options) {
|
|
58
|
+
async function enhanceVideo(item, options, onProgress) {
|
|
44
59
|
const videoName = path.basename(item.videoPath, path.extname(item.videoPath));
|
|
45
|
-
const
|
|
60
|
+
const variants = resolveVariants(item);
|
|
61
|
+
if (variants.length === 0) {
|
|
62
|
+
throw new Error(`No variants enabled for ${videoName}: at least one of withIsolation or withoutIsolation must be true`);
|
|
63
|
+
}
|
|
64
|
+
if (!fs.existsSync(options.outputDir)) {
|
|
65
|
+
fs.mkdirSync(options.outputDir, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
const outputs = [];
|
|
46
68
|
let tempAudioPath = null;
|
|
47
69
|
try {
|
|
48
|
-
// Step 1: Extract audio from video
|
|
70
|
+
// Step 1: Extract audio from video (shared by all variants)
|
|
71
|
+
onProgress?.({ status: "extracting" });
|
|
49
72
|
tempAudioPath = await (0, extractor_1.extractAudio)(item.videoPath, options.ffmpegPath);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
const extractedBuffer = fs.readFileSync(tempAudioPath);
|
|
74
|
+
// Step 2: Run each variant
|
|
75
|
+
for (const variant of variants) {
|
|
76
|
+
const outputPath = variantOutputPath(options.outputDir, videoName, variant);
|
|
77
|
+
try {
|
|
78
|
+
let voiceInput;
|
|
79
|
+
if (variant === "isolated") {
|
|
80
|
+
onProgress?.({ variant, status: "isolating" });
|
|
81
|
+
voiceInput = await (0, elevenlabs_1.isolateAudio)(tempAudioPath, options.apiKey);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
voiceInput = extractedBuffer;
|
|
85
|
+
}
|
|
86
|
+
onProgress?.({ variant, status: "voice-changing" });
|
|
87
|
+
const finalAudio = await (0, elevenlabs_1.changeVoice)(voiceInput, item.voiceId, options.apiKey);
|
|
88
|
+
fs.writeFileSync(outputPath, finalAudio);
|
|
89
|
+
outputs.push({ variant, outputPath, status: "success" });
|
|
90
|
+
onProgress?.({ variant, status: "success", outputPath });
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const errorMsg = err?.message || String(err);
|
|
94
|
+
outputs.push({ variant, outputPath, status: "error", error: errorMsg });
|
|
95
|
+
onProgress?.({ variant, status: "error", error: errorMsg });
|
|
96
|
+
}
|
|
57
97
|
}
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
item,
|
|
61
|
-
videoName,
|
|
62
|
-
outputPath,
|
|
63
|
-
status: "success",
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
return {
|
|
68
|
-
item,
|
|
69
|
-
videoName,
|
|
70
|
-
outputPath,
|
|
71
|
-
status: "error",
|
|
72
|
-
error: err?.message || String(err),
|
|
73
|
-
};
|
|
98
|
+
return { item, videoName, outputs };
|
|
74
99
|
}
|
|
75
100
|
finally {
|
|
76
|
-
// Clean up temp file
|
|
77
101
|
if (tempAudioPath && fs.existsSync(tempAudioPath)) {
|
|
78
102
|
try {
|
|
79
103
|
fs.unlinkSync(tempAudioPath);
|
|
@@ -93,53 +117,27 @@ async function enhanceBatch(items, options, onProgress) {
|
|
|
93
117
|
for (let i = 0; i < items.length; i++) {
|
|
94
118
|
const item = items[i];
|
|
95
119
|
const videoName = path.basename(item.videoPath, path.extname(item.videoPath));
|
|
96
|
-
// Notify: extracting
|
|
97
|
-
onProgress?.({
|
|
98
|
-
current: i + 1,
|
|
99
|
-
total: items.length,
|
|
100
|
-
videoName,
|
|
101
|
-
status: "extracting",
|
|
102
|
-
});
|
|
103
|
-
let tempAudioPath = null;
|
|
104
120
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
status: "isolating",
|
|
113
|
-
});
|
|
114
|
-
// Step 2: Isolate voice
|
|
115
|
-
const isolatedAudio = await (0, elevenlabs_1.isolateAudio)(tempAudioPath, options.apiKey);
|
|
116
|
-
// Notify: voice changing
|
|
117
|
-
onProgress?.({
|
|
118
|
-
current: i + 1,
|
|
119
|
-
total: items.length,
|
|
120
|
-
videoName,
|
|
121
|
-
status: "voice-changing",
|
|
122
|
-
});
|
|
123
|
-
// Step 3: Voice change
|
|
124
|
-
const finalAudio = await (0, elevenlabs_1.changeVoice)(isolatedAudio, item.voiceId, options.apiKey);
|
|
125
|
-
// Save
|
|
126
|
-
const outputPath = path.join(options.outputDir, `${videoName}.mp3`);
|
|
127
|
-
fs.writeFileSync(outputPath, finalAudio);
|
|
128
|
-
successCount++;
|
|
129
|
-
results.push({ item, videoName, outputPath, status: "success" });
|
|
130
|
-
onProgress?.({
|
|
131
|
-
current: i + 1,
|
|
132
|
-
total: items.length,
|
|
133
|
-
videoName,
|
|
134
|
-
status: "success",
|
|
135
|
-
outputPath,
|
|
121
|
+
const result = await enhanceVideo(item, options, (update) => {
|
|
122
|
+
onProgress?.({
|
|
123
|
+
current: i + 1,
|
|
124
|
+
total: items.length,
|
|
125
|
+
videoName,
|
|
126
|
+
...update,
|
|
127
|
+
});
|
|
136
128
|
});
|
|
129
|
+
results.push(result);
|
|
130
|
+
const allOk = result.outputs.every((o) => o.status === "success");
|
|
131
|
+
if (allOk)
|
|
132
|
+
successCount++;
|
|
133
|
+
else
|
|
134
|
+
errorCount++;
|
|
137
135
|
}
|
|
138
136
|
catch (err) {
|
|
139
|
-
|
|
140
|
-
const outputPath = path.join(options.outputDir, `${videoName}.mp3`);
|
|
137
|
+
// Pre-variant failure (e.g., extraction failed, no variants enabled)
|
|
141
138
|
const errorMsg = err?.message || String(err);
|
|
142
|
-
|
|
139
|
+
errorCount++;
|
|
140
|
+
results.push({ item, videoName, outputs: [] });
|
|
143
141
|
onProgress?.({
|
|
144
142
|
current: i + 1,
|
|
145
143
|
total: items.length,
|
|
@@ -148,14 +146,6 @@ async function enhanceBatch(items, options, onProgress) {
|
|
|
148
146
|
error: errorMsg,
|
|
149
147
|
});
|
|
150
148
|
}
|
|
151
|
-
finally {
|
|
152
|
-
if (tempAudioPath && fs.existsSync(tempAudioPath)) {
|
|
153
|
-
try {
|
|
154
|
-
fs.unlinkSync(tempAudioPath);
|
|
155
|
-
}
|
|
156
|
-
catch { }
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
149
|
}
|
|
160
150
|
return { results, totalCount: items.length, successCount, errorCount };
|
|
161
151
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { extractAudio } from "./extractor";
|
|
2
2
|
export { isolateAudio, changeVoice } from "./elevenlabs";
|
|
3
3
|
export { enhanceVideo, enhanceBatch } from "./enhancer";
|
|
4
|
-
export type { EnhanceOptions, EnhanceItem, ItemStatus, ItemResult, BatchEnhanceResult, ProgressCallback, } from "./enhancer";
|
|
4
|
+
export type { EnhanceOptions, EnhanceItem, ItemStatus, ItemResult, VariantResult, Variant, BatchEnhanceResult, ProgressCallback, } from "./enhancer";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED