from pathlib import Path import sys import os # 添加项目根目录到 Python 路径 current_dir = Path(__file__).parent.resolve() project_root = current_dir.parent.parent sys.path.append(str(project_root)) from conf import LOCAL_CHROME_PATH, BASE_DIR import re from datetime import datetime from playwright.async_api import Playwright, async_playwright import os import asyncio #from uploader.tk_uploader.tk_config import Tk_Locator from utils.base_social_media import set_init_script from utils.files_times import get_absolute_path from utils.log import X_logger from utils.anti_detection import create_stealth_browser, create_stealth_context, setup_stealth_page async def cookie_auth(account_file): async with async_playwright() as playwright: browser = await playwright.chromium.launch(headless=True) context = await browser.new_context(storage_state=account_file) context = await set_init_script(context) # 创建一个新的页面 page = await context.new_page() # 访问指定的 URL await page.goto("https://x.com/i/flow/login") #await page.wait_for_load_state('networkidle') await page.wait_for_load_state('domcontentloaded', timeout=30000) try: # 选择所有的 select 元素 select_elements = await page.query_selector_all('select') for element in select_elements: class_name = await element.get_attribute('class') # 使用正则表达式匹配特定模式的 class 名称 if re.match(r'ins-.*-SelectFormContainer.*', class_name): X_logger.error("[+] cookie expired") return False X_logger.success("[+] cookie valid") return True except: X_logger.success("[+] cookie valid") return True async def X_setup(account_file, handle=False): account_file = get_absolute_path(account_file, "X_uploader") if not os.path.exists(account_file) or not await cookie_auth(account_file): if not handle: return False X_logger.info('[+] cookie file is not existed or expired. Now open the browser auto. Please login with your way(gmail phone, whatever, the cookie file will generated after login') await get_X_cookie(account_file) return True async def get_X_cookie(account_file): async with async_playwright() as playwright: options = { 'args': [ '--lang en-GB', ], 'headless': False, # Set headless option here } # Make sure to run headed. browser = await playwright.chromium.launch(**options) # Setup context however you like. context = await browser.new_context() # Pass any options context = await set_init_script(context) # Pause the page, and start recording manually. page = await context.new_page() await page.goto("https://x.com/") await page.pause() # 点击调试器的继续,保存cookie await context.storage_state(path=account_file) class XVideo(object): def __init__(self, title, file_path, tags, publish_date, account_file, thumbnail_path=None, headless=True): self.title = title self.file_path = file_path self.tags = tags self.publish_date = publish_date self.thumbnail_path = thumbnail_path self.account_file = account_file self.local_executable_path = LOCAL_CHROME_PATH self.headless = headless # 是否使用无头模式,默认为True self.locator_base = None async def handle_upload_error(self, page): X_logger.info("X(Twitter)上传错误,正在重试...") try: # 重新查找文件上传元素并重试 upload_element = page.locator('input[type="file"]') await upload_element.wait_for(state='visible', timeout=10000) await upload_element.set_input_files(self.file_path) X_logger.info("重试上传成功") except Exception as e: X_logger.error(f"重试上传失败: {e}") async def upload(self, playwright: Playwright) -> None: # 使用统一的反检测工具 browser = await create_stealth_browser( playwright=playwright, headless=self.headless, executable_path=self.local_executable_path ) context = await create_stealth_context( browser=browser, account_file=self.account_file, headless=self.headless ) page = await setup_stealth_page(context, "https://x.com/") X_logger.info(f'[+]Uploading to X(Twitter)-------{self.title}') await page.wait_for_load_state('domcontentloaded', timeout=30000) try: # 基础的X上传逻辑(需要根据实际页面结构完善) X_logger.info("X上传器基础框架已就绪,需要根据实际页面结构完善上传逻辑") # TODO: 实现具体的上传逻辑 # 1. 点击发推按钮 # 2. 上传媒体文件 # 3. 添加文本内容 # 4. 发布推文 except Exception as e: X_logger.error(f"上传过程中出现错误: {e}") await self.handle_upload_error(page) await context.storage_state(path=f"{self.account_file}") # save cookie X_logger.info(' [-] 更新cookie成功!') await asyncio.sleep(2) # close all await context.close() await browser.close() async def main(self): async with async_playwright() as playwright: await self.upload(playwright) async def main(): """用于测试X上传功能的主函数""" from pathlib import Path # 配置文件路径 account_file = Path(__file__).parent.parent.parent / "cookies" / "X_uploader" / "account.json" account_file.parent.mkdir(exist_ok=True, parents=True) # 首先确保cookie有效 if not await X_setup(str(account_file), handle=True): X_logger.error("Cookie设置失败") return # 创建测试视频对象 video_path = Path(__file__).parent.parent.parent / "videos" / "demo.mp4" if not video_path.exists(): X_logger.error(f"测试视频文件不存在: {video_path}") return x_video = XVideo( title="测试X(Twitter)上传", file_path=str(video_path), tags=["test", "twitter", "upload"], publish_date=datetime.now(), account_file=str(account_file), headless=False # 推荐使用有头模式 ) # 开始上传 await x_video.main() if __name__ == '__main__': account_file = Path(BASE_DIR / "cookies" / "X_uploader" / "account.json") account_file.parent.mkdir(exist_ok=True) cookie_setup = asyncio.run(X_setup(str(account_file), handle=True)) # 如果需要测试上传功能,取消下面的注释 # asyncio.run(main()) # 以下是旧的注释代码,保留作为参考 # class InsVideo(object): # def __init__(self, title, file_path, tags, publish_date, account_file, thumbnail_path=None): # self.title = title # self.file_path = file_path # self.tags = tags # self.publish_date = publish_date # self.thumbnail_path = thumbnail_path # self.account_file = account_file # self.local_executable_path = LOCAL_CHROME_PATH # self.locator_base = None # # click title to remove the focus. # # await self.locator_base.locator("h1:has-text('Upload video')").click() # async def handle_upload_error(self, page): # X_logger.info("Instagram上传错误,正在重试...") # try: # # 重新查找"从电脑中选择"按钮并重试 # select_from_computer_button = page.locator('button._aswp._aswr._aswu._asw_._asx2:has-text("从电脑中选择")') # await select_from_computer_button.wait_for(state='visible', timeout=10000) # async with page.expect_file_chooser() as fc_info: # await select_from_computer_button.click() # file_chooser = await fc_info.value # await file_chooser.set_files(self.file_path) # X_logger.info("重试上传成功") # except Exception as e: # X_logger.error(f"重试上传失败: {e}") # async def upload(self, playwright: Playwright) -> None: # browser = await playwright.chromium.launch(headless=False, executable_path=self.local_executable_path) # context = await browser.new_context(storage_state=f"{self.account_file}") # context = await set_init_script(context) # page = await context.new_page() # # 访问Instagram主页 # await page.goto("https://www.instagram.com/") # X_logger.info(f'[+]Uploading to Instagram-------{self.title}') # await page.wait_for_load_state('domcontentloaded', timeout=30000) # try: # # 点击创建按钮 # create_button = page.locator('span.x1lliihq.x193iq5w.x6ikm8r.x10wlt62.xlyipyv.xuxw1ft:has-text("创建")') # await create_button.wait_for(state='visible', timeout=10000) # await create_button.click() # X_logger.info("成功点击创建按钮") # # 等待弹窗出现并点击"从电脑中选择" # await page.wait_for_timeout(2000) # 等待弹窗加载 # select_from_computer_button = page.locator('button._aswp._aswr._aswu._asw_._asx2:has-text("从电脑中选择")') # await select_from_computer_button.wait_for(state='visible', timeout=10000) # # 选择文件 # async with page.expect_file_chooser() as fc_info: # await select_from_computer_button.click() # file_chooser = await fc_info.value # await file_chooser.set_files(self.file_path) # X_logger.info("成功选择文件") # # 等待文件上传完成 # await page.wait_for_timeout(5000) # select_crop_button = page.locator('role=button >> text=继续') # await select_crop_button.wait_for(state='visible', timeout=10000) # await select_crop_button.click() # select_crop_button = page.locator('role=button >> text=继续') # await select_crop_button.wait_for(state='visible', timeout=10000) # await select_crop_button.click() # # 添加标题和标签 # await self.add_title_tags(page) # # 发布内容 # await self.click_publish(page) # except Exception as e: # X_logger.error(f"上传过程中出现错误: {e}") # # 处理上传错误 # await self.handle_upload_error(page) # await context.storage_state(path=f"{self.account_file}") # save cookie # X_logger.info(' [-] 更新cookie成功!') # await asyncio.sleep(2) # close delay for look the video status # # close all # await context.close() # await browser.close() # async def add_title_tags(self, page): # try: # # 等待Instagram的文本输入框出现 # await page.wait_for_timeout(3000) # 等待页面加载 # # 查找Instagram的文本输入框(可能有多种选择器) # text_selectors = [ # 'textarea[aria-label="写下说明…"]', # 'textarea[placeholder="写下说明…"]', # 'div[contenteditable="true"]', # 'textarea' # ] # text_area = None # for selector in text_selectors: # try: # text_area = page.locator(selector).first # await text_area.wait_for(state='visible', timeout=5000) # break # except: # continue # if text_area: # await text_area.click() # await page.wait_for_timeout(1000) # # 清空现有内容 # await page.keyboard.press("Control+A") # await page.keyboard.press("Delete") # # 输入标题 # await page.keyboard.insert_text(self.title) # await page.keyboard.press("Enter") # await page.keyboard.press("Enter") # # 添加标签 # for index, tag in enumerate(self.tags, start=1): # X_logger.info(f"添加第 {index} 个标签: {tag}") # await page.keyboard.insert_text(f"#{tag} ") # await page.wait_for_timeout(500) # X_logger.info("成功添加标题和标签") # else: # X_logger.warning("未找到文本输入框,跳过添加标题和标签") # # 调试用:暂停10秒,让你有时间观察页面上的标签是否正确输入 # await page.wait_for_timeout(10000) # X_logger.info("观察时间结束,中断程序") # # 强制中断(后续代码不执行) # raise SystemExit("调试中断:标签输入流程完成") # except Exception as e: # X_logger.error(f"添加标题和标签时出错: {e}") # async def click_publish(self, page): # try: # # 等待分享/发布按钮出现 # await page.wait_for_timeout(2000) # # Instagram发布按钮的可能选择器 # publish_selectors = [ # 'button:has-text("分享")', # 'button:has-text("Share")', # 'button:has-text("发布")', # 'button:has-text("Post")', # 'button[type="submit"]' # ] # publish_button = None # for selector in publish_selectors: # try: # publish_button = page.locator(selector).first # await publish_button.wait_for(state='visible', timeout=5000) # break # except: # continue # if publish_button: # await publish_button.click() # X_logger.info("成功点击发布按钮") # # 等待发布完成 # await page.wait_for_timeout(5000) # X_logger.success("Instagram内容发布成功!") # else: # X_logger.warning("未找到发布按钮") # except Exception as e: # X_logger.error(f"发布时出错: {e}") # async def main(): # """用于测试Instagram上传功能的主函数""" # from pathlib import Path # # 配置文件路径 # account_file = Path(__file__).parent.parent.parent / "cookies" / "ins_uploader" / "account.json" # account_file.parent.mkdir(exist_ok=True, parents=True) # # 首先确保cookie有效 # if not await ins_setup(str(account_file), handle=True): # X_logger.error("Cookie设置失败") # return # # 创建测试视频对象 # video_path = Path(__file__).parent.parent.parent / "videos" / "demo.mp4" # if not video_path.exists(): # X_logger.error(f"测试视频文件不存在: {video_path}") # return # ins_video = InsVideo( # title="测试Instagram上传", # file_path=str(video_path), # tags=["test", "instagram", "upload"], # publish_date=datetime.now(), # account_file=str(account_file) # ) # # 开始上传 # async with async_playwright() as playwright: # await ins_video.upload(playwright) # if __name__ == '__main__': # asyncio.run(main())