199 lines
5.4 KiB
TypeScript
199 lines
5.4 KiB
TypeScript
import { GlobalFunctions, TransferAreaProperties } from '@/GlobalFunctions';
|
|
import { apiService } from './api';
|
|
|
|
/**
|
|
* Tipos de vozes disponíveis para geração de áudio
|
|
*/
|
|
export type VoiceType = 'alloy' | 'echo' | 'fable' | 'nova' | 'onyx' | 'shimmer';
|
|
|
|
/**
|
|
* Interface para a resposta da API de geração de áudio
|
|
*/
|
|
export interface AudioGenerationResponse {
|
|
success: boolean;
|
|
audio_url: string; // URL do áudio gerado
|
|
audio_generation_id: string;
|
|
message: string; // Texto que foi convertido em áudio
|
|
}
|
|
|
|
/**
|
|
* Interface para os dados necessários para geração de áudio
|
|
*/
|
|
export interface AudioGenerationRequest {
|
|
message: string;
|
|
voice: VoiceType;
|
|
userEmail?: string;
|
|
estabelecimentoId?: number;
|
|
}
|
|
|
|
/**
|
|
* Informações sobre cada tipo de voz disponível
|
|
*/
|
|
export const VOICE_OPTIONS = {
|
|
alloy: {
|
|
label: "Alloy",
|
|
gender: "Masculina",
|
|
style: "Neutra, equilibrada, tom corporativo",
|
|
description: "Boa para tutoriais e comunicações institucionais."
|
|
},
|
|
echo: {
|
|
label: "Echo",
|
|
gender: "Masculina",
|
|
style: "Forte e profissional, mais grave",
|
|
description: "Ideal para voz de autoridade ou locução firme."
|
|
},
|
|
fable: {
|
|
label: "Fable",
|
|
gender: "Feminina",
|
|
style: "Narrativa, calorosa e envolvente",
|
|
description: "Ótima para storytelling e áudios empáticos."
|
|
},
|
|
onyx: {
|
|
label: "Onyx",
|
|
gender: "Masculina",
|
|
style: "Grave, autoritária, impactante",
|
|
description: "Excelente para trailers, mensagens sérias ou institucionais."
|
|
},
|
|
nova: {
|
|
label: "Nova",
|
|
gender: "Feminina",
|
|
style: "Brilhante, animada, energética",
|
|
description: "Boa para vídeos curtos, marketing ou conteúdos leves."
|
|
},
|
|
shimmer: {
|
|
label: "Shimmer",
|
|
gender: "Feminina",
|
|
style: "Suave, otimista, clara",
|
|
description: "Boa para mensagens acolhedoras, explicações e IA conversacional."
|
|
}
|
|
} as const;
|
|
|
|
/**
|
|
* Serviço de geração de áudio (Text-to-Speech)
|
|
*/
|
|
class AudioGenerationService {
|
|
private readonly AUDIO_GENERATION_ENDPOINT = '/webhook/codex/gerar_audio';
|
|
|
|
/**
|
|
* Gera um arquivo de áudio a partir de texto
|
|
*
|
|
* @param request - Dados da requisição (texto, voz, email, estabelecimento)
|
|
* @returns Promise com a resposta da API
|
|
*/
|
|
async generateAudio(request: AudioGenerationRequest): Promise<AudioGenerationResponse> {
|
|
const { message, voice, userEmail, estabelecimentoId } = request;
|
|
|
|
// Usa valores do .env se não forem fornecidos
|
|
const email = GlobalFunctions.getTransferProperty(TransferAreaProperties.UsuarioEmail);//userEmail || import.meta.env.VITE_USER_EMAIL || '';
|
|
const estabId = GlobalFunctions.getTransferProperty(TransferAreaProperties.EstabelecimentoCodigo);//estabelecimentoId || parseInt(import.meta.env.VITE_ESTABELECIMENTO_ID) || 1;
|
|
|
|
// Valida o texto
|
|
if (!message || message.trim().length === 0) {
|
|
throw {
|
|
success: false,
|
|
message: 'O texto não pode estar vazio',
|
|
};
|
|
}
|
|
|
|
// Valida a voz
|
|
if (!this.isValidVoice(voice)) {
|
|
throw {
|
|
success: false,
|
|
message: `Voz inválida. Opções disponíveis: ${Object.keys(VOICE_OPTIONS).join(', ')}`,
|
|
};
|
|
}
|
|
|
|
// Log para debug (remover em produção se necessário)
|
|
console.log('Gerando áudio:', {
|
|
messageLength: message.length,
|
|
voice,
|
|
userEmail: email,
|
|
estabelecimentoId: estabId,
|
|
});
|
|
|
|
try {
|
|
// Faz a requisição usando o serviço de API
|
|
const response = await apiService.post<AudioGenerationResponse>(
|
|
this.AUDIO_GENERATION_ENDPOINT,
|
|
{
|
|
estabelecimento_id: estabId,
|
|
user_email: email,
|
|
message: message,
|
|
voice: voice,
|
|
}
|
|
);
|
|
|
|
return response.data;
|
|
} catch (error: any) {
|
|
// Trata erros específicos
|
|
console.error('Erro na geração de áudio:', error);
|
|
|
|
throw {
|
|
success: false,
|
|
message: error.message || 'Erro ao gerar áudio',
|
|
status: error.status,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Valida se a voz selecionada é suportada
|
|
*
|
|
* @param voice - Voz a ser validada
|
|
* @returns true se a voz é válida
|
|
*/
|
|
isValidVoice(voice: string): voice is VoiceType {
|
|
return Object.keys(VOICE_OPTIONS).includes(voice);
|
|
}
|
|
|
|
/**
|
|
* Obtém informações sobre uma voz específica
|
|
*
|
|
* @param voice - Tipo de voz
|
|
* @returns Informações da voz
|
|
*/
|
|
getVoiceInfo(voice: VoiceType) {
|
|
return VOICE_OPTIONS[voice];
|
|
}
|
|
|
|
/**
|
|
* Lista todas as vozes disponíveis
|
|
*
|
|
* @returns Array com todas as opções de voz
|
|
*/
|
|
getAllVoices() {
|
|
return Object.entries(VOICE_OPTIONS).map(([key, info]) => ({
|
|
value: key as VoiceType,
|
|
...info,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Valida o texto para geração de áudio
|
|
*
|
|
* @param text - Texto a ser validado
|
|
* @param maxLength - Comprimento máximo (padrão: 4096 caracteres)
|
|
* @returns Objeto com resultado da validação
|
|
*/
|
|
validateText(text: string, maxLength: number = 4096): { valid: boolean; error?: string } {
|
|
if (!text || text.trim().length === 0) {
|
|
return {
|
|
valid: false,
|
|
error: 'O texto não pode estar vazio',
|
|
};
|
|
}
|
|
|
|
if (text.length > maxLength) {
|
|
return {
|
|
valid: false,
|
|
error: `O texto é muito longo. Máximo: ${maxLength} caracteres`,
|
|
};
|
|
}
|
|
|
|
return { valid: true };
|
|
}
|
|
}
|
|
|
|
// Exporta instância única (Singleton)
|
|
export const audioGenerationService = new AudioGenerationService();
|