Merge pull request #34 from joelmathewthomas/client/integration

Client/integration
This commit is contained in:
Karthikeyan-ks
2025-03-16 13:20:48 +05:30
committed by GitHub
8 changed files with 360 additions and 75 deletions
+9 -2
View File
@@ -25,7 +25,7 @@ SECRET_KEY = 'django-insecure-qkh9wpte((ik5#r(l+mj@5rkiy!2-4^&b%0q@7yehgc+i0t20d
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['*']
# Application definition
@@ -38,10 +38,12 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'api'
'api',
"corsheaders",
]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
@@ -100,6 +102,11 @@ AUTH_PASSWORD_VALIDATORS = [
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOWED_ORIGINS = [
"http://localhost:5173",
]
# Internationalization
+2 -1
View File
@@ -15,6 +15,7 @@
"@fontsource/roboto": "^5.0.8",
"@mui/icons-material": "^5.15.10",
"@mui/material": "^5.15.10",
"axios": "^1.8.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.1"
@@ -29,4 +30,4 @@
"eslint-plugin-react-refresh": "^0.4.5",
"vite": "^5.1.0"
}
}
}
+193 -29
View File
@@ -1,72 +1,236 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Typography,
Container,
Paper,
Box,
LinearProgress
} from '@mui/material';
import StepperComponent from '../components/StepperComponent';
import { useMediaContext } from '../contexts/MediaContext';
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { Snackbar, Alert, Button } from "@mui/material";
import {
Typography,
Container,
Paper,
Box,
LinearProgress,
} from "@mui/material";
import StepperComponent from "../components/StepperComponent";
import { useMediaContext } from "../contexts/MediaContext";
import axios from "axios";
function ProcessingPage() {
const navigate = useNavigate();
const { mediaFile } = useMediaContext();
const { mediaFile, response } = useMediaContext();
const [progress, setProgress] = useState(0);
useEffect(() => {
if (!mediaFile) {
navigate('/upload');
const [open, setOpen] = useState(false);
const [message, setMessage] = useState("");
const [severity, setSeverity] = useState<
"success" | "error" | "warning" | "info"
>("info");
const showToast = (
msg: string,
type: "success" | "error" | "warning" | "info"
) => {
setMessage(msg);
setSeverity(type);
setOpen(true);
};
const processNormalize = async () => {
try {
const formData = new FormData();
formData.append("file_uuid", response.file_uuid);
const res = await axios.post("http://127.0.0.1:8000/api/normalize", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
console.log("response from server:", res);
if (res.status === 200 && res.data) {
showToast(res.data.message, "success");
processTrim();
} else {
showToast("Audio Normalization failed", "error");
}
} catch (error) {
console.error("Normalization failed:", error);
}
};
const processTrim = async () => {
try {
const formData = new FormData();
formData.append("file_uuid", response.file_uuid);
const res = await axios.post(
"http://127.0.0.1:8000/api/trim",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
console.log("response from server:", res);
if (res.status === 200 && res.data) {
showToast(res.data.message, "success");
if (response.audio_class === "Music") {
processResampling();
}else{
processNoiseReduce()
}
} else {
showToast("Audio Trimming failed", "error");
}
} catch (error) {
console.error("Trimming failed:", error);
return;
}
};
const processResampling = async () => {
try {
const formData = new FormData();
formData.append("file_uuid", response.file_uuid);
formData.append("sr", String(response.sr));
const res = await axios.post(
"http://127.0.0.1:8000/api/resample",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
console.log("response from server:", res);
if (res.status === 200 && res.data) {
showToast(res.data.message, "success");
processSeparate()
} else {
showToast("Audio Resampling failed", "error");
}
} catch (error) {
console.error("Resampling failed:", error);
}
};
const processNoiseReduce = async () => {
try {
const formData = new FormData();
formData.append("file_uuid", response.file_uuid);
const res = await axios.post(
"http://127.0.0.1:8000/api/noisereduce",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
console.log("response from server:", res);
if (res.status === 200 && res.data) {
showToast(res.data.message, "success");
} else {
showToast("Audio NoiseRemoval failed", "error");
}
} catch (error) {
console.error("NoiseRemoval failed:", error);
}
};
const processSeparate = async () => {
try {
const formData = new FormData();
formData.append("file_uuid", response.file_uuid);
const res = await axios.post(
"http://127.0.0.1:8000/api/separate",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
console.log("response from server:", res);
if (res.status === 200 && res.data) {
showToast("Audio separated successfully", "success");
} else {
showToast("Audio separation failed", "error");
}
} catch (error) {
console.error("Separation failed:", error);
}
};
useEffect(() => {
if (!mediaFile) {
navigate("/upload");
return;
}
console.log("Normalizing....");
processNormalize();
// Simulate processing progress
const interval = setInterval(() => {
setProgress((prevProgress) => {
const newProgress = prevProgress + 10;
if (newProgress >= 100) {
clearInterval(interval);
setTimeout(() => navigate('/results'), 500);
setTimeout(() => navigate("/results"), 500);
return 100;
}
return newProgress;
});
}, 800);
return () => clearInterval(interval);
}, [mediaFile, navigate]);
return (
<Container maxWidth="md" sx={{ py: 4 }}>
<StepperComponent activeStep={2} />
<Paper elevation={3} sx={{ p: 4, mt: 4, textAlign: 'center' }}>
<Paper elevation={3} sx={{ p: 4, mt: 4, textAlign: "center" }}>
<Typography variant="h4" gutterBottom color="primary">
Processing Your Media
</Typography>
<Typography variant="body1" paragraph color="textSecondary">
Please wait while we process your file
</Typography>
<Box sx={{ mt: 4, mb: 2 }}>
<LinearProgress variant="determinate" value={progress} sx={{ height: 10, borderRadius: 5 }} />
<LinearProgress
variant="determinate"
value={progress}
sx={{ height: 10, borderRadius: 5 }}
/>
<Typography variant="body2" color="textSecondary" sx={{ mt: 1 }}>
{Math.round(progress)}% Complete
</Typography>
</Box>
<Box sx={{ mt: 6 }}>
<Typography variant="body1" color="textSecondary">
{progress < 30 ? "Analyzing media..." :
progress < 60 ? "Applying processing..." :
progress < 90 ? "Finalizing..." :
"Almost done..."}
{progress < 30
? "Analyzing media..."
: progress < 60
? "Applying processing..."
: progress < 90
? "Finalizing..."
: "Almost done..."}
</Typography>
</Box>
<Snackbar
open={open}
autoHideDuration={1000}
onClose={() => setOpen(false)}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
>
<Alert
onClose={() => setOpen(false)}
severity={severity}
sx={{ width: "100%" }}
>
{message}
</Alert>
</Snackbar>
</Paper>
</Container>
);
}
export default ProcessingPage;
export default ProcessingPage;
+39 -2
View File
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import {
Typography,
Container,
@@ -22,9 +23,45 @@ import { useMediaContext } from '../contexts/MediaContext';
function ResultsPage() {
const navigate = useNavigate();
const theme = useTheme();
const { mediaFile, clearMedia } = useMediaContext();
const { mediaFile, clearMedia,response } = useMediaContext();
const [isPlaying, setIsPlaying] = useState(false);
const videoRef = useRef(null);
const processDownload = async() => {
try {
console.log('download started..')
const res = await axios.get("http://127.0.0.1:8000/api/download", {
params: { file_uuid: response.file_uuid }, // Attach file_uuid as a query param
responseType: "blob", // Treat response as a file
});
if (res.status === 200) {
const blob = new Blob([res.data]);
const url = window.URL.createObjectURL(blob);
// Extract filename from headers or use default
const contentDisposition = res.headers["content-disposition"];
const filename = contentDisposition
? contentDisposition.split("filename=")[1]
: "downloaded_file";
// Auto-download the file (no user interaction needed)
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", filename);
document.body.appendChild(link);
link.click();
// Cleanup
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} else {
console.error("Download failed, server responded with:", res);
}
} catch (error) {
console.error("Download error:", error);
}
};
useEffect(() => {
if (!mediaFile) {
@@ -152,7 +189,7 @@ function ResultsPage() {
color="primary"
fullWidth
sx={{ mb: 1 }}
onClick={() => alert('Downloading file...')}
onClick={() => processDownload()}
>
Download
</Button>
+95 -36
View File
@@ -1,29 +1,31 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Typography,
Container,
Button,
Paper,
Box,
useTheme
} from '@mui/material';
import {
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import {
Typography,
Container,
Button,
Paper,
Box,
useTheme,
} from "@mui/material";
import {
CloudUpload as CloudUploadIcon,
VolumeUp as VolumeUpIcon,
Movie as MovieIcon
} from '@mui/icons-material';
import StepperComponent from '../components/StepperComponent';
import { useMediaContext } from '../contexts/MediaContext';
Movie as MovieIcon,
} from "@mui/icons-material";
import StepperComponent from "../components/StepperComponent";
import { useMediaContext } from "../contexts/MediaContext";
function UploadPage() {
const navigate = useNavigate();
const theme = useTheme();
const { setMediaFile } = useMediaContext(); // ✅ Correct function name
const { setMediaFile, setResponse, response } = useMediaContext(); // ✅ Correct function name
const [file, setFile] = useState<File | null>(null);
const [isDragging, setIsDragging] = useState(false);
const [fileError, setFileError] = useState('');
const [fileError, setFileError] = useState("");
const [upload, setUpload] = useState(false);
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setIsDragging(true);
@@ -44,16 +46,17 @@ function UploadPage() {
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = e.target.files?.[0] || null;
validateAndSetFile(selectedFile);
handleUpload(selectedFile);
};
const validateAndSetFile = (file: File | null) => {
setFileError('');
setFileError("");
if (!file) return;
const fileType = file.type;
if (!fileType.includes('audio') && !fileType.includes('video')) {
setFileError('Please upload only audio or video files.');
if (!fileType.includes("audio") && !fileType.includes("video")) {
setFileError("Please upload only audio or video files.");
return;
}
@@ -62,22 +65,61 @@ function UploadPage() {
const handleContinue = () => {
if (file) {
setMediaFile({
name: file.name,
url: URL.createObjectURL(file),
type: file.type
type: file.type,
}); // ✅ Corrected function call
navigate('/preview');
navigate("/preview");
} else {
setFileError('Please upload a file to continue.');
setFileError("Please upload a file to continue.");
}
};
const handleUpload = async (file: File | null) => {
if (!file) {
console.error("No file selected!");
return; // Exit early if file is null
}
const formData = new FormData();
formData.append("file", file); // ✅ No more errors because we checked `file` is not null.
try {
const res = await axios.post<{
file_uuid: string;
sr: number;
audio_class: string;
}>("http://127.0.0.1:8000/api/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
console.log("Upload response:", res);
if (res.status === 201 && res.data) {
setResponse( ({
audio_class: res.data.audio_class,
file_uuid: res.data.file_uuid,
sr: res.data.sr,
}));
setUpload(true);
}
} catch (error) {
console.error("Upload failed:", error);
}
};
useEffect(() => {
console.log("Updated response:", response);
}, [response]);
return (
<Container maxWidth="md" sx={{ py: 4 }}>
<StepperComponent activeStep={0} />
<Paper elevation={3} sx={{ p: 4, mt: 4, textAlign: 'center' }}>
<Paper elevation={3} sx={{ p: 4, mt: 4, textAlign: "center" }}>
<Typography variant="h4" gutterBottom color="primary">
Upload Your Media
</Typography>
@@ -87,35 +129,43 @@ function UploadPage() {
<Box
sx={{
border: `2px dashed ${isDragging ? theme.palette.primary.main : theme.palette.divider}`,
border: `2px dashed ${
isDragging ? theme.palette.primary.main : theme.palette.divider
}`,
borderRadius: 2,
p: 6,
mt: 3,
mb: 3,
backgroundColor: isDragging ? 'rgba(63, 81, 181, 0.08)' : 'transparent',
transition: 'all 0.3s ease',
cursor: 'pointer',
backgroundColor: isDragging
? "rgba(63, 81, 181, 0.08)"
: "transparent",
transition: "all 0.3s ease",
cursor: "pointer",
}}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
onClick={() => document.getElementById('fileInput')?.click()}
onClick={() => document.getElementById("fileInput")?.click()}
>
<input
type="file"
id="fileInput"
style={{ display: 'none' }}
style={{ display: "none" }}
onChange={handleFileChange}
accept="audio/*,video/*"
/>
<CloudUploadIcon color="primary" sx={{ fontSize: 64, mb: 2 }} />
<Typography variant="h6" gutterBottom>
{file ? file.name : 'Drop your file here or click to browse'}
{file ? file.name : "Drop your file here or click to browse"}
</Typography>
{file && (
<Typography variant="body2" color="textSecondary">
{file.type.includes('video') ? <MovieIcon sx={{ mr: 1 }} /> : <VolumeUpIcon sx={{ mr: 1 }} />}
{file.type.includes("video") ? (
<MovieIcon sx={{ mr: 1 }} />
) : (
<VolumeUpIcon sx={{ mr: 1 }} />
)}
{file.type} - {(file.size / (1024 * 1024)).toFixed(2)} MB
</Typography>
)}
@@ -127,11 +177,20 @@ function UploadPage() {
</Typography>
)}
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'space-between' }}>
<Button variant="outlined" color="primary" onClick={() => navigate('/')}>
<Box sx={{ mt: 4, display: "flex", justifyContent: "space-between" }}>
<Button
variant="outlined"
color="primary"
onClick={() => navigate("/")}
>
Back to Home
</Button>
<Button variant="contained" color="primary" onClick={handleContinue} disabled={!file}>
<Button
variant="contained"
color="primary"
onClick={handleContinue}
disabled={!upload}
>
Continue to Preview
</Button>
</Box>
+10 -1
View File
@@ -3,15 +3,24 @@ import React, { createContext, useState, useContext } from 'react';
interface MediaContextType {
mediaFile: { name: string; url: string; type: string } | null;
setMediaFile: (file: { name: string; url: string; type: string }) => void;
response: { file_uuid: string; sr: number; audio_class: string };
setResponse: (response: { file_uuid: string; sr: number; audio_class: string }) => void;
}
const MediaContext = createContext<MediaContextType | undefined>(undefined);
export const MediaProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [mediaFile, setMediaFile] = useState<MediaContextType['mediaFile']>(null);
const [response, setResponse] = useState<MediaContextType["response"]>({
audio_class: "",
file_uuid: "",
sr: 0,
});
return (
<MediaContext.Provider value={{ mediaFile, setMediaFile }}>
<MediaContext.Provider value={{ mediaFile, setMediaFile, response, setResponse }}>
{children}
</MediaContext.Provider>
);
+1 -4
View File
@@ -1,4 +1,3 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import '@fontsource/roboto/300.css';
@@ -7,7 +6,5 @@ import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
<App />
);
+11
View File
@@ -20,13 +20,16 @@ click-didyoumean==0.3.1
click-plugins==1.1.1
click-repl==0.3.0
cloudpickle==3.1.1
colorama==0.4.6
contourpy==1.3.1
cors==1.0.1
cycler==0.12.1
decorator==5.2.1
DeepFilterLib==0.5.6
DeepFilterNet==0.5.6
demucs==4.0.1
Django==5.1.6
django-cors-headers==4.7.0
djangorestframework==3.15.2
dora_search==0.1.12
einops==0.8.1
@@ -34,6 +37,9 @@ filelock==3.17.0
fonttools==4.56.0
frozenlist==1.5.0
fsspec==2025.2.0
future==1.0.0
gevent==24.11.1
greenlet==3.1.1
huggingface-hub==0.29.1
idna==3.10
iniconfig==2.0.0
@@ -85,6 +91,7 @@ prompt_toolkit==3.0.50
propcache==0.3.0
pycparser==2.22
pyparsing==3.2.1
PySocks==1.7.1
pystoi==0.4.1
pytest==8.3.4
python-dateutil==2.9.0.post0
@@ -95,6 +102,7 @@ PyYAML==6.0.2
redis==5.2.1
regex==2024.11.6
requests==2.32.3
requests-file==2.1.0
retrying==1.3.4
safetensors==0.5.3
scikit-learn==1.6.1
@@ -107,6 +115,7 @@ sqlparse==0.5.3
submitit==1.5.2
sympy==1.13.1
threadpoolctl==3.5.0
tldextract==5.1.3
tokenizers==0.21.0
torch==2.6.0
torch-optimizer==0.1.0
@@ -124,3 +133,5 @@ urllib3==2.3.0
vine==5.1.0
wcwidth==0.2.13
yarl==1.18.3
zope.event==5.0
zope.interface==7.2