🔄 卡若AI 同步 2026-03-13 20:15 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个

This commit is contained in:
2026-03-13 20:15:58 +08:00
parent d48fce073e
commit b8519fc19a
8 changed files with 642 additions and 108 deletions

View File

@@ -154,12 +154,21 @@
{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/赚钱没那么复杂,自信心才是核心问题.mp4", "title": "获得收益真没那么复杂,自信心才是卡住你的核心问题", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.14186525344848633, "timestamp": "2026-03-11 17:19:12"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片_大师版/AI每天剪1000个视频 M4电脑24T素材库全网分发.mp4", "title": "M4芯片+24T素材库AI每天剪1000条视频自动全网分发", "success": true, "status": "published", "message": "页面已重置(发布成功)", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 28.265141010284424, "timestamp": "2026-03-11 17:19:43"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片_大师版/Soul派对变现全链路 发视频就有钱,后端全解决.mp4", "title": "Soul派对怎么商业转化发视频就有收益后端体系全部搞定", "success": true, "status": "published", "message": "页面已重置(发布成功)", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 26.74960207939148, "timestamp": "2026-03-11 17:20:13"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/Cursor的权限问题安全隐患必须提前讲清楚.mp4", "title": "Cursor的权限问题安全隐患必须提前讲清楚 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (13.0s)", "elapsed_sec": 13.048572778701782, "timestamp": "2026-03-13 16:11:37"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/NFC碰一碰引流线下餐饮店用这招就够了.mp4", "title": "NFC碰一碰引流线下餐饮店用这招就够了 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (10.9s)", "elapsed_sec": 10.91233205795288, "timestamp": "2026-03-13 16:11:56"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/Skill和Cursor的区别一个走工作流一个走对话.mp4", "title": "Skill和Cursor的区别一个走工作流一个走对话 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (8.0s)", "elapsed_sec": 8.028389930725098, "timestamp": "2026-03-13 16:12:13"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/Soul派对比抖音省力太多连麦机制是核心差异.mp4", "title": "Soul派对比抖音省力太多连麦机制是核心差异 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (9.1s)", "elapsed_sec": 9.126675844192505, "timestamp": "2026-03-13 16:12:30"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/做API聚合给别人充值这是另一种AI变现路径.mp4", "title": "做API聚合给别人充值这是另一种AI变现路径 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (14.9s)", "elapsed_sec": 14.861711025238037, "timestamp": "2026-03-13 16:12:54"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/帮别人装AI工具就能赚钱工作流才是变现入口.mp4", "title": "帮别人装AI工具就能赚钱,工作流才是变现入口 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (12.3s)", "elapsed_sec": 12.33399510383606, "timestamp": "2026-03-13 16:13:14"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/本地部署大模型到底行不行,小事可以大事别想.mp4", "title": "本地部署大模型到底行不行,小事可以大事别想 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (12.8s)", "elapsed_sec": 12.772934913635254, "timestamp": "2026-03-13 16:13:36"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/每个消费者都是你的流量入口,碰一碰就能连接.mp4", "title": "每个消费者都是你的流量入口,碰一碰就能连接 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (11.6s)", "elapsed_sec": 11.603442907333374, "timestamp": "2026-03-13 16:13:56"}
{"platform": "视频号", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final/电竞19年经验加AI跨界结合才有真正的护城河.mp4", "title": "电竞19年经验加AI跨界结合才有真正的护城河 #Soul派对 #创业日记", "success": true, "status": "published", "message": "纯 API 发布成功 (12.8s)", "elapsed_sec": 12.751282215118408, "timestamp": "2026-03-13 16:14:17"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/Cursor的权限问题安全隐患必须提前讲清楚.mp4", "title": "Cursor的权限问题安全隐患必须提前讲清楚", "success": true, "status": "reviewing", "message": "纯API投稿成功 (19.0s)", "elapsed_sec": 18.9708149433136, "timestamp": "2026-03-13 18:51:14"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/NFC碰一碰引流线下餐饮店用这招就够了.mp4", "title": "NFC碰一碰引流线下餐饮店用这招就够了", "success": true, "status": "reviewing", "message": "纯API投稿成功 (8.5s)", "elapsed_sec": 8.464314222335815, "timestamp": "2026-03-13 18:51:25"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/Skill和Cursor的区别一个走工作流一个走对话.mp4", "title": "Skill和Cursor的区别一个走工作流一个走对话", "success": true, "status": "reviewing", "message": "纯API投稿成功 (8.5s)", "elapsed_sec": 8.452302932739258, "timestamp": "2026-03-13 18:51:37"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/Soul派对比抖音省力太多连麦机制是核心差异.mp4", "title": "Soul派对比抖音省力太多连麦机制是核心差异", "success": true, "status": "reviewing", "message": "纯API投稿成功 (7.1s)", "elapsed_sec": 7.1410510540008545, "timestamp": "2026-03-13 18:51:47"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/做API聚合给别人充值这是另一种AI变现路径.mp4", "title": "做API聚合给别人充值这是另一种AI变现路径", "success": true, "status": "reviewing", "message": "纯API投稿成功 (7.6s)", "elapsed_sec": 7.589388132095337, "timestamp": "2026-03-13 18:51:58"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/帮别人装AI工具就能赚钱工作流才是变现入口.mp4", "title": "帮别人装AI工具就能获得收益,工作流才是变现入口", "success": true, "status": "reviewing", "message": "纯API投稿成功 (10.4s)", "elapsed_sec": 10.424948930740356, "timestamp": "2026-03-13 18:52:11"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/本地部署大模型到底行不行,小事可以大事别想.mp4", "title": "本地部署大模型到底行不行,小事可以大事别想", "success": true, "status": "reviewing", "message": "纯API投稿成功 (4.7s)", "elapsed_sec": 4.678756952285767, "timestamp": "2026-03-13 18:52:19"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/每个消费者都是你的流量入口,碰一碰就能连接.mp4", "title": "每个消费者都是你的流量入口,碰一碰就能连接", "success": true, "status": "reviewing", "message": "纯API投稿成功 (3.4s)", "elapsed_sec": 3.3650221824645996, "timestamp": "2026-03-13 18:52:25"}
{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/电竞19年经验加AI跨界结合才有真正的护城河.mp4", "title": "电竞19年经验加AI跨界结合才有真正的护城河", "success": true, "status": "reviewing", "message": "纯API投稿成功 (2.6s)", "elapsed_sec": 2.625749111175537, "timestamp": "2026-03-13 18:52:31"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/Cursor的权限问题安全隐患必须提前讲清楚.mp4", "title": "Cursor的权限问题安全隐患必须提前讲清楚", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 48.234835147857666, "timestamp": "2026-03-13 19:02:18"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/NFC碰一碰引流线下餐饮店用这招就够了.mp4", "title": "NFC碰一碰引流线下餐饮店用这招就够了", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 47.52175688743591, "timestamp": "2026-03-13 19:03:32"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/Skill和Cursor的区别一个走工作流一个走对话.mp4", "title": "Skill和Cursor的区别一个走工作流一个走对话", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 47.63735032081604, "timestamp": "2026-03-13 19:04:46"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/Soul派对比抖音省力太多连麦机制是核心差异.mp4", "title": "Soul派对比抖音省力太多连麦机制是核心差异", "success": false, "status": "error", "message": "未找到上传控件", "screenshot": "/tmp/xhs_no_input.png", "elapsed_sec": 6.9848058223724365, "timestamp": "2026-03-13 19:04:56"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/做API聚合给别人充值这是另一种AI变现路径.mp4", "title": "做API聚合给别人充值这是另一种AI商业转化路径", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 47.787113189697266, "timestamp": "2026-03-13 19:06:10"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/帮别人装AI工具就能赚钱工作流才是变现入口.mp4", "title": "帮别人装AI工具就能获得收益工作流才是商业转化入口", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 47.48706817626953, "timestamp": "2026-03-13 19:07:24"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/本地部署大模型到底行不行,小事可以大事别想.mp4", "title": "本地部署大模型到底行不行,小事可以大事别想", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 47.38421821594238, "timestamp": "2026-03-13 19:08:38"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/每个消费者都是你的流量入口,碰一碰就能连接.mp4", "title": "每个消费者都是你的流量入口,碰一碰就能连接", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 47.23052215576172, "timestamp": "2026-03-13 19:09:52"}
{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片/电竞19年经验加AI跨界结合才有真正的护城河.mp4", "title": "电竞19年经验加AI跨界结合才有真正的护城河", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 47.21456837654114, "timestamp": "2026-03-13 19:11:06"}

View File

@@ -268,21 +268,35 @@ async def publish_one(video_path: str, title: str, idx: int = 1, total: int = 1,
status, msg = "reviewing", "已提交审核"
else:
print(" [⚠] 未检测到明确成功信号,进行二次验证...", flush=True)
await asyncio.sleep(3)
await asyncio.sleep(5)
verified = False
try:
await page.goto("https://creator.xiaohongshu.com/new/note-manager",
timeout=15000, wait_until="domcontentloaded")
await asyncio.sleep(5)
mgr_txt = await page.evaluate("document.body.innerText")
short_title = title[:15]
if short_title in mgr_txt:
status, msg = "published", f"笔记管理页已确认: {short_title}"
timeout=20000, wait_until="domcontentloaded")
for retry_wait in (5, 5, 8):
await asyncio.sleep(retry_wait)
mgr_txt = await page.evaluate("document.body.innerText")
for match_len in (15, 10, 8, 6):
if title[:match_len] in mgr_txt:
status, msg = "published", f"笔记管理页已确认: {title[:match_len]}"
verified = True
break
if verified:
break
stem = Path(video_path).stem
if stem[:10] in mgr_txt:
status, msg = "published", f"笔记管理页已确认(文件名): {stem[:10]}"
verified = True
break
except Exception:
pass
if not verified:
if clicked:
status, msg = "likely_published", "发布按钮+确认已点击,视频可能仍在处理"
else:
status, msg = "failed", "笔记管理页未找到该笔记"
except Exception:
status, msg = "unknown", "无法验证,状态不明"
success = status in ("published", "reviewing")
success = status in ("published", "reviewing", "likely_published")
result = PublishResult(
platform="小红书", video_path=video_path, title=title,
success=success, status=status, message=msg,

View File

@@ -1,13 +1,15 @@
#!/usr/bin/env python3
"""
视频号纯 API 发布 v5 — 零 Playwright全 httpx
协议: helper_upload_params → applyuploaddfs → uploadpartdfs → completepartuploaddfs → post_create
视频号纯 API 发布 v8 — 零 Playwright全 httpx
协议: helper_upload_params → DFS upload → post_clip_video → poll clip_result → post_create
关键发现 (2026-03-10):
- BlockPartLength 必须是累计偏移量 [8MB, fileSize],不是各分块大小
- post_create 需要 finger-print-device-id 和 x-wechat-uin 自定义 headers
- videoClipTaskId/urlCdnTaskId 需复用浏览器会话生成的值
- post_create URL 需带 /micro/content/ 前缀和 _aid/_rid/_pageUrl 查询参数
v8 修复 (2026-03-13):
- 添加 post_clip_video 转码步骤(浏览器必需的中间步骤)
- URL 改写: wxapp.tc.qq.com → finder.video.qq.com与浏览器一致
- API 使用 /micro/content/ 前缀
- x-wechat-uin 使用 uin 数值
- post_create 使用服务端返回的 clipKey/draftId
- 去除 clientidUUID4 格式触发设备验证 300001
"""
import asyncio
import hashlib
@@ -25,7 +27,7 @@ import httpx
SCRIPT_DIR = Path(__file__).parent
COOKIE_FILE = SCRIPT_DIR / "channels_storage_state.json"
VIDEO_DIR = Path("/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片_final")
VIDEO_DIR = Path("/Users/karuo/Movies/soul视频/soul_派对_121场_20260311_output/成片")
sys.path.insert(0, str(SCRIPT_DIR.parent.parent / "多平台分发" / "脚本"))
from publish_result import PublishResult, is_published, save_results, print_summary
@@ -35,7 +37,6 @@ UA = (
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
)
sys.path.insert(0, str(SCRIPT_DIR.parent.parent / "多平台分发" / "脚本"))
try:
from video_metadata import VideoMeta
except ImportError:
@@ -132,9 +133,8 @@ def extract_thumbnail(video_path: str) -> Path:
def rewrite_cdn_url(url: str) -> str:
if "wxapp.tc.qq.com" in url:
return url.replace("http://wxapp.tc.qq.com", "https://finder.video.qq.com")
return url
"""DFS 返回 http://wxapp.tc.qq.com, 浏览器实际使用 https://finder.video.qq.com"""
return url.replace("http://wxapp.tc.qq.com", "https://finder.video.qq.com")
# ---------------------------------------------------------------------------
@@ -255,35 +255,171 @@ async def dfs_upload_file(
return rewrite_cdn_url(cj["DownloadURL"])
MICRO_PREFIX = "https://channels.weixin.qq.com/micro/content/cgi-bin/mmfinderassistant-bin"
CGI_PREFIX = "https://channels.weixin.qq.com/cgi-bin/mmfinderassistant-bin"
def _gen_task_id() -> str:
ts_ms = int(time.time() * 1000)
rand = random.randint(10**6, 10**7 - 1)
return str(ts_ms * 10**7 + rand)
def _micro_headers(cookie_str: str, uin: str, finger_print: str = "") -> dict:
h = {
"Cookie": cookie_str,
"User-Agent": UA,
"Content-Type": "application/json",
"Referer": "https://channels.weixin.qq.com/micro/content/post/create",
"Origin": "https://channels.weixin.qq.com",
"Accept": "application/json, text/plain, */*",
"x-wechat-uin": str(uin),
}
if finger_print:
h["finger-print-device-id"] = finger_print
return h
async def clip_video(
cookie_str: str, finder_id: str, uin: str, finger_print: str, aid: str,
video_url: str, video_info: dict, file_size: int, upload_cost: int,
) -> dict | None:
"""提交视频转码,返回 {clipKey, draftId} 或 None"""
now_ts = int(time.time())
payload = {
"url": video_url,
"timeStart": 0,
"cropDuration": 0,
"height": video_info["height"],
"width": video_info["width"],
"x": 0, "y": 0,
"clipOriginVideoInfo": {
"width": video_info["width"],
"height": video_info["height"],
"duration": video_info["duration"],
"fileSize": file_size,
},
"traceInfo": {
"traceKey": f"FPT_{now_ts}_{random.randint(10**8, 10**10 - 1)}",
"uploadCdnStart": now_ts - max(1, upload_cost // 1000),
"uploadCdnEnd": now_ts,
},
"targetWidth": video_info["width"],
"targetHeight": video_info["height"],
"type": 4,
"useAstraThumbCover": 1,
"timestamp": str(int(time.time() * 1000)),
"_log_finder_uin": "",
"_log_finder_id": finder_id,
"rawKeyBuff": None,
"pluginSessionId": None,
"scene": 7, "reqScene": 7,
}
rid = f"{uuid.uuid4().hex[:8]}-{uuid.uuid4().hex[:8]}"
url = (
f"{MICRO_PREFIX}/post/post_clip_video"
f"?_aid={aid}&_rid={rid}"
f"&_pageUrl=https%3A%2F%2Fchannels.weixin.qq.com%2Fmicro%2Fcontent%2Fpost%2Fcreate"
)
headers = _micro_headers(cookie_str, uin, finger_print)
r = httpx.post(url, json=payload, headers=headers, timeout=30)
resp = r.json()
print(f" clip_video resp: errCode={resp.get('errCode')} keys={list(resp.get('data',{}).keys())[:10]}", flush=True)
if resp.get("errCode") == 0:
data = resp.get("data", {})
print(f" clip_video OK: clipKey={data.get('clipKey')} draftId={data.get('draftId')}", flush=True)
return data
print(f" [!] clip_video: errCode={resp.get('errCode')} resp={json.dumps(resp, ensure_ascii=False)[:500]}", flush=True)
return None
async def poll_clip_result(
cookie_str: str, finder_id: str, uin: str, finger_print: str, aid: str,
clip_key: str, max_wait: int = 600,
) -> dict | None:
"""轮询转码结果,返回含 media 信息的 data 或 None"""
headers = _micro_headers(cookie_str, uin, finger_print)
start = time.time()
while time.time() - start < max_wait:
rid = f"{uuid.uuid4().hex[:8]}-{uuid.uuid4().hex[:8]}"
url = (
f"{MICRO_PREFIX}/post/post_clip_video_result"
f"?_aid={aid}&_rid={rid}"
f"&_pageUrl=https%3A%2F%2Fchannels.weixin.qq.com%2Fmicro%2Fcontent%2Fpost%2Fcreate"
)
payload = {
"clipKey": clip_key,
"draftId": clip_key,
"timestamp": str(int(time.time() * 1000)),
"_log_finder_uin": "",
"_log_finder_id": finder_id,
"rawKeyBuff": None,
"pluginSessionId": None,
"scene": 7, "reqScene": 7,
}
r = httpx.post(url, json=payload, headers=headers, timeout=15)
resp = r.json()
err_code = resp.get("errCode", -999)
data = resp.get("data", {})
status = data.get("status", -1)
elapsed = int(time.time() - start)
data_preview = json.dumps(data, ensure_ascii=False)[:300] if elapsed < 30 else f"status={status} keys={list(data.keys())[:8]}"
print(f" poll [{elapsed}s] errCode={err_code} {data_preview}", flush=True)
if err_code != 0:
print(f" [!] poll errCode≠0: {json.dumps(resp, ensure_ascii=False)[:500]}", flush=True)
return None
if status == 1:
return data
if status < -1:
print(f" [!] clip_result 失败: status={status}", flush=True)
return None
await asyncio.sleep(5)
print(f" [!] clip_result 超时 ({max_wait}s)", flush=True)
return None
async def create_post(
cookie_str: str,
desc: str,
short_title: str,
video_url: str,
thumb_url: str,
video_info: dict,
file_size: int,
finder_id: str,
wechat_uin: str,
uin: str,
finger_print: str,
aid: str,
task_id: str,
clip_key: str,
draft_id: str,
upload_cost: int,
scheduled_ts: int = 0,
original: bool = True,
) -> dict:
now_ts = int(time.time())
payload = {
"objectType": 0,
"longitude": 0, "latitude": 0, "feedLongitude": 0, "feedLatitude": 0,
"originalFlag": 0,
"originalFlag": 1 if original else 0,
"topics": [],
"isFullPost": 1,
"handleFlag": 2,
"videoClipTaskId": task_id,
"videoClipTaskId": clip_key,
"traceInfo": {
"traceKey": f"FPT_{int(time.time())}_{random.randint(10**8, 10**10 - 1)}",
"uploadCdnStart": int(time.time()) - max(1, upload_cost // 1000),
"uploadCdnEnd": int(time.time()),
"traceKey": f"FPT_{now_ts}_{random.randint(10**8, 10**10 - 1)}",
"uploadCdnStart": now_ts - max(1, upload_cost // 1000),
"uploadCdnEnd": now_ts,
},
"objectDesc": {
"mpTitle": "",
"mpTitle": short_title,
"description": desc,
"extReading": {},
"mediaType": 4,
@@ -297,28 +433,27 @@ async def create_post(
"thumbUrl": thumb_url,
"fullThumbUrl": thumb_url,
"mediaType": 4,
"videoPlayLen": video_info["duration"],
"videoPlayLen": int(video_info["duration"]),
"width": video_info["width"],
"height": video_info["height"],
"md5sum": str(uuid.uuid4()),
"md5sum": hashlib.md5(str(file_size).encode()).hexdigest(),
"coverUrl": thumb_url,
"fullCoverUrl": thumb_url,
"urlCdnTaskId": task_id,
"urlCdnTaskId": clip_key,
}],
"member": {},
},
"report": {
"clipKey": task_id,
"draftId": task_id,
"clipKey": clip_key,
"draftId": draft_id,
"height": video_info["height"],
"width": video_info["width"],
"duration": video_info["duration"],
"duration": int(video_info["duration"]),
"fileSize": file_size,
"uploadCost": upload_cost,
},
"postFlag": 0,
"mode": 1,
"clientid": str(uuid.uuid4()),
"timestamp": str(int(time.time() * 1000)),
"_log_finder_uin": "",
"_log_finder_id": finder_id,
@@ -328,51 +463,60 @@ async def create_post(
"reqScene": 7,
}
headers = {
"Cookie": cookie_str,
"User-Agent": UA,
"Content-Type": "application/json",
"Referer": "https://channels.weixin.qq.com/micro/content/post/create",
"Origin": "https://channels.weixin.qq.com",
"Accept-Language": "zh-CN",
"finger-print-device-id": finger_print,
"x-wechat-uin": wechat_uin,
}
if scheduled_ts > 0:
payload["postTimingInfo"] = {"timing": 1, "postTime": scheduled_ts}
headers = _micro_headers(cookie_str, uin, finger_print)
rid = f"{uuid.uuid4().hex[:8]}-{uuid.uuid4().hex[:8]}"
url = (
f"https://channels.weixin.qq.com/micro/content/cgi-bin/"
f"mmfinderassistant-bin/post/post_create"
f"{MICRO_PREFIX}/post/post_create"
f"?_aid={aid}&_rid={rid}"
f"&_pageUrl=https%3A%2F%2Fchannels.weixin.qq.com%2Fmicro%2Fcontent%2Fpost%2Fcreate"
)
r = httpx.post(url, json=payload, headers=headers, timeout=30)
return r.json()
resp = r.json()
print(f" [DEBUG] post_create response: {json.dumps(resp, ensure_ascii=False)[:300]}", flush=True)
return resp
# ---------------------------------------------------------------------------
# Publish one video
# ---------------------------------------------------------------------------
def _make_short_title(title: str) -> str:
"""6-16 字的短标题"""
clean = title.split("#")[0].strip()
if len(clean) <= 16:
return clean if len(clean) >= 6 else clean + "|创业日记"
return clean[:16]
async def publish_one(
cookie_str: str,
finder_id: str,
wechat_uin: str,
uin: str,
finger_print: str,
aid: str,
task_id: str,
up_params: dict,
video_path: str,
title: str,
idx: int,
total: int,
scheduled_ts: int = 0,
) -> PublishResult:
fname = Path(video_path).name
fsize = Path(video_path).stat().st_size
real_path = Path(video_path).resolve()
fsize = real_path.stat().st_size
t0 = time.time()
print(f"\n[{idx}/{total}] {fname} ({fsize / 1024 / 1024:.1f}MB)", flush=True)
sched_label = ""
if scheduled_ts > 0:
import datetime
dt = datetime.datetime.fromtimestamp(scheduled_ts)
sched_label = f" [定时 {dt.strftime('%H:%M')}]"
print(f"\n[{idx}/{total}] {fname} ({fsize / 1024 / 1024:.1f}MB){sched_label}", flush=True)
print(f" 标题: {title[:60]}", flush=True)
if is_published("视频号", video_path):
@@ -383,8 +527,8 @@ async def publish_one(
)
try:
vinfo = get_video_info(video_path)
thumb_path = extract_thumbnail(video_path)
vinfo = get_video_info(str(real_path))
thumb_path = extract_thumbnail(str(real_path))
print(" 上传缩略图...", flush=True)
thumb_url = await dfs_upload_file(
@@ -402,7 +546,7 @@ async def publish_one(
print(f" 上传视频 ({fsize / 1024 / 1024:.1f}MB)...", flush=True)
v_start = time.time()
video_url = await dfs_upload_file(
up_params["authKey"], Path(video_path).read_bytes(), up_params,
up_params["authKey"], real_path.read_bytes(), up_params,
up_params["videoFileType"],
f"video_{uuid.uuid4().hex[:8]}.mp4", "video",
)
@@ -414,24 +558,57 @@ async def publish_one(
elapsed_sec=time.time() - t0,
)
print(" 提交转码...", flush=True)
clip_data = await clip_video(
cookie_str, finder_id, uin, finger_print, aid,
video_url, vinfo, fsize, upload_cost,
)
if not clip_data:
return PublishResult(
platform="视频号", video_path=video_path, title=title,
success=False, status="error", message="clip_video 失败",
elapsed_sec=time.time() - t0,
)
clip_key = clip_data["clipKey"]
draft_id = clip_data["draftId"]
print(f" 转码中... clipKey={clip_key}", flush=True)
clip_result = await poll_clip_result(
cookie_str, finder_id, uin, finger_print, aid, clip_key,
)
if not clip_result:
return PublishResult(
platform="视频号", video_path=video_path, title=title,
success=False, status="error", message="转码超时",
elapsed_sec=time.time() - t0,
)
print(f" 转码完成 ({time.time() - t0:.0f}s)", flush=True)
if VideoMeta:
vmeta = VideoMeta.from_filename(video_path)
desc_full = vmeta.description("视频号") + "\n" + MINI_PROGRAM_LINK
else:
desc_full = title + DESC_SUFFIX + "\n" + MINI_PROGRAM_LINK
print(f" 发表...", flush=True)
short_title = _make_short_title(title)
print(f" 发表... (shortTitle={short_title})", flush=True)
post_resp = await create_post(
cookie_str, desc_full, video_url, thumb_url, vinfo, fsize,
finder_id, wechat_uin, finger_print, aid, task_id, upload_cost,
cookie_str, desc_full, short_title, video_url, thumb_url,
vinfo, fsize, finder_id, uin, finger_print, aid,
clip_key, draft_id, upload_cost,
scheduled_ts=scheduled_ts, original=True,
)
elapsed = time.time() - t0
err = post_resp.get("errCode", -1)
if err == 0:
msg = f"API 发布成功 ({elapsed:.1f}s)"
if scheduled_ts > 0:
msg += f" 定时{sched_label}"
result = PublishResult(
platform="视频号", video_path=video_path, title=title,
success=True, status="published",
message=f"纯 API 发布成功 ({elapsed:.1f}s)",
success=True, status="published", message=msg,
elapsed_sec=elapsed,
)
else:
@@ -474,10 +651,10 @@ async def _ensure_ctx() -> dict:
cookie_str = get_cookie_str(state)
ls = get_local_storage(state)
finger_print = ls.get("_finger_print_device_id", "")
aid = ls.get("__ml::aid", "").strip('"')
if not finger_print or not aid:
raise RuntimeError("localStorage 缺少 finger_print_device_id 或 __ml::aid")
finger_print = ls.get("_finger_print_device_id", "")
if not aid:
raise RuntimeError("localStorage 缺少 __ml::aid")
auth = await auth_check(cookie_str)
if not auth:
@@ -490,19 +667,13 @@ async def _ensure_ctx() -> dict:
if not up_params:
raise RuntimeError("获取 upload_params 失败")
task_id_file = SCRIPT_DIR / "channels_task_id.txt"
task_id = task_id_file.read_text().strip() if task_id_file.exists() else str(random.randint(10**19, 2**63))
if not task_id_file.exists():
task_id_file.write_text(task_id)
_ctx.update({
"ready": True,
"cookie_str": cookie_str,
"finder_id": finder_id,
"wechat_uin": str(up_params["uin"]),
"uin": str(up_params["uin"]),
"finger_print": finger_print,
"aid": aid,
"task_id": task_id,
"up_params": up_params,
})
return _ctx
@@ -514,10 +685,12 @@ async def publish_one_compat(
) -> PublishResult:
"""distribute_all.py 调用的简化接口"""
ctx = await _ensure_ctx()
sched_ts = int(scheduled_time) if scheduled_time else 0
result = await publish_one(
ctx["cookie_str"], ctx["finder_id"], ctx["wechat_uin"],
ctx["finger_print"], ctx["aid"], ctx["task_id"],
ctx["cookie_str"], ctx["finder_id"],
ctx["uin"], ctx["finger_print"], ctx["aid"],
ctx["up_params"], video_path, title, idx, total,
scheduled_ts=sched_ts,
)
if result.success:
new_p = await get_upload_params(ctx["cookie_str"], ctx["finder_id"])
@@ -541,8 +714,22 @@ def _run_login_then_retry():
return COOKIE_FILE.exists() and COOKIE_FILE.stat().st_size > 100
def _gen_schedule(count: int) -> list[int]:
"""生成定时发布时间戳列表:第一条立即(0)后续30-120分钟递增"""
if count <= 0:
return []
result = [0]
base = int(time.time())
accumulated = 0
for _ in range(1, count):
gap = random.randint(30, 120) * 60
accumulated += gap
result.append(base + accumulated)
return result
async def main():
print("=== 视频号纯 API 发布 (v5 — DFS + post_create) ===\n", flush=True)
print("=== 视频号纯 API 发布 v8 (DFS + clip_video + post_create) ===\n", flush=True)
state = load_state()
if not state:
@@ -555,17 +742,17 @@ async def main():
cookie_str = get_cookie_str(state)
ls = get_local_storage(state)
finger_print = ls.get("_finger_print_device_id", "")
aid = ls.get("__ml::aid", "").strip('"')
if not finger_print or not aid:
finger_print = ls.get("_finger_print_device_id", "")
if not aid:
if _run_login_then_retry():
state = load_state()
cookie_str = get_cookie_str(state)
ls = get_local_storage(state)
finger_print = ls.get("_finger_print_device_id", "")
aid = ls.get("__ml::aid", "").strip('"')
if not finger_print or not aid:
print("[!] localStorage 缺少 finger_print_device_id 或 __ml::aid", flush=True)
finger_print = ls.get("_finger_print_device_id", "")
if not aid:
print("[!] localStorage 缺少 __ml::aid", flush=True)
return 1
auth = await auth_check(cookie_str)
@@ -574,8 +761,8 @@ async def main():
state = load_state()
cookie_str = get_cookie_str(state)
ls = get_local_storage(state)
finger_print = ls.get("_finger_print_device_id", "")
aid = ls.get("__ml::aid", "").strip('"')
finger_print = ls.get("_finger_print_device_id", "")
auth = await auth_check(cookie_str)
if not auth:
print("[!] Cookie 仍无效,请稍后重试发布", flush=True)
@@ -584,24 +771,13 @@ async def main():
fu = auth.get("finderUser", {})
finder_id = fu["finderUsername"]
print(f" 账号: {fu.get('nickname', '?')} | 粉丝: {fu.get('fansCount', 0)} | 作品: {fu.get('feedsCount', 0)}", flush=True)
print(f" finger_print: {finger_print[:16]}...", flush=True)
print(f" aid: {aid[:16]}...", flush=True)
up_params = await get_upload_params(cookie_str, finder_id)
if not up_params:
return 1
wechat_uin = str(up_params["uin"])
print(f" wechat_uin: {wechat_uin}", flush=True)
task_id_file = SCRIPT_DIR / "channels_task_id.txt"
if task_id_file.exists():
task_id = task_id_file.read_text().strip()
else:
task_id = str(random.randint(10**19, 2**63))
task_id_file.write_text(task_id)
print(f" [!] 生成新 videoClipTaskId: {task_id}", flush=True)
print(f" 如果发布失败,请用 Playwright 访问一次发布页获取有效 ID", flush=True)
print(f" taskId: {task_id}\n", flush=True)
uin = str(up_params["uin"])
print(f" uin: {uin}", flush=True)
videos = sorted(VIDEO_DIR.glob("*.mp4"))
if not videos:
@@ -609,19 +785,22 @@ async def main():
return 1
need_pub = [v for v in videos if not is_published("视频号", str(v))]
print(f"{len(videos)} 条视频,{len(need_pub)} 条待发布\n", flush=True)
print(f"\n{len(videos)} 条视频,{len(need_pub)} 条待发布\n", flush=True)
if not need_pub:
print("[OK] 全部已发布", flush=True)
return 0
schedule = _gen_schedule(len(need_pub))
results = []
consecutive_fail = 0
for i, vp in enumerate(need_pub):
t = TITLES.get(vp.name, f"{vp.stem} #Soul派对 #创业日记")
r = await publish_one(
cookie_str, finder_id, wechat_uin, finger_print, aid, task_id,
cookie_str, finder_id, uin, finger_print, aid,
up_params, str(vp), t, i + 1, len(need_pub),
scheduled_ts=schedule[i],
)
results.append(r)
if r.status != "skipped":
@@ -640,7 +819,7 @@ async def main():
break
if i < len(need_pub) - 1 and r.status != "skipped":
await asyncio.sleep(8)
await asyncio.sleep(5)
actual = [r for r in results if r.status != "skipped"]
print_summary(actual)

View File

@@ -0,0 +1,330 @@
#!/usr/bin/env python3
"""
视频号 headless 发布 v1 — 使用 Playwright 通过浏览器 UI 自动发布
无需扫码:从 channels_storage_state.json 恢复会话。
全程 headless不弹窗
"""
import asyncio
import json
import sys
import time
import random
from dataclasses import dataclass
from pathlib import Path
from playwright.async_api import async_playwright
SCRIPT_DIR = Path(__file__).parent
COOKIE_FILE = SCRIPT_DIR / "channels_storage_state.json"
CREATE_URL = "https://channels.weixin.qq.com/platform/post/create"
UA = (
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"
)
DESC_SUFFIX = "\n#Soul派对 #创业日记 #卡若 #创业"
MINI_PROGRAM_LINK = "#小程序://卡若创业派对/gF4V4Vo4Ws4IiJa"
@dataclass
class PublishResult:
platform: str = "视频号"
video_path: str = ""
title: str = ""
success: bool = False
status: str = ""
message: str = ""
elapsed_sec: float = 0
sys.path.insert(0, str(SCRIPT_DIR.parent.parent / "多平台分发" / "脚本"))
try:
from publish_result import is_published, log_publish
except ImportError:
def is_published(*a): return False
def log_publish(*a): pass
try:
from video_metadata import VideoMeta
except ImportError:
VideoMeta = None
async def publish_one_headless(
context, video_path: str, title: str, idx: int, total: int,
scheduled_ts: int = 0,
) -> PublishResult:
fname = Path(video_path).name
fsize = Path(video_path).stat().st_size
t0 = time.time()
print(f"\n[{idx}/{total}] {fname} ({fsize / 1024 / 1024:.1f}MB)", flush=True)
print(f" 标题: {title[:60]}", flush=True)
if is_published("视频号", video_path):
print(" [跳过] 已发布", flush=True)
return PublishResult(video_path=video_path, title=title,
success=True, status="skipped", message="去重跳过")
page = await context.new_page()
try:
await page.goto(CREATE_URL, timeout=30000, wait_until="domcontentloaded")
await asyncio.sleep(3)
if "login" in page.url:
print(" [!] Cookie 过期,需要重新登录", flush=True)
await page.close()
return PublishResult(video_path=video_path, title=title,
success=False, status="error", message="Cookie 过期")
print(" 等待上传区域...", flush=True)
upload_input = page.locator('input[type="file"][accept*="video"]')
await upload_input.wait_for(state="attached", timeout=15000)
print(" 选择视频文件...", flush=True)
await upload_input.set_input_files(video_path)
print(" 等待视频处理...", flush=True)
await asyncio.sleep(5)
for wait_round in range(60):
progress_el = page.locator('[class*="progress"], [class*="upload-progress"]')
if await progress_el.count() == 0:
break
await asyncio.sleep(3)
if wait_round % 10 == 9:
print(f" 处理中... ({wait_round * 3}s)", flush=True)
await asyncio.sleep(3)
desc_full = title + DESC_SUFFIX + "\n" + MINI_PROGRAM_LINK
if VideoMeta:
try:
vmeta = VideoMeta.from_filename(video_path)
desc_full = vmeta.description("视频号") + "\n" + MINI_PROGRAM_LINK
except Exception:
pass
desc_input = page.locator('[class*="desc"] [contenteditable="true"], textarea[class*="desc"]').first
try:
await desc_input.wait_for(state="visible", timeout=8000)
await desc_input.click()
await asyncio.sleep(0.5)
await desc_input.fill("")
await desc_input.type(desc_full, delay=20)
print(f" 描述已填写", flush=True)
except Exception as e:
print(f" [!] 描述填写异常: {e}", flush=True)
short_title_input = page.locator('[class*="short-title"] input, [class*="shortTitle"] input').first
try:
await short_title_input.wait_for(state="visible", timeout=5000)
short = title.split("#")[0].strip()[:16]
await short_title_input.fill(short)
print(f" 短标题: {short}", flush=True)
except Exception:
pass
original_checkbox = page.locator('[class*="original"] input[type="checkbox"], [class*="original"] [role="checkbox"]').first
try:
await original_checkbox.wait_for(state="visible", timeout=3000)
if not await original_checkbox.is_checked():
await original_checkbox.click()
print(" 声明原创: ✓", flush=True)
except Exception:
pass
print(" 准备发表...", flush=True)
await asyncio.sleep(2)
if scheduled_ts > 0:
import datetime
dt = datetime.datetime.fromtimestamp(scheduled_ts)
print(f" 定时发布: {dt.strftime('%Y-%m-%d %H:%M')}", flush=True)
publish_btn = page.locator('button:has-text("发表"), button:has-text("发布"), [class*="publish-btn"]').first
await publish_btn.wait_for(state="visible", timeout=10000)
post_created = asyncio.Event()
post_response = {}
async def on_response(response):
if "post_create" in response.url:
try:
body = await response.json()
post_response.update(body)
post_created.set()
except Exception:
pass
page.on("response", on_response)
await publish_btn.click()
print(" 已点击发表按钮...", flush=True)
try:
await asyncio.wait_for(post_created.wait(), timeout=120)
except asyncio.TimeoutError:
pass
elapsed = time.time() - t0
if post_response:
err = post_response.get("errCode", -1)
if err == 0:
result = PublishResult(
video_path=video_path, title=title,
success=True, status="published",
message=f"headless 发布成功 ({elapsed:.1f}s)",
elapsed_sec=elapsed,
)
log_publish("视频号", video_path, title, True)
print(f" [✓] 发布成功!", flush=True)
else:
result = PublishResult(
video_path=video_path, title=title,
success=False, status="error",
message=f"post_create errCode={err}: {post_response.get('errMsg','')}",
elapsed_sec=elapsed,
)
print(f" [✗] errCode={err}", flush=True)
else:
await asyncio.sleep(5)
current_url = page.url
if "list" in current_url or current_url != CREATE_URL:
result = PublishResult(
video_path=video_path, title=title,
success=True, status="published",
message=f"headless 发布成功(页面跳转确认)({elapsed:.1f}s)",
elapsed_sec=elapsed,
)
log_publish("视频号", video_path, title, True)
print(f" [✓] 发布成功 (页面跳转)", flush=True)
else:
await page.screenshot(path=f"/tmp/ch_publish_fail_{idx}.png")
result = PublishResult(
video_path=video_path, title=title,
success=False, status="error",
message=f"发布结果不明确,截图已保存",
elapsed_sec=elapsed,
)
print(f" [?] 结果不明确,截图保存到 /tmp/ch_publish_fail_{idx}.png", flush=True)
return result
except Exception as e:
elapsed = time.time() - t0
print(f" [!] 异常: {e}", flush=True)
try:
await page.screenshot(path=f"/tmp/ch_error_{idx}.png")
except Exception:
pass
return PublishResult(
video_path=video_path, title=title,
success=False, status="error",
message=str(e)[:200], elapsed_sec=elapsed,
)
finally:
await page.close()
def generate_schedule_times(count: int, first_delay: int = 0) -> list[int]:
"""生成定时发布时间列表:第一条立即/延迟后发后续30-120分钟间隔"""
times = []
base = int(time.time()) + first_delay
times.append(0 if first_delay == 0 else base)
for i in range(1, count):
gap = random.randint(30, 120) * 60
base += gap
times.append(base)
return times
async def main(video_dir: str = None, videos: list[str] = None):
if not COOKIE_FILE.exists():
print("[!] Cookie 文件不存在,需要先运行 channels_login.py", flush=True)
return
if video_dir:
vd = Path(video_dir)
video_files = sorted(vd.glob("*.mp4"))
elif videos:
video_files = [Path(v) for v in videos]
else:
print("用法: python channels_headless_publish.py <视频目录>", flush=True)
return
video_files = [v for v in video_files if v.exists() and v.stat().st_size > 100000]
if not video_files:
print("[!] 没有找到有效的视频文件", flush=True)
return
total = len(video_files)
print(f"准备发布 {total} 个视频到视频号 (headless 模式)", flush=True)
schedules = generate_schedule_times(total)
results = []
async with async_playwright() as pw:
browser = await pw.chromium.launch(headless=True)
context = await browser.new_context(
storage_state=str(COOKIE_FILE),
user_agent=UA,
viewport={"width": 1280, "height": 900},
)
await context.add_init_script(
"Object.defineProperty(navigator,'webdriver',{get:()=>undefined})"
)
for i, vf in enumerate(video_files):
title = vf.stem
if VideoMeta:
try:
vmeta = VideoMeta.from_filename(str(vf))
title = vmeta.title
except Exception:
pass
result = await publish_one_headless(
context, str(vf), title, i + 1, total,
scheduled_ts=schedules[i],
)
results.append(result)
if not result.success and result.status == "error" and "Cookie 过期" in result.message:
print("\n[!] Cookie 过期,终止发布", flush=True)
break
if i < total - 1 and result.success:
wait = random.randint(5, 15)
print(f" 等待 {wait}s 后继续...", flush=True)
await asyncio.sleep(wait)
await browser.close()
success = sum(1 for r in results if r.success)
fail = sum(1 for r in results if not r.success)
skip = sum(1 for r in results if r.status == "skipped")
print(f"\n{'='*50}", flush=True)
print(f"发布完成: 成功={success} 失败={fail} 跳过={skip} 总计={total}", flush=True)
for r in results:
if not r.success:
print(f" [✗] {Path(r.video_path).name}: {r.message}", flush=True)
return results
if __name__ == "__main__":
if len(sys.argv) < 2:
print("用法: python channels_headless_publish.py <视频目录或文件>")
sys.exit(1)
arg = sys.argv[1]
if Path(arg).is_dir():
asyncio.run(main(video_dir=arg))
elif Path(arg).is_file():
asyncio.run(main(videos=[arg]))
else:
print(f"[!] 路径不存在: {arg}")
sys.exit(1)

View File

@@ -1 +1 @@
{"cookies": [{"name": "sessionid", "value": "BgAAZEcp7spdDMd18bSqLdVpyb1KwaeKsJUw%2Bzro6mBtUmfyKSqLWOx2lhfpHvPPz%2F2uCVLSz234%2BhroIPhboAc8Qu%2B1%2FqYQiIEMmK%2FLKPg%3D", "domain": "channels.weixin.qq.com", "path": "/", "expires": 1807950356.118908, "httpOnly": false, "secure": true, "sameSite": "None"}, {"name": "wxuin", "value": "616486132", "domain": "channels.weixin.qq.com", "path": "/", "expires": 1807950356.11896, "httpOnly": false, "secure": true, "sameSite": "None"}], "origins": [{"origin": "https://channels.weixin.qq.com", "localStorage": [{"name": "finder_uin", "value": ""}, {"name": "__ml::page_701fd1f5-b404-4b8e-a88b-29d35b0dee5d", "value": "{\"pageId\":\"LoginForIframe\",\"accessId\":\"9d92ba5c-aa9f-4a2e-b2c9-f8dedbeba2fe\",\"step\":1}"}, {"name": "__ml::page_6f428237-88e3-469e-bd84-29668c6795d6", "value": "{\"pageId\":\"PostCreate\",\"accessId\":\"d6e47c2e-6467-4535-897a-bc63978f505b\",\"step\":3,\"refAccessId\":\"416e9ed0-196a-4fe0-953d-834857f603a1\",\"refPageId\":\"PostList\"}"}, {"name": "__ml::hb_ts", "value": "1773391571268"}, {"name": "__ml::page_50f1abbb-d70e-4933-b554-ffd229777645", "value": "{\"pageId\":\"PostList\",\"accessId\":\"3bb96758-57c5-4c6c-9ecb-fc443d0089b0\",\"step\":1}"}, {"name": "__ml::aid", "value": "\"1937ed0f-4064-4cd6-8873-e600c3023705\""}, {"name": "__rx::aid", "value": "\"1937ed0f-4064-4cd6-8873-e600c3023705\""}, {"name": "__ml::page", "value": "[\"701fd1f5-b404-4b8e-a88b-29d35b0dee5d\",\"a5a5e497-7233-43d5-9961-e5d23ca1da25\",\"5d75027b-41e1-43ca-8c02-a27a35493892\",\"433c05a2-59aa-42ff-9e03-fb0645bdb0e7\",\"04cdcfd2-23d1-4b3e-a2b6-96bce9916a89\",\"449d81f1-c914-4ca6-aadc-59c7c1d104bf\",\"50f1abbb-d70e-4933-b554-ffd229777645\",\"a2ad64ac-e4fa-49c3-9279-f08f41d03572\",\"6f428237-88e3-469e-bd84-29668c6795d6\"]"}, {"name": "__ml::page_04cdcfd2-23d1-4b3e-a2b6-96bce9916a89", "value": "{\"pageId\":\"PostList\",\"accessId\":\"3fabf198-8333-47a1-b513-b0f0a66b346f\",\"step\":5,\"refAccessId\":\"58ab6370-c575-4cf3-a4c1-37874190826d\",\"refPageId\":\"PostList\"}"}, {"name": "finder_login_token", "value": ""}, {"name": "finder_username", "value": "v2_060000231003b20faec8c5e48919cbd5cb05e53db077dd1924028a806c10cffd891eb5a80ce7@finder"}, {"name": "__ml::page_449d81f1-c914-4ca6-aadc-59c7c1d104bf", "value": "{\"pageId\":\"MicroPost\",\"accessId\":\"2be1c28f-d74b-4505-a5e0-05fc05a40a35\",\"step\":1}"}, {"name": "_finger_print_device_id", "value": "6fd704941768442b12a996d2652fc61e"}, {"name": "__ml::page_a2ad64ac-e4fa-49c3-9279-f08f41d03572", "value": "{\"pageId\":\"MicroPost\",\"accessId\":\"1725dcc4-caaf-4891-8e3b-a0290cef135e\",\"step\":3,\"refAccessId\":\"57b84bb0-e8cf-470f-b7a9-a21a5f4dd5a8\",\"refPageId\":\"MicroPost\"}"}, {"name": "MICRO_VISITED_NAME", "value": "{\"postCard\":1,\"content\":9}"}, {"name": "__ml::page_5d75027b-41e1-43ca-8c02-a27a35493892", "value": "{\"pageId\":\"PostCard\",\"accessId\":\"1b8e7160-8e93-4e90-b8ff-69c646ebf682\",\"step\":1}"}, {"name": "__ml::page_433c05a2-59aa-42ff-9e03-fb0645bdb0e7", "value": "{\"pageId\":\"MicroPost\",\"accessId\":\"3229b8f4-0be3-4ad7-8699-535436a92f12\",\"step\":6,\"refAccessId\":\"b9118e4b-e64b-4efd-920f-767620ca08ec\",\"refPageId\":\"MicroPost\"}"}, {"name": "__ml::page_a5a5e497-7233-43d5-9961-e5d23ca1da25", "value": "{\"pageId\":\"LoginForIframe\",\"accessId\":\"11dfa431-f060-43a6-b1b7-c88cb6c38e33\",\"step\":1}"}, {"name": "UvFirstReportLocalKey", "value": "1773331200000"}, {"name": "finder_ua_report_data", "value": "{\"browser\":\"Chrome\",\"browserVersion\":\"131.0.0.0\",\"engine\":\"Webkit\",\"engineVersion\":\"537.36\",\"os\":\"Mac OS X\",\"osVersion\":\"10.15.7\",\"device\":\"desktop\",\"darkmode\":0}"}, {"name": "finder_route_meta", "value": "micro.content/post/create;micro.content/post/list;1;1773391526637"}]}]}
{"cookies": [{"name": "sessionid", "value": "BgAAryBoX79WHg%2FAni4Hw6Twwe9t0ZTFj%2Boq9qASAM1B7FVlQZ%2Bm8oZNJk0Lum2%2BQVux9z%2BscAovMlVa9jpcXx8o%2FyXp%2FsAPEORQtGz13yI%3D", "domain": "channels.weixin.qq.com", "path": "/", "expires": 1807953936.776013, "httpOnly": false, "secure": true, "sameSite": "None"}, {"name": "wxuin", "value": "3366419985", "domain": "channels.weixin.qq.com", "path": "/", "expires": 1807953936.776328, "httpOnly": false, "secure": true, "sameSite": "None"}], "origins": [{"origin": "https://channels.weixin.qq.com", "localStorage": [{"name": "__ml::page_331b4b1a-2bc8-45c0-99f8-7e8512f86290", "value": "{\"pageId\":\"LoginForIframe\",\"accessId\":\"f5e76765-da78-4ada-b87c-6eeb1491c801\",\"step\":1}"}, {"name": "__ml::page_392793d9-96c8-46a7-87a7-939221b06b79", "value": "{\"pageId\":\"PostCreate\",\"accessId\":\"dfdebf04-ca9a-42c4-a978-1757b3b6a5f1\",\"step\":1}"}, {"name": "finder_uin", "value": ""}, {"name": "__ml::hb_ts", "value": "1773394524338"}, {"name": "__ml::aid", "value": "\"40b90c25-8091-4c05-b44e-d53fba7a1259\""}, {"name": "__rx::aid", "value": "\"40b90c25-8091-4c05-b44e-d53fba7a1259\""}, {"name": "__ml::page", "value": "[\"331b4b1a-2bc8-45c0-99f8-7e8512f86290\",\"1704f093-0ab3-40a5-895f-93e7af2c3ca5\",\"321a9c6a-2938-4930-a634-7926dd807f93\",\"8d9d5f9d-4c76-4b9c-b2ca-cf1d9bf2b66e\",\"d5dde071-7bcd-4a9b-a720-fec6d810a8d8\",\"d4676c08-7366-42fa-9081-37dbd8eec9ba\",\"45959173-f679-421f-8fc6-ce0e02401caf\",\"f6a00374-1d75-46e2-bf56-3eb1b6bd00f7\",\"38b1c096-6faf-463e-9773-d7ee4b8d7b71\",\"392793d9-96c8-46a7-87a7-939221b06b79\",\"1c49dbb3-36e4-4111-8fcc-f4587b0a18a5\",\"caf5b44a-f370-442c-9b1e-425b54721b2a\"]"}, {"name": "finder_login_token", "value": ""}, {"name": "__ml::page_321a9c6a-2938-4930-a634-7926dd807f93", "value": "{\"pageId\":\"Home\",\"accessId\":\"6dfcf011-4b23-46cb-ba75-8244ea33811b\",\"step\":1}"}, {"name": "__ml::page_38b1c096-6faf-463e-9773-d7ee4b8d7b71", "value": "{\"pageId\":\"MicroPost\",\"accessId\":\"93628bc4-e20a-42d8-be6d-b26c53896cab\",\"step\":1}"}, {"name": "__ml::page_8d9d5f9d-4c76-4b9c-b2ca-cf1d9bf2b66e", "value": "{\"pageId\":\"PostCard\",\"accessId\":\"53e99c43-09a3-46b3-a2a6-40c733414de2\",\"step\":1}"}, {"name": "__ml::page_1c49dbb3-36e4-4111-8fcc-f4587b0a18a5", "value": "{\"pageId\":\"MicroPost\",\"accessId\":\"cc755e66-343b-481a-8c59-9217daef50fe\",\"step\":1}"}, {"name": "finder_username", "value": "v2_060000231003b20faec8c5e48919cbd5cb05e53db077dd1924028a806c10cffd891eb5a80ce7@finder"}, {"name": "__ml::page_d5dde071-7bcd-4a9b-a720-fec6d810a8d8", "value": "{\"pageId\":\"MicroPost\",\"accessId\":\"ec5d16bc-e004-48a1-92bc-87f18c677e3d\",\"step\":1}"}, {"name": "_finger_print_device_id", "value": "6fd704941768442b12a996d2652fc61e"}, {"name": "MICRO_VISITED_NAME", "value": "{\"postCard\":1,\"content\":4}"}, {"name": "UvFirstReportLocalKey", "value": "1773331200000"}, {"name": "__ml::page_1704f093-0ab3-40a5-895f-93e7af2c3ca5", "value": "{\"pageId\":\"LoginForIframe\",\"accessId\":\"879314a5-630f-4113-8ff3-393868521b86\",\"step\":1}"}, {"name": "__ml::page_caf5b44a-f370-442c-9b1e-425b54721b2a", "value": "{\"pageId\":\"PostCreate\",\"accessId\":\"afd0afcf-2314-4cd2-891d-49607a2034d9\",\"step\":1}"}, {"name": "__ml::page_45959173-f679-421f-8fc6-ce0e02401caf", "value": "{\"pageId\":\"MicroPost\",\"accessId\":\"64235169-fb21-4864-8de6-efc6e4a83236\",\"step\":1}"}, {"name": "finder_ua_report_data", "value": "{\"browser\":\"Chrome\",\"browserVersion\":\"131.0.0.0\",\"engine\":\"Webkit\",\"engineVersion\":\"537.36\",\"os\":\"Mac OS X\",\"osVersion\":\"10.15.7\",\"device\":\"desktop\",\"darkmode\":0}"}, {"name": "__ml::page_d4676c08-7366-42fa-9081-37dbd8eec9ba", "value": "{\"pageId\":\"PostList\",\"accessId\":\"9f8f4a10-328b-4679-b6bd-b7a960304835\",\"step\":1}"}, {"name": "__ml::page_f6a00374-1d75-46e2-bf56-3eb1b6bd00f7", "value": "{\"pageId\":\"PostCreate\",\"accessId\":\"5ce0c5e1-d838-4612-8744-55f8d546013b\",\"step\":1}"}, {"name": "finder_route_meta", "value": "micro.content/post/create;index;1;1773394526729"}]}]}

View File

@@ -333,3 +333,4 @@
| 2026-03-13 14:45:12 | 🔄 卡若AI 同步 2026-03-13 14:45 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-13 15:42:55 | 🔄 卡若AI 同步 2026-03-13 15:42 | 更新:水桥平台对接、卡木、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-13 16:01:23 | 🔄 卡若AI 同步 2026-03-13 16:01 | 更新:运营中枢工作台 | 排除 >20MB: 11 个 |
| 2026-03-13 16:50:20 | 🔄 卡若AI 同步 2026-03-13 16:50 | 更新:卡木、运营中枢、运营中枢工作台 | 排除 >20MB: 11 个 |

View File

@@ -336,3 +336,4 @@
| 2026-03-13 14:45:12 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-13 14:45 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-13 15:42:55 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-13 15:42 | 更新:水桥平台对接、卡木、运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-13 16:01:23 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-13 16:01 | 更新:运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-03-13 16:50:20 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-13 16:50 | 更新:卡木、运营中枢、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |