Repository navigation
Nicko is small cute femboy - #323
Axoled-Student wants to merge 249 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
There are confirmed functional and security-impacting issues in the new login/auto-login and config initialization paths (e.g., missing imports, incorrect cookie file writes, and miswired login hashing) that must be corrected before merge.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR evolves the fork into “aniGamerPlus+” by modernizing dependencies, adding a plugin mechanism, and expanding the Dashboard into a user-aware UI (online watch, user management, web console), plus automation features like auto-login and danmu updating.
Changes:
- Added a plugin system and command/web console hooks for extensibility.
- Added/rewired Dashboard pages (index/control/watch/login/register/usermanage/userinfo) and supporting JS/CSS assets.
- Added automation features (auto-login via Selenium, optional m3u8 writing, danmu auto-update) and updated CI workflows to build on Windows + Linux.
File summaries
| File | Description |
|---|---|
| requirements.txt | Updates core dependencies and adds Selenium / yt-dlp / auth-related packages. |
| README.md | Renames branding to aniGamerPlus+ and documents added capabilities (plugin system, planned items, config bump). |
| plugins/.gitkeep | Adds placeholder directory for plugins. |
| plugin_system.py | Introduces PluginManager to load plugins, route uploads/commands, and support auto-update hooks. |
| Loginer.py | Adds Selenium-based auto-login and browser fingerprint capture/update workflow. |
| Dashboard/templates/watch.html | Adds a new watch page shell for online playback UI. |
| Dashboard/templates/usermanage.html | Adds admin user management UI (add/change/delete users). |
| Dashboard/templates/userinfo.html | Adds user info page + password change UI. |
| Dashboard/templates/register.html | Expands registration page to include username/password + error handling. |
| Dashboard/templates/monitor.html | Adjusts static paths and links for the new routing structure. |
| Dashboard/templates/login.html | Reworks login UI to username/password and adds client-side hashing logic. |
| Dashboard/templates/index.html | Replaces the old control panel-heavy index with a simple landing page linking to Watch/Control. |
| Dashboard/templates/control.html | Restores the full “control panel” page as a dedicated template. |
| Dashboard/static/js/userapi.js | Adds bootstrap-driven navbar/user-state handling and user-aware navigation. |
| Dashboard/static/js/settings_id_list.js | Adds new config ids (m3u8, auto_update_danmu) for settings sync. |
| Dashboard/static/js/monitor.js | Reworks websocket monitor logic (reconnect, cleanup, protocol handling). |
| Dashboard/static/js/ass.global.min.js | Adds ASS subtitle rendering library bundle for the watch player. |
| Dashboard/static/js/aniGamerPlus.js | Switches some endpoints to relative paths and adds web console command UI logic. |
| Dashboard/static/css/watch.css | Adds full styling for the new watch UI/player experience. |
| Dashboard/sslkey/server.key | Removes committed TLS private key from the repo. |
| Dashboard/sslkey/server.crt | Removes committed TLS certificate from the repo. |
| Dashboard/sslkey/.gitkeep | Keeps sslkey directory while removing sensitive key/cert material. |
| Danmu.py | Adds a display flag so danmu downloads can run quietly. |
| Config.py | Adds new config knobs (plugins, watch/user-control, auto-login, danmu update, m3u8, etc.) and cookie refresh logic. |
| config-sample.json | Updates sample config with new flags (but includes naming mismatches). |
| ColorPrint.py | Avoids subprocess-based tty detection to prevent gevent blocking issues. |
| Anime.py | Adds yt-dlp import and m3u8 playlist writing logic (and other fork-specific adjustments). |
| aniGamerPlus.py | Wires plugin manager, adds web/console command execution, video list updates, auto-login loop changes, and danmu auto-update. |
| .idea/.name | Adds IDE metadata. |
| .idea/.gitignore | Adds IDE ignore rules. |
| .gitignore | Updates ignore rules (video_list.json, cookies.pkl, plugins files, etc.). |
| .github/workflows/Release-build.yml | Expands release build workflow to matrix Windows/Linux and modern actions versions. |
| .github/workflows/Python-build.yml | Expands artifact build workflow to matrix Windows/Linux and modern actions versions. |
| .codex/environments/environment.toml | Adds a Codex environment descriptor. |
Review details
Files not reviewed (2)
- .idea/.gitignore: Generated file
- .idea/.name: Generated file
Suppressed comments (4)
Config.py:866
- invalid_cookie() writes the refreshed cookie to a relative 'cookie.txt', which may not be the configured working_dir cookie_path (cookie_path is an absolute path). This can leave the app still reading the old/missing cookie file.
if settings["auto_login"]["enabled"]:
__color_print(0, 'cookie狀態', '已開啟自動登入,嘗試透過瀏覽器登入...', no_sn=True, display=True)
loginer_return = Loginer.do_all(settings["auto_login"]["username"], settings["auto_login"]["password"], settings["auto_login"]["headless"], settings["auto_login"]["save_browser_cookie"])
if loginer_return:
open('cookie.txt', 'w').write(loginer_return)
__color_print(0, 'cookie狀態', '登入成功!已更新cookie。', no_sn=True, display=True)
aniGamerPlus.py:1212
- auto_update_loop() writes the refreshed cookie to a relative 'cookie.txt' instead of Config.cookie_path, which can desync cookie reads when the working directory differs from settings['working_dir'].
Dashboard/static/js/aniGamerPlus.js:208 - readManualConfig() posts to an absolute /manualTask while other dashboard endpoints were converted to relative ./... URLs. This breaks when the dashboard is served behind a reverse proxy/sub-path.
This issue also appears on line 300 of the same file.
$.ajax({
url: '/manualTask',
type: 'post',
Dashboard/static/js/aniGamerPlus.js:303
- Web console command posts to absolute /console/command (inconsistent with other relative URLs like ./uploadConfig and ./sn_list). This can break deployments where the app isn’t hosted at the domain root.
$.ajax({
url: '/console/command',
type: 'post',
dataType: 'json',
- Files reviewed: 29/37 changed files
- Comments generated: 9
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| <form class="login-content" action="./login" method="POST"> | ||
| <h1>登入</h1> | ||
| <input type="text" class="form-control" id="username" name="username" placeholder="輸入帳號" /> | ||
| <input type="password" class="form-control" id="password" name="password" placeholder="輸入密碼" /> | ||
| <button type="submit" class="btn btn-success">登入</button> | ||
| </form> |
| if os.path.exists('cookies.pkl'): | ||
| __color_print(0, "登入狀態", detail='找到cookie檔案', no_sn=True) | ||
| cookies = pickle.load(open("cookies.pkl", "rb")) | ||
| for cookie in cookies: | ||
| driver.add_cookie(cookie) | ||
| driver.get("https://user.gamer.com.tw/login.php") |
| try: | ||
| m3u8(self._bangumi_dir, self._video_filename, self._title) | ||
| err_print(self._sn, 'M3U8 寫入成功') | ||
| except: | ||
| err_print(self._sn, 'M3U8 寫入失敗 ' + os.path.join(dir, Config.legalize_filename(self._bangumi_name) + '.m3u8'), 'M3U8 寫入失敗', status=1) |
| 'use_dashboard': True, | ||
| 'command_console': { | ||
| 'enabled': False | ||
| }, | ||
| 'dashboard': { | ||
| 'host': '127.0.0.1', | ||
| 'port': 5000, | ||
| 'SSL': False, | ||
| 'BasicAuth': False, | ||
| 'username': 'admin', | ||
| 'password': 'admin' | ||
| 'password': 'admin', | ||
| 'online_watch': False, | ||
| 'online_watch_requires_login': False, | ||
| 'user_control': { | ||
| 'enabled': False, | ||
| 'allow_register': False, | ||
| 'default_user': [ | ||
| {'username': 'admin', 'password': 'admin', 'role': 'admin'} | ||
| ] | ||
| } | ||
| }, | ||
| 'save_logs': True, | ||
| 'quantity_of_logs': 7, | ||
| 'config_version': latest_config_version, | ||
| 'database_version': latest_database_version | ||
| 'database_version': latest_database_version, | ||
| 'm3u8': False, | ||
| 'auto_update_danmu': False, | ||
| 'danmu_download_cd': 5, # 彈幕下載冷却时间 | ||
| 'download_with_youtube': False, | ||
| 'auto_login': { | ||
| 'enabled': False, | ||
| 'use_wdm': True, | ||
| 'headless': False, | ||
| 'save_browser_cookie': False, | ||
| 'username': 'My_Bahamut_Username', | ||
| 'password': 'My_Bahamut_Password' | ||
| }, | ||
| 'check_sn_ended': False, | ||
| 'command_console': False, | ||
| 'plugins': { | ||
| 'enabled': [] | ||
| } |
| def __color_print(sn, err_msg, detail='', status=0, no_sn=False, display=True): | ||
| # 避免与 ColorPrint.py 相互调用产生问题 | ||
| try: | ||
| err_print(sn, err_msg, detail=detail, status=status, no_sn=no_sn, display=display) | ||
| except UnboundLocalError: | ||
| from ColorPrint import err_print | ||
| err_print(sn, err_msg, detail=detail, status=status, no_sn=no_sn, display=display) | ||
|
|
| if len(sys.argv) < 5: | ||
| print("Usage:", sys.argv[0], "[username] [password] [headless?] [save_cookie?]\nreturns raw cookie.\n? = [true/false]") | ||
| exit(1) | ||
| stat = do_all(sys.argv[1], sys.argv[2], bool(sys.argv[3]), bool(sys.argv[4])) |
| def __get_danmu_only(sn, bangumi_name, video_path, display=True, track_progress=False): | ||
| global danmu_tasks_counter | ||
| thread_limiter.acquire() | ||
|
|
||
| download_dir = settings['bangumi_dir'] | ||
| if classify: # 控制是否建立番剧文件夹 | ||
| # 控制是否建立番剧文件夹 | ||
| try: | ||
| if classify: | ||
| download_dir = os.path.join(download_dir, Config.legalize_filename(bangumi_name)) | ||
| except: | ||
| download_dir = os.path.join(download_dir, Config.legalize_filename(bangumi_name)) | ||
|
|
| "config_version": 13.0, | ||
| "database_version": 2.0 | ||
| "database_version": 2.0, | ||
| "m3u8": true, | ||
| "danmu_update": true | ||
| } |
| last_exception = None | ||
| for module_name in candidate_modules: | ||
| try: | ||
| module = importlib.import_module(module_name) | ||
| if not hasattr(module, 'create_plugin'): | ||
| continue | ||
| plugin = module.create_plugin(settings) | ||
| if plugin is None: | ||
| continue | ||
| self._plugins.append(plugin) | ||
| loaded = True | ||
| break | ||
| except BaseException as e: | ||
| last_exception = e |
uwu