136 lines
4.9 KiB
Python
136 lines
4.9 KiB
Python
# coding: utf8
|
|
import os
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
from dataclasses import dataclass, field
|
|
from global_vars import get_with_chained_keys
|
|
|
|
|
|
PLAT = sys.platform
|
|
USER_PATH = os.path.expanduser("~")
|
|
|
|
USER_DATA_PATH_MAP = {
|
|
"win32": {
|
|
"Chrome": Path(USER_PATH, "AppData", "Local", "Google", "Chrome", "User Data"),
|
|
"Edge": Path(USER_PATH, "AppData", "Local", "Microsoft", "Edge", "User Data"),
|
|
"Brave": Path(USER_PATH, "AppData", "Local", "BraveSoftware", "Brave-Browser", "User Data"),
|
|
},
|
|
"darwin": {
|
|
"Chrome": Path(USER_PATH, "Library", "Application Support", "Google", "Chrome"),
|
|
"Edge": Path(USER_PATH, "Library", "Application Support", "Microsoft Edge"),
|
|
"Brave": Path(USER_PATH, "Library", "Application Support", "BraveSoftware", "Brave-Browser"),
|
|
},
|
|
}
|
|
|
|
USER_DATA_PATH = USER_DATA_PATH_MAP[PLAT]
|
|
|
|
|
|
@dataclass
|
|
class ProfileNode(object):
|
|
gaia_given_name: str
|
|
gaia_name: str
|
|
name: str
|
|
shortcut_name: str
|
|
user_name: str
|
|
|
|
|
|
ProfilesData = dict[str, ProfileNode]
|
|
|
|
|
|
def scan_profiles(browser: str) -> ProfilesData:
|
|
local_state_path = Path(USER_DATA_PATH[browser], "Local State")
|
|
local_state_data: dict = json.loads(local_state_path.read_text(encoding="utf8"))
|
|
info_cache_data: dict = get_with_chained_keys(local_state_data, ["profile", "info_cache"])
|
|
|
|
profiles_data: ProfilesData = {}
|
|
|
|
for profile_id in info_cache_data:
|
|
profile_data: dict = info_cache_data[profile_id]
|
|
profile_node = ProfileNode(
|
|
gaia_given_name=profile_data.get("gaia_given_name", ""),
|
|
gaia_name=profile_data.get("gaia_name", ""),
|
|
name=profile_data.get("name", ""),
|
|
shortcut_name=profile_data.get("shortcut_name", ""),
|
|
user_name=profile_data.get("user_name"),
|
|
)
|
|
profiles_data[profile_id] = profile_node
|
|
|
|
return profiles_data
|
|
|
|
|
|
@dataclass
|
|
class ExtensionNode(object):
|
|
icon: str
|
|
name: str
|
|
# path: str
|
|
profiles: list[str] = field(default_factory=list)
|
|
|
|
|
|
ExtensionsData = dict[str, ExtensionNode]
|
|
|
|
|
|
def get_extension_icon_path(ext_icons: dict[str, str], ext_path: str, profile_path: Path) -> str:
|
|
if len(ext_icons) == 0:
|
|
return ""
|
|
|
|
if "128" in ext_icons:
|
|
icon_file = ext_icons["128"]
|
|
else:
|
|
icon_file = ext_icons[str(max(map(lambda x: int(x), ext_icons.keys())))]
|
|
# 如果路径以 / 开头,会被认为是根而忽略其他,因此需要检查一下
|
|
if icon_file.startswith("/"):
|
|
icon_file = icon_file[1:]
|
|
|
|
full_path = Path(profile_path, "Extensions", ext_path, icon_file)
|
|
return str(full_path)
|
|
|
|
|
|
def scan_extensions(browser: str, is_chrome_compat=False) -> ExtensionsData:
|
|
profile_data = scan_profiles(browser)
|
|
extensions_data: ExtensionsData = {}
|
|
|
|
browser_data_path = USER_DATA_PATH[browser]
|
|
if browser == "Chrome" and is_chrome_compat:
|
|
pref_file = "Preferences"
|
|
else:
|
|
pref_file = "Secure Preferences"
|
|
|
|
for profile_id in profile_data:
|
|
profile_path = Path(browser_data_path, profile_id)
|
|
secure_pref_path = Path(profile_path, pref_file)
|
|
secure_pref_data: dict = json.loads(secure_pref_path.read_text(encoding="utf8"))
|
|
ext_settings_data: dict = get_with_chained_keys(secure_pref_data, ["extensions", "settings"], dict())
|
|
|
|
for ext_id in ext_settings_data:
|
|
ext_data: dict = ext_settings_data[ext_id]
|
|
# 这里插件有几种情况
|
|
ext_path: str = ext_data.get("path", "")
|
|
if len(ext_path) == 0:
|
|
# 如果 path 是空,则该插件可能是一些内部插件,不予收集
|
|
continue
|
|
elif not (ext_path.startswith(ext_id) or os.path.exists(ext_path)):
|
|
# 如果 path 以插件 ID 开头,则为商店安装的插件,
|
|
# 如果不是以插件 ID 开头,但是是一个存在的路径,则为离线安装的插件
|
|
# 否则,可能也是内部插件,不予收集
|
|
continue
|
|
ext_manifest: dict = ext_data.get("manifest", {})
|
|
if len(ext_manifest) == 0:
|
|
# 如果 manifest 为空,则该插件可能是离线插件,需要额外找它的 manifest
|
|
ext_manifest_path = Path(ext_path, "manifest.json")
|
|
ext_manifest: dict = json.loads(ext_manifest_path.read_text(encoding="utf8"))
|
|
|
|
if ext_id not in extensions_data:
|
|
ext_node = ExtensionNode(
|
|
icon=get_extension_icon_path(ext_manifest.get("icons", {}), ext_path, profile_path),
|
|
name=ext_manifest.get("name", ""),
|
|
# path=ext_data.get("path", ""),
|
|
profiles=[profile_id],
|
|
)
|
|
extensions_data[ext_id] = ext_node
|
|
else:
|
|
ext_node = extensions_data[ext_id]
|
|
ext_node.profiles += [profile_id]
|
|
|
|
return extensions_data
|