From 5e28092f201ef881a402078cf9eba16e46a6714a Mon Sep 17 00:00:00 2001 From: changyandao Date: Sat, 13 Jun 2026 15:02:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(douyin):=20version=5F2=20=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E9=A1=B5=E9=80=82=E9=85=8D=E2=80=94=E2=80=94=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E8=B6=85=E6=97=B6/=E5=B0=81=E9=9D=A2=E8=BE=93=E5=85=A5/?= =?UTF-8?q?=E8=AF=9D=E9=A2=98=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 标题/描述等待 10s→120s:version_2(post/video) 要等视频上传完才渲染表单(实测约40s) - 封面上传修正:封面弹窗有4个隐藏input,前2个是左侧AI参考图,真封面是 nth(1);旧 .first 传错位置导致封面不生效 - set_thumbnail 先清 shepherd 引导浮层,避免拦截选择封面点击 - 完成按钮用 exact 文本,避免误中完成编辑 --- uploader/douyin_uploader/main.py | 56 ++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/uploader/douyin_uploader/main.py b/uploader/douyin_uploader/main.py index b89c5e4..fea5b3f 100644 --- a/uploader/douyin_uploader/main.py +++ b/uploader/douyin_uploader/main.py @@ -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: