diff --git a/README.md b/README.md index 06cce45..c6a3cc0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# SCNU 砺儒云 (Moodle) 视频自动观看工具 +# FlyVedioAssignmentAway! +> SCNU 砺儒云 (Moodle) 视频自动观看工具 基于 Playwright 的自动化脚本,支持自动登录华南师范大学砺儒云系统、解析视频列表并完成自动播放。 @@ -109,16 +110,16 @@ graph TD --- -## 🔐 登录方式 +## 🔐 获取登录凭证 ### 1. 交互式登录 (推荐) -启动程序后,选择交互式模式。脚本会唤起一个浏览器窗口,您可以手动输入账号密码或通过统一身份认证登录。登录完成后,脚本会自动捕获会话状态。 +启动程序后,选择 `交互式登录` 模式。脚本会唤起一个浏览器窗口,您可以手动输入账号密码通过统一身份认证登录。登录完成后,脚本会自动捕获会话状态。 -### 2. Cookie 文件登录 +### 2. 手动获取 Cookies 登录 1. 安装 [Cookie-Editor](https://microsoftedge.microsoft.com/addons/detail/cookieeditor/neaplmfkghagebokkhpjpoebhdledlfi) 扩展。 2. 在浏览器中登录 [SCNU 砺儒云](https://moodle.scnu.edu.cn/)。 -3. 点击插件,选择 "Export" 并导出为 **JSON** 格式。 -4. 将导出的内容保存为项目根目录下的 `browser_cookies.json` 文件。 +3. 点击插件,选择 "Export" 将 Cookies 导出为 **JSON** 格式。 +4. 运行程序,选择 `使用您手动获取的 Cookies 登录` 模式然后运行程序,将导出的内容粘贴进程序中。 > [详细 Cookie 获取指南](docs/how_to_get_cookie.md) diff --git a/automation/auth.py b/automation/auth.py index 1d90c09..3533c1a 100644 --- a/automation/auth.py +++ b/automation/auth.py @@ -67,6 +67,7 @@ async def refresh_cookies(self, cookie_file: str = "cookies.json"): await refresh_button.click() await asyncio.sleep(1) # 等待cookie更新 await self.save_cookies(cookie_file) + await self.load_cookies(cookie_file) async def check_cookie_validity(self) -> bool: """ @@ -77,8 +78,7 @@ async def check_cookie_validity(self) -> bool: try: page_content = await self.page.content() if "访客不能访问此课程" in page_content: - print("❌ 检测到Cookie已失效!页面显示: 访客不能访问此课程") - print("💡 请重新导出browser_cookies.json并运行脚本") + print("❌ 检测到Cookie已失效") return False return True except Exception as e: @@ -97,12 +97,6 @@ async def login_with_cookies(self, base_url: str, cookie_file: str = "cookies.js # 加载Cookie if not await self.load_cookies(cookie_file): print("\n❌ Cookie加载失败!") - print("💡 请按以下步骤手动获取Cookie:") - print(" 1. 在浏览器中登录网站") - print(" 2. 按F12打开开发者工具 -> Application -> Cookies") - print(" 3. 复制所有Cookie并保存为 cookies.json") - print(" 4. 或使用浏览器扩展导出Cookie(推荐)") - print("\n详细说明请查看: how_to_get_cookie.md") return False # 检查登录状态 return await self.check_login_status(base_url) @@ -135,7 +129,11 @@ async def check_login_status(self, base_url: str) -> bool: print(f"✓ Cookie登录成功,当前页面: {self.page.url}") return True - async def interactive_login_and_save_cookies(self, login_url: str, base_url: str, cookie_file: str = "cookies.json") -> bool: + async def interactive_login_and_save_cookies(self, + login_url: str, + base_url: str, + sso_index_url: str, + cookie_file: str = "cookies.json") -> bool: """ 交互式登录:打开登录页面,等待用户手动登录,然后保存Cookie :param login_url: 登录页面URL @@ -149,6 +147,8 @@ async def interactive_login_and_save_cookies(self, login_url: str, base_url: str print(f"✅ 登录页面已打开: {login_url}") print("📝 请在浏览器中完成登录操作") await asyncio.get_running_loop().run_in_executor(None, input, "🔑 登录完成后,请按回车键继续...") + # 先前往 SSO 主页 + await self.page.goto(sso_index_url) print("🔍 尝试获取cookie...") try: # 查找文本为"砺儒云课堂"的a标签 @@ -165,20 +165,19 @@ async def interactive_login_and_save_cookies(self, login_url: str, base_url: str await moodle_page.wait_for_load_state() print("✅ 成功跳转到目标页面") else: - print("⚠️ 未找到'lry课堂'链接,继续执行后续操作") + print("⚠️ 未找到'砺儒云课堂'链接") except Exception as e: - print(f"⚠️ 点击'lry课堂'链接时出错: {e}") - print("继续执行后续操作...") + print(f"⚠️ 点击'砺儒云课堂'链接时出错: {e}") # 验证Cookie是否有效 print("🔍 验证登录状态...") if await self.check_login_status(base_url): print("✅ 登录验证成功!") else: while not await self.check_login_status(base_url): - print("❌ 登录验证失败!请确认您已完成登录") + print("❌ 登录验证失败!") loop = asyncio.get_running_loop() retry = await loop.run_in_executor(None, input, "是否重试?(y/n): ") - if retry.lower() not in ('y', 'yes'): + if retry.strip().lower() not in ('y', 'yes'): return False print("✅ 登录验证成功!") diff --git a/config.py b/config.py index e305558..bd08b80 100644 --- a/config.py +++ b/config.py @@ -17,9 +17,12 @@ # ============= 其他配置 ============= +# 测试模式 +TEST_LOGIN_MODE = False # 设置为True以启用登录测试模式(仅测试登录功能) # Cookie登录配置 COOKIE_FILE = "cookies.json" # Cookie文件路径 BASE_URL = "https://moodle.scnu.edu.cn/my/" # 网站首页URL(用于验证Cookie) +SSO_INDEX_URL = "https://sso.scnu.edu.cn/AccountService/user/index.html" # SSO主页URL LOGIN_URL = "https://sso.scnu.edu.cn/AccountService/user/login.html" # URL模式匹配(脚本会自动找到所有包含此模式的链接) URL_PATTERN = "https://moodle.scnu.edu.cn/mod/fsresource/view.php?id=" # 视频链接的URL模式 diff --git a/cookie_fix.py b/cookie_fix.py index 2b48768..d06887d 100644 --- a/cookie_fix.py +++ b/cookie_fix.py @@ -1,15 +1,17 @@ # convert_cookies.py import json -def cookie_fix(file_name: str = 'browser_cookies.json'): +def cookie_fix(): try: - # 读取浏览器导出的Cookie - with open(file_name, 'r', encoding='utf-8') as f: - content = f.read().strip() - if content == '': - print("✗ Cookie文件为空,请检查文件内容") - return False - browser_cookies = json.loads(content) + # 从CLI读取浏览器导出的Cookie + print("请粘贴浏览器导出的Cookie JSON (连续敲击两次回车(Enter)结束输入):") + lines = list(iter(input, '')) + content = '\n'.join(lines) + + if content == '': + print("✗ 输入为空,请检查输入内容") + return False + browser_cookies = json.loads(content) # 转换为Playwright格式 playwright_cookies = [] diff --git a/docs/how_to_get_cookie.md b/docs/how_to_get_cookie.md index 44e646d..5cc7449 100644 --- a/docs/how_to_get_cookie.md +++ b/docs/how_to_get_cookie.md @@ -1,7 +1,5 @@ # 如何获取Cookie -本脚本使用Cookie进行登录,无需配置用户名密码。以下是获取Cookie的方法: - ## 方法一:使用浏览器扩展(推荐)⭐ ### Chrome/Edge @@ -15,9 +13,7 @@ 4. 选择 "Export" → "JSON" -5. 新建 `browser_cookies.json` 到项目根目录 - -6. 粘贴你刚刚复制的文本并保存 +此时 Cookies 就已经保存在你的剪贴板中了 ### Firefox @@ -27,7 +23,7 @@ 3. 点击扩展图标 → Export → JSON -4. 保存为 `cookies.json` +此时 Cookies 就已经保存在你的剪贴板中了 --- @@ -78,62 +74,6 @@ console.log(JSON.stringify(cookies, null, 2)); 3. 复制输出的JSON内容 -4. 创建 `cookies.json` 文件并粘贴内容 - ---- - -## 方法三:手动创建Cookie文件 - -如果你知道关键的Cookie(如 session_id、token 等),可以手动创建: - -```json -[ - { - "name": "session_id", - "value": "你的session值", - "domain": ".example.com", - "path": "/", - "expires": -1, - "httpOnly": true, - "secure": true, - "sameSite": "Lax" - } -] -``` - -**注意**: -- `name`: Cookie的名称 -- `value`: Cookie的值(最重要!) -- `domain`: 网站域名(加点号表示包括所有子域名) -- `expires`: 过期时间(-1表示会话Cookie) - ---- - -## 验证Cookie是否有效 - -创建 `cookies.json` 后,运行脚本测试: - -```bash -cd school_vedio_hw -uv run python scripts.py -``` - -如果看到: -- ✅ `✓ Cookie已从文件加载` -- ✅ `✓ Cookie登录成功` - -说明Cookie有效! - -如果看到: -- ❌ `Cookie加载失败` - -检查: -1. 文件名是否为 `cookies.json` -2. 文件是否在 `school_vedio_hw` 目录下 -3. JSON格式是否正确 -4. Cookie是否已过期 - ---- ## Cookie文件示例 @@ -173,34 +113,7 @@ uv run python scripts.py A: Cookie包含你的登录凭证,请注意: - ✅ 不要分享Cookie文件 - ✅ 不要上传到公开平台 -- ✅ 定期更新Cookie -- ✅ 使用后可以删除 ### Q: 如何判断Cookie已过期? A: 运行脚本时如果提示"登录失败",说明Cookie可能已过期,需要重新获取。 - ---- - -## 文件位置 - -确保 `browser_cookies.json` 文件放在正确的位置: - -``` -fly_vedio_assignment_away/ -├── ... -├── browser_cookies.json ← Cookie文件放这里 -└── ... -``` - ----- - -## 快速开始 - -1. ✅ 在浏览器中登录网站 -2. ✅ 使用扩展导出Cookie为json格式到你的剪贴板 -3. ✅ 粘贴到 `browser_cookies.json` 文件里面 -4. ✅ 将文件放到当前目录 -5. ✅ 运行程序 - -就这么简单!🚀 diff --git a/main.py b/main.py index d36be58..f5b3ec4 100644 --- a/main.py +++ b/main.py @@ -4,34 +4,50 @@ """ import asyncio +import traceback +from pathlib import Path from cookie_fix import cookie_fix from automation import BrowserManager, AuthManager, VideoManager - -# 导入配置 -try: - import config -except ImportError: - print("❌ 错误: 找不到 config.py 文件!") - print("请确保 config.py 文件存在于当前目录") - print("你可以从 config_example.py 复制一份并重命名为 config.py") - exit(1) +import config + + +def print_welcome(): + """打印欢迎界面""" + welcome_art = """ +╔══════════════════════════════════════════════════════════════╗ +║ ║ +║ Fly Vedio Assignment Away ║ +║ ║ +╠══════════════════════════════════════════════════════════════╣ +║ ║ +║ 欢迎使用 FlyVedioAssignmentAway ║ +║ 📖 使用说明: github.com/YewFence/fly_vedio_assignment_away ║ +║ ⚙️ 配置文件: config.py ║ +║ 👤 作者: YewFence ║ +║ ║ +╚══════════════════════════════════════════════════════════════╝ + """ + print(welcome_art) + print("🚀 程序启动中...\n") + print("💡 提示: 可按下 Ctrl+C 结束程序\n") async def main(): """主函数""" - - + + # 显示欢迎界面 + print_welcome() + # 从 config.py 读取配置 - print("正在加载配置...") - - # 初始化浏览器管理器 - browser_manager = BrowserManager( - browser_type=config.BROWSER, - headless=config.HEADLESS - ) + print("📦 正在初始化浏览器...") + browser_manager = None try: # 1. 启动浏览器 + browser_manager = BrowserManager( + browser_type=config.BROWSER, + headless=config.HEADLESS + ) await browser_manager.setup() # 2. 初始化认证和视频管理器 page = browser_manager.get_page() @@ -39,48 +55,59 @@ async def main(): auth_manager = AuthManager(page, context) video_manager = VideoManager(page, auth_manager) - - # 3. 选择登录方式 - print("\n🔐 请选择登录方式:") - print(" 1. 交互式登录(推荐)- 自动打开登录页面,您手动登录后程序自动获取Cookie") - print(" 2. Cookie文件登录 - 使用现有的cookies.json文件登录") - print("💡 提示:按 Ctrl+C 可随时结束程序") - login_success = False - while True: - try: - loop = asyncio.get_running_loop() - choice = await loop.run_in_executor(None, input, "请输入选择 (1/2,默认为1): ") - choice = choice.strip() - - if choice in ("", "1"): - # 默认使用交互式登录 - login_success = await auth_manager.interactive_login_and_save_cookies( - config.LOGIN_URL, - config.BASE_URL, - config.COOKIE_FILE - ) - break - elif choice == "2": - # 使用旧的cookie文件登录方式 - if cookie_fix(): - print("✓ Cookie文件格式化成功") - login_success = await auth_manager.login_with_cookies( + # 测试模式下跳过尝试,进行登录凭证获取测试 + if not config.TEST_LOGIN_MODE: + cookie_path = Path(config.COOKIE_FILE) + # 如果 cookies.json 文件已存在,尝试直接使用已有 Cookies 登录 + if cookie_path.exists(): + print(f"📂 检测到已有 Cookie 文件: {config.COOKIE_FILE},尝试直接使用该文件登录...") + login_success = await auth_manager.login_with_cookies( + config.BASE_URL, + config.COOKIE_FILE + ) + if not login_success: + print("登录凭证已失效或不存在") + # 选择登录方式 + print("\n🔐 请选择获取登录凭证(Cookies)的方式:") + print(" 1. 交互式登录(推荐)- 自动打开登录页面,您手动登录后程序自动获取Cookies") + print(" 2. 使用您手动获取的 Cookies 登录 - 在命令行中直接粘贴浏览器导出的 Cookies JSON") + + login_success = False + while True: + try: + loop = asyncio.get_running_loop() + choice = await loop.run_in_executor(None, input, "请输入选择 (1/2,默认为1): ") + choice = choice.strip() + + if choice in ("", "1"): + # 默认使用交互式登录 + login_success = await auth_manager.interactive_login_and_save_cookies( + config.LOGIN_URL, config.BASE_URL, + config.SSO_INDEX_URL, config.COOKIE_FILE ) + break + elif choice == "2": + # 使用手动导出的 cookies 登录 + if cookie_fix(): + print("✓ Cookies 格式化成功") + login_success = await auth_manager.login_with_cookies( + config.BASE_URL, + config.COOKIE_FILE + ) + else: + print("⚠ Cookies 格式化失败,请检查输入的 Cookies 内容是否正确,程序即将结束") + break else: - print("⚠ Cookie文件格式化失败,请检查browser_cookies.json是否配置正确,程序即将结束") - break - else: - print("⚠️ 输入无效,请输入 1 或 2,或按 Ctrl+C 结束程序") - except KeyboardInterrupt: - print("\n\n程序已由用户中断。") - return + print("⚠️ 输入无效,请输入 1 或 2") + except KeyboardInterrupt: + print("\n\n程序已由用户中断。") + return if not login_success: - print("\n❌ 登录失败! 请确保已正确配置 cookies.json 文件或完成手动登录") - print("详细说明请查看: how_to_get_cookie.md") + print("\n❌ 登录失败!") return # 4. 通过URL模式获取视频链接 @@ -101,28 +128,33 @@ async def main(): config.DEFAULT_WAIT_TIME ) else: - print("⚠ 没有找到视频链接") - print("\n💡 故障排查建议:") - print(" 1. 检查 config.py 中是否正确配置了课程链接") - print(" 2. 确认 cookies.json 文件存在") - print(" 3. 确认 Cookie 是否有效") - print(" 4. 确认网络状态良好") + print("❌ 未找到任何视频链接。") + suggestions() except Exception as e: print(f"\n❌ 发生错误: {e}") - import traceback traceback.print_exc() - - print("\n💡 故障排查建议:") - print(" 1. 检查 config.py 中是否正确配置了课程链接") - print(" 2. 确认 cookies.json 文件存在") - print(" 3. 确认 Cookie 是否有效") - print(" 4. 确认网络状态良好") + suggestions() finally: # 6. 关闭浏览器 - input("\n按回车键退出并关闭浏览器...") - await browser_manager.close() - + if browser_manager: + try: + # 检查浏览器是否仍在运行 + browser = browser_manager.browser + if browser and browser.is_connected(): + input("\n按回车键退出并关闭浏览器...") + await browser_manager.close() + except Exception: + # 浏览器已被手动关闭或其他错误,静默处理 + pass + +def suggestions(): + print("\n💡 故障排查建议:") + print(" 1. 检查 config.py 中是否正确配置了课程链接") + print(" 2. 确认 cookies.json 文件存在") + print(" 3. 确认 Cookie 是否有效") + print(" 4. 确认网络状态良好") + print(" 5. 如仍有问题,请提交 issue 至 GitHub 仓库:github.com/YewFence/fly_vedio_assignment_away\n") if __name__ == "__main__": asyncio.run(main())