fix(voice): Hard-Cap an 'Max. Aufnahmedauer'-Setting + Dauer an Bubble
Wake-Word/Barge-In waren hart auf 60s gecappt (schnitt lange Diktate bei 1 min ab). Jetzt lesen sie loadMaxRecordingMs() — der bestehende, aber vom Streaming-Pfad abgeklemmte 'Maximale Aufnahmedauer'-Regler (1-30 min) steuert nun wirklich. Voice-Bubbles zeigen die Aufnahmedauer (durationS aus stt_endpoint) als 'M:SS'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -50,7 +50,7 @@ import VoiceButton from '../components/VoiceButton';
|
||||
import FileUpload, { FileData } from '../components/FileUpload';
|
||||
import CameraUpload, { PhotoData } from '../components/CameraUpload';
|
||||
import MessageText from '../components/MessageText';
|
||||
import { loadConvWindowMs, loadTtsSpeed, TTS_SPEED_DEFAULT, loadSttEndpointMs } from '../services/audio';
|
||||
import { loadConvWindowMs, loadTtsSpeed, TTS_SPEED_DEFAULT, loadSttEndpointMs, loadMaxRecordingMs } from '../services/audio';
|
||||
import Geolocation from '@react-native-community/geolocation';
|
||||
|
||||
// --- Typen ---
|
||||
@@ -93,6 +93,9 @@ interface ChatMessage {
|
||||
* gespiegelt damit wir die EXAKT richtige Placeholder-Bubble ersetzen,
|
||||
* auch wenn mehrere Aufnahmen parallel offen sind. */
|
||||
audioRequestId?: string;
|
||||
/** Laenge der Sprachaufnahme in Sekunden (aus dem stt_endpoint) — fuer die
|
||||
* Dauer-Anzeige an der Voice-Bubble. */
|
||||
durationS?: number;
|
||||
/** Skill-Created-Bubble: ARIA hat einen neuen Skill angelegt */
|
||||
skillCreated?: {
|
||||
name: string;
|
||||
@@ -184,6 +187,14 @@ function stripSystemHints(text: string): string {
|
||||
}
|
||||
return out;
|
||||
}
|
||||
/** Sekunden → "M:SS" fuer die Sprachnachricht-Dauer. */
|
||||
function formatDur(sec: number): string {
|
||||
const s = Math.max(0, Math.round(sec));
|
||||
const m = Math.floor(s / 60);
|
||||
const r = s % 60;
|
||||
return `${m}:${r.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
const DEFAULT_ATTACHMENT_DIR = `${RNFS.DocumentDirectoryPath}/chat_attachments`;
|
||||
const STORAGE_PATH_KEY = 'aria_attachment_storage_path';
|
||||
|
||||
@@ -1668,7 +1679,9 @@ const ChatScreen: React.FC = () => {
|
||||
location: location || null,
|
||||
noSpeechTimeoutMs: windowMs,
|
||||
endpointMs: await loadSttEndpointMs(),
|
||||
hardCapMs: 60000,
|
||||
// Notbremse 5 min (nicht 1 min) — der Stille-Endpoint beendet normale
|
||||
// Turns eh sofort; der Cap darf lange Diktate nicht mitten drin kappen.
|
||||
hardCapMs: await loadMaxRecordingMs(),
|
||||
projectId: focusedProjectIdRef.current,
|
||||
});
|
||||
import('../services/logger').then(m => m.reportAppDebug('wake.cb', `startStreamingRecording returned ok=${ok}`)).catch(()=>{});
|
||||
@@ -1696,6 +1709,13 @@ const ChatScreen: React.FC = () => {
|
||||
if (ev.text && ev.text.trim()) {
|
||||
console.log('[Chat] STT-Endpoint: %r (reason=%s, %dms, %.1fs Audio)',
|
||||
ev.text.slice(0, 80), ev.reason, ev.sttMs, ev.durationS);
|
||||
// Aufnahme-Dauer an die passende Voice-Bubble haengen (Anzeige). Der
|
||||
// spaetere STT-Text-Update spreadet die Message, die Dauer bleibt.
|
||||
if (ev.audioRequestId && typeof ev.durationS === 'number' && ev.durationS > 0) {
|
||||
const dur = ev.durationS;
|
||||
setMessages(prev => prev.map(m =>
|
||||
m.audioRequestId === ev.audioRequestId ? { ...m, durationS: dur } : m));
|
||||
}
|
||||
// Wenn passive lauschend: User hat tatsaechlich was gesagt → uebergang
|
||||
// zu 'conversing' damit der normale Flow greift (TTS, resume, etc.)
|
||||
if (wakeWordService.getState() === 'listening') {
|
||||
@@ -1771,7 +1791,8 @@ const ChatScreen: React.FC = () => {
|
||||
location: location || null,
|
||||
noSpeechTimeoutMs: windowMs,
|
||||
endpointMs: await loadSttEndpointMs(),
|
||||
hardCapMs: 60000,
|
||||
// Notbremse 5 min (s.o.) — lange Diktate nicht bei 1 min abschneiden.
|
||||
hardCapMs: await loadMaxRecordingMs(),
|
||||
projectId: focusedProjectIdRef.current,
|
||||
});
|
||||
if (ok) {
|
||||
@@ -2255,7 +2276,7 @@ const ChatScreen: React.FC = () => {
|
||||
// die Session auch app-seitig haben wir +2s Toleranz.
|
||||
noSpeechTimeoutMs: 0,
|
||||
endpointMs: await loadSttEndpointMs(),
|
||||
hardCapMs: 300000,
|
||||
hardCapMs: await loadMaxRecordingMs(),
|
||||
projectId: focusedProjectIdRef.current,
|
||||
});
|
||||
if (!ok) {
|
||||
@@ -2647,6 +2668,16 @@ const ChatScreen: React.FC = () => {
|
||||
{att.serverPath ? '(tippen zum Laden)' : '(nicht verfuegbar)'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
) : att.type === 'audio' ? (
|
||||
<View style={styles.attachmentFile}>
|
||||
<Text style={styles.attachmentFileIcon}>{'🎙'}</Text>
|
||||
<Text style={styles.attachmentFileName} numberOfLines={1}>
|
||||
{att.name || 'Sprachaufnahme'}
|
||||
</Text>
|
||||
{typeof item.durationS === 'number' && item.durationS > 0 ? (
|
||||
<Text style={styles.attachmentFileSize}>{formatDur(item.durationS)}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
) : (
|
||||
<TouchableOpacity
|
||||
style={styles.attachmentFile}
|
||||
|
||||
Reference in New Issue
Block a user