Implement autostart querying upon application start
This commit is contained in:
1 parent
5ade35d6a5
commit
14a9154366
5 files changed
+87
-46
No files matched your search
@@ -121,6 +121,13 @@ WATCH_INTERVAL = timedelta(seconds=20)
|
|||||||
# Strings
|
# Strings
|
||||||
WINDOW_TITLE = f"Twitch Drops Miner v{__version__} (by DevilXD)"
|
WINDOW_TITLE = f"Twitch Drops Miner v{__version__} (by DevilXD)"
|
||||||
# Logging
|
# Logging
|
||||||
|
LOGGING_LEVELS = {
|
||||||
|
0: logging.ERROR,
|
||||||
|
1: logging.WARNING,
|
||||||
|
2: logging.INFO,
|
||||||
|
3: CALL,
|
||||||
|
4: logging.DEBUG,
|
||||||
|
}
|
||||||
FILE_FORMATTER = logging.Formatter(
|
FILE_FORMATTER = logging.Formatter(
|
||||||
"{asctime}.{msecs:03.0f}:\t{levelname:>7}:\t{message}",
|
"{asctime}.{msecs:03.0f}:\t{levelname:>7}:\t{message}",
|
||||||
style='{',
|
style='{',
|
||||||
|
|||||||
@@ -33,10 +33,16 @@ from cache import ImageCache
|
|||||||
from exceptions import MinerException, ExitRequest
|
from exceptions import MinerException, ExitRequest
|
||||||
from utils import resource_path, set_root_icon, webopen, Game, _T
|
from utils import resource_path, set_root_icon, webopen, Game, _T
|
||||||
from constants import (
|
from constants import (
|
||||||
SELF_PATH, OUTPUT_FORMATTER, WS_TOPICS_LIMIT, MAX_WEBSOCKETS, WINDOW_TITLE, State
|
SELF_PATH,
|
||||||
|
WINDOW_TITLE,
|
||||||
|
LOGGING_LEVELS,
|
||||||
|
MAX_WEBSOCKETS,
|
||||||
|
WS_TOPICS_LIMIT,
|
||||||
|
OUTPUT_FORMATTER,
|
||||||
|
State,
|
||||||
)
|
)
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
from registry import RegistryKey, ValueType
|
from registry import RegistryKey, ValueType, ValueNotFound
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -1492,7 +1498,7 @@ class SettingsPanel:
|
|||||||
self._vars: _SettingsVars = {
|
self._vars: _SettingsVars = {
|
||||||
"proxy": StringVar(master, str(self._settings.proxy)),
|
"proxy": StringVar(master, str(self._settings.proxy)),
|
||||||
"tray": IntVar(master, self._settings.autostart_tray),
|
"tray": IntVar(master, self._settings.autostart_tray),
|
||||||
"autostart": IntVar(master, self._settings.autostart),
|
"autostart": IntVar(master, 0),
|
||||||
"priority_only": IntVar(master, self._settings.priority_only),
|
"priority_only": IntVar(master, self._settings.priority_only),
|
||||||
"tray_notifications": IntVar(master, self._settings.tray_notifications),
|
"tray_notifications": IntVar(master, self._settings.tray_notifications),
|
||||||
}
|
}
|
||||||
@@ -1647,6 +1653,8 @@ class SettingsPanel:
|
|||||||
command=self._twitch.state_change(State.INVENTORY_FETCH),
|
command=self._twitch.state_change(State.INVENTORY_FETCH),
|
||||||
).grid(column=1, row=0)
|
).grid(column=1, row=0)
|
||||||
|
|
||||||
|
self._vars["autostart"].set(self._query_autostart())
|
||||||
|
|
||||||
def clear_selection(self) -> None:
|
def clear_selection(self) -> None:
|
||||||
self._priority_list.selection_clear(0, "end")
|
self._priority_list.selection_clear(0, "end")
|
||||||
self._exclude_list.selection_clear(0, "end")
|
self._exclude_list.selection_clear(0, "end")
|
||||||
@@ -1654,45 +1662,79 @@ class SettingsPanel:
|
|||||||
def update_notifications(self) -> None:
|
def update_notifications(self) -> None:
|
||||||
self._settings.tray_notifications = bool(self._vars["tray_notifications"].get())
|
self._settings.tray_notifications = bool(self._vars["tray_notifications"].get())
|
||||||
|
|
||||||
def _get_autostart_path(self, tray: bool) -> str:
|
def _get_self_path(self) -> str:
|
||||||
self_path = f'"{SELF_PATH.resolve()!s}"'
|
|
||||||
if tray:
|
|
||||||
self_path += " --tray"
|
|
||||||
return self_path
|
|
||||||
|
|
||||||
def update_autostart(self) -> None:
|
|
||||||
enabled = bool(self._vars["autostart"].get())
|
|
||||||
tray = bool(self._vars["tray"].get())
|
|
||||||
self._settings.autostart = enabled
|
|
||||||
self._settings.autostart_tray = tray
|
|
||||||
if sys.platform == "win32":
|
|
||||||
if enabled:
|
|
||||||
# NOTE: we need double quotes in case the path contains spaces
|
# NOTE: we need double quotes in case the path contains spaces
|
||||||
autostart_path = self._get_autostart_path(tray)
|
return f'"{SELF_PATH.resolve()!s}"'
|
||||||
with RegistryKey(self.AUTOSTART_KEY) as key:
|
|
||||||
key.set(self.AUTOSTART_NAME, ValueType.REG_SZ, autostart_path)
|
def _get_autostart_path(self) -> str:
|
||||||
else:
|
flags: list[str] = [''] # this will add a space between self path and flags
|
||||||
with RegistryKey(self.AUTOSTART_KEY) as key:
|
# if non-zero, include the current logging level as well
|
||||||
key.delete(self.AUTOSTART_NAME, silent=True)
|
if self._settings.logging_level > 0:
|
||||||
elif sys.platform == "linux":
|
for lvl_idx, lvl_value in LOGGING_LEVELS.items():
|
||||||
|
if lvl_value == self._settings.logging_level:
|
||||||
|
flags.append(f"-{'v' * lvl_idx}")
|
||||||
|
break
|
||||||
|
if self._vars["tray"].get():
|
||||||
|
flags.append("--tray")
|
||||||
|
return self._get_self_path() + ' '.join(flags)
|
||||||
|
|
||||||
|
def _get_linux_autostart_filepath(self) -> Path:
|
||||||
autostart_folder: Path = Path("~/.config/autostart").expanduser()
|
autostart_folder: Path = Path("~/.config/autostart").expanduser()
|
||||||
if (config_home := os.environ.get("XDG_CONFIG_HOME")) is not None:
|
if (config_home := os.environ.get("XDG_CONFIG_HOME")) is not None:
|
||||||
config_autostart: Path = Path(config_home, "autostart").expanduser()
|
config_autostart: Path = Path(config_home, "autostart").expanduser()
|
||||||
if config_autostart.exists():
|
if config_autostart.exists():
|
||||||
autostart_folder = config_autostart
|
autostart_folder = config_autostart
|
||||||
autostart_file: Path = autostart_folder / f"{self.AUTOSTART_NAME}.desktop"
|
return autostart_folder / f"{self.AUTOSTART_NAME}.desktop"
|
||||||
|
|
||||||
|
def _query_autostart(self) -> bool:
|
||||||
|
if sys.platform == "win32":
|
||||||
|
with RegistryKey(self.AUTOSTART_KEY, read_only=True) as key:
|
||||||
|
try:
|
||||||
|
value_type, value = key.get(self.AUTOSTART_NAME)
|
||||||
|
except ValueNotFound:
|
||||||
|
return False
|
||||||
|
if (
|
||||||
|
value_type is not ValueType.REG_SZ
|
||||||
|
or self._get_self_path() not in value
|
||||||
|
):
|
||||||
|
# TODO: Consider deleting the old value to avoid autostart errors
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
elif sys.platform == "linux":
|
||||||
|
autostart_file: Path = self._get_linux_autostart_filepath()
|
||||||
|
if not autostart_file.exists():
|
||||||
|
return False
|
||||||
|
with autostart_file.open('r', encoding="utf8") as file:
|
||||||
|
# TODO: Consider deleting the old file to avoid autostart errors
|
||||||
|
return self._get_self_path() not in file.read()
|
||||||
|
|
||||||
|
def update_autostart(self) -> None:
|
||||||
|
enabled = bool(self._vars["autostart"].get())
|
||||||
|
self._settings.autostart_tray = bool(self._vars["tray"].get())
|
||||||
|
if sys.platform == "win32":
|
||||||
if enabled:
|
if enabled:
|
||||||
autostart_path = self._get_autostart_path(tray)
|
with RegistryKey(self.AUTOSTART_KEY) as key:
|
||||||
file_contents = dedent(
|
key.set(
|
||||||
|
self.AUTOSTART_NAME,
|
||||||
|
ValueType.REG_SZ,
|
||||||
|
self._get_autostart_path(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
with RegistryKey(self.AUTOSTART_KEY) as key:
|
||||||
|
key.delete(self.AUTOSTART_NAME, silent=True)
|
||||||
|
elif sys.platform == "linux":
|
||||||
|
autostart_file: Path = self._get_linux_autostart_filepath()
|
||||||
|
if enabled:
|
||||||
|
file_contents: str = dedent(
|
||||||
f"""
|
f"""
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=Twitch Drops Miner
|
Name=Twitch Drops Miner
|
||||||
Description=Mine timed drops on Twitch
|
Description=Mine timed drops on Twitch
|
||||||
Exec=sh -c '{autostart_path}'
|
Exec=sh -c '{self._get_autostart_path()}'
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
with autostart_file.open("w", encoding="utf8") as file:
|
with autostart_file.open('w', encoding="utf8") as file:
|
||||||
file.write(file_contents)
|
file.write(file_contents)
|
||||||
else:
|
else:
|
||||||
autostart_file.unlink(missing_ok=True)
|
autostart_file.unlink(missing_ok=True)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ if __name__ == "__main__":
|
|||||||
from version import __version__
|
from version import __version__
|
||||||
from exceptions import CaptchaRequired
|
from exceptions import CaptchaRequired
|
||||||
from utils import lock_file, resource_path, set_root_icon
|
from utils import lock_file, resource_path, set_root_icon
|
||||||
from constants import CALL, SELF_PATH, FILE_FORMATTER, LOG_PATH, LOCK_PATH
|
from constants import LOGGING_LEVELS, SELF_PATH, FILE_FORMATTER, LOG_PATH, LOCK_PATH
|
||||||
|
|
||||||
warnings.simplefilter("default", ResourceWarning)
|
warnings.simplefilter("default", ResourceWarning)
|
||||||
|
|
||||||
@@ -58,13 +58,7 @@ if __name__ == "__main__":
|
|||||||
# TODO: replace int with union of literal values once typeshed updates
|
# TODO: replace int with union of literal values once typeshed updates
|
||||||
@property
|
@property
|
||||||
def logging_level(self) -> int:
|
def logging_level(self) -> int:
|
||||||
return {
|
return LOGGING_LEVELS[min(self._verbose, 4)]
|
||||||
0: logging.ERROR,
|
|
||||||
1: logging.WARNING,
|
|
||||||
2: logging.INFO,
|
|
||||||
3: CALL,
|
|
||||||
4: logging.DEBUG,
|
|
||||||
}[min(self._verbose, 4)]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def debug_ws(self) -> int:
|
def debug_ws(self) -> int:
|
||||||
|
|||||||
+8
-7
@@ -10,7 +10,7 @@ class RegistryError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ValueNotExists(RegistryError):
|
class ValueNotFound(RegistryError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -58,13 +58,14 @@ class ValueType(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class RegistryKey:
|
class RegistryKey:
|
||||||
def __init__(self, path: str):
|
def __init__(self, path: str, *, read_only: bool = False):
|
||||||
main_key, _, path = path.replace('/', '\\').partition('\\')
|
main_key, _, path = path.replace('/', '\\').partition('\\')
|
||||||
self.main_key = MainKey[main_key]
|
self.main_key = MainKey[main_key]
|
||||||
self.path = path
|
self.path = path
|
||||||
self._handle = reg.OpenKey(
|
access_flags = Access.KEY_QUERY_VALUE
|
||||||
self.main_key.value, path, access=(Access.KEY_QUERY_VALUE | Access.KEY_SET_VALUE).value
|
if not read_only:
|
||||||
)
|
access_flags |= Access.KEY_SET_VALUE
|
||||||
|
self._handle = reg.OpenKey(self.main_key.value, path, access=access_flags.value)
|
||||||
|
|
||||||
def __enter__(self) -> RegistryKey:
|
def __enter__(self) -> RegistryKey:
|
||||||
return self
|
return self
|
||||||
@@ -77,7 +78,7 @@ class RegistryKey:
|
|||||||
value, value_type = reg.QueryValueEx(self._handle, name)
|
value, value_type = reg.QueryValueEx(self._handle, name)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
# TODO: consider returning None for missing values
|
# TODO: consider returning None for missing values
|
||||||
raise ValueNotExists(name)
|
raise ValueNotFound(name)
|
||||||
return (ValueType(value_type), value)
|
return (ValueType(value_type), value)
|
||||||
|
|
||||||
def set(self, name: str, value_type: ValueType, value: Any) -> bool:
|
def set(self, name: str, value_type: ValueType, value: Any) -> bool:
|
||||||
@@ -89,7 +90,7 @@ class RegistryKey:
|
|||||||
reg.DeleteValue(self._handle, name)
|
reg.DeleteValue(self._handle, name)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
if not silent:
|
if not silent:
|
||||||
raise ValueNotExists(name)
|
raise ValueNotFound(name)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ if TYPE_CHECKING:
|
|||||||
class SettingsFile(TypedDict):
|
class SettingsFile(TypedDict):
|
||||||
proxy: URL
|
proxy: URL
|
||||||
language: str
|
language: str
|
||||||
autostart: bool
|
|
||||||
exclude: set[str]
|
exclude: set[str]
|
||||||
priority: list[str]
|
priority: list[str]
|
||||||
priority_only: bool
|
priority_only: bool
|
||||||
@@ -27,7 +26,6 @@ default_settings: SettingsFile = {
|
|||||||
"proxy": URL(),
|
"proxy": URL(),
|
||||||
"priority": [],
|
"priority": [],
|
||||||
"exclude": set(),
|
"exclude": set(),
|
||||||
"autostart": False,
|
|
||||||
"priority_only": True,
|
"priority_only": True,
|
||||||
"autostart_tray": False,
|
"autostart_tray": False,
|
||||||
"connection_quality": 1,
|
"connection_quality": 1,
|
||||||
@@ -48,7 +46,6 @@ class Settings:
|
|||||||
# from settings file
|
# from settings file
|
||||||
proxy: URL
|
proxy: URL
|
||||||
language: str
|
language: str
|
||||||
autostart: bool
|
|
||||||
exclude: set[str]
|
exclude: set[str]
|
||||||
priority: list[str]
|
priority: list[str]
|
||||||
priority_only: bool
|
priority_only: bool
|
||||||
|
|||||||
Reference in new issue
Block a user