Merge pull request #43 from joelmathewthomas/feature/logs

Feature/logs
This commit is contained in:
Joel Mathew Thomas
2025-03-19 18:29:32 +05:30
committed by GitHub
11 changed files with 159 additions and 14 deletions
+6 -1
View File
@@ -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'])
+2
View File
@@ -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"),
+1 -1
View File
@@ -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
+2
View File
@@ -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>
);
}
+25 -7
View File
@@ -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>
);
}
+6 -1
View File
@@ -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>
);
}
+38 -2
View File
@@ -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>
);
}
+42
View File
@@ -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;
+4 -2
View File
@@ -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>
);
+5
View File
@@ -20,6 +20,11 @@ const theme = createTheme({
h5: {
fontWeight: 500,
},
logText: {
fontFamily: 'monospace',
fontSize: '0.8rem',
whiteSpace: 'nowrap',
},
},
components: {
MuiButton: {
+28
View File
@@ -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}`;
}