trying to fix crashes
This commit is contained in:
@ -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":
|
||||
|
||||
@ -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">
|
||||
|
||||
20
frontend/package-lock.json
generated
20
frontend/package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user