fix(douyin): version_2 发布页适配——标题超时/封面输入/话题上限

- 标题/描述等待 10s→120s:version_2(post/video) 要等视频上传完才渲染表单(实测约40s)
- 封面上传修正:封面弹窗有4个隐藏input,前2个是左侧AI参考图,真封面是 nth(1);旧 .first 传错位置导致封面不生效
- set_thumbnail 先清 shepherd 引导浮层,避免拦截选择封面点击
- 完成按钮用 exact 文本,避免误中完成编辑
This commit is contained in:
changyandao
2026-06-13 15:16:29 +08:00
parent f5cb31079c
commit 5e28092f20
+38 -18
View File
@@ -264,12 +264,13 @@ class DouYinBaseUploader(BaseVideoUploader):
async def fill_title_and_description(self, page: Page, title: str, description: str, tags: list[str] | None = None):
# 2026-06 抖音发布页 DOM:标题=input[placeholder*=填写作品标题],描述=div.zone-container[contenteditable]
# version_2(post/video) 发布页要等视频上传完才渲染表单(实测约 40s),故等待超时给到 120s
title_input = page.locator('input[placeholder*="填写作品标题"]').first
await title_input.wait_for(state="visible", timeout=10000)
await title_input.wait_for(state="visible", timeout=120000)
await title_input.fill(title[:30])
description_editor = page.locator('div.zone-container[contenteditable="true"]').first
await description_editor.wait_for(state="visible", timeout=10000)
await description_editor.wait_for(state="visible", timeout=120000)
await description_editor.click()
await page.keyboard.press("Control+KeyA")
await page.keyboard.press("Delete")
@@ -467,29 +468,46 @@ class DouYinVideo(DouYinBaseUploader):
return
douyin_logger.info(_msg("🏃", "小人正在设置视频封面"))
await page.click('text="选择封面"', force=True)
# 先清掉 shepherd 新手引导浮层,否则它会拦截“选择封面”点击导致弹窗打不开
await page.evaluate(
"() => document.querySelectorAll('.shepherd-element,.shepherd-modal-overlay-container').forEach(e=>e.remove())"
)
await page.get_by_text("选择封面", exact=True).first.click(force=True)
cover_locator_str = 'div.dy-creator-content-modal'
cover_locator = page.locator(cover_locator_str).first
await page.wait_for_selector(cover_locator_str, timeout=15000)
await page.wait_for_selector(cover_locator_str, timeout=20000)
upload_input = cover_locator.locator("input.semi-upload-hidden-input").first
if self.thumbnail_landscape_path:
await page.wait_for_timeout(1000)
await upload_input.set_input_files(self.thumbnail_landscape_path)
await page.wait_for_timeout(2000)
douyin_logger.info(_msg("🖼️", "横版封面上传完成"))
await page.wait_for_timeout(1500)
# version_2 封面弹窗有 4 个隐藏 file input:
# [0]/[1] 左侧“AI生成参考图”上传/替换,[2]/[3] 才是“上传封面”/替换。
# 旧代码用 .first 传到了 AI 参考图(不会成为封面)→ 这就是“传了却没封面”的根因。
# 取 input.semi-upload-hidden-input 的第 2 个(nth(1)),即真正的封面上传输入。
cover_upload = cover_locator.locator("input.semi-upload-hidden-input").nth(1)
if self.thumbnail_portrait_path:
await cover_locator.locator('button:has-text("设置竖封面")').first.click()
await page.wait_for_timeout(1000)
await upload_input.set_input_files(self.thumbnail_portrait_path)
await page.wait_for_timeout(2000)
douyin_logger.info(_msg("🖼️", "竖版封面上传完成"))
# 弹窗默认就在“设置竖封面”页;防御性点一下 tab(已激活则忽略)
try:
await cover_locator.get_by_text("设置竖封面", exact=True).first.click(timeout=3000)
await page.wait_for_timeout(800)
except Exception:
pass
await cover_upload.set_input_files(self.thumbnail_portrait_path)
await page.wait_for_timeout(3000)
douyin_logger.info(_msg("🖼️", "竖版封面已上传到预览"))
elif self.thumbnail_landscape_path:
try:
await cover_locator.get_by_text("设置横封面", exact=True).first.click(timeout=3000)
await page.wait_for_timeout(800)
except Exception:
pass
await cover_upload.set_input_files(self.thumbnail_landscape_path)
await page.wait_for_timeout(3000)
douyin_logger.info(_msg("🖼️", "横版封面已上传到预览"))
await cover_locator.locator('button:has-text("完成")').first.click()
# 点红色主按钮“完成”应用封面(exact 避免误中“完成编辑”)
await cover_locator.get_by_role("button", name="完成", exact=True).first.click()
douyin_logger.info(_msg("🥳", "视频封面设置完成"))
await cover_locator.wait_for(state="detached", timeout=15000)
await cover_locator.wait_for(state="detached", timeout=20000)
async def upload(self, playwright: Playwright) -> None:
douyin_logger.info(_msg("🧍", "小人先检查 cookie、视频文件、封面和发布时间"))
@@ -508,6 +526,8 @@ class DouYinVideo(DouYinBaseUploader):
douyin_logger.info(_msg("🏃", f"小人开始搬运视频: {self.title}.mp4"))
douyin_logger.info(_msg("🧭", "小人正在赶往上传主页"))
await page.wait_for_url("https://creator.douyin.com/creator-micro/content/upload")
# wait_for_url 完成时上传页可能尚未渲染出文件 input(实测偶发),先等它挂载再 set_input_files
await page.wait_for_selector("div[class^='container'] input", state="attached", timeout=60000)
await page.locator("div[class^='container'] input").set_input_files(self.file_path)
while True: