-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdesktop_reader.py
More file actions
142 lines (119 loc) · 4.77 KB
/
desktop_reader.py
File metadata and controls
142 lines (119 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""デスクトップのショートカット・ファイルを読み取るモジュール"""
import os
import ctypes
import win32com.client
# CSIDL定数(GUIDより確実)
_CSIDL_DESKTOP = 0x0010 # ユーザーデスクトップ
_CSIDL_COMMON_DESKTOP = 0x0019 # パブリックデスクトップ
def _get_folder_path(csidl: int) -> str | None:
"""SHGetFolderPathW でシステムフォルダのパスを取得する"""
buf = ctypes.create_unicode_buffer(260)
hr = ctypes.windll.shell32.SHGetFolderPathW(0, csidl, 0, 0, buf)
if hr == 0 and buf.value:
return buf.value
return None
def get_desktop_path() -> str:
"""ユーザーのデスクトップパスを取得する(OneDrive同期対応)"""
path = _get_folder_path(_CSIDL_DESKTOP)
if path and os.path.isdir(path):
return path
return os.path.join(os.environ["USERPROFILE"], "Desktop")
def get_public_desktop_path() -> str:
"""パブリックデスクトップのパスを取得する"""
path = _get_folder_path(_CSIDL_COMMON_DESKTOP)
if path and os.path.isdir(path):
return path
return os.path.join(os.environ["PUBLIC"], "Desktop")
def resolve_lnk(lnk_path: str) -> dict | None:
"""
.lnk ファイルを解析してリンク先情報を返す。
Returns: {"name", "target", "icon_location", "icon_index", "description"}
"""
try:
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut(lnk_path)
name = os.path.splitext(os.path.basename(lnk_path))[0]
return {
"name": name,
"target": shortcut.Targetpath,
"icon_location": shortcut.IconLocation.split(",")[0].strip(),
"icon_index": int(shortcut.IconLocation.split(",")[-1].strip() or 0),
"description": shortcut.Description or "",
"source_path": lnk_path,
}
except Exception:
return None
def resolve_url(url_path: str) -> dict | None:
"""
.url ファイルを解析してURL情報を返す。
"""
try:
name = os.path.splitext(os.path.basename(url_path))[0]
url = ""
icon_file = ""
with open(url_path, "r", encoding="utf-8", errors="ignore") as f:
for line in f:
line = line.strip()
if line.startswith("URL="):
url = line[4:]
elif line.startswith("IconFile="):
icon_file = line[9:]
return {
"name": name,
"target": url,
"icon_location": icon_file,
"icon_index": 0,
"description": "",
"source_path": url_path,
}
except Exception:
return None
def _collect_desktop_paths() -> list[str]:
"""重複を除いた全デスクトップパスを返す(OneDrive + ローカル + パブリック)"""
paths = []
seen = set()
for p in [
get_desktop_path(), # Windows API(OneDrive側になる場合あり)
os.path.join(os.environ["USERPROFILE"], "Desktop"), # ローカル固定パス
get_public_desktop_path(), # パブリック
]:
rp = os.path.normcase(os.path.realpath(p))
if rp not in seen and os.path.isdir(p):
seen.add(rp)
paths.append(p)
return paths
def scan_desktop() -> list[dict]:
"""
デスクトップ上のショートカット・実行ファイルをスキャンして一覧を返す。
OneDrive同期デスクトップ・ローカルデスクトップ・パブリックデスクトップを全て読む。
"""
items = []
seen_names = set()
for desktop_path in _collect_desktop_paths():
if not os.path.isdir(desktop_path):
continue
for entry in os.listdir(desktop_path):
full_path = os.path.join(desktop_path, entry)
if not os.path.isfile(full_path):
continue
ext = os.path.splitext(entry)[1].lower()
item = None
if ext == ".lnk":
item = resolve_lnk(full_path)
elif ext == ".url":
item = resolve_url(full_path)
elif ext in (".exe", ".bat", ".cmd"):
name = os.path.splitext(entry)[0]
item = {
"name": name,
"target": full_path,
"icon_location": full_path,
"icon_index": 0,
"description": "",
"source_path": full_path,
}
if item and item["name"] not in seen_names:
seen_names.add(item["name"])
items.append(item)
items.sort(key=lambda x: x["name"].lower())
return items