feat: 完成账号桌面快捷方式
- 新增 chrome.create_shortcut 使用 PowerShell WScript.Shell 生成 .lnk - 快捷方式参数复用 Chrome 启动参数,包含调试端口、allow-origins 和账号 user-data-dir - 在账号服务层和 Tab④ 增加快捷方式入口,不包含密码或密钥 - 补充 chrome/accounts/gui 测试并同步任务、API、路由、当前状态和进度文档
This commit is contained in:
@@ -213,6 +213,16 @@ def launch_for_login(account_or_alias, path=None, config=None):
|
||||
return chrome.launch_chrome(account, config=config)
|
||||
|
||||
|
||||
def create_shortcut(account_or_alias, shortcut_path=None, desktop_dir=None, path=None, config=None) -> str:
|
||||
account = resolve_account(account_or_alias, path=path, config=config)
|
||||
return chrome.create_shortcut(
|
||||
account,
|
||||
shortcut_path=shortcut_path,
|
||||
desktop_dir=desktop_dir,
|
||||
config=config,
|
||||
)
|
||||
|
||||
|
||||
def detect_login(account_or_alias, timeout=8, path=None, config=None) -> dict:
|
||||
database_path = _db_path(path, config)
|
||||
account = resolve_account(account_or_alias, path=database_path, config=config)
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
import urllib.error
|
||||
@@ -69,6 +70,81 @@ def build_launch_args(account, config=None) -> list:
|
||||
]
|
||||
|
||||
|
||||
def _safe_shortcut_name(value) -> str:
|
||||
text = str(value or "").strip()
|
||||
if not text:
|
||||
raise ChromeLaunchError("快捷方式名称不能为空")
|
||||
text = re.sub(r'[<>:"/\\|?*\x00-\x1f]+', "_", text)
|
||||
text = re.sub(r"\s+", " ", text).strip(" .")
|
||||
if not text:
|
||||
raise ChromeLaunchError("快捷方式名称不能为空")
|
||||
return text
|
||||
|
||||
|
||||
def _default_shortcut_name(account) -> str:
|
||||
alias = _account_value(account, "alias")
|
||||
account_name = _account_value(account, "account_name")
|
||||
name = alias or account_name or _required_account_value(account, "slug")
|
||||
return _safe_shortcut_name(f"cmshopee-{name}")
|
||||
|
||||
|
||||
def _desktop_dir() -> str:
|
||||
return os.path.join(os.path.expanduser("~"), "Desktop")
|
||||
|
||||
|
||||
def _ps_single_quote(value) -> str:
|
||||
return "'" + str(value).replace("'", "''") + "'"
|
||||
|
||||
|
||||
def create_shortcut(account, shortcut_path=None, desktop_dir=None, name=None, config=None) -> str:
|
||||
"""Create a Windows .lnk shortcut for one account's Chrome session."""
|
||||
|
||||
args = build_launch_args(account, config=config)
|
||||
chrome_path = os.path.abspath(args[0])
|
||||
arguments = subprocess.list2cmdline(args[1:])
|
||||
if shortcut_path is None:
|
||||
root = os.path.abspath(desktop_dir or _desktop_dir())
|
||||
shortcut_name = _safe_shortcut_name(name) if name else _default_shortcut_name(account)
|
||||
shortcut_path = os.path.join(root, shortcut_name + ".lnk")
|
||||
shortcut_path = os.path.abspath(str(shortcut_path))
|
||||
if not shortcut_path.lower().endswith(".lnk"):
|
||||
shortcut_path += ".lnk"
|
||||
os.makedirs(os.path.dirname(shortcut_path), exist_ok=True)
|
||||
|
||||
working_directory = os.path.dirname(chrome_path) or os.getcwd()
|
||||
script = "; ".join(
|
||||
[
|
||||
"$shell = New-Object -ComObject WScript.Shell",
|
||||
f"$shortcut = $shell.CreateShortcut({_ps_single_quote(shortcut_path)})",
|
||||
f"$shortcut.TargetPath = {_ps_single_quote(chrome_path)}",
|
||||
f"$shortcut.Arguments = {_ps_single_quote(arguments)}",
|
||||
f"$shortcut.WorkingDirectory = {_ps_single_quote(working_directory)}",
|
||||
f"$shortcut.IconLocation = {_ps_single_quote(chrome_path)}",
|
||||
"$shortcut.Save()",
|
||||
]
|
||||
)
|
||||
try:
|
||||
subprocess.run(
|
||||
[
|
||||
"powershell",
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-Command",
|
||||
script,
|
||||
],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
except OSError as exc:
|
||||
raise ChromeLaunchError(f"生成快捷方式失败: {exc}") from exc
|
||||
except subprocess.CalledProcessError as exc:
|
||||
error = (exc.stderr or exc.stdout or str(exc)).strip()
|
||||
raise ChromeLaunchError(f"生成快捷方式失败: {error}") from exc
|
||||
return shortcut_path
|
||||
|
||||
|
||||
def launch_chrome(account, config=None) -> subprocess.Popen:
|
||||
"""Launch Chrome for one account and return the process handle."""
|
||||
|
||||
|
||||
+16
@@ -208,6 +208,7 @@ if QT_IMPORT_ERROR is None:
|
||||
self.delete_button = QPushButton("删除")
|
||||
self.launch_button = QPushButton("启动登录")
|
||||
self.check_button = QPushButton("检测登录")
|
||||
self.shortcut_button = QPushButton("快捷方式")
|
||||
|
||||
toolbar = QHBoxLayout()
|
||||
for button in (
|
||||
@@ -216,6 +217,7 @@ if QT_IMPORT_ERROR is None:
|
||||
self.delete_button,
|
||||
self.launch_button,
|
||||
self.check_button,
|
||||
self.shortcut_button,
|
||||
):
|
||||
toolbar.addWidget(button)
|
||||
toolbar.addStretch(1)
|
||||
@@ -233,6 +235,7 @@ if QT_IMPORT_ERROR is None:
|
||||
self.delete_button.clicked.connect(self.delete_account)
|
||||
self.launch_button.clicked.connect(self.launch_login)
|
||||
self.check_button.clicked.connect(self.check_login)
|
||||
self.shortcut_button.clicked.connect(self.create_shortcut)
|
||||
self.table.itemSelectionChanged.connect(self._update_button_state)
|
||||
self.table.doubleClicked.connect(self.edit_account)
|
||||
|
||||
@@ -258,6 +261,7 @@ if QT_IMPORT_ERROR is None:
|
||||
self.delete_button,
|
||||
self.launch_button,
|
||||
self.check_button,
|
||||
self.shortcut_button,
|
||||
):
|
||||
button.setEnabled(has_selection)
|
||||
|
||||
@@ -419,6 +423,18 @@ if QT_IMPORT_ERROR is None:
|
||||
self.refresh_accounts()
|
||||
self._set_status(f"登录检测失败:{error}")
|
||||
|
||||
def create_shortcut(self, checked=False):
|
||||
account = self._selected_account()
|
||||
if account is None:
|
||||
return
|
||||
try:
|
||||
shortcut_path = accounts.create_shortcut(account, config=self.config)
|
||||
except Exception as exc:
|
||||
self._show_error(exc)
|
||||
return
|
||||
self._set_status(f"快捷方式已生成:{shortcut_path}")
|
||||
QMessageBox.information(self, "账号管理", f"快捷方式已生成:\n{shortcut_path}")
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
"""Main application window with the fixed five-tab workflow."""
|
||||
|
||||
Reference in New Issue
Block a user