Merge pull request #44 from joelmathewthomas/feature/websocket

Feature/websocket
This commit is contained in:
Joel Mathew Thomas
2025-03-20 00:42:39 +05:30
committed by GitHub
12 changed files with 331 additions and 200 deletions
+45
View File
@@ -0,0 +1,45 @@
# api/consumers.py
import json
import os
import shutil
from pathlib import Path
from channels.generic.websocket import WebsocketConsumer
UPLOAD_DIR = Path("/tmp/freqsplit")
class MediaConsumer(WebsocketConsumer):
def connect(self):
self.accept()
self.file_uuid = [] # List to store file uuids
self.send(text_data=json.dumps({
"message": "Connected to WebSocket server!"
}))
def receive(self, text_data):
data = json.loads(text_data)
message = data.get("message", "")
# If message contains a file UUID, store it
if "file_uuid" in data:
uuid = data["file_uuid"]
self.file_uuid.append(uuid)
self.send(text_data=json.dumps({"response": f"UUID {uuid} stored."}))
elif message == "ping":
self.send(text_data=json.dumps({"response": "pong"}))
else:
self.send(text_data=json.dumps({"response": f"Received: {message}"}))
def disconnect(self, close_code):
print("Disconnected from Websocket")
print("Stored file UUIDs:", self.file_uuid)
for file_uuid in self.file_uuid:
dir_path = os.path.join(UPLOAD_DIR, file_uuid);
zip_path = os.path.join(UPLOAD_DIR, f"{file_uuid}.zip")
try:
if os.path.exists(dir_path):
shutil.rmtree(dir_path)
if os.path.isfile(zip_path):
os.remove(zip_path)
except Exception as e:
print(f"Error: Failed to cleanup {dir_path} or {zip_path}: {e}")
+7
View File
@@ -0,0 +1,7 @@
# api/routing.py
from django.urls import path
from . import consumers
websocket_urlpatterns= [
path("ws/freqsplit/", consumers.MediaConsumer.as_asgi()),
]
+10 -13
View File
@@ -78,8 +78,15 @@ def music_separation_task(file_path):
# Determine the base directory (output path) # Determine the base directory (output path)
output_path = file_path.parent output_path = file_path.parent
# Run Demucs separation try:
separate_audio_with_demucs(str(file_path), str(output_path)) # Run Demucs separation
separate_audio_with_demucs(str(file_path), str(output_path))
except Exception as e:
print(f"Failed to separate music into sources: {e}")
if os.path.exists(os.path.dirname(file_path)):
shutil.rmtree(os.path.dirname(file_path))
return False
# Define expected output dir # Define expected output dir
demucs_dir = output_path / 'htdemucs' demucs_dir = output_path / 'htdemucs'
@@ -116,6 +123,7 @@ def music_separation_task(file_path):
return True return True
except Exception as e: except Exception as e:
print(f"Failed to separate music into sources: {e}")
return False return False
@shared_task @shared_task
@@ -144,14 +152,3 @@ def generate_spectrogram_task(file_path):
return True, spec_data_json, plot_data['sr'] return True, spec_data_json, plot_data['sr']
except Exception as e: except Exception as e:
return False return False
@shared_task
def cleanup_task(file_path):
"""Celery task to cleanup files"""
file_path = Path(file_path)
# Cleanup
try:
shutil.rmtree(os.path.dirname(file_path))
return True
except Exception as e:
return False
-35
View File
@@ -13,7 +13,6 @@ from .tasks import resample_audio_task
from .tasks import music_separation_task from .tasks import music_separation_task
from .tasks import noisereduce_task from .tasks import noisereduce_task
from .tasks import generate_spectrogram_task from .tasks import generate_spectrogram_task
from .tasks import cleanup_task
from freqsplit.input.format_checker import is_supported_format from freqsplit.input.format_checker import is_supported_format
UPLOAD_DIR = "/tmp/freqsplit" UPLOAD_DIR = "/tmp/freqsplit"
@@ -221,37 +220,3 @@ def download_audio(request):
# Stream the ZIP file # Stream the ZIP file
return FileResponse(open(zip_file_path, "rb"), as_attachment=True, filename=os.path.basename(zip_file_path)) return FileResponse(open(zip_file_path, "rb"), as_attachment=True, filename=os.path.basename(zip_file_path))
@api_view(['POST'])
def cleanup(request):
"""Handles file cleanup after pipeline processing"""
stat, result, status_code = get_audio_file_path(request, UPLOAD_DIR)
if stat == False:
return Response({"error": result}, status=status_code)
# Call Celery task synchronously
task = cleanup_task.apply(args=(result,))
if task.get():
return Response({"message": f"Successfully cleaned up files on the server"}, status=status.HTTP_200_OK)
else:
return Response({"error": "Failed to cleanup files on the server"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@api_view(['POST'])
def cleanup_zip(request):
"""Handles cleanup of all zip files leftover by api/download"""
deleted_files = []
for file in os.listdir(UPLOAD_DIR):
if file.endswith(".zip"):
file_path = os.path.join(UPLOAD_DIR, file)
try:
os.remove(file_path)
deleted_files.append(file)
except Exception as e:
return Response({"message": f"Error deleting {file_path}: {e}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
if deleted_files:
return Response({"message": f"Deleted ZIP files: {deleted_files}"}, status=status.HTTP_200_OK)
else:
return Response({"message": "No ZIP files found to clean up."}, status=status.HTTP_200_OK)
+11 -2
View File
@@ -8,9 +8,18 @@ https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
""" """
import os import os
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import api.routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
application = get_asgi_application() application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
api.routing.websocket_urlpatterns
)
)
})
+9
View File
@@ -31,6 +31,8 @@ ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'daphne',
'channels',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@@ -72,7 +74,14 @@ TEMPLATES = [
] ]
WSGI_APPLICATION = 'backend.wsgi.application' WSGI_APPLICATION = 'backend.wsgi.application'
ASGI_APPLICATION = "backend.asgi.application"
# Redis setup for WebSocket communication
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer"
}
}
# Database # Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases # https://docs.djangoproject.com/en/5.1/ref/settings/#databases
+1 -5
View File
@@ -25,8 +25,6 @@ from api.views import separate_music
from api.views import noisereduce from api.views import noisereduce
from api.views import download_audio from api.views import download_audio
from api.views import generate_spectrogram from api.views import generate_spectrogram
from api.views import cleanup
from api.views import cleanup_zip
urlpatterns = [ urlpatterns = [
path('api/ping', ping, name="ping"), path('api/ping', ping, name="ping"),
@@ -38,7 +36,5 @@ urlpatterns = [
path('api/separate', separate_music, name="separate_music"), path('api/separate', separate_music, name="separate_music"),
path('api/noisereduce', noisereduce, name="noisreduce"), path('api/noisereduce', noisereduce, name="noisreduce"),
path('api/download', download_audio, name="download_audio"), path('api/download', download_audio, name="download_audio"),
path('api/spectrogram', generate_spectrogram, name="generate_spectrogram"), path('api/spectrogram', generate_spectrogram, name="generate_spectrogram")
path('api/cleanup', cleanup, name="cleanup"),
path('api/cleanup_zip', cleanup_zip, name="cleanup_zip")
] ]
+20 -17
View File
@@ -11,6 +11,7 @@ import {
import { Home as HomeIcon } from '@mui/icons-material'; import { Home as HomeIcon } from '@mui/icons-material';
import theme from './theme/theme'; import theme from './theme/theme';
import { MediaProvider } from './contexts/MediaContext'; import { MediaProvider } from './contexts/MediaContext';
import { WebSocketProvider } from './contexts/WebSocketContext';
// Import pages // Import pages
import LandingPage from './Pages/LandingPage'; import LandingPage from './Pages/LandingPage';
import UploadPage from './Pages/UploadPage'; import UploadPage from './Pages/UploadPage';
@@ -25,24 +26,26 @@ const App: React.FC = () => {
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<CssBaseline /> <CssBaseline />
<MediaProvider> <MediaProvider>
<Router> <WebSocketProvider>
<AppBar position="static"> <Router>
<Toolbar> <AppBar position="static">
<Typography variant="h6" sx={{ flexGrow: 1 }}>FreqSplit</Typography> <Toolbar>
<Button color="inherit" href="/"> <HomeIcon sx={{ mr: 1 }} /> Home </Button> <Typography variant="h6" sx={{ flexGrow: 1 }}>FreqSplit</Typography>
</Toolbar> <Button color="inherit" href="/"> <HomeIcon sx={{ mr: 1 }} /> Home </Button>
</AppBar> </Toolbar>
</AppBar>
<Routes> <Routes>
<Route path="/" element={<LandingPage />} /> <Route path="/" element={<LandingPage />} />
<Route path="/upload" element={<UploadPage />} /> <Route path="/upload" element={<UploadPage />} />
<Route path="/preview" element={<PreviewPage />} /> <Route path="/preview" element={<PreviewPage />} />
<Route path="/processing" element={<ProcessingPage />} /> <Route path="/processing" element={<ProcessingPage />} />
<Route path="/results" element={<ResultsPage />} /> <Route path="/results" element={<ResultsPage />} />
<Route path="*" element={<Navigate to="/" replace />} /> <Route path="*" element={<Navigate to="/" replace />} />
<Route path='/audio' element={<AudioVisualizer />}/> <Route path='/audio' element={<AudioVisualizer />}/>
</Routes> </Routes>
</Router> </Router>
</WebSocketProvider>
</MediaProvider> </MediaProvider>
</ThemeProvider> </ThemeProvider>
); );
+8
View File
@@ -16,12 +16,14 @@ import {
Movie as MovieIcon, Movie as MovieIcon,
} from "@mui/icons-material"; } from "@mui/icons-material";
import StepperComponent from "../components/StepperComponent"; import StepperComponent from "../components/StepperComponent";
import { useWebSocket } from "../contexts/WebSocketContext";
import { useMediaContext } from "../contexts/MediaContext"; import { useMediaContext } from "../contexts/MediaContext";
import { formatLogMessage } from "../utils/logUtils"; import { formatLogMessage } from "../utils/logUtils";
function UploadPage() { function UploadPage() {
const navigate = useNavigate(); const navigate = useNavigate();
const theme = useTheme(); const theme = useTheme();
const { socket, isConnected } = useWebSocket();
const { setMediaFile, setResponse, response, setLogs } = useMediaContext(); const { setMediaFile, setResponse, response, setLogs } = useMediaContext();
const [file, setFile] = useState<File | null>(null); const [file, setFile] = useState<File | null>(null);
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
@@ -141,6 +143,12 @@ function UploadPage() {
spectrogram: res.data.spectrogram, spectrogram: res.data.spectrogram,
spec_sr: res.data.spec_sr spec_sr: res.data.spec_sr
})); }));
if (socket && isConnected){
socket.send(JSON.stringify({ file_uuid: res.data.file_uuid }))
} else {
console.error("Websocket not connected!");
}
setUpload(true); setUpload(true);
setLogs((prevLogs) => [...prevLogs, formatLogMessage(`freqsplit/input: Uploaded file successfully`)]) 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: file_uuid: ${res.data.file_uuid}`)])
+54
View File
@@ -0,0 +1,54 @@
import React, { createContext, useContext, useEffect, useState } from "react";
const WEBSOCKET_URL = "ws://localhost:8000/ws/freqsplit/";
interface WebSocketContextType {
socket: WebSocket | null;
isConnected: boolean;
}
const WebSocketContext = createContext<WebSocketContextType>({
socket: null,
isConnected: false,
});
export const WebSocketProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [socket, setSocket] = useState<WebSocket | null>(null);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
const newSocket = new WebSocket(WEBSOCKET_URL);
newSocket.onopen = () => {
console.log("Connected to WebSocket!");
setIsConnected(true);
};
newSocket.onclose = () => {
console.warn("Disconnected from WebSocket");
setIsConnected(false);
};
newSocket.onerror = (error) => {
console.error("WebSocket error:", error);
};
newSocket.onmessage = (event) => {
console.log("Message from server:", event.data);
};
setSocket(newSocket);
return () => {
newSocket.close();
};
}, []);
return (
<WebSocketContext.Provider value={{ socket, isConnected }}>
{children}
</WebSocketContext.Provider>
);
};
export const useWebSocket = () => useContext(WebSocketContext);
+151 -127
View File
@@ -6,133 +6,157 @@ build-backend = "setuptools.build_meta"
name = "freqsplit" name = "freqsplit"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"aiohappyeyeballs==2.4.6", "aiohappyeyeballs" = "2.4.6",
"aiohttp==3.11.13", "aiohttp" = "3.11.13",
"aiosignal==1.3.2", "aiosignal" = "1.3.2",
"amqp==5.3.1", "amqp" = "5.3.1",
"antlr4-python3-runtime==4.9.3", "antlr4-python3-runtime" = "4.9.3",
"appdirs==1.4.4", "appdirs" = "1.4.4",
"asgiref==3.8.1", "asgiref" = "3.8.1",
"asteroid==0.7.0", "asteroid" = "0.7.0",
"asteroid-filterbanks==0.4.0", "asteroid-filterbanks" = "0.4.0",
"attrs==25.1.0", "attrs" = "25.1.0",
"audioread==3.0.1", "audioread" = "3.0.1",
"billiard==4.2.1", "autobahn" = "24.4.2",
"cached-property==2.0.1", "Automat" = "24.8.1",
"celery==5.4.0", "billiard" = "4.2.1",
"certifi==2025.1.31", "cached-property" = "2.0.1",
"cffi==1.17.1", "celery" = "5.4.0",
"charset-normalizer==3.4.1", "certifi" = "2025.1.31",
"click==8.1.8", "cffi" = "1.17.1",
"click-didyoumean==0.3.1", "channels" = "4.2.0",
"click-plugins==1.1.1", "charset-normalizer" = "3.4.1",
"click-repl==0.3.0", "click" = "8.1.8",
"cloudpickle==3.1.1", "click-didyoumean" = "0.3.1",
"contourpy==1.3.1", "click-plugins" = "1.1.1",
"cycler==0.12.1", "click-repl" = "0.3.0",
"decorator==5.2.1", "cloudpickle" = "3.1.1",
"DeepFilterLib==0.5.6", "colorama" = "0.4.6",
"DeepFilterNet==0.5.6", "constantly" = "23.10.4",
"demucs==4.0.1", "contourpy" = "1.3.1",
"Django==5.1.6", "cors" = "1.0.1",
"django-cors-headers==4.7.0", "cryptography" = "44.0.2",
"djangorestframework==3.15.2", "cycler" = "0.12.1",
"dora_search==0.1.12", "daphne" = "4.1.2",
"einops==0.8.1", "decorator" = "5.2.1",
"filelock==3.17.0", "DeepFilterLib" = "0.5.6",
"fonttools==4.56.0", "DeepFilterNet" = "0.5.6",
"frozenlist==1.5.0", "demucs" = "4.0.1",
"fsspec==2025.2.0", "Django" = "5.1.6",
"huggingface-hub==0.29.1", "django-cors-headers" = "4.7.0",
"idna==3.10", "djangorestframework" = "3.15.2",
"iniconfig==2.0.0", "dora_search" = "0.1.12",
"Jinja2==3.1.5", "einops" = "0.8.1",
"joblib==1.4.2", "filelock" = "3.17.0",
"julius==0.2.7", "fonttools" = "4.56.0",
"kiwisolver==1.4.8", "frozenlist" = "1.5.0",
"kombu==5.4.2", "fsspec" = "2025.2.0",
"lameenc==1.8.1", "future" = "1.0.0",
"lazy_loader==0.4", "gevent" = "24.11.1",
"librosa==0.10.2.post1", "greenlet" = "3.1.1",
"lightning-utilities==0.12.0", "huggingface-hub" = "0.29.1",
"llvmlite==0.44.0", "hyperlink" = "21.0.0",
"loguru==0.7.3", "idna" = "3.10",
"MarkupSafe==3.0.2", "incremental" = "24.7.2",
"matplotlib==3.10.0", "iniconfig" = "2.0.0",
"mir_eval==0.8.2", "Jinja2" = "3.1.5",
"mpmath==1.3.0", "joblib" = "1.4.2",
"msgpack==1.1.0", "julius" = "0.2.7",
"multidict==6.1.0", "kiwisolver" = "1.4.8",
"networkx==3.4.2", "kombu" = "5.4.2",
"numba==0.61.0", "lameenc" = "1.8.1",
"numpy==1.26.4", "lazy_loader" = "0.4",
"nvidia-cublas-cu12==12.4.5.8", "librosa" = "0.10.2.post1",
"nvidia-cuda-cupti-cu12==12.4.127", "lightning-utilities" = "0.12.0",
"nvidia-cuda-nvrtc-cu12==12.4.127", "llvmlite" = "0.44.0",
"nvidia-cuda-runtime-cu12==12.4.127", "loguru" = "0.7.3",
"nvidia-cudnn-cu12==9.1.0.70", "MarkupSafe" = "3.0.2",
"nvidia-cufft-cu12==11.2.1.3", "matplotlib" = "3.10.0",
"nvidia-curand-cu12==10.3.5.147", "mir_eval" = "0.8.2",
"nvidia-cusolver-cu12==11.6.1.9", "mpmath" = "1.3.0",
"nvidia-cusparse-cu12==12.3.1.170", "msgpack" = "1.1.0",
"nvidia-cusparselt-cu12==0.6.2", "multidict" = "6.1.0",
"nvidia-nccl-cu12==2.21.5", "networkx" = "3.4.2",
"nvidia-nvjitlink-cu12==12.4.127", "numba" = "0.61.0",
"nvidia-nvtx-cu12==12.4.127", "numpy" = "1.26.4",
"omegaconf==2.3.0", "nvidia-cublas-cu12" = "12.4.5.8",
"openunmix==1.3.0", "nvidia-cuda-cupti-cu12" = "12.4.127",
"packaging==23.2", "nvidia-cuda-nvrtc-cu12" = "12.4.127",
"pandas==2.2.3", "nvidia-cuda-runtime-cu12" = "12.4.127",
"panns-inference==0.1.1", "nvidia-cudnn-cu12" = "9.1.0.70",
"pb-bss-eval==0.0.2", "nvidia-cufft-cu12" = "11.2.1.3",
"pesq==0.0.4", "nvidia-curand-cu12" = "10.3.5.147",
"pillow==11.1.0", "nvidia-cusolver-cu12" = "11.6.1.9",
"platformdirs==4.3.6", "nvidia-cusparse-cu12" = "12.3.1.170",
"pluggy==1.5.0", "nvidia-cusparselt-cu12" = "0.6.2",
"pooch==1.8.2", "nvidia-nccl-cu12" = "2.21.5",
"prompt_toolkit==3.0.50", "nvidia-nvjitlink-cu12" = "12.4.127",
"propcache==0.3.0", "nvidia-nvtx-cu12" = "12.4.127",
"pycparser==2.22", "omegaconf" = "2.3.0",
"pyparsing==3.2.1", "openunmix" = "1.3.0",
"pystoi==0.4.1", "packaging" = "23.2",
"pytest==8.3.4", "pandas" = "2.2.3",
"python-dateutil==2.9.0.post0", "panns-inference" = "0.1.1",
"pytorch-lightning==2.5.0.post0", "pb-bss-eval" = "0.0.2",
"pytorch-ranger==0.1.1", "pesq" = "0.0.4",
"pytz==2025.1", "pillow" = "11.1.0",
"PyYAML==6.0.2", "platformdirs" = "4.3.6",
"redis==5.2.1", "pluggy" = "1.5.0",
"regex==2024.11.6", "pooch" = "1.8.2",
"requests==2.32.3", "prompt_toolkit" = "3.0.50",
"retrying==1.3.4", "propcache" = "0.3.0",
"safetensors==0.5.3", "pyasn1" = "0.6.1",
"scikit-learn==1.6.1", "pyasn1_modules" = "0.4.1",
"scipy==1.15.2", "pycparser" = "2.22",
"setuptools==75.8.1", "pyOpenSSL" = "25.0.0",
"six==1.17.0", "pyparsing" = "3.2.1",
"soundfile==0.13.1", "PySocks" = "1.7.1",
"soxr==0.5.0.post1", "pystoi" = "0.4.1",
"sqlparse==0.5.3", "pytest" = "8.3.4",
"submitit==1.5.2", "python-dateutil" = "2.9.0.post0",
"sympy==1.13.1", "pytorch-lightning" = "2.5.0.post0",
"threadpoolctl==3.5.0", "pytorch-ranger" = "0.1.1",
"tokenizers==0.21.0", "pytz" = "2025.1",
"torch==2.6.0", "PyYAML" = "6.0.2",
"torch-optimizer==0.1.0", "redis" = "5.2.1",
"torch-stoi==0.2.3", "regex" = "2024.11.6",
"torchaudio==2.6.0", "requests" = "2.32.3",
"torchlibrosa==0.1.0", "requests-file" = "2.1.0",
"torchmetrics==0.11.4", "retrying" = "1.3.4",
"tqdm==4.67.1", "safetensors" = "0.5.3",
"transformers==4.49.0", "scikit-learn" = "1.6.1",
"treetable==0.2.5", "scipy" = "1.15.2",
"triton==3.2.0", "service-identity" = "24.2.0",
"typing_extensions==4.12.2", "setuptools" = "75.8.1",
"tzdata==2025.1", "six" = "1.17.0",
"urllib3==2.3.0", "soundfile" = "0.13.1",
"vine==5.1.0", "soxr" = "0.5.0.post1",
"wcwidth==0.2.13", "sqlparse" = "0.5.3",
"yarl==1.18.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",
"torch-stoi" = "0.2.3",
"torchaudio" = "2.6.0",
"torchlibrosa" = "0.1.0",
"torchmetrics" = "0.11.4",
"tqdm" = "4.67.1",
"transformers" = "4.49.0",
"treetable" = "0.2.5",
"triton" = "3.2.0",
"Twisted" = "24.11.0",
"txaio" = "23.1.1",
"typing_extensions" = "4.12.2",
"tzdata" = "2025.1",
"urllib3" = "2.3.0",
"vine" = "5.1.0",
"wcwidth" = "0.2.13",
"yarl" = "1.18.3",
"zope.event" = "5.0",
"zope.interface" = "7.2",
] ]
[tool.setuptools] [tool.setuptools]
+14
View File
@@ -9,11 +9,14 @@ asteroid==0.7.0
asteroid-filterbanks==0.4.0 asteroid-filterbanks==0.4.0
attrs==25.1.0 attrs==25.1.0
audioread==3.0.1 audioread==3.0.1
autobahn==24.4.2
Automat==24.8.1
billiard==4.2.1 billiard==4.2.1
cached-property==2.0.1 cached-property==2.0.1
celery==5.4.0 celery==5.4.0
certifi==2025.1.31 certifi==2025.1.31
cffi==1.17.1 cffi==1.17.1
channels==4.2.0
charset-normalizer==3.4.1 charset-normalizer==3.4.1
click==8.1.8 click==8.1.8
click-didyoumean==0.3.1 click-didyoumean==0.3.1
@@ -21,9 +24,12 @@ click-plugins==1.1.1
click-repl==0.3.0 click-repl==0.3.0
cloudpickle==3.1.1 cloudpickle==3.1.1
colorama==0.4.6 colorama==0.4.6
constantly==23.10.4
contourpy==1.3.1 contourpy==1.3.1
cors==1.0.1 cors==1.0.1
cryptography==44.0.2
cycler==0.12.1 cycler==0.12.1
daphne==4.1.2
decorator==5.2.1 decorator==5.2.1
DeepFilterLib==0.5.6 DeepFilterLib==0.5.6
DeepFilterNet==0.5.6 DeepFilterNet==0.5.6
@@ -41,7 +47,9 @@ future==1.0.0
gevent==24.11.1 gevent==24.11.1
greenlet==3.1.1 greenlet==3.1.1
huggingface-hub==0.29.1 huggingface-hub==0.29.1
hyperlink==21.0.0
idna==3.10 idna==3.10
incremental==24.7.2
iniconfig==2.0.0 iniconfig==2.0.0
Jinja2==3.1.5 Jinja2==3.1.5
joblib==1.4.2 joblib==1.4.2
@@ -89,7 +97,10 @@ pluggy==1.5.0
pooch==1.8.2 pooch==1.8.2
prompt_toolkit==3.0.50 prompt_toolkit==3.0.50
propcache==0.3.0 propcache==0.3.0
pyasn1==0.6.1
pyasn1_modules==0.4.1
pycparser==2.22 pycparser==2.22
pyOpenSSL==25.0.0
pyparsing==3.2.1 pyparsing==3.2.1
PySocks==1.7.1 PySocks==1.7.1
pystoi==0.4.1 pystoi==0.4.1
@@ -107,6 +118,7 @@ retrying==1.3.4
safetensors==0.5.3 safetensors==0.5.3
scikit-learn==1.6.1 scikit-learn==1.6.1
scipy==1.15.2 scipy==1.15.2
service-identity==24.2.0
setuptools==75.8.1 setuptools==75.8.1
six==1.17.0 six==1.17.0
soundfile==0.13.1 soundfile==0.13.1
@@ -127,6 +139,8 @@ tqdm==4.67.1
transformers==4.49.0 transformers==4.49.0
treetable==0.2.5 treetable==0.2.5
triton==3.2.0 triton==3.2.0
Twisted==24.11.0
txaio==23.1.1
typing_extensions==4.12.2 typing_extensions==4.12.2
tzdata==2025.1 tzdata==2025.1
urllib3==2.3.0 urllib3==2.3.0