mirror of
https://github.com/zhouxiaoka/autoclip.git
synced 2026-10-02 02:34:34 +08:00
* docs: isolate visual understanding and game ad creative work * docs: map current AutoClip interface and interaction flows * refactor(frontend): remove legacy UI and isolate clip export flow * feat(studio): unify creative workflow and configurable vision models * feat(studio): preserve independent language variants for 1.4 * feat(studio): connect candidate editing and verify full render workflow * feat(studio): fill portrait frames and add styled title templates * feat(studio): add shared artwork templates with bundled fonts * fix(studio): preserve original audio across reordered cuts * feat(studio): refine four versioned title presets against design * feat(studio): add glossy, pixel and frosted title presets * Refine comic title geometry and preserve ASR word timing * Add optional punchier comic title variant for visual review * Calibrate all six launch title presets and add bilingual review pack * Use design reference thumbnails for six title preset choices * Close out first-release video packaging and defer motion captions * docs: audit game recording to finished creative workflow * docs: prioritize a unified import edit and export experience * feat: confirm smart import plans before production and preserve highlight sequences * docs: record confirmed import and continuous highlight verification * fix: preserve independent highlights instead of merging adjacent events * fix: distinguish source duration from per-highlight duration * fix: isolate SQLite file connections during concurrent preview reads * Prioritize gameplay evidence before selecting and refining highlights Filter explicit non-play categories before the six-highlight cap, rank optional model viewing-value estimates, and retain plain-scene endpoint compatibility. Preserve independent events and audit selection; reject menu-only results without fabricating highlights. 126 distinct offline regression cases pass. One bounded real Seed scan failed after 189.72s; no live quality improvement is claimed. See release log. * Avoid repeated visual analysis after shared failures Share one analysis attempt across selected visual goals; retain independent content processing and completed highlights when promotional copy fails. Persist safe request error codes, stages, durations and HTTP statuses without private payloads. Offline regression: 71 passed; no paid calls. * fix(studio): distinguish draft and versioned export states * fix(studio): preserve full exported frame in result thumbnail * docs: record export state integration checks and remaining gaps * chore: prepare 1.4 integration on latest v1.3.2 main * fix(ui): keep project timestamps readable over cover images * ci: install FFmpeg for real rendering regression tests * feat(publish): use immutable Studio exports in shared publishing flow * feat: observe Studio outcomes with anonymous aggregate events * feat: localize Studio results and export history in eight languages * feat: localize unified import and Studio editing workflow * fix: generate bounded cached still thumbnails for Studio drafts * fix: sample thumbnail frames inside scene boundaries * fix: resume legacy clips and collections in the shared editor * docs: record unified editor and bundled macOS validation gates * test: retain upstream regressions after latest main rebase * fix: bound Studio observation retries and stop terminal polling * docs: record bounded polling integration verification * test: add reproducible offline macOS bundle verification * docs: record bundled runtime verification and new main integration blocker * fix: complete v1.3.3 integration and expose native download response * fix: save Studio exports with the native desktop downloader * docs: record v1.3.4 candidate integration and remaining RC gates * docs: record current macOS bundle evidence and RC rollback gates * fix: localize Studio processing stages and export warnings * fix: recover Studio exports when task dispatch fails * fix: translate export dispatch recovery and record integration checks * fix: preserve import confirmation when production dispatch fails * fix: translate production dispatch recovery and record verification * fix: isolate YouTube imports and plan opt-in visual analysis rollout
66 lines
4.2 KiB
Python
66 lines
4.2 KiB
Python
"""Render first-release six-preset review using production FFmpeg, without model calls."""
|
|
import argparse
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
sys.path.insert(0,str(Path(__file__).resolve().parents[1]))
|
|
from PIL import Image,ImageDraw
|
|
|
|
STYLES=[('comic','漫画冲击'),('neon','荧光挑战'),('arena','竞技斜切'),('pixel','像素街机'),('editorial','极简大字'),('frosted','磨砂字幕卡')]
|
|
CROPS=[(36,89,362,691),(382,89,705,691),(724,89,1050,691),(36,754,362,1357),(382,754,705,1357),(724,754,1050,1357)]
|
|
|
|
|
|
def main():
|
|
p=argparse.ArgumentParser(description=__doc__)
|
|
for name in ['source','reference','baseline','comic-baseline','output']:p.add_argument('--'+name,type=Path,required=True)
|
|
p.add_argument('--styles',nargs='+',choices=[s[0] for s in STYLES],default=[s[0] for s in STYLES])
|
|
args=p.parse_args();out=args.output;out.mkdir(parents=True,exist_ok=True)
|
|
ref=Image.open(args.reference)
|
|
shutil.copy2(args.reference,out/'design-reference.png')
|
|
with tempfile.TemporaryDirectory(prefix='autoclip-six-review-') as data:
|
|
os.environ['AUTOCLIP_DATA_DIR']=data
|
|
from backend.services.studio.models import Draft,Scene
|
|
from backend.services.studio.render import render_draft
|
|
from backend.services.studio.store import directory
|
|
from backend.services.studio.title_art import font_for
|
|
from backend.utils.ffmpeg_utils import get_ffmpeg_path
|
|
directory('review').mkdir(parents=True)
|
|
prior=json.loads((out/'evidence.json').read_text()) if (out/'evidence.json').exists() else []
|
|
evidence=[e for e in prior if e['draft']['title_style'] not in args.styles]
|
|
board=Image.new('RGB',(1200,1170),'#111821');d=ImageDraw.Draw(board)
|
|
for i,(style,label) in enumerate(STYLES):
|
|
for lang,hook in [('en','CAN YOU\nESCAPE?'),('zh','你能逃出\n这里吗?')]:
|
|
key=f'{style}-{lang}'
|
|
if style not in args.styles:continue
|
|
draft=Draft(id=key,title=label,hook=hook,title_style=style,title_template_version=6,aspect='portrait',layout='crop',subtitles=False,scenes=[Scene(id='a',start=8,end=14),Scene(id='b',start=16,end=20)])
|
|
result=render_draft('review',args.source,draft,key,lambda _:None)
|
|
video=out/f'{key}.mp4';shutil.copy2(directory('review')/'output/studio'/video.name,video)
|
|
subprocess.run([get_ffmpeg_path(),'-v','error','-ss','1','-i',str(video),'-frames:v','1','-y',str(out/f'{key}.jpg')],check=True)
|
|
evidence.append({'file':video.name,'draft':draft.model_dump(),'result':result})
|
|
print(key,result,flush=True)
|
|
previous=args.comic_baseline/'comic-v5' if style=='comic' else args.baseline/f'{style}-v3'
|
|
for suffix in ['.mp4','.jpg']:shutil.copy2(str(previous)+suffix,out/f'{style}-old{suffix}')
|
|
panel=ref.crop(CROPS[i]);panel.save(out/f'{style}-reference.jpg',quality=95)
|
|
comparison=Image.new('RGB',(1500,520),'#111821');cd=ImageDraw.Draw(comparison)
|
|
for col,name in enumerate(['设计参考','已确认漫画' if style=='comic' else '上一轮','本轮优化']):
|
|
cd.text((col*500+16,12),name,font=font_for(name,28),fill='white')
|
|
comparison.paste(panel.crop((0,0,panel.width,round(panel.width*.86))).resize((480,413)),(10,65))
|
|
for col,key in [(1,'old'),(2,'en')]:
|
|
image=Image.open(out/f'{style}-{key}.jpg')
|
|
comparison.paste(image.crop((0,0,1080,929)).resize((480,413)),(col*500+10,65))
|
|
comparison.save(out/f'{style}-comparison.jpg',quality=95)
|
|
x=(i%3)*400;y=(i//3)*585
|
|
d.text((x+15,y+10),f'{i+1:02} {label}',font=font_for(label,27),fill='white')
|
|
frame=Image.open(out/f'{style}-en.jpg').crop((0,0,1080,1450)).resize((390,524))
|
|
board.paste(frame,(x+5,y+51))
|
|
board.save(out/'contact.jpg',quality=95)
|
|
(out/'evidence.json').write_text(json.dumps(evidence,ensure_ascii=False,indent=2))
|
|
shutil.copy2(Path(__file__).with_name('title_six_review.html'),out/'index.html')
|
|
print(out/'index.html',flush=True)
|
|
|
|
if __name__=='__main__':main()
|