chore(scripts): 从 mc vault 迁入 ai_gen.py (AI 图片/视频生成 CLI, 原 mc/小唯/配置/scripts/)

This commit is contained in:
小唯 A06 2026-09-10 23:13:45 +08:00
parent 7f04e7476b
commit 1462301816
1 changed files with 289 additions and 0 deletions

289
scripts/ai_gen.py Normal file
View File

@ -0,0 +1,289 @@
#!/usr/bin/env python3
"""
AI 图片/视频生成工具
支持平台: 通义万相(免费额度)海螺AIMiniMax API
用法:
python3 ai_gen.py image "一只橘猫在沙发上" [--model wax3]
python3 ai_gen.py video "海浪拍打沙滩" [--model minimax-video-01]
python3 ai_gen.py status
"""
import os, sys, json, time, argparse, subprocess, urllib.parse, urllib.request
from pathlib import Path
# ─── 配置区 ──────────────────────────────────────────
# 通义万相(阿里云百炼)- 有免费额度
# 获取: https://bailian.console.aliyun.com/
DASHSCOPE_API_KEY = os.environ.get("DASHSCOPE_API_KEY", "")
# 体验地址: https://tongyi.aliyun.com/wanxiang/
# 海螺AI (MiniMax) - API
# 获取: https://www.minimax.io/ 或 https://platform.minimaxi.com/
MINIMAX_API_KEY = os.environ.get("MINIMAX_API_KEY", "")
MINIMAX_GROUP_ID = os.environ.get("MINIMAX_GROUP_ID", "")
# ─── 工具函数 ─────────────────────────────────────────
def run(cmd):
r = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return r.stdout.strip(), r.stderr.strip(), r.returncode
def save_image(url, name=None):
if not name:
name = f"/tmp/ai_gen_{int(time.time())}.png"
subprocess.run(f"curl -sL '{url}' -o '{name}'", shell=True)
print(f" 📷 已保存: {name}")
return name
def save_video(url, name=None):
if not name:
name = f"/tmp/ai_gen_{int(time.time())}.mp4"
subprocess.run(f"curl -sL '{url}' -o '{name}'", shell=True)
print(f" 🎬 已保存: {name}")
return name
# ─── 通义万相 ──────────────────────────────────────────
import requests
def wanxiang_image(prompt, model="wanx3.1-t2i-turbo"):
"""通义万相 - 图像生成(免费额度)"""
if not DASHSCOPE_API_KEY:
print(" ⚠️ 未设置 DASHSCOPE_API_KEY")
print(" 体验地址: https://tongyi.aliyun.com/wanxiang/")
print(" 或获取API: https://bailian.console.aliyun.com/")
return None
url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis"
headers = {
"Authorization": f"Bearer {DASHSCOPE_API_KEY}",
"Content-Type": "application/json",
}
data = {
"model": model,
"input": {"prompt": prompt},
"parameters": {
"size": "1024*1024",
"n": 1,
"style": "<auto>",
}
}
resp = requests.post(url, headers=headers, json=data, timeout=30)
result = resp.json()
if resp.status_code != 200:
print(f" ❌ 错误 {resp.status_code}: {result}")
return None
task_id = result.get("output", {}).get("task_id")
if not task_id:
print(f" ❌ 无task_id: {result}")
return None
# 轮询结果
print(f" ⏳ 任务ID: {task_id},等待生成...")
for i in range(30):
time.sleep(2)
status_url = f"https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}"
s = requests.get(status_url, headers=headers, timeout=10).json()
status = s.get("output", {}).get("task_status", "")
print(f" [{i+1}] {status}")
if status == "succeeded":
img_url = s["output"]["results"][0]["image_url"]
return save_image(img_url)
elif status == "failed":
print(f" ❌ 生成失败: {s}")
return None
print(" ❌ 超时")
return None
# ─── Pollinations免费无需API Key──────────────────
def pollinations_image(prompt, width=1024, height=1024, model=None):
"""Pollinations - 完全免费的AI生图"""
encoded_prompt = urllib.parse.quote(prompt)
url = f"https://image.pollinations.ai/prompt/{encoded_prompt}?width={width}&height={height}"
if model:
url += f"&model={model}"
save_path = f"/tmp/pollen_{int(time.time())}.png"
print(f" 🔗 URL: {url}")
print(f" ⏳ 生成中通常10-30秒...")
try:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"})
with urllib.request.urlopen(req, timeout=60) as resp:
with open(save_path, "wb") as f:
f.write(resp.read())
size = os.path.getsize(save_path)
print(f" ✅ 已保存: {save_path} ({size//1024}KB)")
return save_path
except Exception as e:
print(f" ❌ 下载失败: {e}")
return None
def pollinations_video(prompt, duration=5):
"""Pollinations - 免费视频生成experimental"""
encoded_prompt = urllib.parse.quote(prompt)
url = f"https://video.pollinations.ai/v1?prompt={encoded_prompt}&duration={duration}"
save_path = f"/tmp/pollen_vid_{int(time.time())}.mp4"
print(f" 🔗 {url}")
try:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"})
with urllib.request.urlopen(req, timeout=60) as resp:
with open(save_path, "wb") as f:
f.write(resp.read())
size = os.path.getsize(save_path)
print(f" ✅ 已保存: {save_path} ({size//1024}KB)")
return save_path
except Exception as e:
print(f" ❌ 下载失败: {e}")
return None
# ─── 海螺AI (MiniMax) ─────────────────────────────────
def hailuo_image(prompt, model="image-01"):
"""海螺AI - 图像生成"""
if not MINIMAX_API_KEY:
print(" ⚠️ 未设置 MINIMAX_API_KEY")
print(" 获取: https://platform.minimaxi.com/")
return None
url = "https://api.minimax.chat/v1/image_generation"
headers = {
"Authorization": f"Bearer {MINIMAX_API_KEY}",
"Content-Type": "application/json",
}
data = {
"model": model,
"prompt": prompt,
"aspect_ratio": "1:1",
"response_format": "url",
}
resp = requests.post(url, headers=headers, json=data, timeout=30)
result = resp.json()
if "data" in result and result["data"]:
img_url = result["data"][0]["url"]
return save_image(img_url)
else:
print(f" ❌ 错误: {result}")
return None
def hailuo_video(prompt, model="video-01"):
"""海螺AI - 视频生成(推荐,免费额度高)"""
if not MINIMAX_API_KEY or not MINIMAX_GROUP_ID:
print(" ⚠️ 未设置 MINIMAX_API_KEY 或 MINIMAX_GROUP_ID")
print(" 获取: https://platform.minimaxi.com/")
return None
url = "https://api.minimax.chat/v1/video_generation"
headers = {
"Authorization": f"Bearer {MINIMAX_API_KEY}",
"Content-Type": "application/json",
"GroupId": MINIMAX_GROUP_ID,
}
data = {
"model": model,
"prompt": prompt,
"duration": 5,
"resolution": "720p",
"response_format": "url",
}
resp = requests.post(url, headers=headers, json=data, timeout=30)
result = resp.json()
print(f" 📋 响应: {json.dumps(result, ensure_ascii=False)[:200]}")
task_id = result.get("data", {}).get("task_id") or result.get("task_id")
if not task_id:
print(f" ❌ 无task_id: {result}")
return None
print(f" ⏳ 任务ID: {task_id}等待生成视频约需30-60秒...")
for i in range(60):
time.sleep(3)
# 查询状态
poll_url = f"https://api.minimax.chat/v1/video_generation?task_id={task_id}"
s = requests.get(poll_url, headers=headers, timeout=10).json()
status = s.get("data", {}).get("status", "processing")
print(f" [{i+1}] {status}")
if status == "success" or status == "completed":
video_url = s["data"].get("video_info", {}).get("video_url", "")
if not video_url:
# 尝试其他字段
video_url = s["data"].get("video_url", "")
if video_url:
return save_video(video_url)
print(f" 📋 结果: {json.dumps(s, ensure_ascii=False)[:300]}")
return None
elif status == "failed":
print(f" ❌ 生成失败: {s}")
return None
print(" ❌ 超时")
return None
# ─── 主程序 ───────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(description="AI 图片/视频生成工具")
sub = parser.add_subparsers(dest="cmd")
p_img = sub.add_parser("image", help="生成图片")
p_img.add_argument("prompt", help="图片描述")
p_img.add_argument("--model", default="pollen", help="模型: pollen(默认/免费), wanxiang, hailuo")
p_img.add_argument("--width", type=int, default=1024)
p_img.add_argument("--height", type=int, default=1024)
p_vid = sub.add_parser("video", help="生成视频")
p_vid.add_argument("prompt", help="视频描述")
p_vid.add_argument("--model", default="hailuo", help="模型: hailuo (默认 hailuo)")
sub.add_parser("status", help="查看配置状态")
args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else ["status"])
if args.cmd == "status":
print("=" * 40)
print("AI 生成工具状态")
print("=" * 40)
print(f" Pollinations (图像): ✅ 随时可用(免费)")
has_dash = bool(DASHSCOPE_API_KEY)
has_mini = bool(MINIMAX_API_KEY)
print(f" 通义万相 (图像): {'✅ 已配置' if has_dash else '❌ 未配置'}")
print(f" 海螺AI (图像+视频): {'✅ 已配置' if has_mini else '❌ 未配置'}")
print()
print("获取API密钥:")
print(" 通义万相: https://bailian.console.aliyun.com/ (有免费额度)")
print(" 海螺AI: https://platform.minimaxi.com/")
print()
print("使用方法:")
print(" python3 ai_gen.py image '一只橘猫' # 默认 Pollinations")
print(" python3 ai_gen.py image '一只橘猫' --width 1024 --height 1024")
print(" python3 ai_gen.py image '风景' --model wanxiang # 通义万相")
print(" python3 ai_gen.py video '海浪拍打沙滩' # 海螺AI视频")
print(" python3 ai_gen.py status")
return
if args.cmd == "image":
print(f"\n🎨 生成图片: {args.prompt}")
if args.model == "pollen":
print(" 平台: Pollinations免费无需API Key")
result = pollinations_image(args.prompt, width=args.width, height=args.height)
elif args.model == "wanxiang":
print(" 平台: 通义万相")
result = wanxiang_image(args.prompt)
elif args.model == "hailuo":
print(" 平台: 海螺AI")
result = hailuo_image(args.prompt)
else:
print(f" ❌ 未知模型: {args.model}")
result = None
if result:
print(f"\n✅ 完成: {result}")
elif args.cmd == "video":
print(f"\n🎬 生成视频: {args.prompt}")
result = hailuo_video(args.prompt)
if result:
print(f"\n✅ 完成: {result}")
else:
parser.print_help()
if __name__ == "__main__":
main()