voice
This commit is contained in:
parent
4b6e1aa69c
commit
add124970e
3 changed files with 42 additions and 5 deletions
|
|
@ -32,6 +32,7 @@ log(` JWT_SECRET: ${process.env.JWT_SECRET ? 'SET (length: ' + process.env.JWT_
|
||||||
log(` PORT: ${process.env.PORT || 'NOT SET (default: 3000)'}`);
|
log(` PORT: ${process.env.PORT || 'NOT SET (default: 3000)'}`);
|
||||||
log(` HOST: ${process.env.HOST || 'NOT SET (default: 0.0.0.0)'}`);
|
log(` HOST: ${process.env.HOST || 'NOT SET (default: 0.0.0.0)'}`);
|
||||||
log(` CORS_ORIGIN: ${process.env.CORS_ORIGIN || 'NOT SET (default: http://localhost:5173)'}`);
|
log(` CORS_ORIGIN: ${process.env.CORS_ORIGIN || 'NOT SET (default: http://localhost:5173)'}`);
|
||||||
|
log(` VOICE_SERVICE_HOST: ${process.env.VOICE_SERVICE_HOST || 'NOT SET'}`);
|
||||||
|
|
||||||
// Проверяем и исправляем формат DATABASE_URL
|
// Проверяем и исправляем формат DATABASE_URL
|
||||||
if (process.env.DATABASE_URL) {
|
if (process.env.DATABASE_URL) {
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,30 @@ import {
|
||||||
Res,
|
Res,
|
||||||
HttpStatus,
|
HttpStatus,
|
||||||
HttpException,
|
HttpException,
|
||||||
|
Logger,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import type { Response } from 'express';
|
import type { Response } from 'express';
|
||||||
import { VoiceService } from './voice.service';
|
import { VoiceService } from './voice.service';
|
||||||
|
|
||||||
@Controller('voice')
|
@Controller('voice')
|
||||||
export class VoiceController {
|
export class VoiceController {
|
||||||
constructor(private voiceService: VoiceService) {}
|
private readonly logger = new Logger(VoiceController.name);
|
||||||
|
|
||||||
|
constructor(private voiceService: VoiceService) {
|
||||||
|
this.logger.log('VoiceController initialized');
|
||||||
|
}
|
||||||
|
|
||||||
@Post('tts')
|
@Post('tts')
|
||||||
async generateTTS(
|
async generateTTS(
|
||||||
@Body() body: { text: string; voice?: string },
|
@Body() body: { text: string; voice?: string },
|
||||||
@Res() res: Response,
|
@Res() res: Response,
|
||||||
) {
|
) {
|
||||||
|
this.logger.log('POST /voice/tts - Request received');
|
||||||
const { text, voice = 'sarah' } = body;
|
const { text, voice = 'sarah' } = body;
|
||||||
|
this.logger.debug(`Request body: text="${text?.substring(0, 50)}...", voice=${voice}`);
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
|
this.logger.warn('POST /voice/tts - Text is missing');
|
||||||
return res.status(HttpStatus.BAD_REQUEST).json({
|
return res.status(HttpStatus.BAD_REQUEST).json({
|
||||||
error: 'Text is required',
|
error: 'Text is required',
|
||||||
});
|
});
|
||||||
|
|
@ -31,6 +39,7 @@ export class VoiceController {
|
||||||
try {
|
try {
|
||||||
const audioBuffer = await this.voiceService.generateTTS(text, voice);
|
const audioBuffer = await this.voiceService.generateTTS(text, voice);
|
||||||
|
|
||||||
|
this.logger.log(`POST /voice/tts - Success, sending ${audioBuffer.length} bytes`);
|
||||||
res.setHeader('Content-Type', 'audio/mpeg');
|
res.setHeader('Content-Type', 'audio/mpeg');
|
||||||
res.setHeader('Content-Length', audioBuffer.length.toString());
|
res.setHeader('Content-Length', audioBuffer.length.toString());
|
||||||
res.send(audioBuffer);
|
res.send(audioBuffer);
|
||||||
|
|
@ -39,6 +48,7 @@ export class VoiceController {
|
||||||
? error.getStatus()
|
? error.getStatus()
|
||||||
: HttpStatus.INTERNAL_SERVER_ERROR;
|
: HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
const message = error.message || 'Failed to generate speech';
|
const message = error.message || 'Failed to generate speech';
|
||||||
|
this.logger.error(`POST /voice/tts - Error: ${message} (status: ${status})`);
|
||||||
return res.status(status).json({
|
return res.status(status).json({
|
||||||
error: message,
|
error: message,
|
||||||
});
|
});
|
||||||
|
|
@ -50,9 +60,11 @@ export class VoiceController {
|
||||||
@Param('effectType') effectType: string,
|
@Param('effectType') effectType: string,
|
||||||
@Res() res: Response,
|
@Res() res: Response,
|
||||||
) {
|
) {
|
||||||
|
this.logger.log(`GET /voice/effects/${effectType} - Request received`);
|
||||||
try {
|
try {
|
||||||
const audioBuffer = await this.voiceService.getEffect(effectType);
|
const audioBuffer = await this.voiceService.getEffect(effectType);
|
||||||
|
|
||||||
|
this.logger.log(`GET /voice/effects/${effectType} - Success, sending ${audioBuffer.length} bytes`);
|
||||||
res.setHeader('Content-Type', 'audio/mpeg');
|
res.setHeader('Content-Type', 'audio/mpeg');
|
||||||
res.setHeader('Content-Length', audioBuffer.length.toString());
|
res.setHeader('Content-Length', audioBuffer.length.toString());
|
||||||
res.send(audioBuffer);
|
res.send(audioBuffer);
|
||||||
|
|
@ -61,6 +73,7 @@ export class VoiceController {
|
||||||
? error.getStatus()
|
? error.getStatus()
|
||||||
: HttpStatus.INTERNAL_SERVER_ERROR;
|
: HttpStatus.INTERNAL_SERVER_ERROR;
|
||||||
const message = error.message || 'Failed to get sound effect';
|
const message = error.message || 'Failed to get sound effect';
|
||||||
|
this.logger.error(`GET /voice/effects/${effectType} - Error: ${message} (status: ${status})`);
|
||||||
return res.status(status).json({
|
return res.status(status).json({
|
||||||
error: message,
|
error: message,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,30 @@
|
||||||
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
import { Injectable, HttpException, HttpStatus, Logger } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class VoiceService {
|
export class VoiceService {
|
||||||
|
private readonly logger = new Logger(VoiceService.name);
|
||||||
private readonly voiceServiceUrl: string;
|
private readonly voiceServiceUrl: string;
|
||||||
|
|
||||||
constructor(private configService: ConfigService) {
|
constructor(private configService: ConfigService) {
|
||||||
|
this.logger.log('Initializing VoiceService...');
|
||||||
const voiceServiceHost = this.configService.get<string>('VOICE_SERVICE_HOST');
|
const voiceServiceHost = this.configService.get<string>('VOICE_SERVICE_HOST');
|
||||||
|
|
||||||
if (!voiceServiceHost) {
|
if (!voiceServiceHost) {
|
||||||
throw new Error('VOICE_SERVICE_HOST environment variable is not set');
|
const error = 'VOICE_SERVICE_HOST environment variable is not set';
|
||||||
|
this.logger.error(error);
|
||||||
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.voiceServiceUrl = voiceServiceHost;
|
this.voiceServiceUrl = voiceServiceHost;
|
||||||
|
this.logger.log(`VoiceService initialized with URL: ${this.voiceServiceUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateTTS(text: string, voice: string = 'sarah'): Promise<Buffer> {
|
async generateTTS(text: string, voice: string = 'sarah'): Promise<Buffer> {
|
||||||
|
this.logger.log(`Generating TTS for text: "${text.substring(0, 50)}..." with voice: ${voice}`);
|
||||||
try {
|
try {
|
||||||
const url = `${this.voiceServiceUrl}/api/voice/tts`;
|
const url = `${this.voiceServiceUrl}/api/voice/tts`;
|
||||||
|
this.logger.debug(`Making request to: ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -26,7 +33,10 @@ export class VoiceService {
|
||||||
body: JSON.stringify({ text, voice }),
|
body: JSON.stringify({ text, voice }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.debug(`Response status: ${response.status} ${response.statusText}`);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
this.logger.error(`Voice service error: ${response.status} ${response.statusText}`);
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
`Voice service error: ${response.statusText}`,
|
`Voice service error: ${response.statusText}`,
|
||||||
response.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
response.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
|
@ -34,11 +44,15 @@ export class VoiceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrayBuffer = await response.arrayBuffer();
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
return Buffer.from(arrayBuffer);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
this.logger.log(`TTS generated successfully, size: ${buffer.length} bytes`);
|
||||||
|
return buffer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) {
|
if (error instanceof HttpException) {
|
||||||
|
this.logger.error(`HttpException in generateTTS: ${error.message}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
this.logger.error(`Error in generateTTS: ${error.message}`, error.stack);
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
`Failed to generate speech: ${error.message}`,
|
`Failed to generate speech: ${error.message}`,
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
|
@ -47,13 +61,18 @@ export class VoiceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEffect(effectType: string): Promise<Buffer> {
|
async getEffect(effectType: string): Promise<Buffer> {
|
||||||
|
this.logger.log(`Getting effect: ${effectType}`);
|
||||||
try {
|
try {
|
||||||
const url = `${this.voiceServiceUrl}/api/voice/effects/${effectType}`;
|
const url = `${this.voiceServiceUrl}/api/voice/effects/${effectType}`;
|
||||||
|
this.logger.debug(`Making request to: ${url}`);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.logger.debug(`Response status: ${response.status} ${response.statusText}`);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
this.logger.error(`Voice service error: ${response.status} ${response.statusText}`);
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
`Voice service error: ${response.statusText}`,
|
`Voice service error: ${response.statusText}`,
|
||||||
response.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
response.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
|
@ -61,11 +80,15 @@ export class VoiceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrayBuffer = await response.arrayBuffer();
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
return Buffer.from(arrayBuffer);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
this.logger.log(`Effect retrieved successfully, size: ${buffer.length} bytes`);
|
||||||
|
return buffer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof HttpException) {
|
if (error instanceof HttpException) {
|
||||||
|
this.logger.error(`HttpException in getEffect: ${error.message}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
this.logger.error(`Error in getEffect: ${error.message}`, error.stack);
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
`Failed to get sound effect: ${error.message}`,
|
`Failed to get sound effect: ${error.message}`,
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue