Install native audio separator with model packages WAV tests and restore recipe
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"""Owned audio-separator environment and explicitly supported model packages."""
|
||||
import hashlib,json,os,shutil,sys,time
|
||||
from pathlib import Path
|
||||
from ltx_original_runtime import LTXOriginalRuntime
|
||||
VERSION='0.47.0'
|
||||
MODELS={
|
||||
'vocals_mel_band_roformer.ckpt':('MelBand RoFormer · Gesang / Instrumental','vocals_mel_band_roformer.yaml'),
|
||||
'model_bs_roformer_ep_317_sdr_12.9755.ckpt':('BS-RoFormer · Gesang / Instrumental','model_bs_roformer_ep_317_sdr_12.9755.yaml')}
|
||||
class SeparatorRuntime(LTXOriginalRuntime):
|
||||
name='Audio Separator'
|
||||
def __init__(self,root):super().__init__(root,None)
|
||||
def paths(self):
|
||||
marker=self.root/'active.json'
|
||||
if marker.is_file():
|
||||
ident=json.loads(marker.read_text()).get('id','')
|
||||
if len(ident)==32 and all(c in '0123456789abcdef' for c in ident):
|
||||
base=self.root/ident
|
||||
if (base/'ready').is_file():return base/'python/bin/python',base
|
||||
return self.root/'missing-python',self.root/'missing-runtime'
|
||||
def status(self):
|
||||
python,base=self.paths()
|
||||
return dict(installed=python.is_file() and (base/'ready').is_file(),version=VERSION,required_disk_gib=15,job=dict(self.job) if self.job else None,models=[dict(id=name,name=label,configuration=config,installed=self.model_ready(name)) for name,(label,config) in MODELS.items()])
|
||||
def model_ready(self,name):return name in MODELS and (self.root/'models'/(name+'.ready')).is_file() and all((self.root/'models'/f).is_file() and (self.root/'models'/f).stat().st_size>0 for f in (name,MODELS[name][1]))
|
||||
def start(self):
|
||||
if not shutil.which('ffmpeg'):raise ValueError('FFmpeg fehlt. Deck-Systemvoraussetzungen installieren.')
|
||||
with self.lock:
|
||||
if self.job and self.job['state']=='running':raise ValueError('Einrichtungsauftrag läuft bereits.')
|
||||
self.requested_model=None
|
||||
return super().start()
|
||||
def download(self,name):
|
||||
import threading,uuid
|
||||
if not isinstance(name,str) or name not in MODELS:raise ValueError('Dieses Modellpaket ist nicht unterstützt.')
|
||||
with self.lock:
|
||||
if not self.status()['installed']:raise ValueError('Zuerst Audio Separator installieren.')
|
||||
if self.job and self.job['state']=='running':raise ValueError('Einrichtungsauftrag läuft bereits.')
|
||||
if shutil.disk_usage(self.root).free<3*1024**3:raise ValueError('Mindestens 3 GiB freier Speicher erforderlich.')
|
||||
self.cancel.clear();self.requested_model=name;self.job=dict(id=uuid.uuid4().hex,state='running',phase='Modellpaket herunterladen',started_at=time.time());self._save()
|
||||
self.worker=threading.Thread(target=self._run,daemon=True);self.worker.start();return self.status()
|
||||
def _run(self):
|
||||
base=self.root/self.job['id'];state='failed';phase='Einrichtung fehlgeschlagen.';model=getattr(self,'requested_model',None)
|
||||
try:
|
||||
if model:
|
||||
directory=base;directory.mkdir(mode=0o700)
|
||||
self._command([self.paths()[0],'-c','from audio_separator.separator import Separator; import sys; Separator(model_file_dir=sys.argv[1],info_only=True).download_model_files(sys.argv[2])',directory,model])
|
||||
filenames=(model,MODELS[model][1])
|
||||
if any(not (directory/f).is_file() or (directory/f).stat().st_size==0 for f in filenames):raise ValueError('Modell oder YAML-Konfiguration fehlt.')
|
||||
if self.cancel.is_set():raise InterruptedError()
|
||||
destination=self.root/'models';destination.mkdir(exist_ok=True);hashes={}
|
||||
for f in filenames:
|
||||
digest=hashlib.sha256()
|
||||
with (directory/f).open('rb') as source:
|
||||
for chunk in iter(lambda:source.read(1024**2),b''):digest.update(chunk)
|
||||
hashes[f]=digest.hexdigest();(directory/f).replace(destination/f)
|
||||
(destination/(model+'.ready')).write_text(json.dumps(hashes))
|
||||
state='complete';phase='Modell und passende Konfiguration heruntergeladen. Kein Modell geladen.'
|
||||
else:
|
||||
base.mkdir(mode=0o700);tmp=base/'tmp';tmp.mkdir()
|
||||
env=dict(os.environ,TMPDIR=str(tmp),PIP_NO_INPUT='1',PIP_DISABLE_PIP_VERSION_CHECK='1')
|
||||
python=base/'python/bin/python';self._phase('Eigene Python-Umgebung erstellen');self._command([sys.executable,'-m','venv',base/'python'],env)
|
||||
self._phase('CUDA-PyTorch herunterladen · mehrere GiB');self._command([python,'-m','pip','install','--no-cache-dir','torch==2.11.0','torchaudio==2.11.0','--index-url','https://download.pytorch.org/whl/cu128'],env)
|
||||
self._phase('Audio Separator und GPU-Abhängigkeiten installieren');self._command([python,'-m','pip','install','--no-cache-dir',f'audio-separator[gpu]=={VERSION}','onnxruntime-gpu==1.22.0','audioread==3.1.0'],env)
|
||||
self._phase('Paketkonsistenz und CUDA-Unterstützung prüfen');self._command([python,'-m','pip','check'],env)
|
||||
self._command([python,'-c','import torch; from audio_separator.separator import Separator; assert torch.version.cuda'],env)
|
||||
if self.cancel.is_set():raise InterruptedError()
|
||||
(base/'ready').write_text(VERSION);marker=self.root/'active.tmp';marker.write_text(json.dumps({'id':base.name}));marker.replace(self.root/'active.json')
|
||||
state='complete';phase='Audio Separator installiert. Jetzt Modellpaket auswählen.'
|
||||
except InterruptedError:state='cancelled';phase='Einrichtung abgebrochen.'
|
||||
except Exception as exc:phase=str(exc)
|
||||
finally:
|
||||
if model or state!='complete':shutil.rmtree(base,ignore_errors=True)
|
||||
with self.lock:self.process=None;self.job.update(state=state,phase=phase,finished_at=time.time());self._save()
|
||||
Reference in New Issue
Block a user