diff --git a/uploader/douyin_uploader/main.py b/uploader/douyin_uploader/main.py index 83160a4..5b25597 100644 --- a/uploader/douyin_uploader/main.py +++ b/uploader/douyin_uploader/main.py @@ -367,6 +367,35 @@ class DouYinBaseUploader(BaseVideoUploader): douyin_logger.error(_msg("😢", f"设置商品链接时出错: {str(e)}")) return False + async def set_self_declaration(self, page: Page, declaration: str = "内容为个人观点或见解") -> None: + """抖音「自主声明」为发布必选项:打开声明弹窗 → 选指定类型 → 确定。 + + 入口和弹窗都是异步渲染,等不到就记 warning 跳过、继续发布,绝不因此中断 + (与小红书话题、视频号声明原创的容错策略保持一致)。 + """ + try: + # 发布页底部「自主声明」行,未选时显示占位文案「请选择自主声明」 + entry = page.get_by_text("请选择自主声明").first + await entry.wait_for(state="visible", timeout=6000) + await entry.click() + + # 弹窗标题「对作品内容添加声明」 + dialog = page.locator(".semi-modal-content").filter(has_text="对作品内容添加声明").first + await dialog.wait_for(state="visible", timeout=6000) + + # 单选项:Semi 的文字是 .semi-radio-addon(常带 pointer-events:none,直接点会卡 30s 超时), + # 要点可交互的 .semi-radio 外层;找不到外层再退回 force 强制点文字。exact 避免误命中预览「作者声明:…」。 + option = dialog.locator(".semi-radio").filter(has_text=declaration).first + if await option.count(): + await option.click(timeout=6000) + else: + await dialog.get_by_text(declaration, exact=True).first.click(timeout=6000, force=True) + await dialog.get_by_role("button", name="确定").click(timeout=6000) + await dialog.wait_for(state="hidden", timeout=6000) + douyin_logger.info(_msg("🧾", f"自主声明已选择「{declaration}」")) + except Exception as exc: + douyin_logger.warning(_msg("🧾", f"自主声明设置失败,跳过该步骤继续发布:{exc}")) + class DouYinVideo(DouYinBaseUploader): def __init__( @@ -532,6 +561,8 @@ class DouYinVideo(DouYinBaseUploader): await self.set_thumbnail(page) + await self.set_self_declaration(page) + third_part_element = '[class^="info"] > [class^="first-part"] div div.semi-switch' if await page.locator(third_part_element).count(): if "semi-switch-checked" not in await page.eval_on_selector(third_part_element, "div => div.className"):