124 lines
3.8 KiB
TypeScript
124 lines
3.8 KiB
TypeScript
import fs from "fs";
|
|
import randomstring from "randomstring";
|
|
|
|
const TEMP_DIR = "./temp-tts";
|
|
|
|
const sub_name = randomstring.generate({ length: 5, charset: 'alphanumeric' });
|
|
|
|
const HOST = "https://office4-chatterbox.wibudev.com";
|
|
|
|
async function fetchRetry(url: string, options: any = {}, retries = 3) {
|
|
for (let i = 0; i < retries; i++) {
|
|
try {
|
|
return await fetch(url, options);
|
|
} catch (err) {
|
|
console.warn(`Fetch attempt ${i + 1} failed:`, err);
|
|
if (i === retries - 1) throw err;
|
|
await new Promise((r) => setTimeout(r, 1000 * (i + 1)));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
async function fetchResult(jobId: string, partIndex: number) {
|
|
const res = await fetchRetry(`${HOST}/result/${jobId}`);
|
|
|
|
if (!res) {
|
|
throw new Error("Failed to fetch result");
|
|
}
|
|
|
|
const type = res.headers.get("content-type") || "";
|
|
|
|
if (type.includes("application/json")) {
|
|
try {
|
|
return await res.json();
|
|
} catch {
|
|
throw new Error("Invalid JSON response on fetchResult");
|
|
}
|
|
}
|
|
|
|
// Audio response
|
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
|
|
if (buf.length < 44) {
|
|
throw new Error("Invalid WAV: too small");
|
|
}
|
|
|
|
const file = `${TEMP_DIR}/${jobId}_${sub_name}_${(partIndex + 1).toString().padStart(4, "0")}.wav`;
|
|
fs.writeFileSync(file, buf);
|
|
return { status: "done", filePath: file };
|
|
}
|
|
|
|
export async function main() {
|
|
const jobsPath = TEMP_DIR + "/jobs.json";
|
|
if (!fs.existsSync(jobsPath)) {
|
|
console.error("❌ jobs.json not found");
|
|
process.exit(1);
|
|
}
|
|
|
|
// POLLING SEMUA
|
|
const jobs = JSON.parse(fs.readFileSync(jobsPath, "utf-8"));
|
|
console.log("⏳ Waiting for processing...");
|
|
const resultFiles: string[] = [];
|
|
|
|
for (let i = 0; i < jobs.length; i++) {
|
|
const jobId = jobs[i];
|
|
let status = "processing";
|
|
let delay = 5000;
|
|
let attempts = 0;
|
|
|
|
while (status === "processing" || status === "pending") {
|
|
try {
|
|
const res = await fetchResult(jobId as string, i);
|
|
status = res.status;
|
|
|
|
if (status === "done" && res.filePath) {
|
|
resultFiles.push(res.filePath as string);
|
|
console.log(` ✓ Job ${i + 1}/${jobs.length} completed: ${jobId}`);
|
|
|
|
const resFile = await fetchRetry(`${HOST}/file/${jobId}`);
|
|
if (!resFile) {
|
|
throw new Error("Failed to fetch file");
|
|
}
|
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
|
|
if (buf.length < 44) {
|
|
throw new Error("Invalid WAV: too small");
|
|
}
|
|
|
|
const file = `${TEMP_DIR}/${jobId}_${sub_name}_${(i + 1).toString().padStart(4, "0")}.wav`;
|
|
fs.writeFileSync(file, buf);
|
|
|
|
break;
|
|
} else if (status === "error") {
|
|
console.error(
|
|
` ❌ Job ${i + 1}/${jobs.length} failed: ${res.error || "Unknown error"}`
|
|
);
|
|
throw new Error(`Job ${jobId} failed`);
|
|
}
|
|
} catch (error) {
|
|
console.error(`❌ Error fetching result for job ${i + 1}:`, error);
|
|
process.exit(1);
|
|
}
|
|
|
|
attempts++;
|
|
await new Promise((r) => setTimeout(r, delay));
|
|
delay = Math.min(delay * 1.2, 5000);
|
|
|
|
// Timeout after 2 minutes
|
|
if (attempts > 80) {
|
|
console.error(`❌ Job ${jobId} timeout after 2 minutes`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log("🎉 DONE!");
|
|
return resultFiles;
|
|
}
|
|
|
|
if (import.meta.main) {
|
|
main().then((resultFiles) => {
|
|
console.log("Result files:", resultFiles);
|
|
});
|
|
} |