"""Owned, GUI-installable VoxCPM runtime; never downloads or loads models.""" import json,os,shutil,sys,time from pathlib import Path from ltx_original_runtime import LTXOriginalRuntime VERSION='2.0.3' class VoxCPMRuntime(LTXOriginalRuntime): name='VoxCPM' def paths(self): try: ident=json.loads((self.root/'active.json').read_text())['id'] if len(ident)==32 and all(c in '0123456789abcdef' for c in ident): base=self.root/ident if (base/'ready').read_text().strip()==VERSION:return base/'python/bin/python',base except (OSError,ValueError,KeyError):pass return self.root/'missing-python',self.root/'missing-runtime' def status(self): python,base=self.paths();shared=self.image_runtime.paths()[0].is_file() if self.image_runtime else False return dict(installed=python.is_file() and (base/'ready').is_file(),version=VERSION,job=dict(self.job) if self.job else None,required_disk_gib=8 if shared else 25,shared_cuda=shared,worker_available=True,origin='OpenBMB/VoxCPM · offizielles Python-Paket',message='Laufzeitinstallation lädt keine Gewichte. Generierung und Stimmklonen unter Stimmwerkzeuge → Testen.') def start(self): if not shutil.which('ffmpeg'):raise ValueError('FFmpeg fehlt. Systemvoraussetzungen über den Deck-Installer einrichten.') return super().start() def _run(self): base=self.root/self.job['id'];state='failed';phase='VoxCPM-Einrichtung fehlgeschlagen.' try: base.mkdir(mode=0o700);tmp=base/'tmp';tmp.mkdir() # A new environment may reference existing CUDA packages but never alters them. env={k:v for k,v in os.environ.items() if not k.startswith(('HF_','DECK_','LLAMA_'))} env.update(TMPDIR=str(tmp),PIP_NO_INPUT='1',PIP_DISABLE_PIP_VERSION_CHECK='1',HF_HUB_OFFLINE='1',OMP_NUM_THREADS='2') shared=self.image_runtime.paths()[0] if self.image_runtime else Path('/missing') bootstrap=shared if shared.is_file() else Path(sys.executable) self._phase('Eigene VoxCPM-Python-Umgebung erstellen');self._command([bootstrap,'-m','venv',base/'python'],env) python=base/'python/bin/python' self._command([python,'-c','import sys; assert (3,10)<=sys.version_info[:2]<(3,13), "VoxCPM benötigt Python 3.10 bis 3.12"'],env) if shared.is_file(): self._phase('Vorhandene CUDA-Pakete lesend wiederverwenden') self._command([python,'-c','import json,site,subprocess,sys;from pathlib import Path;p=json.loads(subprocess.check_output([sys.argv[1],"-c","import json,site;print(json.dumps(site.getsitepackages()))"],text=True));Path(site.getsitepackages()[0],"deck-shared-cuda.pth").write_text("\\n".join(p)+"\\n")',shared],env) self._phase('CUDA-PyTorch prüfen / installieren') 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('VoxCPM '+VERSION+' und Abhängigkeiten installieren') self._command([python,'-m','pip','install','--no-cache-dir','-c',Path(__file__).parent/'deploy/voxcpm-requirements.lock','voxcpm=='+VERSION],env) self._phase('Paketkonsistenz und VoxCPM2-Unterstützung prüfen') self._command([python,'-m','pip','check'],env) self._command([python,'-c','import torch,soundfile;from voxcpm import VoxCPM;from voxcpm.model.voxcpm2 import VoxCPM2Model;assert torch.version.cuda;assert callable(VoxCPM.from_pretrained)'],env) if self.cancel.is_set():raise InterruptedError() (base/'ready').write_text(VERSION);marker=self.root/'active.tmp';marker.write_text(json.dumps(dict(id=base.name)));marker.replace(self.root/'active.json') state='complete';phase='VoxCPM-Laufzeit installiert · kein Modell geladen · unter Stimmwerkzeuge testen' except InterruptedError:state='cancelled';phase='VoxCPM-Einrichtung abgebrochen.' except Exception as exc:phase=str(exc) finally: if 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()