python+html实现手动版ai短剧生成工具BfwStoryForge代码
代码语言:python
所属分类:其他
代码描述:python+html实现手动版ai短剧生成工具BfwStoryForge代码,从剧本分镜图片及视频等生成全程提供每一步的提示词和参考图片,去给免费的ai大模型生成需要的数据,粘贴进去一步一步生成ai短剧,图片和视频也是通过ai提示词复制生成,最后合并在一起,就是一个ai无限时长的短剧。注意,此版本无 api,完全通过复制提示词来操作,方便管理。
代码标签: python html 手动版 ai 短剧 生成 工具 BfwStoryForge 代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AI有声视频生成工作流系统 - 纯手动离线版
精简版:去除造型与变体,增强构图与位置描述,自动注入【图X】参考标记
"""
import os
import re
import json
import uuid
import base64
from datetime import datetime
from pathlib import Path
from flask import Flask, request, jsonify, send_from_directory
# ============================================================
# 配置与初始化
# ============================================================
WORK_DIR = Path("workspace")
WORK_DIR.mkdir(exist_ok=True)
PROJECTS_FILE = WORK_DIR / "projects.json"
app = Flask(__name__)
# ============================================================
# 辅助工具
# ============================================================
def extract_json_from_text(text):
"""从 LLM 返回的文本中提取 JSON(去掉 markdown 标记等)"""
text = text.strip()
try:
return json.loads(text)
except:
pass
match = re.search(r'(\{.*\}|\[.*\])', text, re.DOTALL)
if match:
try:
return json.loads(match.group(1))
except:
pass
raise ValueError("无法从提供的文本中解析出合法的 JSON 格式,请检查大模型的回复。如果大模型输出未完成(被截断),请让它继续输出完毕。")
def _normalize_scene_characters(scene: dict):
chars = scene.get("characters")
char_ids = scene.get("character_ids")
if isinstance(chars, list) and chars:
scene["character_ids"] = chars[:]
elif isinstance(char_ids, list) and char_ids:
scene["characters"] = char_ids[:]
elif scene.get("speaking_character"):
scene["characters"] = [scene.get("speaking_character")]
scene["character_ids"] = [scene.get("speaking_character")]
else:
scene["characters"] = []
scene["character_ids"] = []
for key in["props", "prop_ids", "scenes", "scene_ids"]:
if not isinstance(scene.get(key), list):
scene[key] =[]
scene.setdefault("transition_note", "")
scene.setdefault("continuity_note", "")
def _normalize_script_structure(script: dict):
for key in ["characters", "props", "scenes", "segments"]:
script.setdefault(key,[])
if "segment_outlines" in script and not script["segments"]:
script["segments"] = script.pop("segment_outlines")
return script
def save_base64_media(b64_str, output_path: Path):
if "," in b64_str:
b64_str = b64_str.split(",")[1]
with open(output_path, "wb") as f:
f.write(base64.b64decode(b64_str))
return str(output_path)
def _merge_with_ffmpeg(video_clips, output_path):
import subprocess
list_file = WORK_DIR / f"concat_{uuid.uuid4().hex[:8]}.txt"
with open(str(list_file), 'w', encoding='utf-8') as f:
for clip in video_clips:
f.write(f"file '{os.path.abspath(clip)}'\n")
cmd =['ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', str(list_file), '-c', 'copy', output_path]
result = subprocess.run(cmd, capture_output=True, timeout=120)
if result.returncode != 0:
cmd =['ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', str(list_file),
'-c:v', 'libx264', '-preset', 'fast', '-pix_fmt', 'yuv420p',
'-c:a', 'aac', '-b:a', '128k', output_path]
subprocess.run(cmd, capture_output=True, timeout=120)
list_file.unlink(missing_ok=True)
return output_path
# ============================================================
# 项目状态管理
# ============================================================
projects = {}
def load_projects():
global projects
if PROJECTS_FILE.exists():
try:
with open(PROJECTS_FILE, "r", encoding="utf-8") as f:
projects = json.load(f)
except Exception as e:
print("Failed to load projects:", e)
projects = {}
def save_projects():
with open(PROJECTS_FILE, "w", encoding="utf-8") as f:
json.dump(projects, f, ensure_ascii=False, indent=2)
def get_project_dir(project_id):
d = WORK_DIR / project_id
d.mkdir(exist_ok=True)
return d
def get_project(project_id):
if project_id not in projects:
projects[project_id] = {
"id": project_id, "theme": "", "aspect_ratio": "16:9", "visual_style": "realistic",
"total_duration": 300, "script": None, "characters": [], "props": [], "scenes": [],
"segments":[], "final_video_url": None, "status": "init",
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
save_projects()
return projects[project_id]
# ============================================================
# Flask 路由
# ============================================================
@app.route("/")
def index():
return HTML_PAGE
@app.route("/workspace/<project_id>/<path:filename>")
def serve_project_file(project_id, filename):
return send_from_directory(str(WORK_DIR / project_id), filename)
@app.route("/api/projects", methods=["GET"])
def api_list_projects():
return jsonify(list(projects.values()))
@app.route("/api/project/<project_id>", methods=["GET"])
def api_get_project(project_id):
if project_id in projects:
return jsonify(projects[project_id])
return jsonify({"error": "Project not found"}), 404
@app.route("/api/save_manual_script", methods=["POST"])
def api_save_manual_script():
data = request.json
project_id = data.get("project_id") or str(uuid.uuid4().hex[:8])
json_text = data.get("json_text", "")
try:
script = extract_json_from_text(json_text)
except Exception as e:
return jsonify({"error": str(e)}), 400
project = get_project(project_id)
project["theme"] = data.get("theme", "")
project["aspect_ratio"] = data.get("aspect_ratio", "16:9")
project["visual_style"] = data.get("style", "realistic")
project["total_duration"] = data.get("duration", 300)
script = _normalize_script_structure(script)
project["script"] = script
project["characters"] = script.get("characters", [])
project["props"] = script.get("props",[])
project["scenes"] = script.get("scenes", [])
segments =[]
raw_segments = script.get("segments", [])
for i, seg in enumerate(raw_segments):
shots =[]
for shot in seg.get("shots",[]):
_normalize_scene_characters(shot)
shots.append(shot)
segments.append({
"segment_id": seg.get("segment_id", i + 1),
"segment_title": seg.get("segment_title", f"片段{i + 1}"),
&qu.........完整代码请登录后点击上方下载按钮下载查看















网友评论0