166 lines
4.9 KiB
Python
166 lines
4.9 KiB
Python
#!/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()
|