const express = require('express');
const WebSocket = require('ws');
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const ffmpeg = require('fluent-ffmpeg');
const app = express();
const PORT = 7860;
// Konfigurasi TTS
const TRUSTED_CLIENT_TOKEN = "6A5AA1D4EAFF4E9FB37E23D68491D6F4";
const GEC_VERSION = "1-143.0.3650.75";
const WSS_URL = "wss://speech.text-to-speech.online/consumer/speech/synthesize/readaloud/edge/v1";
// Pastikan folder temp ada
const tempDir = path.join(__dirname, 'temp');
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir);
/**
* RE Logics
*/
function generateMSHash() {
const WIN_EPOCH = 11644473600n;
const S_TO_NS = 1000000000n;
let ticks = BigInt(Math.floor(Date.now() / 1000));
ticks += WIN_EPOCH;
ticks -= (ticks % 300n);
ticks *= (S_TO_NS / 100n);
const strToHash = `${ticks}${TRUSTED_CLIENT_TOKEN}`;
return crypto.createHash('sha256').update(strToHash).digest('hex').toUpperCase();
}
function createGuid() {
return crypto.randomBytes(16).toString('hex').toUpperCase();
}
/**
* Core TTS Synthesis
*/
function synthesize(text, voice, speed) {
return new Promise((resolve, reject) => {
const requestId = createGuid();
const connectionId = createGuid();
const gec = generateMSHash();
const fullUrl = `${WSS_URL}?TrustedClientToken=${TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${gec}&Sec-MS-GEC-Version=${GEC_VERSION}&Authorization=bearer%20undefined&ConnectionId=${connectionId}`;
const ws = new WebSocket(fullUrl, {
origin: 'https://www.text-to-speech.online',
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36' }
});
let audioChunks = [];
const ttsRate = speed >= 1 ? `+${(speed - 1) * 100}%` : `-${(1 - speed) * 100}%`;
ws.on('open', () => {
ws.send(`Path: speech.config\r\nX-RequestId: ${requestId}\r\nX-Timestamp: ${new Date().toISOString()}\r\nContent-Type: application/json\r\n\r\n{"context":{"system":{"name":"SpeechSDK","version":"1.19.0","build":"JavaScript","lang":"JavaScript"},"os":{"platform":"Browser/Win32","name":"Chrome","version":"120.0.0.0"}}}`);
ws.send(`Path: synthesis.context\r\nX-RequestId: ${requestId}\r\nX-Timestamp: ${new Date().toISOString()}\r\nContent-Type: application/json\r\n\r\n{"synthesis":{"audio":{"metadataOptions":{"bookmarkEnabled":false,"sentenceBoundaryEnabled":false,"visemeEnabled":false,"wordBoundaryEnabled":false},"outputFormat":"audio-24khz-48kbitrate-mono-mp3"},"language":{"autoDetection":false}}}`);
ws.send(`Path: ssml\r\nX-RequestId: ${requestId}\r\nX-Timestamp: ${new Date().toISOString()}\r\nContent-Type: application/ssml+xml\r\n\r\n