trying to fix crashes

This commit is contained in:
2026-04-08 01:04:27 -06:00
parent 38ca9cfbad
commit 2406b0a2e7
8 changed files with 109 additions and 13 deletions

View File

@ -2,12 +2,15 @@ import logging
import os
import stat
import sys
import tempfile
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI, Query, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from fastapi.responses import StreamingResponse, FileResponse
import ffmpeg
from routers import transcribe, export, ai, captions, audio
@ -61,20 +64,69 @@ MIME_MAP = {
@app.get("/file")
async def serve_local_file(request: Request, path: str = Query(...)):
"""Stream a local file with HTTP Range support (required for video seeking)."""
async def serve_local_file(request: Request, path: str = Query(...), format: str = Query(None)):
"""Stream a local file with HTTP Range support (required for video seeking).
Optionally transcode audio files to MP3 for better browser compatibility."""
file_path = Path(path)
if not file_path.is_file():
logger.warning(f"[serve_file] File not found: {path}")
raise HTTPException(status_code=404, detail=f"File not found: {path}")
file_size = file_path.stat().st_size
content_type = MIME_MAP.get(file_path.suffix.lower(), "application/octet-stream")
original_ext = file_path.suffix.lower()
# Check if we should transcode this file
should_transcode = (
original_ext == '.wav' and
(format == 'mp3' or file_size > 10 * 1024 * 1024) # Transcode WAV if > 10MB or explicitly requested
)
if should_transcode:
logger.info(f"[serve_file] Transcoding {file_path.name} to MP3 (size: {file_size})")
# Create cache directory
cache_dir = Path(__file__).parent / "cache"
cache_dir.mkdir(exist_ok=True)
# Create cache filename
import hashlib
file_hash = hashlib.md5(str(file_path).encode()).hexdigest()
cache_path = cache_dir / f"{file_hash}.mp3"
# Check if cached version exists
if not cache_path.exists():
logger.info(f"[serve_file] Creating cached MP3: {cache_path}")
try:
# Transcode to MP3 using ffmpeg
(
ffmpeg
.input(str(file_path))
.output(str(cache_path), acodec='libmp3lame', ab='128k')
.run(overwrite_output=True, quiet=True)
)
except ffmpeg.Error as e:
logger.error(f"[serve_file] Transcoding failed: {e}")
# Fall back to original file
cache_path = file_path
else:
logger.info(f"[serve_file] Transcoding completed: {cache_path}")
else:
logger.info(f"[serve_file] Using cached MP3: {cache_path}")
# Use the transcoded file
file_path = cache_path
file_size = file_path.stat().st_size
content_type = "audio/mpeg"
else:
content_type = MIME_MAP.get(original_ext, "application/octet-stream")
range_header = request.headers.get("range")
logger.info(
f"[serve_file] {file_path.name} | size={file_size} | "
f"type={content_type} | range={range_header or 'none'}"
f"[serve_file] Serving {file_path.name} | size={file_size} | "
f"type={content_type} | range={range_header or 'none'} | transcoded={should_transcode}"
)
if content_type == "application/octet-stream":

View File

@ -3,10 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' ipc: http://ipc.localhost http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*; media-src 'self' file: blob: http://localhost:* http://127.0.0.1:*; img-src 'self' data: blob:;" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*; media-src 'self' file: blob: http://localhost:* http://127.0.0.1:*; img-src 'self' data: blob:;" />
<title>TalkEdit</title>
</head>
<body class="bg-editor-bg text-editor-text antialiased">

View File

@ -8,6 +8,8 @@
"name": "talkedit-frontend",
"version": "0.1.0",
"dependencies": {
"@fontsource/inter": "^5.2.8",
"@fontsource/jetbrains-mono": "^5.2.8",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2",
"@tauri-apps/plugin-fs": "^2",
@ -768,6 +770,24 @@
"node": ">=18"
}
},
"node_modules/@fontsource/inter": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz",
"integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@fontsource/jetbrains-mono": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz",
"integrity": "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",

