AI 生成: 一个基于 AI 的全自动短视频生成工具,输入主题即可自动生成脚本、配音、字幕和合成视频。使用 Python + Flask 提供 Web 界面,支持命令行模式。
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Flask Web 入口
|
||||
|
||||
提供简单的 Web 界面,输入主题后自动生成短视频。
|
||||
"""
|
||||
|
||||
import os
|
||||
import uuid
|
||||
import traceback
|
||||
from flask import Flask, render_template, request, jsonify, send_from_directory
|
||||
|
||||
from core.script_gen import generate_script
|
||||
from core.tts import text_to_speech
|
||||
from core.subtitle import build_srt
|
||||
from core.video import compose_video
|
||||
|
||||
app = Flask(__name__)
|
||||
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "output")
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
|
||||
def _probe_duration(path: str) -> float:
|
||||
"""获取音频时长(秒)"""
|
||||
from moviepy.editor import AudioFileClip
|
||||
clip = AudioFileClip(path)
|
||||
dur = clip.duration
|
||||
clip.close()
|
||||
return float(dur)
|
||||
|
||||
|
||||
def run_pipeline(topic: str, scenes: int = 5, rate: int = 160) -> dict:
|
||||
"""完整生成流水线"""
|
||||
job_id = uuid.uuid4().hex[:8]
|
||||
job_dir = os.path.join(OUTPUT_DIR, job_id)
|
||||
audio_dir = os.path.join(job_dir, "audio")
|
||||
os.makedirs(audio_dir, exist_ok=True)
|
||||
|
||||
script = generate_script(topic, scenes)
|
||||
|
||||
audio_paths = []
|
||||
durations = []
|
||||
for s in script:
|
||||
ap = os.path.join(audio_dir, f"{s['index']}.mp3")
|
||||
text_to_speech(s["text"], ap, rate=rate)
|
||||
audio_paths.append(ap)
|
||||
durations.append(_probe_duration(ap))
|
||||
|
||||
srt_path = os.path.join(job_dir, "subtitle.srt")
|
||||
build_srt(script, durations, srt_path)
|
||||
|
||||
video_path = os.path.join(job_dir, "video.mp4")
|
||||
compose_video(script, audio_paths, durations, video_path)
|
||||
|
||||
return {
|
||||
"job_id": job_id,
|
||||
"video": f"output/{job_id}/video.mp4",
|
||||
"subtitle": f"output/{job_id}/subtitle.srt",
|
||||
"script": script,
|
||||
}
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@app.route("/api/generate", methods=["POST"])
|
||||
def api_generate():
|
||||
data = request.get_json(force=True, silent=True) or {}
|
||||
topic = (data.get("topic") or "").strip()
|
||||
scenes = int(data.get("scenes") or 5)
|
||||
rate = int(data.get("rate") or 160)
|
||||
|
||||
if not topic:
|
||||
return jsonify({"ok": False, "error": "请填写主题"}), 400
|
||||
if scenes < 1 or scenes > 8:
|
||||
return jsonify({"ok": False, "error": "场景数需在 1-8 之间"}), 400
|
||||
|
||||
try:
|
||||
result = run_pipeline(topic, scenes, rate)
|
||||
return jsonify({"ok": True, **result})
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return jsonify({"ok": False, "error": str(e)}), 500
|
||||
|
||||
|
||||
@app.route("/output/<path:filename>")
|
||||
def serve_output(filename):
|
||||
return send_from_directory(OUTPUT_DIR, filename)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||
Reference in New Issue
Block a user