Primeiro Commit
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
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 = userEmail || import.meta.env.VITE_USER_EMAIL || '';
|
||||
const estabId = 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();
|
||||
Reference in New Issue
Block a user