This commit is contained in:
fengchuanhn@gmail.com
2026-05-21 00:19:11 +08:00
parent 1325ff92e5
commit 7f57dfa9ed
31 changed files with 10437 additions and 12 deletions

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
实时封面预览生成器(移植版:调用 cover_generator.py不依赖 subtitle_cover_generator_simple
"""
import os
import sys
import json
import hashlib
import tempfile
import subprocess
from typing import Dict, Any
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
if SCRIPT_DIR not in sys.path:
sys.path.insert(0, SCRIPT_DIR)
COVER_GENERATOR = os.path.join(SCRIPT_DIR, "cover_generator.py")
STYLE_CONFIGS = {
"default": {
"titlePosition": "top",
"titleColor": "#FFFFFF",
"titleFontSize": 72,
"titleStrokeWidth": 0,
},
"blur-bg": {
"titlePosition": "top",
"titleColor": "#FFFFFF",
"titleFontSize": 72,
"backgroundBlurEnabled": True,
},
"outline": {
"titlePosition": "top",
"titleColor": "#FFFFFF",
"titleFontSize": 72,
"titleStrokeWidth": 4,
"titleStrokeColor": "#000000",
},
"gradient": {
"titlePosition": "top",
"titleColor": "#FFFFFF",
"titleFontSize": 72,
},
"split": {
"titlePosition": "center",
"titleColor": "#FFFFFF",
"titleFontSize": 64,
},
}
class CoverPreviewGenerator:
def __init__(self):
self.temp_dir = os.path.join(tempfile.gettempdir(), "aiclient-cover-previews")
os.makedirs(self.temp_dir, exist_ok=True)
self.preview_cache = {}
self.COVER_STYLES = list(STYLE_CONFIGS.keys())
def get_video_hash(self, video_path: str) -> str:
h = hashlib.md5()
with open(video_path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
h.update(chunk)
return h.hexdigest()[:16]
def _run_cover_generator(self, video_path: str, title: str, output_path: str, style: str) -> bool:
if not os.path.isfile(COVER_GENERATOR):
print(f"Missing {COVER_GENERATOR}", file=sys.stderr)
return False
config = dict(STYLE_CONFIGS.get(style, STYLE_CONFIGS["default"]))
python_exe = os.environ.get("AICLIENT_PYTHON_PATH") or sys.executable
cmd = [
python_exe,
COVER_GENERATOR,
"--video",
video_path,
"--title",
title,
"--output",
output_path,
"--config",
json.dumps(config, ensure_ascii=False),
"--timestamp",
"1",
]
env = os.environ.copy()
env["PYTHONIOENCODING"] = "utf-8"
try:
r = subprocess.run(
cmd,
cwd=SCRIPT_DIR,
capture_output=True,
text=True,
timeout=120,
env=env,
)
if r.returncode != 0:
print(r.stderr or r.stdout, file=sys.stderr)
return r.returncode == 0 and os.path.isfile(output_path)
except Exception as e:
print(f"cover_generator failed: {e}", file=sys.stderr)
return False
def generate_preview(self, video_path: str, style: str, output_path: str) -> bool:
return self._run_cover_generator(
video_path,
"封面预览",
output_path,
style,
)
def generate_all_previews(self, video_path: str) -> Dict[str, Any]:
if not os.path.exists(video_path):
return {"error": f"Video file not found: {video_path}"}
video_hash = self.get_video_hash(video_path)
if video_hash in self.preview_cache:
return self.preview_cache[video_hash]
previews = []
errors = []
for style in self.COVER_STYLES:
output_path = os.path.join(self.temp_dir, f"preview_{video_hash}_{style}.jpg")
ok = self.generate_preview(video_path, style, output_path)
if ok:
previews.append({"style": style, "path": output_path})
else:
errors.append(style)
response = {
"previews": previews,
"videoHash": video_hash,
"tempDir": self.temp_dir,
}
if errors:
response["errors"] = errors
self.preview_cache[video_hash] = response
return response
def main():
import argparse
parser = argparse.ArgumentParser(description="Generate cover style previews")
parser.add_argument("video_path", help="Path to the video file")
parser.add_argument("--output-dir", help="Output directory for previews")
args = parser.parse_args()
if not os.path.exists(args.video_path):
print(json.dumps({"error": f"not found: {args.video_path}"}, ensure_ascii=False))
sys.exit(1)
gen = CoverPreviewGenerator()
if args.output_dir:
gen.temp_dir = args.output_dir
os.makedirs(gen.temp_dir, exist_ok=True)
result = gen.generate_all_previews(args.video_path)
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
main()