From 364fc7a8fef22133b0e667fb6e5728fe74854d1d Mon Sep 17 00:00:00 2001 From: Maxim Vershinin Date: Thu, 20 Aug 2026 19:59:11 +0500 Subject: [PATCH] FEATURE: adds automatic format detected to the frontend --- web/backend/api/info.py | 41 ++++++++++++++++++++-- web/frontend/src/api/client.ts | 5 +++ web/frontend/src/components/UploadZone.tsx | 17 +++++++-- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/web/backend/api/info.py b/web/backend/api/info.py index 261fa80..3c5a609 100644 --- a/web/backend/api/info.py +++ b/web/backend/api/info.py @@ -5,7 +5,12 @@ from fastapi import APIRouter, HTTPException from backend.config import settings from backend.services.file_manager import FileManager from backend.services.task_manager import task_manager -from backend.ffmpeg import get_stream_info + +# Import core functions +from audio_splitter.ffmpeg import get_stream_info, get_container_format, get_audio_codec +from audio_splitter.formats import determine_default_format +from audio_splitter.constants import FORMAT_INFO +from audio_splitter.defaults import DEFAULT_FORMAT router = APIRouter(prefix="/api", tags=["info"]) @@ -13,7 +18,8 @@ router = APIRouter(prefix="/api", tags=["info"]) @router.get("/info/{task_id}") async def get_task_info(task_id: str): """ - Return stream information (has_audio, has_video, has_subtitle) for the uploaded file. + Return stream information (has_audio, has_video, has_subtitle, audio_codec) + for the uploaded file. """ if not task_manager.has_task(task_id): raise HTTPException(status_code=404, detail=f"Task {task_id} not found") @@ -33,3 +39,34 @@ async def get_task_info(task_id: str): } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to retrieve stream info: {str(e)}") + + +@router.get("/info/recommended-format/{task_id}") +async def get_recommended_format(task_id: str): + """ + Return the recommended output format (container name) for the uploaded file, + based on its container and audio codec. + """ + if not task_manager.has_task(task_id): + raise HTTPException(status_code=404, detail=f"Task {task_id} not found") + + input_path = FileManager.get_input_path(task_id) + if not input_path or not input_path.exists(): + raise HTTPException(status_code=404, detail="Input file not found") + + try: + container = get_container_format(str(input_path)) + codec = get_audio_codec(str(input_path)) + + fmt = determine_default_format(container, codec) + + # Fallback if detection fails or format is unsupported + if fmt is None: + fmt = DEFAULT_FORMAT + if fmt not in FORMAT_INFO: + fmt = "mp3" # ultimate fallback + + return {"format": fmt} + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Failed to determine format: {str(e)}") diff --git a/web/frontend/src/api/client.ts b/web/frontend/src/api/client.ts index 39cb635..8677837 100644 --- a/web/frontend/src/api/client.ts +++ b/web/frontend/src/api/client.ts @@ -63,3 +63,8 @@ export const getTaskInfo = async (task_id: string): Promise<{ const response = await api.get(`/info/${task_id}`) return response.data } + +export const getRecommendedFormat = async (task_id: string): Promise<{ format: string }> => { + const response = await api.get(`/info/recommended-format/${task_id}`) + return response.data +} diff --git a/web/frontend/src/components/UploadZone.tsx b/web/frontend/src/components/UploadZone.tsx index 4432f85..c49c47f 100644 --- a/web/frontend/src/components/UploadZone.tsx +++ b/web/frontend/src/components/UploadZone.tsx @@ -3,7 +3,8 @@ import { useDropzone } from 'react-dropzone' import { Box, Typography, Paper, LinearProgress, Alert } from '@mui/material' import { CloudUpload, InsertDriveFile } from '@mui/icons-material' import { useUploadStore } from '../stores/uploadStore' -import { uploadFile, getTaskInfo } from '../api/client' +import { useOptionsStore } from '../stores/optionsStore' // NEW +import { uploadFile, getTaskInfo, getRecommendedFormat } from '../api/client' // NEW import { useTaskStore } from '../stores/taskStore' const ALLOWED_EXTENSIONS = ['.mp3', '.flac', '.wav', '.m4a', '.ogg', '.opus', '.aac', '.wma', '.aiff', '.alac', '.ac3'] @@ -30,6 +31,7 @@ export const UploadZone: React.FC = () => { } = useUploadStore() const { setTaskId: setTaskIdStore } = useTaskStore() + const { setOptions } = useOptionsStore() // NEW const onDrop = useCallback( async (acceptedFiles: File[]) => { @@ -65,9 +67,18 @@ export const UploadZone: React.FC = () => { setHasAudio(info.has_audio) setHasSubtitle(info.has_subtitle) setAudioCodec(info.audio_codec) + + // Fetch recommended format and update options + try { + const rec = await getRecommendedFormat(taskId) + // Update only the format; keep other options (e.g., transcode_to) as defaults + setOptions({ format: rec.format }) + } catch (err) { + console.warn('Failed to fetch recommended format, using default', err) + } } catch (err) { console.error('Failed to fetch stream info:', err) - // Don't block the upload flow if this fails; we'll just assume no video + // Don't block upload flow; user can manually change options } } catch (err: any) { setError(err.response?.data?.detail || err.message || 'Upload failed') @@ -78,7 +89,7 @@ export const UploadZone: React.FC = () => { setFileSize(0) } }, - [setFile, setFileName, setFileSize, setIsUploading, setUploadProgress, setError, setTaskId, setTaskIdStore] + [setFile, setFileName, setFileSize, setIsUploading, setUploadProgress, setError, setTaskId, setTaskIdStore, setHasVideo, setHasAudio, setHasSubtitle, setAudioCodec, setOptions] ) const { getRootProps, getInputProps, isDragActive } = useDropzone({