Replace Deck video generation with selected external service lifecycle

This commit is contained in:
Mikei386
2026-09-29 14:24:52 +02:00
parent 71e8345606
commit badc7701fa
23 changed files with 219 additions and 636 deletions
+4 -39
View File
@@ -36,7 +36,7 @@ class Endpoint:
def rows(self):
rows=self.profiles.status()['profiles']
with self.lock:enabled=set(self.config['enabled_profiles'])
return [dict(p,enabled=(p['id']==self.video.selected if self.video else False) if p['kind']=='video' else p['id'] in enabled) for p in rows]
return [dict(p,enabled=p['id'] in enabled) for p in rows if p['kind']!='video']
def status(self):
rows=self.rows();worker=self.worker.status();job=self.images.status()['job'];counts={}
for key,kind in [('llm','chat'),('image','image'),('tts','audio'),('stt','stt')]:
@@ -62,11 +62,6 @@ class Endpoint:
row=next((p for p in rows if p['id']==data['id']),None)
if not row:raise ValueError('Profil nicht gefunden.')
if data['enabled'] and not row['runnable']:raise ValueError('Profil nicht ausführbar: '+' '.join(row['blockers']))
if row['kind']=='video':
if not self.video:raise ValueError('Video-Verwaltung nicht eingerichtet.')
if data['enabled']:self.video.select(row['id'])
elif self.video.selected==row['id']:self.video.select(None)
return self.status()
with self.lock:
enabled=set(self.config['enabled_profiles'])
if data['enabled']:
@@ -99,10 +94,6 @@ class Endpoint:
http=self.http
if http:http.shutdown();http.server_close()
while True:
if self.video:
vs=self.video.status()
if vs['state']=='switching':time.sleep(.1);continue
if vs['mode']=='video':self.video.switch('llm');time.sleep(.1);continue
with self.lock:pending=self.inflight
if not pending and self.scheduler.unload_idle():break
time.sleep(.1)
@@ -159,35 +150,9 @@ class APIHandler(BaseHTTPRequestHandler):
if self.sent:return
self.send({'error':{'message':str(exc),'type':getattr(exc,'code','server_error'),'param':None,'code':getattr(exc,'code','worker_unavailable')}},getattr(exc,'status',503))
def video_route(self,ep):
if not self.path.startswith('/v1/videos'):return False
if not ep.video:raise APIError('Video-Worker nicht eingerichtet.',503)
if self.command=='GET' and self.path=='/v1/videos/models':
ident=ep.video.selected
self.send({'object':'list','data':[{'id':'athena-video','object':'model','owned_by':'athena-deck'}] if any(p['id']==ident and p['runnable'] for p in ep.rows()) else []});return True
if self.command=='POST' and self.path=='/v1/videos':
if self.headers.get('Transfer-Encoding'):raise APIError('Chunked Upload nicht unterstützt.')
try:length=int(self.headers.get('Content-Length','0'))
except ValueError:raise APIError('Ungültige Länge.')
if not 0<length<=65536 or self.headers.get('Content-Type','').split(';')[0]!='application/json':raise APIError('JSON bis 64 KiB erforderlich.',413)
try:
data=json.loads(self.rfile.read(length))
if not isinstance(data,dict):raise ValueError('JSON-Objekt erforderlich.')
job=ep.video.generate(data)
except ValueError as exc:raise APIError(str(exc),409,'video_not_ready') from None
self.send(job,202);return True
parts=self.path.split('/')
if self.command=='GET' and len(parts) in (4,5) and re.fullmatch('[a-f0-9]{32}',parts[3]):
job=ep.video.status()['job']
if not job or job['id']!=parts[3]:raise APIError('Video-Auftrag nicht gefunden.',404)
if len(parts)==4:self.send(job);return True
if parts[4]=='content':
try:path=ep.video.result(parts[3])
except ValueError as exc:raise APIError(str(exc),409) from None
self.sent=True;self.send_response(200);self.send_header('Content-Type','video/mp4');self.send_header('Content-Length',str(path.stat().st_size));self.send_header('Cache-Control','no-store');self.send_header('Connection','close');self.end_headers()
with path.open('rb') as source:
while chunk:=source.read(1024*1024):self.wfile.write(chunk)
self.close_connection=True;return True
raise APIError('Video-Route nicht gefunden.',404)
if self.path.startswith('/v1/videos'):
raise APIError('Video wird ausschließlich über die originale LTX-API auf dem separaten LTX-Port bedient.',410,'use_ltx_api')
return False
def do_GET(self):self.route()
def do_POST(self):self.route()
def route(self):