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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user