From 794e8feaea0304d2580f0598c40d7d8900e4feeb Mon Sep 17 00:00:00 2001 From: Edan's Win11 <961150665@qq.com> Date: Mon, 2 Jun 2025 20:30:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20ai2video=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf.py | 2 +- examples/upload_video_to_baijiahao.py | 8 +- uploader/baijiahao_uploader/main.py | 257 ++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 5 deletions(-) diff --git a/conf.py b/conf.py index b653ae4..a6908c0 100644 --- a/conf.py +++ b/conf.py @@ -2,4 +2,4 @@ from pathlib import Path BASE_DIR = Path(__file__).parent.resolve() XHS_SERVER = "http://127.0.0.1:11901" -LOCAL_CHROME_PATH = "" # change me necessary! for example C:/Program Files/Google/Chrome/Application/chrome.exe +LOCAL_CHROME_PATH = "C:\Program Files\Google\Chrome\Application\chrome.exe" # change me necessary! for example C:/Program Files/Google/Chrome/Application/chrome.exe diff --git a/examples/upload_video_to_baijiahao.py b/examples/upload_video_to_baijiahao.py index fa3f487..cd6c0e2 100644 --- a/examples/upload_video_to_baijiahao.py +++ b/examples/upload_video_to_baijiahao.py @@ -20,8 +20,8 @@ if __name__ == '__main__': title, tags = get_title_and_hashtags(str(file)) thumbnail_path = file.with_suffix('.png') # 打印视频文件名、标题和 hashtag - print(f"视频文件名:{file}") - print(f"标题:{title}") - print(f"Hashtag:{tags}") + # print(f"视频文件名:{file}") + # print(f"标题:{title}") + # print(f"Hashtag:{tags}") app = BaiJiaHaoVideo(title, file, tags, publish_datetimes[index], account_file) - asyncio.run(app.main(), debug=False) + asyncio.run(app.mainAi(), debug=False) diff --git a/uploader/baijiahao_uploader/main.py b/uploader/baijiahao_uploader/main.py index c6f1fc0..30ded1a 100644 --- a/uploader/baijiahao_uploader/main.py +++ b/uploader/baijiahao_uploader/main.py @@ -4,6 +4,7 @@ from datetime import datetime from playwright.async_api import Playwright, async_playwright, Page import os +import time import asyncio from conf import LOCAL_CHROME_PATH @@ -246,3 +247,259 @@ class BaiJiaHaoVideo(object): async with async_playwright() as playwright: await self.upload(playwright) + + + # 使用 AI成片 功能 + async def ai2video(self, playwright: Playwright) -> None: + # 使用 Chromium 浏览器启动一个浏览器实例 + browser = await playwright.chromium.launch(headless=False, executable_path=self.local_executable_path, proxy=self.proxy_setting) + # 创建一个浏览器上下文,使用指定的 cookie 文件 + context = await browser.new_context( + viewport={"width": 1600, "height": 900}, + storage_state=f"{self.account_file}", + user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.4324.150 Safari/537.36' + ) + # context = await set_init_script(context) + await context.grant_permissions(['geolocation']) + + # 创建一个新的页面 + page = await context.new_page() + # 访问指定的 URL + await page.goto("https://aigc.baidu.com/make", timeout=60000) + # 等待页面跳转到指定的 URL,没进入,则自动等待到超时 + baijiahao_logger.info('正在打开主页...') + await page.wait_for_url("https://aigc.baidu.com/make", timeout=60000) + + # 点击"全网"标签 + await page.locator('div.rounded-lg.border:has-text("全网")').click() + await asyncio.sleep(1) # 这里延迟是为了方便眼睛直观的观看 + + # 点击 "上传视频" 按钮 + # await page.locator("div[class^='video-main-container'] input").set_input_files(self.file_path) + + # region 操作处 + + # 生成日期时间键名(格式:ai2video_YYYYMMDDHHMM) + now = datetime.now() + datetime_str = now.strftime("%Y%m%d%H%M") + processed_key = "ai2video_processed_titles" + batch_key = f"ai2video_{datetime_str}" + + # 初始化LocalStorage + await page.evaluate(f""" + if (!localStorage.getItem("{processed_key}")) {{ + localStorage.setItem("{processed_key}", JSON.stringify([])); + }} + if (!localStorage.getItem("{batch_key}")) {{ + localStorage.setItem("{batch_key}", JSON.stringify([])); + }} + """) + + # 定位新闻列表容器(转义特殊CSS字符) + container_selector = '.overflow-auto.flex-grow.h-0.saas-scrollbar.mt\-\[-4px\].pl\-\[24px\].pr\-\[10px\].pb\-\[18px\]' + news_items = await page.locator(container_selector).locator('div.py\-\[6px\].group.cursor-pointer').all() + + for item in news_items: + try: + # 获取新闻标题 + title_elem = item.locator('div.flex.text-gray-darker.items-center.relative.pr\-\[56px\] > span') + title = await title_elem.text_content() + if not title: + continue + + # 检查是否已处理过 + is_processed = await page.evaluate( + f"""title => {{ + const processedList = JSON.parse(localStorage.getItem("{processed_key}") || "[]"); + return processedList.includes(title); + }}""", + title + ) + + if is_processed: + print(f"[跳过] {title}") + continue + + # 悬停显示按钮(根据HTML结构,按钮在悬停时显示) + await item.hover() + + # 点击生成文案按钮 + button = item.locator('button:has-text("生成文案")') + await button.click() + print(f"[点击] {title}") + + # 等待30秒 + # await page.wait_for_timeout(30000) + print(f"[等待完成] {title}") + + # 监听"一键成片"按钮 + print(f"[开始监听] 一键成片按钮") + should_exit_while_loop = False # 添加标志变量 + while True: + # 定位"一键成片"按钮 + one_key_button = page.locator("button:has-text('一键成片')") + + # 检查按钮是否存在 + if await one_key_button.count() > 0: + # 检查按钮是否有disabled属性 + is_disabled = await one_key_button.get_attribute("disabled") + + if is_disabled is None: + # 按钮不再被禁用,点击它 + print(f"[发现可点击按钮] 一键成片") + await one_key_button.click() # 先点击一键成片按钮 + + # 等待可能出现的"温馨提示"窗口 + print(f"[检查] 是否出现温馨提示窗口") + await page.wait_for_timeout(2000) # 等待2秒,让窗口有时间显示 + + try: + # 检查是否存在"温馨提示"窗口,设置较短的超时时间 + tip_window = page.locator("div:has-text('温馨提示') >> visible=true") + if await tip_window.count() > 0: + print(f"[发现] 温馨提示窗口") + + # 定位并点击"知道了"按钮,设置较短的超时时间 + know_button = page.locator("button:has-text('知道了')") + if await know_button.count() > 0: + try: + # 设置较短的超时时间进行点击 + await know_button.click(timeout=5000) + print(f"[已点击] 知道了按钮") + except Exception as e: + print(f"[警告] 点击知道了按钮时出错: {str(e)}") + else: + print(f"[警告] 未找到知道了按钮") + else: + print(f"[信息] 未出现温馨提示窗口,继续执行") + except Exception as e: + print(f"[警告] 处理温馨提示窗口时出错: {str(e)}") + # 继续执行,不要因为这个错误中断流程 + + # 记录到LocalStorage前打印日志 + print(f"[开始记录] 准备将标题 '{title}' 记录到LocalStorage") + + # 记录到LocalStorage + await page.evaluate( + f""" + (title, processedKey, batchKey) => {{ + // 更新已处理列表 + const processedList = JSON.parse(localStorage.getItem(processedKey) || "[]"); + if (!processedList.includes(title)) {{ + processedList.push(title); + localStorage.setItem(processedKey, JSON.stringify(processedList)); + }} + + // 更新当前批次记录 + const batchList = JSON.parse(localStorage.getItem(batchKey) || "[]"); + if (!batchList.includes(title)) {{ + batchList.push(title); + localStorage.setItem(batchKey, JSON.stringify(batchList)); + }} + }} + """, + title, processed_key, batch_key + ) + + # 记录完成后打印日志 + print(f"[记录完成] 标题 '{title}' 已成功记录到LocalStorage") + + print(f"[记录完成] {title}") + + # 监听新打开的标签页 + print(f"[监听] 等待新标签页打开") + # 获取当前所有页面 + current_pages = context.pages + current_page_count = len(current_pages) + + # 等待新标签页打开(最多等待10秒) + new_page = None + max_wait_time = 10 # 最大等待时间(秒) + start_time = time.time() + + while time.time() - start_time < max_wait_time: + # 获取最新的页面列表 + pages = context.pages + # 如果页面数量增加,说明新标签页已打开 + if len(pages) > current_page_count: + # 获取最新打开的页面(通常是列表中的最后一个) + new_page = pages[-1] + print(f"[发现] 新标签页已打开") + break + # 短暂等待后再次检查 + await asyncio.sleep(0.5) + + # 如果找到新标签页,获取其标题和URL并保存 + if new_page: + # 等待页面加载完成 + try: + await new_page.wait_for_load_state("domcontentloaded", timeout=5000) + # 获取页面标题和URL + page_title = await new_page.title() + page_url = new_page.url + + print(f"[获取] 标题: {page_title}") + print(f"[获取] URL: {page_url}") + + # 将标题和URL保存到url.txt文件 + with open("url.txt", "a", encoding="utf-8") as f: + f.write(f"{page_title}\n{page_url}\n\n") + + print(f"[保存] 标题和URL已保存到url.txt") + + # 等待5秒后关闭新标签页 + print(f"[等待] 5秒后将关闭新标签页") + await asyncio.sleep(5) + await new_page.close() + print(f"[关闭] 新标签页已关闭") + except Exception as e: + print(f"[错误] 处理新标签页时出错: {str(e)}") + try: + # 尝试关闭页面,即使出错 + await new_page.close() + print(f"[关闭] 新标签页已关闭(出错后)") + except: + pass + else: + print(f"[警告] 未检测到新标签页打开") + + # 跳出整个while循环 + print(f"[操作] 跳出所有循环,不再处理其他新闻") + should_exit_while_loop = True # 设置标志变量 + break # 跳出while循环 + + # 检查是否需要跳出while循环 + if should_exit_while_loop: + break + + # 每秒检查一次按钮状态 + await page.wait_for_timeout(1000) + + # 检查是否需要跳出for循环 + if should_exit_while_loop: + print(f"[操作] 跳出for循环,完全结束处理") + break # 跳出for循环 + except Exception as e: + print(f"处理新闻时出错: {str(e)}") + continue + + + # endregion 操作处 + + print(f"[循环完成] 准备关闭浏览器") + + # 暂停 1000s + await asyncio.sleep(1000) # 这里延迟是为了方便眼睛直观的观看 + + # 退出前保存 storage 信息 + await context.storage_state(path=self.account_file) # 保存cookie + baijiahao_logger.info('cookie更新完毕!') + await asyncio.sleep(2) # 这里延迟是为了方便眼睛直观的观看 + # 关闭浏览器上下文和浏览器实例 + await context.close() + await browser.close() + + + async def mainAi(self): + async with async_playwright() as playwright: + await self.ai2video(playwright)