python实现ai短剧漫剧生成管理seedance免费提示词版本BfwStoryForge代码
代码语言:python
所属分类:其他
版本3(最新):python实现ai短剧漫剧生成管理seedance免费提示词版本BfwStoryForge代码
版本2(旧版):python+agnesai的api实现免费ai短剧生成系统bfwStoryForge代码
版本1(旧版):python+阿里大模型api实现模拟小云雀ai短剧生成工具BfwStoryForge代码
代码描述:python实现ai短剧漫剧生成管理seedance免费提示词版本BfwStoryForge代码,可以管理多个不同的短剧漫剧,根据故事主题剧本生成提示词给任意聊天大模型免费生成aif结构化的角色场景道具音色及变种,还有所有的seedance2提示词及参考资源库,告别手动管理短剧时代。
代码标签: python ai 短剧 漫剧 生成 管理 seedance 免费 提示词 版本 BfwStoryF
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AI有声视频生成工作流系统 - 纯手动离线版 · AIF
无 shots 模式:分段镜头节奏由 seedance2_prompt 时间戳自由划分;
资产库支持变种(含AI生图提示词一键复制);分段出镜资产可选;@图片N/@音频N 按 asset_refs 顺序编号;
自动注入全局风格与画面比例;多人物分镜强制描述站位。
"""
import os
import re
import json
import ast
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__)
KIND_KEY = {"char": "characters", "prop": "props", "scene": "scenes"}
# ============================================================
# 辅助工具
# ============================================================
def _aif_scalar(value):
value = value.strip()
if not value:
return ""
if value.startswith("`") and value.endswith("`") and len(value) >= 2:
value = value[1:-1].strip()
if value in ("null", "NULL", "None", "~"):
return None
if value.lower() in ("true", "false"):
return value.lower() == "true"
if (len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"')):
try:
return ast.literal_eval(value)
except Exception:
return value[1:-1]
if value.startswith("[") and value.endswith("]"):
inner = value[1:-1].strip()
if not inner:
return []
parts, buf, quote, depth = [], [], None, 0
for ch in inner:
if quote:
buf.append(ch)
if ch == quote and (len(buf) < 2 or buf[-2] != "\\"):
quote = None
elif ch in ("'", '"'):
quote = ch
buf.append(ch)
elif ch in "[{":
depth += 1; buf.append(ch)
elif ch in "]}":
depth -= 1; buf.append(ch)
elif ch == "," and depth == 0:
parts.append("".join(buf).strip()); buf = []
else:
buf.append(ch)
if buf:
parts.append("".join(buf).strip())
return [_aif_scalar(x) for x in parts if x != ""]
try:
if re.fullmatch(r"-?\d+", value):
return int(value)
if re.fullmatch(r"-?(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?", value):
return float(value)
except Exception:
pass
return value
def _aif_clean_lines(text):
text = text.replace("\r\n", "\n").replace("\r", "\n").strip()
text = re.sub(r"^\s*```(?:aif|AIF|ai-flow|text)?\s*\n?", "", text)
text = re.sub(r"\n?\s*```\s*$", "", text)
lines = text.split("\n")
header_idx = None
for i, line in enumerate(lines):
if line.strip().lower() in ("aif", "aif 1", "aif/1", "aif 1.0", "aif/1.0"):
header_idx = i
break
if header_idx is not None:
lines = lines[header_idx + 1:]
else:
first = None
for i, line in enumerate(lines):
s = line.strip()
if re.match(r"^[A-Za-z_][\w-]*\s*:", s) or re.match(r"^[A-Za-z_][\w-]*\s*$", s):
first = i
break
if first is not None:
lines = lines[first:]
return lines
def parse_aif(text):
lines = _aif_clean_lines(text)
while lines and not lines[0].strip():
lines.pop(0)
if not lines:
raise ValueError("AIF 内容为空。")
rows = []
for raw in lines:
if not raw.strip() or raw.lstrip().startswith("#"):
continue
indent = len(raw) - len(raw.lstrip(" "))
rows.append((indent, raw.strip()))
def split_key(s):
depth, quote = 0, None
for i, ch in enumerate(s):
if quote:
if ch == quote and (i == 0 or s[i-1] != "\\"):
quote = None
elif ch in ("'", '"'):
quote = ch
elif ch in "[{(":
depth += 1
elif ch in "]})":
depth -= 1
elif ch == ":" and depth == 0:
key = s[:i].strip()
val = s[i+1:].strip()
if key:
return key, val
return None, None
def parse_block(pos, indent):
if pos >= len(rows) or rows[pos][0] < indent:
return {}, pos
is_list = rows[pos][0] == indent and rows[pos][1].startswith("-")
obj = [] if is_list else {}
while pos < len(rows):
ind, content = rows[pos]
if ind < indent:
break
if ind > indent:
break
if is_list:
if not content.startswith("-"):
break
item = content[1:].strip()
if not item:
if pos + 1 < len(rows) and rows[pos+1][0] > indent:
child, pos = parse_block(pos+1, rows[pos+1][0])
obj.append(child)
else:
obj.append(None); pos += 1
continue
k, v = split_key(item)
if k is not None:
d = {}
if v == "":
if pos + 1 < len(rows) and rows[pos+1][0] > indent:
child, nextpos = parse_block(pos+1, rows[pos+1][0])
d[k] = child; pos = nextpos
else:
d[k] = {}; pos += 1
elif v == "|":
vals=[]; pos += 1
while pos < len(rows) and rows[pos][0] > indent:
vals.append(rows[pos][1]); pos += 1
d[k] = "\n".join(vals)
else:
d[k] = _aif_scalar(v); pos += 1
if pos < len(rows) and rows[pos][0] > indent:
child, nextpos = parse_block(pos, rows[pos][0])
if isinstance(child, dict):
d.update(child)
pos = nextpos
obj.append(d)
else:
obj.append(_aif_scalar(item)); pos += 1
else:
k, v = split_key(content)
if k is None:
k = content.strip()
v = ""
if v == "":
if pos + 1 < len(rows) and rows[pos+1][0] > indent:
child, pos = parse_block(pos+1, rows[pos+1][0])
obj[k] = child
else:
obj[k] = {}; pos += 1
elif v == "|":
vals=[]; pos += 1
while pos < len(rows) and rows[pos][0] > indent:
vals.append(rows[pos][1]); pos += 1
obj[k] = "\n".join(vals)
else:
obj[k] = _aif_scalar(v); pos += 1
return obj, pos
root, pos = parse_block(0, rows[0][0])
if not isinstance(root, dict):
raise ValueError("AIF 根节点必须是对象。")
return root
def extract_aif_from_text(text):
if not isinstance(text, str) or not text.strip():
raise ValueError("没有提供 AIF 文本。")
try:
return parse_aif(text)
except Exception as first_error:
m = re.search(r'(?m)^[ \t]*(?:AIF(?:/1(?:\.0)?)?|title\s*:|script\s*$)', text)
if m:
try:
return parse_aif(text[m.start():])
except Exception:
pass
raise ValueError(f"无法解析 AIF:{first_error}")
def _aif_quote(value):
if value is None:
return "null"
if isinstance(value, bool):
return "true" if value else "false"
if isinstance(value, (int, float)):
return str(value)
s = str(value)
if (not s or s.strip() != s or "\n" in s or ":" in s or s.startswith(("[", "{", "#", "-", "`"))):
return json.dumps(s, ensure_ascii=False)
return s
def dump_aif(data, indent=0):
out = ["AIF 1"]
def emit_value(key, value, level):
pad = " " * level
if isinstance(value, dict):
out.append(f"{pad}{key}:")
for k, v in value.items():
emit_value(k, v, level + 2)
elif isinstance(value, list):
if not value:
out.append(f"{pad}{key}: []")
else:
out.append(f"{pad}{key}:")
for item in value:
if isinstance(item, dict):
first = True
for k, v in item.items():
if first:
if isinstance(v, (dict, list)):
out.append(" " * (level + 2) + f"- {k}:")
if isinstance(v, dict):
for sk, sv in v.items(): emit_value(sk, sv, level + 6)
else:
emit_value("", v, level + 6)
else:
out.append(" " * (level + 2) + f"- {k}: {_aif_quote(v)}")
first = False
else:
emit_value(k, v, level + 4)
else:
out.append(" " * (level + 2) + f"- {_aif_quote(item)}")
elif isinstance(value, str) and "\n" in value:
out.append(f"{pad}{key}: |")
for line in value.splitlines():
out.append(" " * (level + 2) + line)
else:
out.append(f"{pad}{key}: {_aif_quote(value)}")
if isinstance(data, dict):
for k, v in data.items():
emit_value(k, v, 0)
return "\n".join(out) + "\n"
# ============================================================
# 资产变种 / 出镜资产 / 编号 相关
# ============================================================
def _ensure_group_variants(group, kind):
if not isinstance(group, dict):
return group
group.setdefault("id", f"{kind}_{uuid.uuid4().hex[:6]}")
group.setdefault("name", "未命名")
group.setdefault("description", "")
vs = group.get("variants")
if not isinstance(vs, list) or not vs:
v = {"id": group["id"], "label": "默认", "image_url": group.get("im.........完整代码请登录后点击上方下载按钮下载查看














网友评论0