HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //home/panomity.de/vr.panomity.com/api/stt.php
<?php
/**
 * Spracheingabe für den KI-Concierge.
 *
 * Nimmt eine Audio-Datei (multipart "audio") entgegen, legt sie temporär ab
 * und lässt sie über die GPUQ-Queue (vr_stt → gpuq-stt-exec.php)
 * transkribieren. Antwort: { ok, text, language }.
 */

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
header('Content-Type: application/json; charset=utf-8');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['ok' => false, 'error' => 'Method not allowed']);
    exit;
}

require_once __DIR__ . '/gpuq_client.php';

if (empty($_FILES['audio']) || !is_uploaded_file($_FILES['audio']['tmp_name'])) {
    http_response_code(400);
    echo json_encode(['ok' => false, 'error' => 'audio file required']);
    exit;
}
if ((int)$_FILES['audio']['size'] > 8 * 1024 * 1024) {
    http_response_code(413);
    echo json_encode(['ok' => false, 'error' => 'audio too large']);
    exit;
}

$language = strtolower(trim((string)($_POST['language'] ?? '')));
if (!preg_match('/^[a-z]{2}$/', $language)) {
    $language = '';
}

$sttDir = __DIR__ . '/../admin/data/cache/stt';
if (!is_dir($sttDir)) {
    @mkdir($sttDir, 0770, true);
}

// Alte Reste aufräumen (> 1h)
foreach (glob($sttDir . '/stt-*') ?: [] as $old) {
    if (filemtime($old) < time() - 3600) {
        @unlink($old);
    }
}

$ext = 'webm';
$name = (string)($_FILES['audio']['name'] ?? '');
if (preg_match('/\.(webm|ogg|wav|mp4|m4a|mp3)$/i', $name, $m)) {
    $ext = strtolower($m[1]);
}
$tmpPath = $sttDir . '/stt-' . bin2hex(random_bytes(8)) . '.' . $ext;
if (!move_uploaded_file($_FILES['audio']['tmp_name'], $tmpPath)) {
    http_response_code(500);
    echo json_encode(['ok' => false, 'error' => 'failed to store audio']);
    exit;
}

$cfg = gpuq_config();
$payload = [
    '_gpuq_secret' => (string)($cfg['self_exec_secret'] ?? ''),
    'audio_path' => $tmpPath,
    'language' => $language,
];

$jobId = gpuq_submit('vr_stt', (string)($cfg['stt_exec_url'] ?? 'https://vr.panomity.com/api/gpuq-stt-exec.php'), $payload, 'high', 120);
if ($jobId === null) {
    @unlink($tmpPath);
    http_response_code(502);
    echo json_encode(['ok' => false, 'error' => 'GPUQ submit failed']);
    exit;
}

$job = gpuq_wait($jobId, 90, 0.6);
@unlink($tmpPath);

if (($job['status'] ?? '') !== 'completed') {
    http_response_code(502);
    echo json_encode(['ok' => false, 'error' => 'GPUQ job ' . ($job['status'] ?? 'unknown')]);
    exit;
}

echo json_encode([
    'ok' => true,
    'text' => (string)($job['result']['text'] ?? ''),
    'language' => (string)($job['result']['language'] ?? $language),
], JSON_UNESCAPED_UNICODE);