Add audio separation web app and verified recovery sources
This commit is contained in:
1 parent
b5ee7c1e01
commit
433e6709cf
25 files changed
+381
-46
No files matched your search
+43
-6
@@ -3,9 +3,22 @@ import hashlib,json,os,shutil,sys,time
|
||||
from pathlib import Path
|
||||
from ltx_original_runtime import LTXOriginalRuntime
|
||||
VERSION='0.47.0'
|
||||
SOURCES=json.loads(Path(__file__).with_name('separator_sources.json').read_text())
|
||||
def package_files(name):return [x['file'] for x in SOURCES[name]['files']]
|
||||
def verify_package(directory,name):
|
||||
for item in SOURCES[name]['files']:
|
||||
path=Path(directory)/item['file']
|
||||
if not path.is_file() or path.is_symlink():return False
|
||||
digest=hashlib.sha256()
|
||||
with path.open('rb') as stream:
|
||||
for chunk in iter(lambda:stream.read(1024**2),b''):digest.update(chunk)
|
||||
if digest.hexdigest()!=item['sha256']:return False
|
||||
return True
|
||||
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')}
|
||||
'model_bs_roformer_ep_317_sdr_12.9755.ckpt':('BS-RoFormer · Gesang / Instrumental','model_bs_roformer_ep_317_sdr_12.9755.yaml'),
|
||||
'htdemucs_ft.yaml':('Demucs FT · Gesang / Schlagzeug / Bass / Rest','htdemucs_ft.yaml'),
|
||||
'htdemucs_6s.yaml':('Demucs 6s · zusätzlich Gitarre / Piano','htdemucs_6s.yaml')}
|
||||
class SeparatorRuntime(LTXOriginalRuntime):
|
||||
name='Audio Separator'
|
||||
def __init__(self,root):super().__init__(root,None)
|
||||
@@ -19,8 +32,15 @@ class SeparatorRuntime(LTXOriginalRuntime):
|
||||
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]))
|
||||
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,source=SOURCES[name],installed=self.model_ready(name)) for name,(label,config) in MODELS.items()])
|
||||
def model_ready(self,name):
|
||||
if name not in MODELS:return False
|
||||
directory=self.root/'models';marker=directory/(name+'.ready')
|
||||
try:
|
||||
recorded=json.loads(marker.read_text())
|
||||
return recorded=={x['file']:x['sha256'] for x in SOURCES[name]['files']} and all((directory/f).is_file() and not (directory/f).is_symlink() and (directory/f).stat().st_size>0 for f in package_files(name))
|
||||
except (OSError,ValueError):return False
|
||||
|
||||
def start(self):
|
||||
if not shutil.which('ffmpeg'):raise ValueError('FFmpeg fehlt. Deck-Systemvoraussetzungen installieren.')
|
||||
with self.lock:
|
||||
@@ -41,9 +61,26 @@ class SeparatorRuntime(LTXOriginalRuntime):
|
||||
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.')
|
||||
import urllib.request
|
||||
for item in SOURCES[model]['files']:
|
||||
self._phase('Modellpaket: '+item['file'])
|
||||
succeeded=False
|
||||
for url in item['urls']:
|
||||
try:
|
||||
digest=hashlib.sha256();size=0
|
||||
with urllib.request.urlopen(url,timeout=60) as response,(directory/item['file']).open('wb') as out:
|
||||
while chunk:=response.read(1024**2):
|
||||
if self.cancel.is_set():raise InterruptedError()
|
||||
size+=len(chunk)
|
||||
if size>2*1024**3:raise ValueError('Quelldatei zu groß.')
|
||||
digest.update(chunk);out.write(chunk)
|
||||
if digest.hexdigest()!=item['sha256']:raise ValueError('Quelldatei stimmt nicht mit dem geprüften Rezept überein.')
|
||||
succeeded=True;break
|
||||
except InterruptedError:raise
|
||||
except Exception:continue
|
||||
if not succeeded:raise ValueError('Geprüfte Quelle nicht verfügbar: '+item['file'])
|
||||
filenames=package_files(model)
|
||||
if not verify_package(directory,model):raise ValueError('Modellpaket-Prüfsumme stimmt nicht.')
|
||||
if self.cancel.is_set():raise InterruptedError()
|
||||
destination=self.root/'models';destination.mkdir(exist_ok=True);hashes={}
|
||||
for f in filenames:
|
||||
|
||||
Reference in new issue
Block a user