diff --git a/.env b/.env
index 05af5ad..8449e55 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,2 @@
-version=1.9.03
-VERSION=1.9.03
+version=1.9.05
+VERSION=1.9.05
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1daddd0..6395afc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -153,7 +153,7 @@ jobs:
- name: Build in ARM64 Docker container
run: |
- docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.9-slim bash -c "
+ docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.10-slim bash -c "
apt-get update && apt-get install -y build-essential
pip install --upgrade pip
pip install pyinstaller
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2f0f41..6c9effa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,20 @@
# Change Log
-## v1.9.03
+## v1.9.05
+1. Refactor: Using match-case to refactor language mapping and menu selection logic, making the code clearer and more maintainable. | 使用 match-case 重构语言映射和菜单选择逻辑,使代码更清晰、可维护性更高。
+2. Ci: Update the Python version in the ARM64 Docker build container to 3.10, making it more compatible and easier to migrate in the future. | 更新 ARM64 Docker 构建容器中的 Python 版本至 3.10,兼容性更强,方便未来迁移。
+3. Fix: f-string backslash expression errors in multiple files | 修復多個文件中的 f-string 反斜杠表達式錯誤
+4. Fix: Some Issues | 修復一些問題
+
+## v1.9.04
+1. Add: Opera GX Support | 添加 Opera GX 支持
+2. Same as v1.9.03 | 與 v1.9.03 相同
+3. Hotfix: Some Issues | 修復一些問題
+4. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
+5. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
+6. Fix: Some Issues | 修復一些問題
+
+## v1.9.03[Skip & Merge to v1.9.04]
1. Hotfix: Some Issues | 修復一些問題
2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
diff --git a/README.md b/README.md
index 774b7dc..800e53a 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,10 @@
-[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
+[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
[](https://creativecommons.org/licenses/by-nc-nd/4.0/)
-[](https://github.com/yeongpin/cursor-free-vip/stargazers)
-[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
+[](https://github.com/yeongpin/cursor-free-vip/stargazers)
+[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
diff --git a/config.py b/config.py
index 400353d..38cc332 100644
--- a/config.py
+++ b/config.py
@@ -60,18 +60,17 @@ def setup_config(translator=None):
'Browser': {
'default_browser': 'chrome',
'chrome_path': get_default_browser_path('chrome'),
- 'edge_path': get_default_browser_path('edge'),
- 'firefox_path': get_default_browser_path('firefox'),
- 'brave_path': get_default_browser_path('brave'),
'chrome_driver_path': get_default_driver_path('chrome'),
+ 'edge_path': get_default_browser_path('edge'),
'edge_driver_path': get_default_driver_path('edge'),
+ 'firefox_path': get_default_browser_path('firefox'),
'firefox_driver_path': get_default_driver_path('firefox'),
+ 'brave_path': get_default_browser_path('brave'),
'brave_driver_path': get_default_driver_path('brave'),
'opera_path': get_default_browser_path('opera'),
- 'opera_driver_path': get_default_driver_path('opera')
- },
- 'Chrome': {
- 'chromepath': get_default_browser_path('chrome')
+ 'opera_driver_path': get_default_driver_path('opera'),
+ 'operagx_path': get_default_browser_path('operagx'),
+ 'operagx_driver_path': get_default_driver_path('chrome') # Opera GX 使用 Chrome 驱动
},
'Turnstile': {
'handle_turnstile_time': '2',
diff --git a/cursor_register_manual.py b/cursor_register_manual.py
index d33df91..e0f8b3e 100644
--- a/cursor_register_manual.py
+++ b/cursor_register_manual.py
@@ -79,7 +79,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False
- print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}")
return True
except Exception as e:
diff --git a/delete_cursor_google.py b/delete_cursor_google.py
index baa233f..3bffac9 100644
--- a/delete_cursor_google.py
+++ b/delete_cursor_google.py
@@ -247,11 +247,11 @@ class CursorGoogleAccountDeleter(OAuthHandler):
except:
# Try direct JavaScript input as fallback
try:
- self.browser.run_js(f"""
+ self.browser.run_js(r"""
arguments[0].value = "Delete";
- const event = new Event('input', {{ bubbles: true }});
+ const event = new Event('input', { bubbles: true });
arguments[0].dispatchEvent(event);
- const changeEvent = new Event('change', {{ bubbles: true }});
+ const changeEvent = new Event('change', { bubbles: true });
arguments[0].dispatchEvent(changeEvent);
""", delete_input)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}")
diff --git a/main.py b/main.py
index e3555a9..c3759db 100644
--- a/main.py
+++ b/main.py
@@ -110,18 +110,24 @@ class Translator:
threadid = user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
- # Map language ID to our language codes
- language_map = {
- 0x0409: 'en', # English
- 0x0404: 'zh_tw', # Traditional Chinese
- 0x0804: 'zh_cn', # Simplified Chinese
- 0x0422: 'vi', # Vietnamese
- 0x0419: 'ru', # Russian
- 0x0415: 'tr', # Turkish
- 0x0402: 'bg', # Bulgarian
- }
-
- return language_map.get(layout_id, 'en')
+ # Map language ID to our language codes using match-case
+ match layout_id:
+ case 0x0409:
+ return 'en' # English
+ case 0x0404:
+ return 'zh_tw' # Traditional Chinese
+ case 0x0804:
+ return 'zh_cn' # Simplified Chinese
+ case 0x0422:
+ return 'vi' # Vietnamese
+ case 0x0419:
+ return 'ru' # Russian
+ case 0x0415:
+ return 'tr' # Turkish
+ case 0x0402:
+ return 'bg' # Bulgarian
+ case _:
+ return 'en' # Default to English
except:
return self._detect_unix_language()
@@ -136,53 +142,56 @@ class Translator:
system_locale = system_locale.lower()
- # Map locale to our language codes
- if system_locale.startswith('zh_tw') or system_locale.startswith('zh_hk'):
- return 'zh_tw'
- elif system_locale.startswith('zh_cn'):
- return 'zh_cn'
- elif system_locale.startswith('en'):
- return 'en'
- elif system_locale.startswith('vi'):
- return 'vi'
- elif system_locale.startswith('nl'):
- return 'nl'
- elif system_locale.startswith('de'):
- return 'de'
- elif system_locale.startswith('fr'):
- return 'fr'
- elif system_locale.startswith('pt'):
- return 'pt'
- elif system_locale.startswith('ru'):
- return 'ru'
- elif system_locale.startswith('tr'):
- return 'tr'
- elif system_locale.startswith('bg'):
- return 'bg'
- # Try to get language from LANG environment variable as fallback
- env_lang = os.getenv('LANG', '').lower()
- if 'tw' in env_lang or 'hk' in env_lang:
- return 'zh_tw'
- elif 'cn' in env_lang:
- return 'zh_cn'
- elif 'vi' in env_lang:
- return 'vi'
- elif 'nl' in env_lang:
- return 'nl'
- elif 'de' in env_lang:
- return 'de'
- elif 'fr' in env_lang:
- return 'fr'
- elif 'pt' in env_lang:
- return 'pt'
- elif 'ru' in env_lang:
- return 'ru'
- elif 'tr' in env_lang:
- return 'tr'
- elif 'bg' in env_lang:
- return 'bg'
-
- return 'en'
+ # Map locale to our language codes using match-case
+ match system_locale:
+ case s if s.startswith('zh_tw') or s.startswith('zh_hk'):
+ return 'zh_tw'
+ case s if s.startswith('zh_cn'):
+ return 'zh_cn'
+ case s if s.startswith('en'):
+ return 'en'
+ case s if s.startswith('vi'):
+ return 'vi'
+ case s if s.startswith('nl'):
+ return 'nl'
+ case s if s.startswith('de'):
+ return 'de'
+ case s if s.startswith('fr'):
+ return 'fr'
+ case s if s.startswith('pt'):
+ return 'pt'
+ case s if s.startswith('ru'):
+ return 'ru'
+ case s if s.startswith('tr'):
+ return 'tr'
+ case s if s.startswith('bg'):
+ return 'bg'
+ case _:
+ # Try to get language from LANG environment variable as fallback
+ env_lang = os.getenv('LANG', '').lower()
+ match env_lang:
+ case s if 'tw' in s or 'hk' in s:
+ return 'zh_tw'
+ case s if 'cn' in s:
+ return 'zh_cn'
+ case s if 'vi' in s:
+ return 'vi'
+ case s if 'nl' in s:
+ return 'nl'
+ case s if 'de' in s:
+ return 'de'
+ case s if 'fr' in s:
+ return 'fr'
+ case s if 'pt' in s:
+ return 'pt'
+ case s if 'ru' in s:
+ return 'ru'
+ case s if 'tr' in s:
+ return 'tr'
+ case s if 'bg' in s:
+ return 'bg'
+ case _:
+ return 'en'
except:
return 'en'
@@ -566,87 +575,88 @@ def main():
choice_num = 17
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
- if choice == "0":
- print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
- print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
- return
- elif choice == "1":
- import reset_machine_manual
- reset_machine_manual.run(translator)
- print_menu()
- elif choice == "2":
- import cursor_register
- cursor_register.main(translator)
- print_menu()
- elif choice == "3":
- import cursor_register_google
- cursor_register_google.main(translator)
- print_menu()
- elif choice == "4":
- import cursor_register_github
- cursor_register_github.main(translator)
- print_menu()
- elif choice == "5":
- import cursor_register_manual
- cursor_register_manual.main(translator)
- print_menu()
- elif choice == "6":
- import github_cursor_register
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}")
- # github_cursor_register.main(translator)
- print_menu()
- elif choice == "7":
- import quit_cursor
- quit_cursor.quit_cursor(translator)
- print_menu()
- elif choice == "8":
- if select_language():
+ match choice:
+ case "0":
+ print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
+ print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
+ return
+ case "1":
+ import reset_machine_manual
+ reset_machine_manual.run(translator)
+ print_menu()
+ case "2":
+ import cursor_register
+ cursor_register.main(translator)
+ print_menu()
+ case "3":
+ import cursor_register_google
+ cursor_register_google.main(translator)
+ print_menu()
+ case "4":
+ import cursor_register_github
+ cursor_register_github.main(translator)
+ print_menu()
+ case "5":
+ import cursor_register_manual
+ cursor_register_manual.main(translator)
+ print_menu()
+ case "6":
+ import github_cursor_register
+ print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}")
+ # github_cursor_register.main(translator)
+ print_menu()
+ case "7":
+ import quit_cursor
+ quit_cursor.quit_cursor(translator)
+ print_menu()
+ case "8":
+ if select_language():
+ print_menu()
+ continue
+ case "9":
+ import disable_auto_update
+ disable_auto_update.run(translator)
+ print_menu()
+ case "10":
+ import totally_reset_cursor
+ totally_reset_cursor.run(translator)
+ # print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
+ print_menu()
+ case "11":
+ import logo
+ print(logo.CURSOR_CONTRIBUTORS)
+ print_menu()
+ case "12":
+ from config import print_config
+ print_config(get_config(), translator)
+ print_menu()
+ case "13":
+ from oauth_auth import OAuthHandler
+ oauth = OAuthHandler(translator)
+ oauth._select_profile()
+ print_menu()
+ case "14":
+ import delete_cursor_google
+ delete_cursor_google.main(translator)
+ print_menu()
+ case "15":
+ import bypass_version
+ bypass_version.main(translator)
+ print_menu()
+ case "16":
+ import check_user_authorized
+ check_user_authorized.main(translator)
+ print_menu()
+ case "17":
+ import bypass_token_limit
+ bypass_token_limit.run(translator)
+ print_menu()
+ case _:
+ print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()
- continue
- elif choice == "9":
- import disable_auto_update
- disable_auto_update.run(translator)
- print_menu()
- elif choice == "10":
- import totally_reset_cursor
- totally_reset_cursor.run(translator)
- # print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
- print_menu()
- elif choice == "11":
- import logo
- print(logo.CURSOR_CONTRIBUTORS)
- print_menu()
- elif choice == "12":
- from config import print_config
- print_config(get_config(), translator)
- print_menu()
- elif choice == "13":
- from oauth_auth import OAuthHandler
- oauth = OAuthHandler(translator)
- oauth._select_profile()
- print_menu()
- elif choice == "14":
- import delete_cursor_google
- delete_cursor_google.main(translator)
- print_menu()
- elif choice == "15":
- import bypass_version
- bypass_version.main(translator)
- print_menu()
- elif choice == "16":
- import check_user_authorized
- check_user_authorized.main(translator)
- print_menu()
- elif choice == "17":
- import bypass_token_limit
- bypass_token_limit.run(translator)
- print_menu()
- else:
- print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
- print_menu()
except KeyboardInterrupt:
- print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
+ print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'═' * 50}{Style.RESET_ALL}")
return
except Exception as e:
diff --git a/oauth_auth.py b/oauth_auth.py
index f70e243..8a2185f 100644
--- a/oauth_auth.py
+++ b/oauth_auth.py
@@ -176,10 +176,15 @@ class OAuthHandler:
browser_path = self._get_browser_path()
if not browser_path:
- raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
- "- Windows: Google Chrome, Chromium\n" +
- "- macOS: Google Chrome, Chromium\n" +
- "- Linux: Google Chrome, Chromium, chromium-browser")
+ error_msg = (
+ f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}" +
+ "\n" +
+ f"{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
+ "- Windows: Google Chrome, Chromium\n" +
+ "- macOS: Google Chrome, Chromium\n" +
+ "- Linux: Google Chrome, Chromium, chromium-browser"
+ )
+ raise Exception(error_msg)
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=user_data_dir) if self.translator else f'Found browser data directory: {user_data_dir}'}{Style.RESET_ALL}")
@@ -305,7 +310,8 @@ class OAuthHandler:
'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'),
'edge': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Microsoft', 'Edge', 'User Data'),
'firefox': os.path.join(os.environ.get('APPDATA', ''), 'Mozilla', 'Firefox', 'Profiles'),
- 'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable')
+ 'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable'),
+ 'operagx': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera GX Stable')
}
elif sys.platform == 'darwin': # macOS
user_data_dirs = {
@@ -313,7 +319,8 @@ class OAuthHandler:
'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'),
'edge': os.path.expanduser('~/Library/Application Support/Microsoft Edge'),
'firefox': os.path.expanduser('~/Library/Application Support/Firefox/Profiles'),
- 'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera')
+ 'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'),
+ 'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX')
}
else: # Linux
user_data_dirs = {
@@ -321,7 +328,8 @@ class OAuthHandler:
'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'),
'edge': os.path.expanduser('~/.config/microsoft-edge'),
'firefox': os.path.expanduser('~/.mozilla/firefox'),
- 'opera': os.path.expanduser('~/.config/opera')
+ 'opera': os.path.expanduser('~/.config/opera'),
+ 'operagx': os.path.expanduser('~/.config/opera-gx')
}
# 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的
@@ -955,7 +963,8 @@ class OAuthHandler:
value = cookie.get("value", "")
token = get_token_from_cookie(value, self.translator)
except Exception as e:
- print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}")
+ error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
+ print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
elif name == "cursor_email":
email = cookie.get("value")
@@ -968,11 +977,13 @@ class OAuthHandler:
missing.append("email")
if not token:
missing.append("token")
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}")
+ error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing))
+ print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False, None
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}")
+ error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e))
+ print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False, None
def _delete_current_account(self):
@@ -1014,7 +1025,8 @@ class OAuthHandler:
return True
except Exception as e:
- print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}")
+ error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e))
+ print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}")
return False
def main(auth_type, translator=None):
diff --git a/utils.py b/utils.py
index 8a66dbd..8922f86 100644
--- a/utils.py
+++ b/utils.py
@@ -84,14 +84,24 @@ def get_default_browser_path(browser_type='chrome'):
r"C:\Program Files\Opera\opera.exe",
r"C:\Program Files (x86)\Opera\opera.exe",
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
- os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe'),
- os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
- os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
+ os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe')
]
for path in opera_paths:
if os.path.exists(path):
return path
return opera_paths[0] # 返回第一个路径,即使它不存在
+ elif browser_type == 'operagx':
+ # 尝试多个可能的 Opera GX 路径
+ operagx_paths = [
+ os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
+ os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe'),
+ r"C:\Program Files\Opera GX\opera.exe",
+ r"C:\Program Files (x86)\Opera GX\opera.exe"
+ ]
+ for path in operagx_paths:
+ if os.path.exists(path):
+ return path
+ return operagx_paths[0] # 返回第一个路径,即使它不存在
elif browser_type == 'brave':
# Brave 浏览器的默认安装路径
paths = [
@@ -115,6 +125,8 @@ def get_default_browser_path(browser_type='chrome'):
return "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
elif browser_type == 'opera':
return "/Applications/Opera.app/Contents/MacOS/Opera"
+ elif browser_type == 'operagx':
+ return "/Applications/Opera GX.app/Contents/MacOS/Opera"
else: # Linux
if browser_type == 'chrome':
@@ -135,6 +147,18 @@ def get_default_browser_path(browser_type='chrome'):
return "/usr/bin/firefox"
elif browser_type == 'opera':
return "/usr/bin/opera"
+ elif browser_type == 'operagx':
+ # 尝试常见的 Opera GX 路径
+ operagx_names = ["opera-gx"]
+ for name in operagx_names:
+ try:
+ import shutil
+ path = shutil.which(name)
+ if path:
+ return path
+ except:
+ pass
+ return "/usr/bin/opera-gx"
elif browser_type == 'brave':
# 尝试常见的 Brave 路径
brave_names = ["brave", "brave-browser"]