MIF_E31222851/app/Http/Controllers/TTSController.php

418 lines
13 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Services\SimpleTTSService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
class TTSController extends Controller
{
protected $ttsService;
public function __construct(SimpleTTSService $ttsService)
{
$this->ttsService = $ttsService;
}
/**
* Show TTS management page
*/
public function index()
{
return view('admin.tts.index');
}
/**
* Generate TTS untuk nomor antrian
*/
public function generateQueueTTS(Request $request)
{
$request->validate([
'queue_number' => 'required|string',
'service_name' => 'nullable|string'
]);
try {
$queueNumber = $request->input('queue_number');
$serviceName = $request->input('service_name', '');
// Generate TTS audio
$audioPath = $this->ttsService->generateQueueNumberTTS($queueNumber, $serviceName);
if (!$audioPath) {
return response()->json([
'success' => false,
'message' => 'Gagal generate TTS audio'
], 500);
}
// Get file info
$fileName = basename($audioPath);
$fileSize = filesize($audioPath);
$fileType = pathinfo($audioPath, PATHINFO_EXTENSION) === 'wav' ? 'audio/wav' : 'audio/mpeg';
return response()->json([
'success' => true,
'message' => 'TTS audio berhasil di-generate',
'data' => [
'file_name' => $fileName,
'file_path' => $audioPath,
'file_size' => $fileSize,
'file_type' => $fileType,
'queue_number' => $queueNumber,
'service_name' => $serviceName
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Error: ' . $e->getMessage()
], 500);
}
}
/**
* Play TTS audio
*/
public function playTTS(Request $request)
{
// Handle both GET and POST requests
$filePath = $request->input('file_path') ?? $request->input('file');
if (!$filePath) {
return response()->json([
'success' => false,
'message' => 'File parameter tidak ditemukan'
], 400);
}
try {
// If only filename is provided, construct full path
if (!file_exists($filePath)) {
$scriptPath = storage_path('app/tts_scripts');
$fullPath = $scriptPath . '/' . $filePath;
if (!file_exists($fullPath)) {
return response()->json([
'success' => false,
'message' => 'File audio tidak ditemukan: ' . $filePath
], 404);
}
$filePath = $fullPath;
}
// Get file info
$fileName = basename($filePath);
$fileSize = filesize($filePath);
$fileType = pathinfo($filePath, PATHINFO_EXTENSION) === 'wav' ? 'audio/wav' : 'audio/mpeg';
// Return audio file for streaming
return response()->file($filePath, [
'Content-Type' => $fileType,
'Content-Disposition' => 'inline; filename="' . $fileName . '"'
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Error: ' . $e->getMessage()
], 500);
}
}
/**
* Test TTS service
*/
public function testTTS()
{
try {
$result = $this->ttsService->testTTS();
return response()->json($result);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Error: ' . $e->getMessage()
], 500);
}
}
/**
* Test TTS service for public access
*/
public function testPublicTTS()
{
try {
$result = $this->ttsService->testTTS();
if ($result['success']) {
// Return a simple HTML page with audio player
$audioUrl = '/tts/audio/' . basename($result['audio_path']);
return response()->view('tts.test-public', [
'audioUrl' => $audioUrl,
'result' => $result
]);
} else {
return response()->json($result, 500);
}
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Error: ' . $e->getMessage()
], 500);
}
}
/**
* Get available TTS voices
*/
public function getVoices()
{
try {
// Buat script Python untuk mendapatkan daftar suara
$scriptContent = 'import pyttsx3
def get_voices():
try:
engine = pyttsx3.init()
voices = engine.getProperty("voices")
voice_list = []
for i, voice in enumerate(voices):
voice_info = {
"id": voice.id,
"name": voice.name,
"languages": voice.languages,
"gender": voice.gender,
"age": voice.age
}
voice_list.append(voice_info)
print("VOICES_START")
for voice in voice_list:
print(f"{voice[\'id\']}|{voice[\'name\']}|{voice[\'languages\']}|{voice[\'gender\']}|{voice[\'age\']}")
print("VOICES_END")
except Exception as e:
print(f"Error: {str(e)}")
if __name__ == "__main__":
get_voices()';
$scriptFile = storage_path('app/tts_scripts/get_voices.py');
file_put_contents($scriptFile, $scriptContent);
// Eksekusi script
$command = "python \"{$scriptFile}\" 2>&1";
$output = shell_exec($command);
// Parse output
$voices = [];
if (preg_match('/VOICES_START(.*?)VOICES_END/s', $output, $matches)) {
$lines = explode("\n", trim($matches[1]));
foreach ($lines as $line) {
if (trim($line)) {
$parts = explode('|', $line);
if (count($parts) >= 5) {
$voices[] = [
'id' => $parts[0],
'name' => $parts[1],
'languages' => $parts[2],
'gender' => $parts[3],
'age' => $parts[4]
];
}
}
}
}
return response()->json([
'success' => true,
'voices' => $voices,
'total' => count($voices)
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Error: ' . $e->getMessage()
], 500);
}
}
/**
* Clean up old TTS files
*/
public function cleanupFiles(Request $request)
{
try {
$maxAge = $request->input('max_age', 3600); // Default 1 jam
$deletedCount = $this->ttsService->cleanupOldFiles($maxAge);
return response()->json([
'success' => true,
'message' => "Berhasil menghapus {$deletedCount} file lama",
'deleted_count' => $deletedCount
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Error: ' . $e->getMessage()
], 500);
}
}
/**
* Get TTS status
*/
public function getStatus()
{
try {
$status = [
'python' => false,
'pyttsx3' => false,
'gtts' => false,
'audio_files' => 0,
'last_cleanup' => null
];
// Check Python
$pythonCheck = shell_exec('python --version 2>&1');
$status['python'] = !empty($pythonCheck) && strpos($pythonCheck, 'Python') !== false;
// Check pyttsx3
if ($status['python']) {
$pyttsx3Check = shell_exec('python -c "import pyttsx3; print(\'OK\')" 2>&1');
$status['pyttsx3'] = $pyttsx3Check === 'OK';
}
// Check gTTS
if ($status['python']) {
$gttsCheck = shell_exec('python -c "from gtts import gTTS; print(\'OK\')" 2>&1');
$status['gtts'] = $gttsCheck === 'OK';
}
// Check audio files
$scriptPath = storage_path('app/tts_scripts');
if (file_exists($scriptPath)) {
$audioFiles = glob($scriptPath . '/*.{wav,mp3}', GLOB_BRACE);
$status['audio_files'] = count($audioFiles);
}
// Get last cleanup time
$status['last_cleanup'] = now()->subHours(1)->format('Y-m-d H:i:s');
return response()->json([
'success' => true,
'status' => $status
]);
} catch (\Exception $e) {
Log::error('TTS Status Error: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Error checking TTS status: ' . $e->getMessage()
], 500);
}
}
/**
* Play TTS audio for public access (display page)
*/
public function playPublicAudio($filename)
{
try {
// Validate filename
if (empty($filename) || !preg_match('/^[a-zA-Z0-9_-]+\.(wav|mp3)$/', $filename)) {
return response()->json([
'success' => false,
'message' => 'Invalid filename'
], 400);
}
$scriptPath = storage_path('app/tts_scripts');
$filePath = $scriptPath . '/' . $filename;
if (!file_exists($filePath)) {
return response()->json([
'success' => false,
'message' => 'Audio file not found: ' . $filename
], 404);
}
// Get file info
$fileSize = filesize($filePath);
$fileType = pathinfo($filePath, PATHINFO_EXTENSION) === 'wav' ? 'audio/wav' : 'audio/mpeg';
// Return audio file for streaming
return response()->file($filePath, [
'Content-Type' => $fileType,
'Content-Disposition' => 'inline; filename="' . $filename . '"',
'Cache-Control' => 'public, max-age=3600' // Cache for 1 hour
]);
} catch (\Exception $e) {
Log::error('Public TTS Audio Error: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Error playing audio: ' . $e->getMessage()
], 500);
}
}
/**
* Play audio sequence for display page (legacy support)
*/
public function playAudioSequence(Request $request)
{
try {
$request->validate([
'poli_name' => 'required|string',
'queue_number' => 'required|string'
]);
$poliName = $request->input('poli_name');
$queueNumber = $request->input('queue_number');
// Generate TTS audio using our new service
$audioPath = $this->ttsService->generateQueueNumberTTS($queueNumber, $poliName);
if (!$audioPath || !file_exists($audioPath)) {
return response()->json([
'success' => false,
'message' => 'Failed to generate TTS audio'
], 500);
}
// Return audio sequence format that display page expects
return response()->json([
'success' => true,
'audio_sequence' => [
[
'type' => 'audio_file',
'url' => '/tts/audio/' . basename($audioPath),
'duration' => 5000, // 5 seconds estimated
'text' => "Nomor antrian {$queueNumber} untuk {$poliName}. Silakan menuju ke loket yang tersedia."
]
]
]);
} catch (\Exception $e) {
Log::error('TTS Audio Sequence Error: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Error generating audio sequence: ' . $e->getMessage()
], 500);
}
}
}