# -*- coding: utf-8 -*-
|
from playwright.async_api import Playwright, Page
|
import asyncio
|
import datetime
|
|
from auto_media_publisher.uploaders.uploader_base import UploaderBase
|
|
CONTENT_UPLOAD_URL="https://baijiahao.baidu.com/builder/rc/edit?type=videoV2"
|
VIDEO_PUBLISH_SECCESSFULL_URL = "https://baijiahao.baidu.com/builder/rc/clue**"
|
|
|
class UploaderBaijiahao(UploaderBase):
|
|
|
async def virtual_scroll_select(self,page,text, wrapper_index = 0,panel_selector="div.rc-virtual-list-holder-inner",
|
item_selector_template='div.cheetah-select-item-option-content:has-text("{text}")',
|
step=200, # 每次滚动像素
|
max_attempts=15,
|
direction="down" # "down" 或 "up"
|
):
|
await page.locator('div.select-wrap').nth(wrapper_index).click()
|
await asyncio.sleep(1)
|
|
panel = page.locator(panel_selector).nth(wrapper_index)
|
item_selector = item_selector_template.format(text=text)
|
|
|
for i in range(max_attempts):
|
# 尝试查找
|
items = page.locator(item_selector)
|
if await items.count() > 0:
|
# 找到了,滚下去保证可见,再点击
|
await items.first.scroll_into_view_if_needed()
|
await items.first.click()
|
return
|
|
# 没找到就滚一轮
|
delta = step if direction == "down" else -step
|
await panel.evaluate(f"(el) => el.scrollTop += {delta}")
|
await asyncio.sleep(0.2)
|
await asyncio.sleep(1)
|
|
|
async def set_schedule_time(self, page):
|
"""
|
TODO 时间选择,日后在处理 百家号的时间选择不准确,目前是随机
|
"""
|
scheduled_time_day = f"{self.scheduled_time.month}月{self.scheduled_time.day}日" if self.scheduled_time.day >9 else f"{self.scheduled_time.month}月0{self.scheduled_time.day}日"
|
scheduled_time_hour = f"{self.scheduled_time.hour}点"
|
scheduled_time_min = f"{self.scheduled_time.minute}分"
|
await page.locator("div.cheetah-modal-confirm-body-wrapper").wait_for(state='visible',timeout=30000)
|
|
current_hour = datetime.datetime.now().hour
|
current_min = datetime.datetime.now().minute
|
direction_hour = "down" if self.scheduled_time.hour > current_hour else "up"
|
direction_minute = "down" if self.scheduled_time.minute > current_min else "up"
|
|
print(direction_hour)
|
print(dir)
|
|
|
try:
|
await page.locator('div.select-wrap').nth(0).click()
|
await asyncio.sleep(1)
|
target = page.locator(f'div[title="{scheduled_time_day}"]')
|
#await target.wait_for(state='visible')
|
await target.click()
|
await asyncio.sleep(1)
|
await self.virtual_scroll_select(page,scheduled_time_hour,wrapper_index=1,direction=direction_hour)
|
await self.virtual_scroll_select(page,scheduled_time_min,wrapper_index=2,direction=direction_minute)
|
#await page.locator('div.select-wrap').nth(2).click()
|
#await asyncio.sleep(1)
|
#target = page.locator(f'div.cheetah-select-item-option-content:has-text("{scheduled_time_min}")')
|
#await target.wait_for(state='visible')
|
#await target.scroll_into_view_if_needed()
|
#await target.click()
|
#await asyncio.sleep(1)
|
|
except Exception as e:
|
self.logger.error(f"[Baidu BaiJiaHao] 设置时间发生错误.{e}")
|
|
#await page.locator("div.cheetah-modal-confirm-btns > button").nth(1).click()
|
|
async def handle_upload_error(self, page):
|
# 日后实现,目前没遇到
|
return
|
print("视频出错了,重新上传中")
|
|
async def uploading_video(self, page):
|
while True:
|
upload_failed = await page.locator('div .cover-overlay:has-text("上传失败")').count()
|
if upload_failed:
|
self.logger.error("发现上传出错了...")
|
# await self.handle_upload_error(page) # 假设这是处理上传错误的函数
|
return False
|
|
uploading = await page.locator('div .cover-overlay:has-text("上传中")').count()
|
if uploading:
|
self.logger.info("正在上传视频中...")
|
await asyncio.sleep(2) # 等待2秒再次检查
|
continue
|
|
# 检查上传是否成功
|
if not uploading and not upload_failed:
|
self.logger.info("视频上传完毕")
|
return True
|
|
async def set_schedule_publish(self, page):
|
while True:
|
try:
|
self.logger.info("开始点击发布定时...")
|
buttons = page.get_by_role("button", name="定时发布")
|
count = await buttons.count()
|
for i in range(count):
|
btn = buttons.nth(i)
|
if await btn.is_visible():
|
await btn.click()
|
break
|
await asyncio.sleep(2) # 等待2秒再次检查
|
await self.set_schedule_time(page)
|
except Exception as e:
|
self.logger.error(f"定时发布失败: {e}")
|
raise # 重新抛出异常,让重试装饰器捕获
|
|
async def publish_video(self, page: Page):
|
if self.scheduled_time != 0:
|
# 定时发布
|
await self.set_schedule_publish(page)
|
else:
|
# 立即发布
|
await self.direct_publish(page)
|
|
async def direct_publish(self, page):
|
try:
|
publish_button = page.locator("button >> text=发布")
|
if await publish_button.count():
|
await publish_button.click()
|
except Exception as e:
|
self.logger.error(f"直接发布视频失败: {e}")
|
raise # 重新抛出异常,让重试装饰器捕获
|
|
async def add_title_tags(self, page):
|
title_page = page.get_by_placeholder('添加标题获得更多推荐')
|
if len(self.title) <= 8:
|
self.title += " 你不知道的"
|
await title_page.fill(self.title[:30])
|
|
|
async def _upload_core(self) -> bool:
|
try:
|
|
page = await self.context.new_page()
|
await page.goto(CONTENT_UPLOAD_URL, timeout=120000)
|
self.logger.info(f"正在上传-------{self.path_media}")
|
await page.wait_for_url(CONTENT_UPLOAD_URL, timeout=120000)
|
|
# 点击 "上传视频" 按钮
|
upload_div = page.locator(".PL3VsCg3MxIPY5UYiCS_")
|
await upload_div.wait_for(state="visible", timeout=10000)
|
async with page.expect_file_chooser() as fc_info:
|
await upload_div.click()
|
file_chooser = await fc_info.value
|
await file_chooser.set_files(self.path_media)
|
await asyncio.sleep(2)
|
|
# 等待页面跳转到指定的 URL
|
for i in range(10):
|
try:
|
await page.locator("div#formMain:visible").wait_for(timeout=30000)
|
break
|
except:
|
self.logger.info(f"第 {i+1} 次尝试:正在等待进入视频发布页面...")
|
await asyncio.sleep(0.1)
|
else:
|
self.logger.warning("超过最大尝试次数,仍未进入视频发布页面。")
|
|
# 填充标题和话题
|
# 这里为了避免页面变化,故使用相对位置定位:作品标题父级右侧第一个元素的input子元素
|
await asyncio.sleep(1)
|
self.logger.info("正在填充标题和话题...")
|
await self.add_title_tags(page)
|
|
upload_status = await self.uploading_video(page)
|
if not upload_status:
|
self.logger.error(f"发现上传出错了... 文件:{self.path_media}")
|
raise
|
|
# 判断视频封面图是否生成成功
|
while True:
|
self.logger.info("正在确认封面完成, 准备去点击定时/发布...")
|
if await page.locator("div.erVanTLKMOvnU7OgFaoD img").count():
|
self.logger.info("封面已完成,点击定时/发布...")
|
break
|
else:
|
self.logger.info("等待封面生成...")
|
await asyncio.sleep(3)
|
|
await self.publish_video(page)
|
await page.wait_for_timeout(2000)
|
if await page.locator('div.passMod_dialog-page >> text=百度安全验证:visible').count():
|
self.logger.error("出现验证,退出")
|
raise Exception("出现验证,退出")
|
await page.wait_for_url(VIDEO_PUBLISH_SECCESSFULL_URL, timeout=20000)
|
self.logger.info("视频发布成功")
|
|
await self.context.storage_state(path=self.cookie_path) # 保存cookie
|
self.logger.info('cookie更新完毕!')
|
await asyncio.sleep(2) # 这里延迟是为了方便眼睛直观的观看
|
|
return True
|
except Exception as e:
|
self.logger.error(f"{self.user} upload video {self.path_media} fails, error is :{e}")
|
return False
|