|
@@ -348,6 +348,97 @@ class BaijiahaoPublisher(BasePublisher):
|
|
|
|
|
|
|
|
return {'need_captcha': False, 'captcha_type': ''}
|
|
return {'need_captcha': False, 'captcha_type': ''}
|
|
|
|
|
|
|
|
|
|
+ async def _check_login_resolved(self) -> bool:
|
|
|
|
|
+ """检查用户是否已完成登录(页面不再停留在登录页)"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ current_url = self.page.url
|
|
|
|
|
+ for indicator in self.login_indicators:
|
|
|
|
|
+ if indicator in current_url:
|
|
|
|
|
+ return False
|
|
|
|
|
+ # 检查传统登录弹窗
|
|
|
|
|
+ login_selectors = [
|
|
|
|
|
+ 'text="请登录"',
|
|
|
|
|
+ 'text="登录后继续"',
|
|
|
|
|
+ '[class*="login-dialog"]',
|
|
|
|
|
+ ]
|
|
|
|
|
+ for selector in login_selectors:
|
|
|
|
|
+ try:
|
|
|
|
|
+ loc = self.page.locator(selector).first
|
|
|
|
|
+ if await loc.count() > 0 and await loc.is_visible():
|
|
|
|
|
+ return False
|
|
|
|
|
+ except:
|
|
|
|
|
+ pass
|
|
|
|
|
+ return True
|
|
|
|
|
+ except:
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ async def _check_captcha_resolved(self) -> bool:
|
|
|
|
|
+ """检查用户是否已完成验证码验证"""
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 先用传统方式检查(速度快,无需 API 调用)
|
|
|
|
|
+ captcha_result = await self.check_captcha()
|
|
|
|
|
+ if not captcha_result['need_captcha']:
|
|
|
|
|
+ # 传统方式未检测到验证码,再用 AI 确认
|
|
|
|
|
+ ai_captcha = await self.ai_check_captcha()
|
|
|
|
|
+ if not ai_captcha['has_captcha']:
|
|
|
|
|
+ return True
|
|
|
|
|
+ return False
|
|
|
|
|
+ except:
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ async def _wait_for_user_resolve(
|
|
|
|
|
+ self,
|
|
|
|
|
+ check_fn,
|
|
|
|
|
+ timeout: int = 300,
|
|
|
|
|
+ poll_interval: int = 5,
|
|
|
|
|
+ prompt: str = "",
|
|
|
|
|
+ ) -> bool:
|
|
|
|
|
+ """
|
|
|
|
|
+ 有头浏览器模式下,等待用户手动解决验证码/登录问题。
|
|
|
|
|
+
|
|
|
|
|
+ Args:
|
|
|
|
|
+ check_fn: 异步函数,返回 True 表示问题已解决
|
|
|
|
|
+ timeout: 超时时间(秒),默认 5 分钟
|
|
|
|
|
+ poll_interval: 轮询间隔(秒)
|
|
|
|
|
+ prompt: 展示给用户的提示信息
|
|
|
|
|
+
|
|
|
|
|
+ Returns:
|
|
|
|
|
+ True 表示用户已成功解决,False 表示超时
|
|
|
|
|
+ """
|
|
|
|
|
+ import time
|
|
|
|
|
+
|
|
|
|
|
+ if prompt:
|
|
|
|
|
+ print(f"[{self.platform_name}] {prompt}", flush=True)
|
|
|
|
|
+
|
|
|
|
|
+ start_time = time.time()
|
|
|
|
|
+ attempt = 0
|
|
|
|
|
+ while time.time() - start_time < timeout:
|
|
|
|
|
+ attempt += 1
|
|
|
|
|
+ elapsed = int(time.time() - start_time)
|
|
|
|
|
+ remaining = timeout - elapsed
|
|
|
|
|
+ print(
|
|
|
|
|
+ f"[{self.platform_name}] 等待用户操作... ({elapsed}s/{timeout}s, 剩余 {remaining}s)",
|
|
|
|
|
+ flush=True,
|
|
|
|
|
+ )
|
|
|
|
|
+ self.report_progress(
|
|
|
|
|
+ 12,
|
|
|
|
|
+ f"等待用户操作中... (已等待 {elapsed}s)",
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ resolved = await check_fn()
|
|
|
|
|
+ if resolved:
|
|
|
|
|
+ print(f"[{self.platform_name}] 用户操作完成(第 {attempt} 次检测)", flush=True)
|
|
|
|
|
+ await asyncio.sleep(2) # 额外等待页面稳定
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ await asyncio.sleep(poll_interval)
|
|
|
|
|
+
|
|
|
|
|
+ print(
|
|
|
|
|
+ f"[{self.platform_name}] 等待用户操作超时 ({timeout}s)",
|
|
|
|
|
+ flush=True,
|
|
|
|
|
+ )
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
async def _ai_analyze_upload_state(self, screenshot_base64: str = None) -> dict:
|
|
async def _ai_analyze_upload_state(self, screenshot_base64: str = None) -> dict:
|
|
|
"""
|
|
"""
|
|
|
使用 AI 识别当前上传状态,返回:
|
|
使用 AI 识别当前上传状态,返回:
|
|
@@ -807,65 +898,121 @@ class BaijiahaoPublisher(BasePublisher):
|
|
|
# 检查是否跳转到登录页
|
|
# 检查是否跳转到登录页
|
|
|
current_url = self.page.url
|
|
current_url = self.page.url
|
|
|
print(f"[{self.platform_name}] 当前页面: {current_url}")
|
|
print(f"[{self.platform_name}] 当前页面: {current_url}")
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for indicator in self.login_indicators:
|
|
for indicator in self.login_indicators:
|
|
|
if indicator in current_url:
|
|
if indicator in current_url:
|
|
|
|
|
+ if not self.headless:
|
|
|
|
|
+ # 有头浏览器模式:等待用户手动完成登录
|
|
|
|
|
+ print(f"[{self.platform_name}] 有头模式检测到登录跳转,等待用户手动登录...")
|
|
|
|
|
+ self.report_progress(12, "检测到需要登录,请在浏览器中手动完成登录...")
|
|
|
|
|
+ login_resolved = await self._wait_for_user_resolve(
|
|
|
|
|
+ check_fn=self._check_login_resolved,
|
|
|
|
|
+ timeout=300,
|
|
|
|
|
+ prompt="请在打开的浏览器中完成登录",
|
|
|
|
|
+ )
|
|
|
|
|
+ if login_resolved:
|
|
|
|
|
+ current_url = self.page.url
|
|
|
|
|
+ print(f"[{self.platform_name}] 用户已完成登录,当前页面: {current_url}")
|
|
|
|
|
+ break # 登录已解决,继续发布流程
|
|
|
|
|
+ else:
|
|
|
|
|
+ screenshot_base64 = await self.capture_screenshot()
|
|
|
|
|
+ return PublishResult(
|
|
|
|
|
+ success=False,
|
|
|
|
|
+ platform=self.platform_name,
|
|
|
|
|
+ error="等待用户登录超时(5分钟),请重试",
|
|
|
|
|
+ need_captcha=True,
|
|
|
|
|
+ captcha_type='login',
|
|
|
|
|
+ screenshot_base64=screenshot_base64,
|
|
|
|
|
+ page_url=current_url,
|
|
|
|
|
+ status='need_captcha'
|
|
|
|
|
+ )
|
|
|
|
|
+ else:
|
|
|
|
|
+ screenshot_base64 = await self.capture_screenshot()
|
|
|
|
|
+ return PublishResult(
|
|
|
|
|
+ success=False,
|
|
|
|
|
+ platform=self.platform_name,
|
|
|
|
|
+ error="Cookie 已过期,需要重新登录",
|
|
|
|
|
+ need_captcha=True,
|
|
|
|
|
+ captcha_type='login',
|
|
|
|
|
+ screenshot_base64=screenshot_base64,
|
|
|
|
|
+ page_url=current_url,
|
|
|
|
|
+ status='need_captcha'
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # 检查验证码(有头模式下等待用户手动解决,无头模式下直接返回)
|
|
|
|
|
+ captcha_detected = False
|
|
|
|
|
+ captcha_type = ''
|
|
|
|
|
+
|
|
|
|
|
+ # 使用 AI 检查验证码
|
|
|
|
|
+ ai_captcha = await self.ai_check_captcha()
|
|
|
|
|
+ if ai_captcha['has_captcha']:
|
|
|
|
|
+ captcha_detected = True
|
|
|
|
|
+ captcha_type = ai_captcha['captcha_type']
|
|
|
|
|
+ print(f"[{self.platform_name}] AI检测到验证码: {captcha_type}", flush=True)
|
|
|
|
|
+
|
|
|
|
|
+ # AI 未检测到时再用传统方式检查
|
|
|
|
|
+ if not captcha_detected:
|
|
|
|
|
+ captcha_result = await self.check_captcha()
|
|
|
|
|
+ if captcha_result['need_captcha']:
|
|
|
|
|
+ captcha_detected = True
|
|
|
|
|
+ captcha_type = captcha_result['captcha_type']
|
|
|
|
|
+
|
|
|
|
|
+ if captcha_detected:
|
|
|
|
|
+ if not self.headless:
|
|
|
|
|
+ # 有头浏览器模式:等待用户手动完成验证码
|
|
|
|
|
+ print(f"[{self.platform_name}] 有头模式检测到验证码({captcha_type}),等待用户手动解决...")
|
|
|
|
|
+ self.report_progress(12, f"检测到验证码,请在浏览器中手动完成验证...")
|
|
|
|
|
+ captcha_resolved = await self._wait_for_user_resolve(
|
|
|
|
|
+ check_fn=self._check_captcha_resolved,
|
|
|
|
|
+ timeout=300,
|
|
|
|
|
+ prompt="请在打开的浏览器中完成验证码验证",
|
|
|
|
|
+ )
|
|
|
|
|
+ if not captcha_resolved:
|
|
|
|
|
+ screenshot_base64 = await self.capture_screenshot()
|
|
|
|
|
+ return PublishResult(
|
|
|
|
|
+ success=False,
|
|
|
|
|
+ platform=self.platform_name,
|
|
|
|
|
+ error=f"等待用户完成验证码超时(5分钟),请重试",
|
|
|
|
|
+ need_captcha=True,
|
|
|
|
|
+ captcha_type=captcha_type,
|
|
|
|
|
+ screenshot_base64=screenshot_base64,
|
|
|
|
|
+ page_url=current_url,
|
|
|
|
|
+ status='need_captcha'
|
|
|
|
|
+ )
|
|
|
|
|
+ print(f"[{self.platform_name}] 用户已完成验证码验证,继续发布流程")
|
|
|
|
|
+ else:
|
|
|
screenshot_base64 = await self.capture_screenshot()
|
|
screenshot_base64 = await self.capture_screenshot()
|
|
|
return PublishResult(
|
|
return PublishResult(
|
|
|
success=False,
|
|
success=False,
|
|
|
platform=self.platform_name,
|
|
platform=self.platform_name,
|
|
|
- error="Cookie 已过期,需要重新登录",
|
|
|
|
|
|
|
+ error=f"检测到{captcha_type}验证码,需要使用有头浏览器完成验证",
|
|
|
need_captcha=True,
|
|
need_captcha=True,
|
|
|
- captcha_type='login',
|
|
|
|
|
|
|
+ captcha_type=captcha_type,
|
|
|
screenshot_base64=screenshot_base64,
|
|
screenshot_base64=screenshot_base64,
|
|
|
page_url=current_url,
|
|
page_url=current_url,
|
|
|
status='need_captcha'
|
|
status='need_captcha'
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- # 使用 AI 检查验证码
|
|
|
|
|
- ai_captcha = await self.ai_check_captcha()
|
|
|
|
|
- if ai_captcha['has_captcha']:
|
|
|
|
|
- print(f"[{self.platform_name}] AI检测到验证码: {ai_captcha['captcha_type']}", flush=True)
|
|
|
|
|
- screenshot_base64 = await self.capture_screenshot()
|
|
|
|
|
- return PublishResult(
|
|
|
|
|
- success=False,
|
|
|
|
|
- platform=self.platform_name,
|
|
|
|
|
- error=f"检测到{ai_captcha['captcha_type']}验证码,需要使用有头浏览器完成验证",
|
|
|
|
|
- need_captcha=True,
|
|
|
|
|
- captcha_type=ai_captcha['captcha_type'],
|
|
|
|
|
- screenshot_base64=screenshot_base64,
|
|
|
|
|
- page_url=current_url,
|
|
|
|
|
- status='need_captcha'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- # 传统方式检查验证码
|
|
|
|
|
- captcha_result = await self.check_captcha()
|
|
|
|
|
- if captcha_result['need_captcha']:
|
|
|
|
|
- screenshot_base64 = await self.capture_screenshot()
|
|
|
|
|
- return PublishResult(
|
|
|
|
|
- success=False,
|
|
|
|
|
- platform=self.platform_name,
|
|
|
|
|
- error=f"需要{captcha_result['captcha_type']}验证码,请使用有头浏览器完成验证",
|
|
|
|
|
- need_captcha=True,
|
|
|
|
|
- captcha_type=captcha_result['captcha_type'],
|
|
|
|
|
- screenshot_base64=screenshot_base64,
|
|
|
|
|
- page_url=current_url,
|
|
|
|
|
- status='need_captcha'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
self.report_progress(15, "正在选择视频文件...")
|
|
self.report_progress(15, "正在选择视频文件...")
|
|
|
|
|
|
|
|
# 等待页面加载完成
|
|
# 等待页面加载完成
|
|
|
await asyncio.sleep(2)
|
|
await asyncio.sleep(2)
|
|
|
|
|
|
|
|
- # 关闭可能的弹窗
|
|
|
|
|
|
|
+ # 关闭可能的弹窗(有头模式下使用更保守的选择器,避免关闭用户需要交互的验证码弹窗)
|
|
|
try:
|
|
try:
|
|
|
- close_buttons = [
|
|
|
|
|
- 'button:has-text("我知道了")',
|
|
|
|
|
- 'button:has-text("知道了")',
|
|
|
|
|
- '[class*="close"]',
|
|
|
|
|
- '[class*="modal-close"]',
|
|
|
|
|
- ]
|
|
|
|
|
|
|
+ if self.headless:
|
|
|
|
|
+ close_buttons = [
|
|
|
|
|
+ 'button:has-text("我知道了")',
|
|
|
|
|
+ 'button:has-text("知道了")',
|
|
|
|
|
+ '[class*="close"]',
|
|
|
|
|
+ '[class*="modal-close"]',
|
|
|
|
|
+ ]
|
|
|
|
|
+ else:
|
|
|
|
|
+ # 有头模式:只关闭明确的提示性弹窗,不触碰可能含验证码的对话框
|
|
|
|
|
+ close_buttons = [
|
|
|
|
|
+ 'button:has-text("我知道了")',
|
|
|
|
|
+ 'button:has-text("知道了")',
|
|
|
|
|
+ ]
|
|
|
for btn_selector in close_buttons:
|
|
for btn_selector in close_buttons:
|
|
|
try:
|
|
try:
|
|
|
btn = self.page.locator(btn_selector).first
|
|
btn = self.page.locator(btn_selector).first
|