@reverieit/reverie-client
Version:
Reverie's API access through node package manager
713 lines (612 loc) • 23 kB
JavaScript
/*!
* reverie-client
* Copyright(c) 2025 Reverie Language Technologies
* MIT Licensed
*/
'use strict'
function isEditable(element) {
if (!element) return false;
// Check if it's an input or textarea
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
return true;
}
// Check if contenteditable is set to true
if (element.isContentEditable) {
return true;
}
return false;
}
function voiceText(event, callback, element, lang) {
let stt_text = event.data;
if (element) {
if (isEditable(element)) {
// Start voice recognition
if (event.event === "FINAL_RESULT") {
document.execCommand('insertText', false, stt_text);
} else if (event.event === "PARTIAL_RESULTS") {
if (["es-ES", "fr-FR", "ar-SA"].includes(lang)) {
document.execCommand('insertText', false, stt_text);
}
}
}
}
if (!element || isEditable(element)) {
if (callback) {
let stt_event = { ...event }
callback({ stt_event })
}
}
}
class ReverieClient {
constructor(parameters) {
this.apiKey = parameters.apiKey || 'REVERIE_API_KEY';
this.appId = parameters.appId || 'REVERIE_APP_ID';
this.baseUrl = parameters.baseUrl || 'https://revapi.reverieinc.com';
this.isListening = false;
let script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/reverie-stt-sdk/dist/bundle.js';
document.body.appendChild(script);
}
async transliterate({ text, src_lang, tgt_lang, domain = 1 }) {
try {
if (!src_lang) {
throw new Error('Source language is required');
return null;
}
if (!tgt_lang) {
throw new Error('Target language is required');
return null;
}
if (!text) {
throw new Error('Text to transliterate is required');
return null;
}
let resp = await fetch(this.baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"REV-API-KEY": this.apiKey,
"REV-APP-ID": this.appId,
"src_lang": src_lang,
"tgt_lang": tgt_lang,
"domain": domain,
"cnt_lang": "en",
"REV-APPNAME": "transliteration",
},
body: JSON.stringify({
data: [text],
isBulk: false,
ignoreTaggedEntities: false
})
});
resp = await resp.json();
if (resp && resp.responseList && resp.responseList.length > 0) {
return resp.responseList[0].outString[0];
}
else {
throw new Error("Failed to transliterate text");
return null;
}
}
catch (error) {
return error.message;
}
}
async analyze_text({ text, src_lang, tgt_lang, translation_domain, moderation_types }) {
try {
if (!src_lang) {
throw new Error('Content language is required');
}
if (!text) {
throw new Error('Text is required for text analysis');
}
const url = `${this.baseUrl}/api/v2/text-analyse?translate=true&summary=true&sentiment=false&detect_entities=true&content_safety=true&pii_redaction=true`;
const headers = {
'Content-Type': 'application/json',
'REV-API-KEY': this.apiKey,
'REV-APP-ID': this.appId,
'REV-APPNAME': 'text-analysis'
};
const data = {
"text": text,
"language": src_lang,
"pii_redaction": {
"redact_pii_sub": "entity_name",
"redact_pii_types": []
},
"summary": {
"summary_model": "gemma2:2b",
"summary_type": "gist"
},
"entity_recognition": {
"entity_types": []
},
"sentiment": {
"level": "whole"
},
"content_moderation": {
"moderation_types": moderation_types || [
"hate_speech",
"profanity"
]
}
};
if (tgt_lang) {
data["traslation"] = {
"target_language": tgt_lang,
"translation_domain": translation_domain || "generic"
}
}
let response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
})
response = await response.json();
if (response && response.results) {
return response.results;
}
else {
throw new Error("Failed to analyze text");
}
} catch (error) {
return error.message;
}
}
async identify_language_by_text({ text }) {
try {
if (!text) {
throw new Error('Text to identify is required');
}
if (text.length > 512) {
throw new Error('Text exceeds maximum length of 512 characters');
}
let resp = await fetch(this.baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"REV-API-KEY": this.apiKey,
"REV-APP-ID": this.appId,
"REV-APPNAME": "lang_id_text",
},
body: JSON.stringify({
"text": text,
"max_length": Math.pow(2, Math.floor(Math.sqrt(text.length)) + 1)
})
});
resp = await resp.json();
if (resp) {
return resp;
}
else {
throw new Error("Failed to detect text");
}
}
catch (error) {
return error.message;
}
}
async translate({ text, src_lang, tgt_lang, domain }) {
try {
if (!src_lang) {
throw new Error('Source language is required');
return null;
}
if (!tgt_lang) {
throw new Error('Target language is required');
return null;
}
if (!text) {
throw new Error('Text to translate is required');
return null;
}
let resp = await fetch(this.baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"REV-API-KEY": this.apiKey,
"REV-APP-ID": this.appId,
"src_lang": src_lang,
"tgt_lang": tgt_lang,
'domain': domain || 'generic',
'REV-APPNAME': 'localization',
'REV-APPVERSION': '3.0'
},
body: JSON.stringify({
data: [text],
"nmtMask": true,
"nmtMaskTerms": {},
"enableNmt": true,
"enableLookup": true
})
});
resp = await resp.json();
if (resp && resp.responseList && resp.responseList.length > 0) {
return resp.responseList[0].outString;
}
else {
throw new Error("Failed to translate text");
return null;
}
}
catch (error) {
return error.message;
}
}
async stt_file({ audioFile, src_lang, domain }) {
try {
if (!src_lang) {
throw new Error('Source language is required');
return null;
}
if (!audioFile) {
throw new Error('File to transcribe is required');
}
const formData = new FormData();
formData.append("audio_file", audioFile);
let resp = await fetch("https://revapi.reverieinc.com/", {
method: "POST",
headers: {
"src_lang": src_lang,
"domain": domain || "generic",
"REV-APPNAME": "stt_file",
"REV-API-KEY": this.apiKey,
"REV-APP-ID": this.appId,
},
body: formData
});
resp = await resp.json();
if (resp && resp.display_text) {
return resp.display_text;
}
else {
throw new Error("Failed to transcribe text");
}
}
catch (error) {
return error.message;
}
}
async stt_batch({ audioFile, language, subtitles }) {
return await this.transcribeAudio({ audioFile, language, subtitles });
}
async text_to_speech({ text, speaker, speed, pitch, format = 'WAV' }) {
try {
if (!speaker) {
throw new Error('Speaker is required for TTS');
}
if (!text) {
throw new Error('Text is required for TTS');
}
if (!['WAV', 'MP3'].includes(format)) {
throw new Error('Invalid audio format. Supported formats are wav and mp3');
}
const requestData = {
text,
speed: parseFloat(speed),
pitch: parseFloat(pitch),
format: "WAV",
speaker
};
let resp = await fetch(this.baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"REV-API-KEY": this.apiKey,
"REV-APP-ID": this.appId,
"REV-APPNAME": "tts",
"speaker": speaker,
},
body: JSON.stringify(requestData)
});
let blob = await resp.blob();
if (resp) {
return blob;
}
else {
throw new Error("Failed to do tts for the text");
}
}
catch (error) {
return error;
}
}
async init_stt({ src_lang, domain, silence, continuous, logging, timeout, callback, element }) {
if (!src_lang) {
throw new Error('Source language is required for STT');
}
if (!callback && !element) {
throw new Error('Callback or element is required for STT');
}
if (callback && typeof callback !== 'function') {
throw new Error('Callback must be a function');
}
if (element && typeof element !== 'object') {
throw new Error('Element must be a DOM element');
}
if (domain && typeof domain !== 'string') {
throw new Error('Domain must be a string');
}
if (silence && typeof silence !== 'number') {
throw new Error('Silence must be a number');
}
if (continuous && typeof continuous !== 'boolean') {
throw new Error('Continuous must be a boolean');
}
if (logging && typeof logging !== 'boolean') {
throw new Error('Logging must be a boolean');
}
if (timeout && typeof timeout !== 'number') {
throw new Error('Timeout must be a number');
}
try {
await window.stt_stream.initSTT({
apikey: this.apiKey,
appId: this.appId,
language: src_lang,
domain: domain || "generic",
silence: silence || 1,
continuous: continuous || 1,
logging: logging && logging === false ? false : true,
timeout: timeout || 180,
eventHandler: (event) => voiceText(event, callback, element, src_lang),
errorHandler: (error) => console.error('Error:', error),
});
console.log('STT Streaming initialized successfully');
} catch (error) {
console.error('STT Initialization failed:', error);
}
}
async start_stt() {
await window.stt_stream.startSTT();
this.isListening = true;
}
async stop_stt() {
await window.stt_stream.stopSTT();
this.isListening = false;
}
async toggle_stt() {
if (this.isListening) {
await this.stop_stt();
} else {
await this.start_stt();
}
}
async translateDocument({ sourceLanguage, targetLanguage, uploadedFile }) {
try {
const formData = new FormData();
formData.append('sourceLanguage', sourceLanguage || 'english');
formData.append('targetLanguage', targetLanguage || 'hindi');
formData.append('projectFiles', uploadedFile);
const response = await fetch('https://revapi.reverieinc.com/translate_doc_import', {
method: 'POST',
headers: {
'REV-APP-ID': this.appId,
'REV-APPNAME': 'doc_translation',
'REV-API-KEY': this.apiKey
},
body: formData
});
const data = await response.json();
if (response.ok && data.projectId) {
return this.checkStatus(data.projectId, targetLanguage);
} else {
throw new Error('Error in translation process');
}
} catch (error) {
throw new Error('Translation failed: ' + error.message);
}
}
async translate_document({ sourceLanguage, targetLanguage, uploadedFile }) {
return await this.translateDocument({ sourceLanguage, targetLanguage, uploadedFile });
}
async checkStatus(docId, targetLanguage) {
return new Promise((resolve, reject) => {
const pollStatus = async () => {
try {
const response = await fetch(`https://revapi.reverieinc.com/translate_doc_status?doc_id=${docId}`, {
headers: {
'REV-APP-ID': this.appId,
'REV-APPNAME': 'doc_translation',
'REV-API-KEY': this.apiKey
}
});
const statusData = await response.json();
if (statusData.success && statusData.message === 'completed') {
resolve(this.downloadTranslatedFile(docId, targetLanguage));
} else {
setTimeout(pollStatus, 2000);
}
} catch (error) {
reject(new Error('Error checking status: ' + error.message));
}
};
pollStatus();
});
}
async check_status(docId, targetLanguage) {
return await this.checkStatus(docId, targetLanguage);
}
async downloadTranslatedFile(docId, targetLanguage) {
try {
const response = await fetch('https://revapi.reverieinc.com/translate_doc_export', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'REV-APP-ID': this.appId,
'REV-APPNAME': 'doc_translation',
'REV-API-KEY': this.apiKey
},
body: JSON.stringify({ unitId: docId, targetLanguages: [targetLanguage] })
});
const responseData = await response.json();
if (responseData.success) {
Object.values(responseData.data.targetURLS).forEach(languages => {
Object.values(languages).forEach(fileURL => {
window.open(fileURL, '_blank');
});
});
return 'Translation complete! File is ready for download.';
} else {
throw new Error('Error downloading translation');
}
} catch (error) {
throw new Error('Download failed: ' + error.message);
}
}
async download_translated_file(docId, targetLanguage) {
return await this.downloadTranslatedFile(docId, targetLanguage);
}
async uploadDocument({ file, file_type, languages, ocr_type }) {
try {
if (!file) {
throw new Error('Please select a file');
}
if (!file_type) {
file_type = 'txt';
}
if (!languages) {
throw new Error("Languages parameter is required");
}
if (!ocr_type || (ocr_type !== 'layout_ocr' && ocr_type !== 'only_ocr')) {
throw new Error("Invalid ocr_type. Must be 'layout_ocr' or 'only_ocr'");
}
if (file_type === 'img' && ocr_type === 'layout_ocr') {
throw new Error('Layout OCR is not supported for images. Please select Only OCR.');
}
const formData = new FormData();
formData.append('file', file);
formData.append('file_type', file_type);
formData.append('languages', languages);
formData.append('ocr_type', ocr_type);
let response = await fetch(this.baseUrl + '/process_doc', {
method: 'POST',
headers: {
'REV-APP-ID': this.appId,
'REV-API-KEY': this.apiKey,
'REV-APPNAME': 'ocr'
},
body: formData
});
if (ocr_type === 'layout_ocr') {
return await response.blob();
} else {
return await response.text();
}
} catch (error) {
return error.message;
}
}
async upload_document({ file, file_type, languages, ocr_type }) {
return await this.uploadDocument({ file, file_type, languages, ocr_type });
}
async uploadAudio({ file }) {
try {
if (!file) throw new Error('Please select an audio file');
const formData = new FormData();
formData.append('audio_file', file);
let response = await fetch(this.baseUrl + '/upload', {
method: 'POST',
headers: {
'REV-API-KEY': this.apiKey,
'REV-APP-ID': this.appId,
'REV-APPNAME': 'slid'
},
body: formData
});
let result = await response.json();
if (!result.success) throw new Error(result.cause);
return {
transcriptionId: result.id,
language: result.language,
confidence: result.confidence
};
} catch (error) {
throw error;
}
}
async upload_audio({ file }) {
return await this.uploadAudio({ file });
}
async transcribeAudio({ language, subtitles, audioFile }) {
if (!language || typeof subtitles === 'undefined' || !audioFile) {
throw new Error('Missing required fields: language, domain, subtitles, or audioFile');
}
try {
const formData = new FormData();
formData.append('file', audioFile);
const response = await fetch(`${this.baseUrl}/upload`, {
method: 'POST',
headers: {
'src_lang': language,
'domain': "generic",
'REV-API-KEY': this.apiKey,
'REV-APPNAME': 'stt_batch',
'REV-APP-ID': this.appId,
'subtitles': subtitles.toString()
},
body: formData
});
const data = await response.json();
if (response.ok && data.job_id) {
return this.checkAudioStatus(data.job_id);
} else {
throw new Error('Upload failed or invalid response');
}
} catch (error) {
throw new Error('Transcription failed: ' + error.message);
}
}
async checkAudioStatus(jobId) {
return new Promise((resolve, reject) => {
const poll = async () => {
try {
const response = await fetch(`${this.baseUrl}/status?job_id=${jobId}`, {
headers: {
'REV-API-KEY': this.apiKey,
'REV-APPNAME': 'stt_batch',
'REV-APP-ID': this.appId
}
});
const status = await response.json();
if (status.status === 'completed') {
resolve(this.getTranscript(jobId));
} else if (status.status === 'failed') {
reject(new Error('Transcription failed on server'));
} else {
setTimeout(poll, 3000);
}
} catch (error) {
reject(new Error('Error checking transcription status: ' + error.message));
}
};
poll();
});
}
async check_audio_status(jobId) {
return await this.checkAudioStatus(jobId);
}
async getTranscript(jobId) {
try {
const response = await fetch(`${this.baseUrl}/transcript?job_id=${jobId}`, {
headers: {
'REV-API-KEY': this.apiKey,
'REV-APPNAME': 'stt_batch',
'REV-APP-ID': this.appId
}
});
const data = await response.json();
if (response.ok && data.result) {
return data.result;
} else {
throw new Error('Transcript not available');
}
} catch (error) {
throw new Error('Error fetching transcript: ' + error.message);
}
}
async get_transcript(jobId) {
return await this.getTranscript(jobId);
}
}
module.exports = ReverieClient