Files
jenna-tools/xpooling.ts
bipproduction 822b68c10f tambahannya
2025-12-07 09:00:54 +08:00

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);
});
}