+6
-1
@@ -21,7 +21,12 @@ UPLOAD_DIR = "/tmp/freqsplit"
|
||||
# Ensure the temp directory exists
|
||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
|
||||
#
|
||||
# Endpoint to ping server
|
||||
@api_view(['GET'])
|
||||
def ping(request):
|
||||
"""Endpoint to ping the server"""
|
||||
if (request):
|
||||
return Response(status=status.HTTP_200_OK);
|
||||
|
||||
# Endpoint to upload audio and classify it to audio_class
|
||||
@api_view(['POST'])
|
||||
|
||||
@@ -16,6 +16,7 @@ Including another URLconf
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from api.views import ping
|
||||
from api.views import upload_audio
|
||||
from api.views import normalize_audio
|
||||
from api.views import trim_audio
|
||||
@@ -28,6 +29,7 @@ from api.views import cleanup
|
||||
from api.views import cleanup_zip
|
||||
|
||||
urlpatterns = [
|
||||
path('api/ping', ping, name="ping"),
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/upload', upload_audio, name='upload_audio'),
|
||||
path('api/normalize', normalize_audio, name="normalize_audio"),
|
||||
|
||||
@@ -45,7 +45,7 @@ function LandingPage() {
|
||||
Welcome to FreqSplit
|
||||
</Typography>
|
||||
<Typography variant="h5" paragraph color="textSecondary">
|
||||
Upload, preview, and process your audio and video files with ease
|
||||
Upload, preview, and process your audio files with ease
|
||||
</Typography>
|
||||
<Box sx={{ mt: 4 }}>
|
||||
<Button
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Box,
|
||||
LinearProgress,
|
||||
} from '@mui/material';
|
||||
import Logs from "../components/Logs"
|
||||
import { VolumeUp as VolumeUpIcon, ErrorOutline as ErrorIcon } from '@mui/icons-material';
|
||||
import StepperComponent from '../components/StepperComponent';
|
||||
import { useMediaContext } from '../contexts/MediaContext';
|
||||
@@ -129,6 +130,7 @@ function PreviewPage() {
|
||||
height: 2,
|
||||
}}
|
||||
/>
|
||||
<Logs />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,17 +5,24 @@ import StepperComponent from "../components/StepperComponent";
|
||||
import { useMediaContext } from "../contexts/MediaContext";
|
||||
import axios from "axios";
|
||||
import JSZip from "jszip";
|
||||
import Logs from "../components/Logs"
|
||||
import { formatLogMessage } from "../utils/logUtils";
|
||||
|
||||
function ProcessingPage() {
|
||||
const navigate = useNavigate();
|
||||
const { mediaFile, response, setExtractedFiles, setDownloadedFileURL, setDownloadedFileSpectrogram } = useMediaContext();
|
||||
const { mediaFile, response, setExtractedFiles, setDownloadedFileURL, setDownloadedFileSpectrogram, setLogs } = useMediaContext();
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [statusText, setStatusText] = useState("Analyzing media...");
|
||||
|
||||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
const processStep = async (url: string, nextStep: () => void, progressValue: number, status: string, extraData = {}) => {
|
||||
const processStep = async (url: string, log: string | null, nextStep: () => void, progressValue: number, status: string, extraData = {}) => {
|
||||
try {
|
||||
|
||||
if (log !== null) {
|
||||
setLogs((prevLogs) => [...prevLogs, log]);
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file_uuid", response.file_uuid);
|
||||
Object.entries(extraData).forEach(([key, value]) =>
|
||||
@@ -67,6 +74,10 @@ function ProcessingPage() {
|
||||
responseType: "blob",
|
||||
});
|
||||
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/postprocessing: Exporting source file`)]);
|
||||
setTimeout(() => {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/postprocessing: Downloading file`)]);
|
||||
}, 100);
|
||||
if (res.status === 200) {
|
||||
console.log("Download successful");
|
||||
const blob = new Blob([res.data], { type: "audio/wav" });
|
||||
@@ -78,6 +89,7 @@ function ProcessingPage() {
|
||||
// Get spectrogram
|
||||
setProgress(95);
|
||||
setStatusText("Calculating Spectrogram");
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/spectrogram: Calculating spectrogram`)]);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file_uuid", response.file_uuid);
|
||||
@@ -114,6 +126,10 @@ function ProcessingPage() {
|
||||
|
||||
|
||||
const handleDownload = async (downloadData: Blob) => {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/postprocessing: Exporting source files`)]);
|
||||
setTimeout(() => {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/postprocessing: Downloading files`)]);
|
||||
}, 100);
|
||||
const zipBlob = new Blob([downloadData], { type: "application/zip" });
|
||||
const zip = await JSZip.loadAsync(zipBlob);
|
||||
|
||||
@@ -127,6 +143,7 @@ function ProcessingPage() {
|
||||
// Get spectrograms
|
||||
setProgress(95);
|
||||
setStatusText("Calculating Spectrograms");
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/spectrogram: Calculating spectrograms`)]);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file_uuid", response.file_uuid);
|
||||
@@ -161,14 +178,14 @@ function ProcessingPage() {
|
||||
|
||||
console.log("Starting processing...");
|
||||
|
||||
processStep("/api/normalize", () => {
|
||||
processStep("/api/trim", () => {
|
||||
processStep("/api/normalize", formatLogMessage("freqsplit/preprocessing: Applying amplitude scaling"), () => {
|
||||
processStep("/api/trim", formatLogMessage("freqsplit/preprocessing: Pruning silent segments from audio"), () => {
|
||||
if (response.audio_class === "Music") {
|
||||
processStep("/api/resample", () => {
|
||||
processStep("/api/separate", () => fetchZipDownload(), 90, "Separating music into vocals, bass, drums and other...");
|
||||
processStep("/api/resample", formatLogMessage(`freqsplit/preprocessing: Performing rate conversion: ${response.sr}Hz -> 44100Hz`), () => {
|
||||
processStep("/api/separate", formatLogMessage(`freqsplit/separation: Demucs: Applying Time-domain source extraction`), () => fetchZipDownload(), 90, "Separating music into vocals, bass, drums and other...");
|
||||
}, 75, "Resampling audio to 44100Hz...", { sr: "44100" });
|
||||
} else {
|
||||
processStep("/api/noisereduce", () => fetchDownload(), 90, "Reducing background noise from the audio...");
|
||||
processStep("/api/noisereduce", formatLogMessage(`freqsplit/refinement: DeepFilterNet: Applying Spectral noise gating`), () => fetchDownload(), 90, "Reducing background noise from the audio...");
|
||||
}
|
||||
}, 50, "Trimming silent parts from the audio...");
|
||||
}, 25, "Normalizing audio frequency...");
|
||||
@@ -217,6 +234,7 @@ function ProcessingPage() {
|
||||
height: 2,
|
||||
}}
|
||||
/>
|
||||
<Logs />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@ import StepperComponent from '../components/StepperComponent';
|
||||
import { useMediaContext } from '../contexts/MediaContext';
|
||||
// @ts-ignore
|
||||
import SpectrogramPlayer from "react-audio-spectrogram-player"
|
||||
import Logs from "../components/Logs"
|
||||
import { formatLogMessage } from "../utils/logUtils";
|
||||
|
||||
function ResultsPage() {
|
||||
const navigate = useNavigate();
|
||||
const { mediaFile, response, extractedFiles, downloadedFileURL, downloadedFileSpectrogram } = useMediaContext();
|
||||
const { mediaFile, response, extractedFiles, downloadedFileURL, downloadedFileSpectrogram, setLogs } = useMediaContext();
|
||||
console.log("Extracted files are", extractedFiles);
|
||||
// const [isPlaying, setIsPlaying] = useState(false);
|
||||
const audioClass = response.audio_class
|
||||
@@ -31,6 +33,7 @@ function ResultsPage() {
|
||||
|
||||
const handleDownloadAll = () => {
|
||||
if (audioClass === 'Music') {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage("Saving files")]);
|
||||
extractedFiles.forEach(({ name, url }) => {
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
@@ -40,6 +43,7 @@ function ResultsPage() {
|
||||
document.body.removeChild(link);
|
||||
});
|
||||
} else {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage("Saving file")]);
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadedFileURL;
|
||||
link.download = mediaFile?.name ?? 'downloaded_file';
|
||||
@@ -195,6 +199,7 @@ function ResultsPage() {
|
||||
<Button variant="contained" color="primary" onClick={handleDownloadAll}>Download All Files</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
<Logs />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Logs from "../components/Logs"
|
||||
import axios from "axios";
|
||||
import {
|
||||
Typography,
|
||||
@@ -16,15 +17,45 @@ import {
|
||||
} from "@mui/icons-material";
|
||||
import StepperComponent from "../components/StepperComponent";
|
||||
import { useMediaContext } from "../contexts/MediaContext";
|
||||
import { formatLogMessage } from "../utils/logUtils";
|
||||
|
||||
function UploadPage() {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
const { setMediaFile, setResponse, response } = useMediaContext();
|
||||
const { setMediaFile, setResponse, response, setLogs } = useMediaContext();
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [fileError, setFileError] = useState("");
|
||||
const [upload, setUpload] = useState(false);
|
||||
const [inputEnabled, setInputEnabled] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const startLogs = async () => {
|
||||
setLogs([formatLogMessage("Initializing freqsplit")]);
|
||||
|
||||
setTimeout(() => {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage("Connecting to server")]);
|
||||
|
||||
// Send GET request to /api/ping
|
||||
axios.get("/api/ping")
|
||||
.then((ping_resp) => {
|
||||
if (ping_resp.status === 200) {
|
||||
setTimeout(() => {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage("Connection established successfully")]);
|
||||
setInputEnabled(true);
|
||||
}, 80);
|
||||
} else {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage("Failed to connect to server")]);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage("Failed to connect to server")]);
|
||||
});
|
||||
}, 90);
|
||||
};
|
||||
|
||||
startLogs();
|
||||
}, []);
|
||||
|
||||
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
@@ -87,6 +118,7 @@ function UploadPage() {
|
||||
formData.append("file", file);
|
||||
|
||||
try {
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage("freqsplit/input: Uploading audio file")]);
|
||||
const res = await axios.post<{
|
||||
file_uuid: string;
|
||||
sr: number;
|
||||
@@ -110,6 +142,9 @@ function UploadPage() {
|
||||
spec_sr: res.data.spec_sr
|
||||
}));
|
||||
setUpload(true);
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/input: Uploaded file successfully`)])
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/input: file_uuid: ${res.data.file_uuid}`)])
|
||||
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/input: audio_class: ${res.data.audio_class}`)])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Upload failed:", error);
|
||||
@@ -154,6 +189,7 @@ function UploadPage() {
|
||||
style={{ display: "none" }}
|
||||
onChange={handleFileChange}
|
||||
accept="audio/*,video/*"
|
||||
disabled={!inputEnabled}
|
||||
/>
|
||||
|
||||
<CloudUploadIcon color="primary" sx={{ fontSize: 64, mb: 2 }} />
|
||||
@@ -196,7 +232,7 @@ function UploadPage() {
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<Logs />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { useMediaContext } from "../contexts/MediaContext";
|
||||
|
||||
const Logs = () => {
|
||||
const { logs } = useMediaContext();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: '#000',
|
||||
color: '#fff',
|
||||
padding: '8px',
|
||||
borderRadius: '4px',
|
||||
width: '100%',
|
||||
minHeight: '200px',
|
||||
maxHeight: '200px',
|
||||
overflowY: 'auto',
|
||||
boxShadow: '0 0 5px rgba(0, 0, 0, 0.8)',
|
||||
mt: '40px',
|
||||
scrollBehavior: 'smooth',
|
||||
}}
|
||||
ref={(el: HTMLDivElement | null) => {
|
||||
if (el) el.scrollTop = el.scrollHeight;
|
||||
}}
|
||||
>
|
||||
{logs.length > 0 ? (
|
||||
logs.map((log, index) => (
|
||||
<Typography key={index} variant="logText" display="block">
|
||||
{`> ${log}`}
|
||||
</Typography>
|
||||
))
|
||||
) : (
|
||||
<Typography variant="logText" sx={{ opacity: 0.5 }}>
|
||||
Waiting for logs...
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
export default Logs;
|
||||
@@ -11,6 +11,8 @@ interface MediaContextType {
|
||||
setDownloadedFileURL: ( file: string) => void;
|
||||
downloadedFileSpectrogram: { spectrogram: string, spec_sr: number};
|
||||
setDownloadedFileSpectrogram: (spectrogram: {spectrogram: string, spec_sr: number}) => void;
|
||||
logs: string[];
|
||||
setLogs: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +33,10 @@ export const MediaProvider: React.FC<{ children: React.ReactNode }> = ({ childre
|
||||
spectrogram: "",
|
||||
spec_sr: 0
|
||||
});
|
||||
|
||||
const [logs, setLogs] = useState<MediaContextType["logs"]>([""]);
|
||||
|
||||
return (
|
||||
<MediaContext.Provider value={{ mediaFile, setMediaFile, response, setResponse, extractedFiles, setExtractedFiles, downloadedFileURL, setDownloadedFileURL, downloadedFileSpectrogram, setDownloadedFileSpectrogram }}>
|
||||
<MediaContext.Provider value={{ mediaFile, setMediaFile, response, setResponse, extractedFiles, setExtractedFiles, downloadedFileURL, setDownloadedFileURL, downloadedFileSpectrogram, setDownloadedFileSpectrogram, logs, setLogs }}>
|
||||
{children}
|
||||
</MediaContext.Provider>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,11 @@ const theme = createTheme({
|
||||
h5: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
logText: {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.8rem',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiButton: {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Formats a log message with the current date and time, and a 'freqsplit' prefix.
|
||||
* @param message - The log message to format.
|
||||
* @returns The formatted log message.
|
||||
*/
|
||||
export function formatLogMessage(message: string): string {
|
||||
const now = new Date();
|
||||
|
||||
// Pad single-digit numbers with leading zeros
|
||||
const pad = (number: number, length = 2): string => number.toString().padStart(length, '0');
|
||||
|
||||
// Extract date components
|
||||
const year = now.getFullYear();
|
||||
const month = pad(now.getMonth() + 1); // Months are zero-based
|
||||
const day = pad(now.getDate());
|
||||
|
||||
// Extract time components
|
||||
const hours = pad(now.getHours());
|
||||
const minutes = pad(now.getMinutes());
|
||||
const seconds = pad(now.getSeconds());
|
||||
const milliseconds = pad(now.getMilliseconds(), 3);
|
||||
|
||||
// Construct the formatted date-time string
|
||||
const timestamp = `[${year}-${month}-${day} ${hours}:${minutes}:${seconds},${milliseconds}: freqsplit]`;
|
||||
|
||||
// Return the combined log message
|
||||
return `${timestamp}: ${message}`;
|
||||
}
|
||||
Reference in New Issue
Block a user