feat: EveryPublish Web 管理端重构与全平台发布接入

- 新增 React + TDesign 管理端(sau_web_react),替代历史 Vue 前端
- 后端接入 11 平台登录/发布(视频+图文),check_cookie 扩展全平台
- 异步发布任务 + 实时进度监控 + 发布日志
- Open API 开放体系(API Key 鉴权 + 7 个开放接口 + API 文档页)
- 账号单/批量校验与批量删除
- 全局配置(登录方式 + 发布静默模式)
- 统一浏览器为系统 Chrome,品牌文案统一为 EveryPublish
This commit is contained in:
Qiufeng
2026-08-21 14:41:47 +08:00
parent 1c66b7db4b
commit fd944b08bd
120 changed files with 6684 additions and 210 deletions
+63 -3
View File
@@ -1187,13 +1187,73 @@ class TencentNote(TencentBaseUploader):
self.image_paths = normalized_image_paths
async def switch_to_note_mode(self, page: Page) -> None:
raise NotImplementedError("请在 TencentNote.switch_to_note_mode 中补充视频号切换到图文发布模式的逻辑")
"""切换到图文发布模式。
视频号发布页(/platform/post/create)默认是视频模式,图文需切到图片模式。
优先点「发表图片」入口,再尝试页面内「图片/图文」tab,找不到则按当前页面继续。
"""
for label in ["发表图片", "发布图片", "图片动态"]:
try:
entry = page.locator("button", has_text=label).first
if await entry.count():
await entry.wait_for(state="visible", timeout=8000)
await entry.click()
await page.wait_for_timeout(1500)
tencent_logger.info(_msg("🖼️", f"已点击「{label}」切换到图文模式"))
return
except Exception:
continue
for text in ["图片", "图文"]:
try:
tab = page.locator('[role="tab"]', has_text=text).first
if await tab.count():
await tab.click(timeout=5000)
await page.wait_for_timeout(1000)
tencent_logger.info(_msg("🖼️", f"已点击「{text}」tab 切换到图文模式"))
return
except Exception:
continue
tencent_logger.warning(_msg("⚠️", "未找到明确的图文入口,按当前页面继续尝试上传图片"))
async def upload_note_images(self, page: Page) -> None:
raise NotImplementedError("请在 TencentNote.upload_note_images 中补充视频号图文图片上传逻辑")
"""上传图文图片(优先 accept 含 image 的 file input,兜底任意 file input)。"""
async def find_image_input():
for fr in page.frames:
try:
fi = fr.locator('input[type="file"][accept*="image"]')
if await fi.count():
return fi.first
except Exception:
continue
for fr in page.frames:
try:
fi = fr.locator('input[type="file"]')
if await fi.count():
return fi.first
except Exception:
continue
return None
fi = await find_image_input()
for _ in range(60):
if fi is not None:
break
await asyncio.sleep(1)
fi = await find_image_input()
if fi is None:
shot = Path(BASE_DIR) / "debug_tencent_note_no_input.png"
try:
await page.screenshot(path=str(shot), full_page=True)
tencent_logger.info(_msg("📸", f"图文失败现场已截图 {shot}; url={page.url}; frames={[fr.url[:80] for fr in page.frames]}"))
except Exception:
pass
raise RuntimeError("未找到视频号图文图片上传框")
await fi.set_input_files(self.image_paths)
tencent_logger.info(_msg("📤", f"已上传 {len(self.image_paths)} 张图文图片"))
async def fill_note_title_and_tags(self, page: Page) -> None:
raise NotImplementedError("请在 TencentNote.fill_note_title_and_tags 中补充视频号图文标题/话题填写逻辑")
"""填写图文标题和话题(复用视频的 div.input-editor 编辑区)。"""
await self.fill_title_and_tags(page)
async def fill_note_body(self, page: Page) -> None:
return None