Import aus Monorepo (frischer Start)
All checks were successful
Build & Deploy / deploy (push) Successful in 10s
All checks were successful
Build & Deploy / deploy (push) Successful in 10s
This commit is contained in:
230
src/services/scriptalizer-service.js
Normal file
230
src/services/scriptalizer-service.js
Normal file
@@ -0,0 +1,230 @@
|
||||
const https = require("https");
|
||||
const { parseStringPromise } = require("xml2js");
|
||||
const config = require("../config");
|
||||
|
||||
// API Request Counter (resets daily)
|
||||
const stats = {
|
||||
requestsToday: 0,
|
||||
textsToday: 0,
|
||||
lastResetDate: new Date().toDateString(),
|
||||
};
|
||||
|
||||
/**
|
||||
* Prüft ob der Zähler zurückgesetzt werden muss (neuer Tag)
|
||||
*/
|
||||
function checkAndResetDailyStats() {
|
||||
const today = new Date().toDateString();
|
||||
if (stats.lastResetDate !== today) {
|
||||
console.log(`[Scriptalizer] 📅 New day detected, resetting counters`);
|
||||
console.log(`[Scriptalizer] 📊 Yesterday's stats: ${stats.requestsToday} API calls, ${stats.textsToday} texts processed`);
|
||||
stats.requestsToday = 0;
|
||||
stats.textsToday = 0;
|
||||
stats.lastResetDate = today;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die aktuellen Stats zurück
|
||||
*/
|
||||
function getStats() {
|
||||
checkAndResetDailyStats();
|
||||
return { ...stats };
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruft die Scriptalizer API auf
|
||||
* @param {string} inputText - Text zum Scriptalisieren
|
||||
* @param {string} fontName - Font-Name (z.B. 'PremiumUltra79')
|
||||
* @param {number} errFrequency - Fehlerfrequenz
|
||||
* @returns {Promise<{status: string, outputText: string}>}
|
||||
*/
|
||||
async function callScriptalizer(inputText, fontName, errFrequency = 10) {
|
||||
// Prüfe ob neuer Tag
|
||||
checkAndResetDailyStats();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!config.scriptalizer.licenseKey) {
|
||||
reject(new Error("Scriptalizer License Key fehlt in Konfiguration"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Use x-www-form-urlencoded format
|
||||
const params = new URLSearchParams();
|
||||
params.append("LicenseKey", config.scriptalizer.licenseKey);
|
||||
params.append("FontName", fontName);
|
||||
params.append("ErrFrequency", String(errFrequency));
|
||||
params.append("InputText", inputText);
|
||||
|
||||
const body = params.toString();
|
||||
|
||||
// Check input size (48KB limit)
|
||||
if (Buffer.byteLength(body) > config.scriptalizer.maxInputSize) {
|
||||
reject(
|
||||
new Error(
|
||||
`Input size exceeds 48KB limit (${Buffer.byteLength(body)} bytes)`
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
hostname: "www.scriptalizer.co.uk",
|
||||
port: 443,
|
||||
path: "/QuantumScriptalize.asmx/Scriptalize",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
|
||||
"Content-Length": Buffer.byteLength(body),
|
||||
},
|
||||
timeout: 30000, // 30 second timeout
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let data = "";
|
||||
|
||||
res.on("data", (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on("end", async () => {
|
||||
try {
|
||||
const result = await parseStringPromise(data, {
|
||||
explicitArray: false,
|
||||
});
|
||||
const response = result.ScriptalizerResponse;
|
||||
|
||||
if (!response || response.Status !== "OK") {
|
||||
reject(new Error(response?.Status || "UNKNOWN_STATUS"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Zähler erhöhen bei erfolgreichem Request
|
||||
stats.requestsToday++;
|
||||
console.log(`[Scriptalizer] 📊 API Request #${stats.requestsToday} today completed`);
|
||||
|
||||
resolve({ status: "OK", outputText: response.OutputText });
|
||||
} catch (err) {
|
||||
reject(
|
||||
new Error(`Failed to parse Scriptalizer response: ${err.message}`)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on("error", (err) => {
|
||||
reject(new Error(`Scriptalizer request failed: ${err.message}`));
|
||||
});
|
||||
|
||||
req.on("timeout", () => {
|
||||
req.destroy();
|
||||
reject(new Error("Scriptalizer request timed out"));
|
||||
});
|
||||
|
||||
req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scriptalisiert mehrere Texte in 25er Batches
|
||||
* @param {string[]} texts - Array von Texten
|
||||
* @param {string} font - Font-Key (tilda, alva, ellie)
|
||||
* @param {number} errFrequency - Fehlerfrequenz
|
||||
* @returns {Promise<string[]>} - Array von scriptalisierten Texten
|
||||
*/
|
||||
async function scriptalizeBatch(texts, font = "tilda", errFrequency = 10) {
|
||||
if (!Array.isArray(texts) || texts.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const fontName =
|
||||
config.scriptalizer.fontMap[font.toLowerCase()] ||
|
||||
config.scriptalizer.fontMap.tilda;
|
||||
const separator = config.scriptalizer.separator;
|
||||
const BATCH_SIZE = 18; // 18 Texte pro Scriptalizer API Call (Scriptalizer-API-Limit: 32000 Zeichen pro Call → ca. 1700 Zeichen pro Brief sicher)
|
||||
|
||||
console.log(
|
||||
`[Scriptalizer] Processing ${texts.length} texts in batches of ${BATCH_SIZE}`
|
||||
);
|
||||
|
||||
const allScriptalized = [];
|
||||
|
||||
// Teile in 25er Batches auf
|
||||
for (let i = 0; i < texts.length; i += BATCH_SIZE) {
|
||||
const batch = texts.slice(i, i + BATCH_SIZE);
|
||||
const batchNumber = Math.floor(i / BATCH_SIZE) + 1;
|
||||
const totalBatches = Math.ceil(texts.length / BATCH_SIZE);
|
||||
|
||||
console.log(
|
||||
`[Scriptalizer] Processing batch ${batchNumber}/${totalBatches} (${
|
||||
batch.length
|
||||
} texts, indices ${i}-${i + batch.length - 1})`
|
||||
);
|
||||
|
||||
// Kombiniere Texte mit Separator
|
||||
const combinedText = batch.join(separator);
|
||||
|
||||
try {
|
||||
const result = await callScriptalizer(
|
||||
combinedText,
|
||||
fontName,
|
||||
errFrequency
|
||||
);
|
||||
|
||||
// Trenne Ergebnis wieder
|
||||
const scriptalized = result.outputText.split(separator);
|
||||
|
||||
if (scriptalized.length !== batch.length) {
|
||||
console.warn(
|
||||
`[Scriptalizer] Batch ${batchNumber}: returned ${scriptalized.length} parts, expected ${batch.length}`
|
||||
);
|
||||
}
|
||||
|
||||
allScriptalized.push(...scriptalized);
|
||||
console.log(
|
||||
`[Scriptalizer] Batch ${batchNumber}/${totalBatches} completed successfully`
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(`[Scriptalizer] Batch ${batchNumber} failed:`, err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Textzähler erhöhen
|
||||
stats.textsToday += allScriptalized.length;
|
||||
|
||||
console.log(
|
||||
`[Scriptalizer] All batches completed. Total scriptalized: ${allScriptalized.length}`
|
||||
);
|
||||
console.log(
|
||||
`[Scriptalizer] 📊 Daily stats: ${stats.requestsToday} API calls, ${stats.textsToday} texts processed`
|
||||
);
|
||||
return allScriptalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scriptalisiert einen einzelnen Text
|
||||
* @param {string} text - Text zum Scriptalisieren
|
||||
* @param {string} font - Font-Key (tilda, alva, ellie)
|
||||
* @param {number} errFrequency - Fehlerfrequenz
|
||||
* @returns {Promise<string>} - Scriptalisierter Text
|
||||
*/
|
||||
async function scriptalizeSingle(text, font = "tilda", errFrequency = 10) {
|
||||
const fontName =
|
||||
config.scriptalizer.fontMap[font.toLowerCase()] ||
|
||||
config.scriptalizer.fontMap.tilda;
|
||||
|
||||
try {
|
||||
const result = await callScriptalizer(text, fontName, errFrequency);
|
||||
return result.outputText;
|
||||
} catch (err) {
|
||||
console.error("Scriptalizer single call failed:", err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
scriptalizeBatch,
|
||||
scriptalizeSingle,
|
||||
getStats,
|
||||
};
|
||||
Reference in New Issue
Block a user