62 lines
5.8 KiB
Python
62 lines
5.8 KiB
Python
"""Reproduce the existing storyboard request in a private DB copy; never change live projects or submit media."""
|
|
import contextlib,json,os,pathlib,re,sqlite3,subprocess,time,urllib.request,urllib.error
|
|
ROOT=pathlib.Path(__file__).resolve().parent.parent
|
|
OUT=ROOT/'integration-work'/('storyboard-loop-diagnostic-'+time.strftime('%Y%m%d-%H%M%S'));OUT.mkdir()
|
|
(ROOT/'integration-work/current-storyboard-diagnostic.txt').write_text(str(OUT),'utf-8')
|
|
dbfile=OUT/'huobao.sqlite3'
|
|
with contextlib.closing(sqlite3.connect((ROOT/'huobao-data/huobao.sqlite3').as_uri()+'?mode=ro',uri=True)) as live,contextlib.closing(sqlite3.connect(dbfile)) as db:live.backup(db)
|
|
with sqlite3.connect(dbfile) as db:
|
|
db.row_factory=sqlite3.Row
|
|
db.execute('delete from sys_task');db.execute('delete from video_merges');db.execute("delete from app_settings where key like 'storyboard_job:%'")
|
|
ep=dict(db.execute('select * from episodes where id=8').fetchone())
|
|
cfg=dict(db.execute("select * from ai_service_configs where provider='volcengine' and service_type='text' and is_active=1 order by id desc limit 1").fetchone())
|
|
model=json.loads(cfg['model'])[0]
|
|
chars=[dict(r) for r in db.execute('select id,name from characters where drama_id=? and deleted_at is null',(ep['drama_id'],))]
|
|
scenes=[dict(r) for r in db.execute('select id,location from scenes where drama_id=? and deleted_at is null',(ep['drama_id'],))]
|
|
body={'drama_id':ep['drama_id'],'episode_id':8,'config_id':cfg['id'],'model':model,'message':'请基于当前集剧本拆分完整分镜,并为每个分镜段落同时生成 video_prompt(中文视频提示词)和 video_prompt_en(英文制作提示词,中文对白及 @/# 素材引用原样保留)。本次视频模型:MiniMax-H3。当前集已有角色:'+json.dumps(chars,ensure_ascii=False)+'。当前集已有场景:'+json.dumps(scenes,ensure_ascii=False)+'。必须从已有角色和场景中选择正确 ID 绑定,按顺序分批保存全部分镜。'}
|
|
source=(ROOT/'huobao-source/desktop/build/backend.mjs').read_text('utf-8').replace('maxSteps: 30,', 'maxSteps: 6,')
|
|
stage=ROOT/'integration-work/stage-4.0.11/app'
|
|
source=source.replace('const require = __hbCreateRequire(import.meta.url)','const require = __hbCreateRequire('+json.dumps(str(stage/'resources/app.asar/build/backend.mjs'))+')',1)
|
|
source=re.sub(r'^import (\w+) from "(better-sqlite3|sharp)";',r'const \1 = require("\2");',source,flags=re.M)
|
|
instrument=r'''
|
|
const __diagFetch=globalThis.fetch; let __diagN=0;
|
|
globalThis.fetch=async(input,init)=>{
|
|
const url=new URL(typeof input==='string'?input:input.url||String(input));
|
|
if(url.hostname==='127.0.0.1')return __diagFetch(input,init);
|
|
const n=++__diagN,t=Date.now();let b={};try{b=JSON.parse(init?.body||'{}')}catch{}
|
|
console.log('DIAGNOSTIC '+JSON.stringify({event:'request',n,model:b.model,stream:b.stream,max_tokens:b.max_tokens,thinking:b.thinking,messages:b.messages?.length,input_chars:JSON.stringify(b.messages||[]).length,tools:b.tools?.map(x=>x.function.name),recent_tools:b.messages?.filter(x=>x.role==='tool').slice(-2).map(x=>({id:x.tool_call_id,content:String(x.content).slice(0,1200)})),recent_calls:b.messages?.filter(x=>x.tool_calls).slice(-1).map(x=>x.tool_calls.map(t=>({name:t.function.name,args:t.function.arguments.slice(0,150)})))}));
|
|
try{const r=await __diagFetch(input,{...init,signal:AbortSignal.any([...(init?.signal?[init.signal]:[]),AbortSignal.timeout(240000)])});
|
|
console.log('DIAGNOSTIC '+JSON.stringify({event:'headers',n,status:r.status,seconds:(Date.now()-t)/1000}));
|
|
r.clone().text().then(s=>console.log('DIAGNOSTIC '+JSON.stringify({event:'body',n,chars:s.length,seconds:(Date.now()-t)/1000,finish:s.match(/"finish_reason"\s*:\s*"[^" ]+"/g)?.slice(-2)}))).catch(()=>{});return r;
|
|
}catch(e){console.log('DIAGNOSTIC '+JSON.stringify({event:'error',n,name:e.name,seconds:(Date.now()-t)/1000}));throw e}
|
|
};
|
|
'''
|
|
(OUT/'backend.mjs').write_text(instrument+source,'utf-8')
|
|
env={**os.environ,'ELECTRON_RUN_AS_NODE':'1','PORT':'18982','HUOBAO_DATA_DIR':str(OUT),'SQLITE_PATH':str(dbfile),'FRONTEND_DIST':str(ROOT/'huobao-source/frontend/.output/public'),'WORKSPACE_PATH':str(stage/'resources/workspace-template')}
|
|
process=None
|
|
try:
|
|
with (OUT/'backend.log').open('w',encoding='utf-8') as log:
|
|
process=subprocess.Popen([str(stage/'HuobaoDrama.exe'),str(OUT/'backend.mjs')],cwd=OUT,env=env,stdout=log,stderr=log,creationflags=subprocess.CREATE_NO_WINDOW)
|
|
for _ in range(100):
|
|
if process.poll() is not None:raise RuntimeError('Isolated backend failed to start')
|
|
try:
|
|
urllib.request.urlopen('http://127.0.0.1:18982/api/v1/health',timeout=1).close();break
|
|
except OSError:time.sleep(.2)
|
|
start=time.monotonic();print(json.dumps({'started':str(OUT),'episode':8,'liveProjectUnmodified':True}),flush=True)
|
|
req=urllib.request.Request('http://127.0.0.1:18982/api/v1/agent/storyboard_breaker/jobs',data=json.dumps(body).encode(),headers={'Content-Type':'application/json'})
|
|
try:
|
|
with urllib.request.urlopen(req,timeout=600) as response:result=json.load(response)
|
|
except urllib.error.HTTPError as e:result=json.load(e)
|
|
except Exception as e:result={'error':type(e).__name__}
|
|
|
|
if result.get('data',{}).get('status')=='running':
|
|
while time.monotonic()-start<600:
|
|
time.sleep(3)
|
|
with urllib.request.urlopen('http://127.0.0.1:18982/api/v1/agent/storyboard_breaker/jobs/8',timeout=10) as response: result=json.load(response)
|
|
if result.get('data',{}).get('status')!='running':break
|
|
(OUT/'result.json').write_text(json.dumps(result,ensure_ascii=False,indent=2),'utf-8')
|
|
with sqlite3.connect(dbfile) as db:count=db.execute('select count(*) from storyboards where episode_id=8').fetchone()[0]
|
|
print(json.dumps({'seconds':round(time.monotonic()-start,1),'shots':count,'code':result.get('code'),'error':result.get('message') or result.get('error')}),flush=True)
|
|
finally:
|
|
if process and process.poll() is None:process.terminate();process.wait(15)
|