"""LadyPoly UI -> authenticated Deck music API. No local model engine.""" import json, os, time, urllib.request, urllib.error from pathlib import Path from fastapi import HTTPException from fastapi.responses import HTMLResponse from pydantic import BaseModel CONNECTION = Path('/data/deck-connection.json') BASE = 'http://127.0.0.1:8120' def request(path, data=None, connection=None): c = connection or (json.loads(CONNECTION.read_text()) if CONNECTION.exists() else {}) if not c.get('token'): raise RuntimeError('Zuerst unter Deck-Verbindung den API-Token hinterlegen.') req = urllib.request.Request(BASE + path, data=json.dumps(data).encode() if data is not None else None, headers={'Authorization': 'Bearer ' + c['token'], 'Content-Type':'application/json'}) try: with urllib.request.urlopen(req, timeout=1850) as response: return response.read() except urllib.error.HTTPError as exc: try: message=json.loads(exc.read()).get('error', {}).get('message', 'Deck-Anfrage abgelehnt') except Exception: message='Deck-Anfrage abgelehnt' raise RuntimeError(message) from None class Loras: def __init__(self, root):self.extra_dirs=[] def resolve(self, values):return [], [] def status(self, values):return {'adapters':[], 'selected':[], 'missing':[], 'dirs':[]} def discover(self):return [] class Connection(BaseModel): token: str model: str PAGE='''

LadyPoly · Deck-Verbindung

In Deck das YuE2-Profil am API-Endpunkt freigeben und den Musik-Modus aktivieren. Hier den API-Token aus „Zugang & API“ und den genauen Profilnamen eingeben.

LadyPoly öffnen →

Diese Anbindung unterstützt Liedtext, Stil, Seed und Syntheseschritte. Score, LoRA, eigener Writer und Cover-Generator sind hier noch nicht angebunden. Deck lädt und entlädt die Gewichte.

''' def install(g): app=g['app'];g['SETTINGS'].song_engine='gguf';g['SETTINGS'].art_auto=False;g['SETTINGS'].stall_timeout=0 @app.get('/deck-connection', response_class=HTMLResponse) def connection_page():return PAGE @app.post('/api/deck/connection') def connection_save(body: Connection): c=body.model_dump() if not 1<=len(body.token)<=512:raise HTTPException(400,'Ungültiger Token.') try: models=json.loads(request('/v1/audio/music/models',connection=c))['data'] if body.model not in [x['id'] for x in models]:raise RuntimeError('Profil nicht freigegeben oder nicht ausführbar. Bitte in Deck prüfen.') except Exception as e:raise HTTPException(400,str(e)) from None CONNECTION.write_text(json.dumps(c));CONNECTION.chmod(0o600) return {'ok':True} def render(job,spec): if spec.get('abc') or spec.get('cfg_scale') is not None or spec.get('abc_sampling') or spec.get('semantic_sampling') or g['SETTINGS'].loras: raise RuntimeError('Score, Sampling-Overrides und LoRA sind in der Deck-Anbindung noch nicht verfügbar.') c=json.loads(CONNECTION.read_text()) if CONNECTION.exists() else {} spec['cot']='off' job.setup='Deck lädt YuE2 und erzeugt Musik · Musik-Modus erforderlich';job.setup_at=time.time();job.push(force=True) start=time.monotonic() audio=request('/v1/audio/music',dict(model=c.get('model',''),lyrics=spec['lyrics'],style=spec['style'],seed=spec['seed'],steps=g['SETTINGS'].ode_steps,max_tokens=9000)) if job.cancel_flag.is_set():raise InterruptedError('UI-Auftrag abgebrochen. Laufenden Deck-Job in Deck stoppen.') work=g['OUTPUTS']/('.pending-'+job.id);work.mkdir(parents=True,exist_ok=True) wav=work/'audio.wav';wav.write_bytes(audio) return g['GgufSong'](spec,wav,{'e2e_seconds':time.monotonic()-start},'',{'runtime':'Athena Deck audio.cpp','profile':c.get('model')}) g['render_gguf']=render;g['render_torch']=render g['ENGINE'].detail='Remote-Laufzeit in Athena Deck · Musik-Modus erforderlich' @app.middleware('http') async def deck_guard(req,call_next): path=req.url.path origin=req.headers.get('Origin') if req.method!='GET' and origin and origin not in ('http://'+req.headers.get('Host',''),'https://'+req.headers.get('Host','')): return g['JSONResponse']({'detail':'Cross-Origin-Zugriff nicht erlaubt.'},status_code=403) if req.method!='GET' and not (path in {'/api/generate','/api/deck/connection','/api/unlock'} or (req.method=='DELETE' and path.startswith('/api/library/') and path.count('/')==3)): return g['JSONResponse']({'detail':'Laufzeiten, Modelle und Einstellungen werden in Athena Deck verwaltet.'},status_code=409) response=await call_next(req) response.headers['Cache-Control']='no-store' return response