All checks were successful
Deploy bericht / deploy (push) Successful in 1s
Bericht-Vorlagen (Phase 5.5):
- DB: is_template, template_label in llx_bericht
- Bericht::fetchAllTemplates() und createFromTemplate()
- fetchAllForElement() blendet Vorlagen aus
- ajax/save_as_template.php erzeugt Vorlage aus aktuellem Bericht
- Desktop-Editor: '📋 Als Vorlage' Button im Action-Bereich
- Bericht-Übersicht: Vorlagen-Dropdown beim + Neu Button
- api/templates.php: GET list + POST create_from_template
Schnell-Bericht (Phase 4.a/4.i):
- api/reports.php?action=create POST-Endpoint: Titel, Format,
Orientation, ODT-Template, optional template_id
- api/odt_templates.php: Liste der Deckblatt-Vorlagen
Whisper-Transkription (Phase 5.7):
- api/transcribe.php: POST mit relpath, nutzt externen Whisper-
HTTP-Endpoint (whisper.cpp server ODER OpenAI-kompatibel)
- Konfiguration im Admin: BERICHT_WHISPER_URL/MODE/API_KEY/LANG
- Sprache default 'de'
Cron-Fix:
- BerichtUploadToken::cleanupExpired() ist jetzt Instanz-Methode
(Dolibarr ruft new Klasse($db) bei jobtype=method auf)
- Returnwert für Cron-Success/Failure
- Statische Variante als cleanupExpiredStatic() für direkte Aufrufe
- Damit läuft der tägliche Cron 'Expired Upload-Tokens bereinigen'
nicht mehr hängend
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
92 lines
3 KiB
PHP
92 lines
3 KiB
PHP
<?php
|
|
/* POST /api/transcribe.php
|
|
* Body: { relpath: "commande/SO.../voice_xxx.webm" }
|
|
* → Lädt die Audio-Datei zu einem Whisper-HTTP-Endpoint und gibt den Text zurück.
|
|
*
|
|
* Der Whisper-Endpoint wird über die Konstante BERICHT_WHISPER_URL konfiguriert.
|
|
* Default: leer → Feature deaktiviert, liefert Hinweis.
|
|
*
|
|
* Unterstützt gängige Whisper-HTTP-APIs:
|
|
* - whisper.cpp server (POST /inference mit file= field)
|
|
* - OpenAI-kompatibel (POST /v1/audio/transcriptions mit file + model=whisper-1)
|
|
* - faster-whisper-server (POST /v1/audio/transcriptions)
|
|
*
|
|
* Auswahl per BERICHT_WHISPER_MODE: 'whispercpp' (default) | 'openai'
|
|
*/
|
|
require_once __DIR__.'/_inc.php';
|
|
|
|
api_authenticate();
|
|
global $db, $user;
|
|
|
|
$in = api_input();
|
|
$relpath = (string) ($in['relpath'] ?? $_POST['relpath'] ?? '');
|
|
if (empty($relpath)) api_fail('relpath fehlt');
|
|
|
|
if (!preg_match('#^(commande|facture|propal|bericht)/#', $relpath)) {
|
|
api_fail('Pfad nicht erlaubt', 403);
|
|
}
|
|
|
|
$full = bericht_resolve_data_path($relpath);
|
|
if (!$full || !file_exists($full)) api_fail('Datei nicht gefunden', 404);
|
|
|
|
$whisper_url = getDolGlobalString('BERICHT_WHISPER_URL', '');
|
|
$mode = getDolGlobalString('BERICHT_WHISPER_MODE', 'whispercpp');
|
|
$api_key = getDolGlobalString('BERICHT_WHISPER_API_KEY', '');
|
|
$language = getDolGlobalString('BERICHT_WHISPER_LANG', 'de');
|
|
|
|
if (empty($whisper_url)) {
|
|
api_fail('Whisper ist nicht konfiguriert. Admin muss BERICHT_WHISPER_URL setzen.', 501);
|
|
}
|
|
|
|
// Datei-MIME ermitteln
|
|
$mime = function_exists('mime_content_type') ? mime_content_type($full) : 'audio/webm';
|
|
|
|
$ch = curl_init();
|
|
$cfile = new CURLFile($full, $mime, basename($full));
|
|
|
|
if ($mode === 'openai') {
|
|
curl_setopt_array($ch, array(
|
|
CURLOPT_URL => rtrim($whisper_url, '/').'/v1/audio/transcriptions',
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => array(
|
|
'file' => $cfile,
|
|
'model' => 'whisper-1',
|
|
'language' => $language,
|
|
),
|
|
CURLOPT_HTTPHEADER => $api_key ? array('Authorization: Bearer '.$api_key) : array(),
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 120,
|
|
));
|
|
} else {
|
|
// whisper.cpp server /inference
|
|
curl_setopt_array($ch, array(
|
|
CURLOPT_URL => rtrim($whisper_url, '/').'/inference',
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => array(
|
|
'file' => $cfile,
|
|
'language' => $language,
|
|
'response_format' => 'json',
|
|
),
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 120,
|
|
));
|
|
}
|
|
|
|
$resp = curl_exec($ch);
|
|
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$err = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($err) api_fail('Whisper-Fehler: '.$err, 502);
|
|
if ($status !== 200) api_fail('Whisper HTTP '.$status.': '.substr($resp, 0, 200), 502);
|
|
|
|
$data = json_decode($resp, true);
|
|
$text = '';
|
|
if (is_array($data)) {
|
|
$text = $data['text'] ?? ($data['transcription'] ?? '');
|
|
} else {
|
|
$text = (string) $resp;
|
|
}
|
|
$text = trim($text);
|
|
|
|
api_ok(array('text' => $text, 'language' => $language));
|