first commit
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
"""将用户选中的 .lnk / .url 解析为实际要存储的路径(目标程序或 URL)。"""
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def _looks_like_shell_lnk(path: str) -> bool:
|
||||
"""Windows Shell Link 文件头为 4C 00 00 00(扩展名异常时仍可识别)。"""
|
||||
try:
|
||||
with open(path, "rb") as f:
|
||||
return f.read(4) == b"L\x00\x00\x00"
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def _read_internet_shortcut_url(path: str) -> str | None:
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8", errors="ignore") as f:
|
||||
for line in f:
|
||||
s = line.strip()
|
||||
if s[:4].upper() == "URL=":
|
||||
return s[4:].strip()
|
||||
except OSError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_windows_lnk(lnk_path: str) -> str | None:
|
||||
if sys.platform != "win32":
|
||||
return None
|
||||
env = os.environ.copy()
|
||||
env["LNK_B64"] = base64.b64encode(os.path.normpath(lnk_path).encode("utf-8")).decode(
|
||||
"ascii"
|
||||
)
|
||||
ps = (
|
||||
"$p=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:LNK_B64));"
|
||||
"$sc=(New-Object -ComObject WScript.Shell).CreateShortcut($p);"
|
||||
"@{ t=[string]$sc.TargetPath; a=[string]$sc.Arguments }|ConvertTo-Json -Compress"
|
||||
)
|
||||
try:
|
||||
r = subprocess.run(
|
||||
[
|
||||
"powershell",
|
||||
"-NoProfile",
|
||||
"-Sta",
|
||||
"-Command",
|
||||
ps,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
errors="replace",
|
||||
env=env,
|
||||
timeout=15,
|
||||
creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0),
|
||||
)
|
||||
raw = (r.stdout or "").strip().lstrip("\ufeff")
|
||||
if not raw:
|
||||
return None
|
||||
data = json.loads(raw)
|
||||
target = (data.get("t") or "").strip()
|
||||
args = (data.get("a") or "").strip()
|
||||
if not target:
|
||||
return None
|
||||
low = target.lower()
|
||||
if low.endswith("cmd.exe") or low.endswith("\\cmd.exe"):
|
||||
for m in re.finditer(
|
||||
r"([a-zA-Z]:[^\"'\s]+\.(?:bat|cmd))|\x22([a-zA-Z]:[^\"]+\.(?:bat|cmd))\x22",
|
||||
args,
|
||||
re.IGNORECASE,
|
||||
):
|
||||
cand = (m.group(1) or m.group(2) or "").strip('"')
|
||||
if cand and os.path.isfile(cand):
|
||||
return os.path.normpath(cand)
|
||||
if os.path.isfile(target) or low.startswith("\\\\"):
|
||||
return os.path.normpath(target)
|
||||
return os.path.normpath(target)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def path_for_storage(local_path: str) -> str:
|
||||
"""
|
||||
拖拽/选择文件时写入数据库的 path:.lnk/.url 解析为目标,其它保持原路径。
|
||||
解析失败时退回本地路径。
|
||||
"""
|
||||
if not local_path:
|
||||
return local_path
|
||||
local_path = os.path.normpath(local_path.strip())
|
||||
|
||||
ext = os.path.splitext(local_path)[1].lower()
|
||||
if ext == ".url":
|
||||
try:
|
||||
if os.path.isfile(local_path):
|
||||
url = _read_internet_shortcut_url(local_path)
|
||||
if url:
|
||||
return url
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
return local_path
|
||||
|
||||
try:
|
||||
is_file = os.path.isfile(local_path)
|
||||
except (OSError, ValueError):
|
||||
is_file = False
|
||||
|
||||
is_lnk = ext in (".lnk", ".ink") or (
|
||||
is_file and _looks_like_shell_lnk(local_path)
|
||||
)
|
||||
if is_lnk and is_file:
|
||||
resolved = _resolve_windows_lnk(local_path)
|
||||
if resolved:
|
||||
return resolved
|
||||
return local_path
|
||||
|
||||
|
||||
def item_name_from_sources(local_path: str, store_path: str) -> str:
|
||||
"""列表显示名:仍用快捷方式/所选文件的文件名(无扩展名),与原来行为一致。"""
|
||||
base = os.path.splitext(os.path.basename(local_path))[0]
|
||||
return base or os.path.basename(store_path)
|
||||
Reference in New Issue
Block a user