初始化提交:contentm_agent 工作区全量快照
内容分四块: 1、产品规划产出 —— MCN 短视频整合营销工作台的①段五份(1a 需求/1b 竞品/1c 画像/1d 策略/1e 场景)、②段两份(2a 功能/2b 布局)、③段界面(DESIGN.md 契约与令牌表 + mcn-workbench.html 原型 + 实测/会诊/审查三份 + 23 张闸门截图)。 2、开源竞品调研 —— 5 个内容工作台项目的取证原始件与 1b 系列分析文档。 3、参考资料 —— 竞品视频抽帧 1145 张 + 2 个源视频 + 功能点截图。 4、机制侧 —— 协作脚本与状态台账、工作区记忆日志、抽帧/OCR 脚本。 .gitignore 只排运行时日志、脚本备份副本与一次性探针输出,其余按原样入库。
This commit is contained in:
commit
df56c2c137
1773 files changed
+205840
No files matched your search
@@ -0,0 +1,50 @@
|
||||
# scripts 目录说明
|
||||
|
||||
辅助脚本清单与用法。
|
||||
|
||||
## check_env.py
|
||||
|
||||
投放环境检查:POSTIZ_API_KEY、sau 命令、Python 版本、requests 库。
|
||||
|
||||
```bash
|
||||
python check_env.py
|
||||
```
|
||||
|
||||
返回码:0=双通道就绪,1=有缺失项。投放流程步骤 3 开始时运行。
|
||||
|
||||
## postiz_client.py
|
||||
|
||||
Postiz REST API 封装(标准库 + requests),用于海外投放:
|
||||
|
||||
```bash
|
||||
# 列出已连接渠道(取 integration id)
|
||||
python postiz_client.py integrations
|
||||
|
||||
# 上传视频文件(返回 URL)
|
||||
python postiz_client.py upload D:/videos/demo.mp4
|
||||
|
||||
# 创建/排期/发布(请求体为 JSON 文件)
|
||||
python postiz_client.py posts post_body.json
|
||||
|
||||
# 查询时间段内帖子
|
||||
python postiz_client.py query 2026-08-30T00:00:00.000Z 2026-08-31T00:00:00.000Z
|
||||
```
|
||||
|
||||
**凭证**:环境变量 `POSTIZ_API_KEY`(必填),`POSTIZ_API_BASE` 可覆盖(自托管实例)。
|
||||
|
||||
post_body.json 示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "now",
|
||||
"posts": [
|
||||
{
|
||||
"integration": {"id": "yt-integration-id"},
|
||||
"value": [{"content": "视频简介", "image": []}],
|
||||
"settings": {"__type": "youtube", "title": "视频标题", "type": "public"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> 依赖安装:`pip install requests`(upload 子命令需要)。
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""投放环境检查脚本:检查海外(Postiz)与国内(SAU)通道依赖。
|
||||
|
||||
用法:python check_env.py
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
GREEN = "\033[32m"
|
||||
YELLOW = "\033[33m"
|
||||
RED = "\033[31m"
|
||||
RESET = "\033[0m"
|
||||
|
||||
|
||||
def check(label, ok, detail=""):
|
||||
if ok:
|
||||
print(f" {GREEN}[OK]{RESET} {label} {detail}")
|
||||
else:
|
||||
print(f" {RED}[X ]{RESET} {label} {detail}")
|
||||
return ok
|
||||
|
||||
|
||||
def main():
|
||||
print("=== 社媒视频多平台投放 - 环境检查 ===\n")
|
||||
|
||||
# Python
|
||||
py_ok = sys.version_info >= (3, 10)
|
||||
check("Python >= 3.10", py_ok, sys.version.split()[0])
|
||||
|
||||
# 海外通道:Postiz
|
||||
print("\n[海外通道 Postiz]")
|
||||
key = os.environ.get("POSTIZ_API_KEY", "").strip()
|
||||
check("POSTIZ_API_KEY 已设置", bool(key), "" if key else "→ postiz.com → Settings → Developers → Public API 生成")
|
||||
|
||||
# 国内通道:SAU
|
||||
print("\n[国内通道 social-auto-upload]")
|
||||
sau = shutil.which("sau")
|
||||
check("sau 命令可用", sau is not None, f"({sau})" if sau else "→ git clone github.com/dreammis/social-auto-upload && pip install -r requirements.txt")
|
||||
|
||||
# 辅助脚本依赖
|
||||
print("\n[脚本依赖]")
|
||||
try:
|
||||
import requests # noqa
|
||||
check("requests 库", True)
|
||||
except ImportError:
|
||||
check("requests 库", False, "→ pip install requests(postiz_client.py upload 需要)")
|
||||
|
||||
print("\n=== 结论 ===")
|
||||
missing = []
|
||||
if not key:
|
||||
missing.append("海外通道:配置 POSTIZ_API_KEY")
|
||||
if sau is None:
|
||||
missing.append("国内通道:安装 social-auto-upload")
|
||||
if missing:
|
||||
print(f" {YELLOW}需处理{RESET}:")
|
||||
for m in missing:
|
||||
print(f" - {m}")
|
||||
else:
|
||||
print(f" {GREEN}双通道就绪,可进行投放{RESET}")
|
||||
return 0 if not missing else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Postiz REST API 客户端封装(海外投放通道)。
|
||||
|
||||
依赖:requests(pip install requests)
|
||||
凭证:环境变量 POSTIZ_API_KEY(禁止硬编码)
|
||||
用法:
|
||||
python postiz_client.py integrations # 列出已连接渠道
|
||||
python postiz_client.py upload <video_path> # 上传视频,返回 URL
|
||||
python postiz_client.py posts <json_file> # 创建/排期/发布(JSON 文件为请求体)
|
||||
python postiz_client.py query <start> <end> # 查询时间段帖子(ISO UTC)
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
BASE = os.environ.get("POSTIZ_API_BASE", "https://api.postiz.com/public/v1")
|
||||
|
||||
|
||||
def get_key():
|
||||
key = os.environ.get("POSTIZ_API_KEY", "").strip()
|
||||
if not key:
|
||||
print("ERROR: 环境变量 POSTIZ_API_KEY 未设置。请在 postiz.com → Settings → Developers → Public API 生成后配置。")
|
||||
sys.exit(2)
|
||||
return key
|
||||
|
||||
|
||||
def request(method, path, body=None):
|
||||
key = get_key()
|
||||
url = BASE + path
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
req = urllib.request.Request(url, data=data, method=method)
|
||||
req.add_header("Authorization", key)
|
||||
if data:
|
||||
req.add_header("Content-Type", "application/json")
|
||||
try:
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return resp.status, json.loads(resp.read().decode("utf-8") or "null")
|
||||
except urllib.error.HTTPError as e:
|
||||
detail = e.read().decode("utf-8", "ignore")[:500]
|
||||
return e.code, {"error": detail}
|
||||
|
||||
|
||||
def cmd_integrations():
|
||||
status, data = request("GET", "/integrations")
|
||||
print(f"[{status}]")
|
||||
if isinstance(data, list):
|
||||
for it in data:
|
||||
print(f" {it.get('id', '?')} | {it.get('name', it.get('provider', '?'))}")
|
||||
else:
|
||||
print(json.dumps(data, ensure_ascii=False, indent=2)[:2000])
|
||||
|
||||
|
||||
def cmd_upload(path):
|
||||
if not os.path.exists(path):
|
||||
print(f"ERROR: 文件不存在: {path}")
|
||||
sys.exit(3)
|
||||
# 上传走 multipart;此处用 requests 更稳,urllib 需要手写 multipart。
|
||||
# 建议使用 requests 库:
|
||||
try:
|
||||
import requests # noqa
|
||||
except ImportError:
|
||||
print("ERROR: 需要 requests 库:pip install requests")
|
||||
sys.exit(2)
|
||||
key = get_key()
|
||||
with open(path, "rb") as f:
|
||||
resp = requests.post(
|
||||
BASE + "/upload",
|
||||
headers={"Authorization": key},
|
||||
files={"file": (os.path.basename(path), f)},
|
||||
timeout=600,
|
||||
)
|
||||
print(f"[{resp.status_code}]")
|
||||
print(resp.text[:2000])
|
||||
|
||||
|
||||
def cmd_posts(json_file):
|
||||
if not os.path.exists(json_file):
|
||||
print(f"ERROR: 请求体文件不存在: {json_file}")
|
||||
sys.exit(3)
|
||||
with open(json_file, "r", encoding="utf-8") as f:
|
||||
body = json.load(f)
|
||||
status, data = request("POST", "/posts", body)
|
||||
print(f"[{status}]")
|
||||
print(json.dumps(data, ensure_ascii=False, indent=2)[:3000])
|
||||
|
||||
|
||||
def cmd_query(start, end):
|
||||
status, data = request("GET", f"/posts?startDate={start}&endDate={end}")
|
||||
print(f"[{status}]")
|
||||
print(json.dumps(data, ensure_ascii=False, indent=2)[:3000])
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
if not args:
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
cmd, rest = args[0], args[1:]
|
||||
if cmd == "integrations":
|
||||
cmd_integrations()
|
||||
elif cmd == "upload" and rest:
|
||||
cmd_upload(rest[0])
|
||||
elif cmd == "posts" and rest:
|
||||
cmd_posts(rest[0])
|
||||
elif cmd == "query" and len(rest) >= 2:
|
||||
cmd_query(rest[0], rest[1])
|
||||
else:
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user