View File

@ -10,6 +10,8 @@
"preview": "vite preview"
},
"dependencies": {
"@fontsource/inter": "^5.2.8",
"@fontsource/jetbrains-mono": "^5.2.8",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-dialog": "^2",
"@tauri-apps/plugin-fs": "^2",

View File

@ -72,6 +72,7 @@ export default function VideoPlayer() {
src={videoUrl}
className="max-w-full max-h-full"
controls={false}
preload="none"
onClick={togglePlay}
onError={(e) => {
console.error('Audio load error:', e);
@ -80,6 +81,9 @@ export default function VideoPlayer() {
onLoadStart={() => console.log('Audio load start:', videoUrl)}
onLoadedData={() => console.log('Audio loaded data')}
onCanPlay={() => console.log('Audio can play')}
onProgress={() => console.log('Audio progress event')}
onStalled={() => console.log('Audio stalled')}
onSuspend={() => console.log('Audio suspended')}
/>
) : (
<video
@ -87,6 +91,7 @@ export default function VideoPlayer() {
src={videoUrl}
className="max-w-full max-h-full object-contain"
playsInline
preload="none"
onClick={togglePlay}
onError={(e) => {
console.error('Video load error:', e);
@ -95,6 +100,9 @@ export default function VideoPlayer() {
onLoadStart={() => console.log('Video load start:', videoUrl)}
onLoadedData={() => console.log('Video loaded data')}
onCanPlay={() => console.log('Video can play')}
onProgress={() => console.log('Video progress event')}
onStalled={() => console.log('Video stalled')}
onSuspend={() => console.log('Video suspended')}
/>
)}
</div>

View File

@ -1,3 +1,11 @@
@import '@fontsource/inter/300.css';
@import '@fontsource/inter/400.css';
@import '@fontsource/inter/500.css';
@import '@fontsource/inter/600.css';
@import '@fontsource/inter/700.css';
@import '@fontsource/jetbrains-mono/400.css';
@import '@fontsource/jetbrains-mono/500.css';
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -116,7 +116,13 @@ export const useEditorStore = create<EditorState & EditorActions>()(
loadVideo: (path) => {
const backend = get().backendUrl;
const url = `${backend}/file?path=${encodeURIComponent(path)}`;
const buildMediaUrl = (filePath: string) => {
const isWav = filePath.toLowerCase().endsWith('.wav');
return isWav
? `${backend}/file?path=${encodeURIComponent(filePath)}&format=mp3`
: `${backend}/file?path=${encodeURIComponent(filePath)}`;
};
const url = buildMediaUrl(path);
set({
...initialState,
backendUrl: backend,
@ -304,7 +310,10 @@ export const useEditorStore = create<EditorState & EditorActions>()(
loadProject: (data) => {
const backend = get().backendUrl;
const url = `${backend}/file?path=${encodeURIComponent(data.videoPath)}`;
const isWav = data.videoPath.toLowerCase().endsWith('.wav');
const url = isWav
? `${backend}/file?path=${encodeURIComponent(data.videoPath)}&format=mp3`
: `${backend}/file?path=${encodeURIComponent(data.videoPath)}`;
let globalIdx = 0;
const annotatedSegments = (data.segments || []).map((seg: Segment) => {

View File

@ -23,7 +23,7 @@
}
],
"security": {
"csp": "default-src 'self'; connect-src 'self' http://127.0.0.1:* http://localhost:* ws://127.0.0.1:* ws://localhost:*; media-src 'self' http://127.0.0.1:* http://localhost:* file: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; img-src 'self' data: blob:"
"csp": "default-src 'self'; connect-src 'self' http://127.0.0.1:* http://localhost:* ws://127.0.0.1:* ws://localhost:*; media-src 'self' http://127.0.0.1:* http://localhost:* file: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: blob:"
}
},
"bundle": {