Add the advanced settings section;
Implement the checkboxes for badge and emote mining toggle, and the extra available drops check toggle
This commit is contained in:
1 parent
165f9eccb3
commit
5f54c26f4a
6 files changed
+112
-44
No files matched your search
+1
-1
@@ -37,7 +37,7 @@ class Stream:
|
|||||||
self.channel: Channel = channel
|
self.channel: Channel = channel
|
||||||
self.broadcast_id = int(id)
|
self.broadcast_id = int(id)
|
||||||
self.viewers: int = viewers
|
self.viewers: int = viewers
|
||||||
self.drops_enabled: bool = True
|
self.drops_enabled: bool = not channel._twitch.settings.available_drops_check
|
||||||
self.game: Game | None = Game(game) if game else None
|
self.game: Game | None = Game(game) if game else None
|
||||||
self.title: str = title
|
self.title: str = title
|
||||||
self._stream_url: URLType | None = None
|
self._stream_url: URLType | None = None
|
||||||
|
|||||||
@@ -1553,6 +1553,8 @@ class _SettingsVars(TypedDict):
|
|||||||
language: StringVar
|
language: StringVar
|
||||||
priority_mode: StringVar
|
priority_mode: StringVar
|
||||||
tray_notifications: IntVar
|
tray_notifications: IntVar
|
||||||
|
enable_badges_emotes: IntVar
|
||||||
|
available_drops_check: IntVar
|
||||||
|
|
||||||
|
|
||||||
class SettingsPanel:
|
class SettingsPanel:
|
||||||
@@ -1586,6 +1588,12 @@ class SettingsPanel:
|
|||||||
"dark_mode": IntVar(master, int(self._settings.dark_mode)),
|
"dark_mode": IntVar(master, int(self._settings.dark_mode)),
|
||||||
"priority_mode": StringVar(master, self.PRIORITY_MODES[priority_mode]),
|
"priority_mode": StringVar(master, self.PRIORITY_MODES[priority_mode]),
|
||||||
"tray_notifications": IntVar(master, self._settings.tray_notifications),
|
"tray_notifications": IntVar(master, self._settings.tray_notifications),
|
||||||
|
"enable_badges_emotes": IntVar(
|
||||||
|
master, int(self._settings.enable_badges_emotes)
|
||||||
|
),
|
||||||
|
"available_drops_check": IntVar(
|
||||||
|
master, int(self._settings.available_drops_check)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
self._game_names: set[str] = set()
|
self._game_names: set[str] = set()
|
||||||
master.rowconfigure(0, weight=1)
|
master.rowconfigure(0, weight=1)
|
||||||
@@ -1593,6 +1601,7 @@ class SettingsPanel:
|
|||||||
# use a frame to center the content within the tab
|
# use a frame to center the content within the tab
|
||||||
center_frame = ttk.Frame(master)
|
center_frame = ttk.Frame(master)
|
||||||
center_frame.grid(column=0, row=0)
|
center_frame.grid(column=0, row=0)
|
||||||
|
|
||||||
# General section
|
# General section
|
||||||
general_frame = ttk.LabelFrame(
|
general_frame = ttk.LabelFrame(
|
||||||
center_frame, padding=(4, 0, 4, 4), text=_("gui", "settings", "general", "name")
|
center_frame, padding=(4, 0, 4, 4), text=_("gui", "settings", "general", "name")
|
||||||
@@ -1602,11 +1611,11 @@ class SettingsPanel:
|
|||||||
# NOTE: this can be adjusted or removed later on if more options were to be added
|
# NOTE: this can be adjusted or removed later on if more options were to be added
|
||||||
general_frame.rowconfigure(0, weight=1)
|
general_frame.rowconfigure(0, weight=1)
|
||||||
general_frame.columnconfigure(0, weight=1)
|
general_frame.columnconfigure(0, weight=1)
|
||||||
center_frame2 = ttk.Frame(general_frame)
|
general_center = ttk.Frame(general_frame)
|
||||||
center_frame2.grid(column=0, row=0)
|
general_center.grid(column=0, row=0)
|
||||||
|
|
||||||
# language frame
|
# language frame
|
||||||
language_frame = ttk.Frame(center_frame2)
|
language_frame = ttk.Frame(general_center)
|
||||||
language_frame.grid(column=0, row=0)
|
language_frame.grid(column=0, row=0)
|
||||||
ttk.Label(language_frame, text="Language 🌐 (requires restart): ").grid(column=0, row=0)
|
ttk.Label(language_frame, text="Language 🌐 (requires restart): ").grid(column=0, row=0)
|
||||||
SelectCombobox(
|
SelectCombobox(
|
||||||
@@ -1617,7 +1626,7 @@ class SettingsPanel:
|
|||||||
).grid(column=1, row=0)
|
).grid(column=1, row=0)
|
||||||
|
|
||||||
# checkboxes frame
|
# checkboxes frame
|
||||||
checkboxes_frame = ttk.Frame(center_frame2)
|
checkboxes_frame = ttk.Frame(general_center)
|
||||||
checkboxes_frame.grid(column=0, row=1)
|
checkboxes_frame.grid(column=0, row=1)
|
||||||
ttk.Label(
|
ttk.Label(
|
||||||
checkboxes_frame, text=_("gui", "settings", "general", "autostart")
|
checkboxes_frame, text=_("gui", "settings", "general", "autostart")
|
||||||
@@ -1637,7 +1646,9 @@ class SettingsPanel:
|
|||||||
ttk.Checkbutton(
|
ttk.Checkbutton(
|
||||||
checkboxes_frame,
|
checkboxes_frame,
|
||||||
variable=self._vars["tray_notifications"],
|
variable=self._vars["tray_notifications"],
|
||||||
command=self.update_notifications,
|
command=lambda: setattr(
|
||||||
|
self._settings, "tray_notifications", bool(self._vars["tray_notifications"].get())
|
||||||
|
),
|
||||||
).grid(column=1, row=irow, sticky="w")
|
).grid(column=1, row=irow, sticky="w")
|
||||||
ttk.Label(
|
ttk.Label(
|
||||||
checkboxes_frame, text=_("gui", "settings", "general", "dark_mode")
|
checkboxes_frame, text=_("gui", "settings", "general", "dark_mode")
|
||||||
@@ -1658,7 +1669,7 @@ class SettingsPanel:
|
|||||||
).grid(column=1, row=irow, sticky="w")
|
).grid(column=1, row=irow, sticky="w")
|
||||||
|
|
||||||
# proxy frame
|
# proxy frame
|
||||||
proxy_frame = ttk.Frame(center_frame2)
|
proxy_frame = ttk.Frame(general_center)
|
||||||
proxy_frame.grid(column=0, row=2)
|
proxy_frame.grid(column=0, row=2)
|
||||||
ttk.Label(proxy_frame, text=_("gui", "settings", "general", "proxy")).grid(column=0, row=0)
|
ttk.Label(proxy_frame, text=_("gui", "settings", "general", "proxy")).grid(column=0, row=0)
|
||||||
self._proxy = PlaceholderEntry(
|
self._proxy = PlaceholderEntry(
|
||||||
@@ -1671,11 +1682,48 @@ class SettingsPanel:
|
|||||||
)
|
)
|
||||||
self._proxy.config(validatecommand=partial(proxy_validate, self._proxy, self._settings))
|
self._proxy.config(validatecommand=partial(proxy_validate, self._proxy, self._settings))
|
||||||
self._proxy.grid(column=0, row=1)
|
self._proxy.grid(column=0, row=1)
|
||||||
|
|
||||||
|
# Advanced section
|
||||||
|
advanced_frame = ttk.LabelFrame(
|
||||||
|
center_frame, padding=(4, 0, 4, 4), text=_("gui", "settings", "advanced", "name")
|
||||||
|
)
|
||||||
|
advanced_frame.grid(column=0, row=1, sticky="nsew")
|
||||||
|
advanced_frame.columnconfigure(0, weight=1)
|
||||||
|
advanced_frame.rowconfigure(0, weight=1)
|
||||||
|
advanced_center = ttk.Frame(advanced_frame)
|
||||||
|
advanced_center.grid(column=0, row=0)
|
||||||
|
|
||||||
|
# Toggles for badges and emotes, and available drops check
|
||||||
|
ttk.Label(
|
||||||
|
advanced_center, text=_("gui", "settings", "advanced", "enable_badges_emotes")
|
||||||
|
).grid(column=0, row=(irow := 0), sticky="e")
|
||||||
|
ttk.Checkbutton(
|
||||||
|
advanced_center,
|
||||||
|
variable=self._vars["enable_badges_emotes"],
|
||||||
|
command=lambda: setattr(
|
||||||
|
self._settings,
|
||||||
|
"enable_badges_emotes",
|
||||||
|
bool(self._vars["enable_badges_emotes"].get()),
|
||||||
|
),
|
||||||
|
).grid(column=1, row=irow, sticky="w")
|
||||||
|
ttk.Label(
|
||||||
|
advanced_center, text=_("gui", "settings", "advanced", "available_drops_check")
|
||||||
|
).grid(column=0, row=(irow := irow + 1), sticky="e")
|
||||||
|
ttk.Checkbutton(
|
||||||
|
advanced_center,
|
||||||
|
variable=self._vars["available_drops_check"],
|
||||||
|
command=lambda: setattr(
|
||||||
|
self._settings,
|
||||||
|
"available_drops_check",
|
||||||
|
bool(self._vars["available_drops_check"].get()),
|
||||||
|
),
|
||||||
|
).grid(column=1, row=irow, sticky="w")
|
||||||
|
|
||||||
# Priority section
|
# Priority section
|
||||||
priority_frame = ttk.LabelFrame(
|
priority_frame = ttk.LabelFrame(
|
||||||
center_frame, padding=(4, 0, 4, 4), text=_("gui", "settings", "priority")
|
center_frame, padding=(4, 0, 4, 4), text=_("gui", "settings", "priority")
|
||||||
)
|
)
|
||||||
priority_frame.grid(column=1, row=0, sticky="nsew")
|
priority_frame.grid(column=1, row=0, rowspan=2, sticky="nsew")
|
||||||
self._priority_entry = PlaceholderCombobox(
|
self._priority_entry = PlaceholderCombobox(
|
||||||
priority_frame, placeholder=_("gui", "settings", "game_name"), width=30
|
priority_frame, placeholder=_("gui", "settings", "game_name"), width=30
|
||||||
)
|
)
|
||||||
@@ -1686,7 +1734,7 @@ class SettingsPanel:
|
|||||||
).grid(column=1, row=0)
|
).grid(column=1, row=0)
|
||||||
self._priority_list = PaddedListbox(
|
self._priority_list = PaddedListbox(
|
||||||
priority_frame,
|
priority_frame,
|
||||||
height=10,
|
height=12,
|
||||||
padding=(1, 0),
|
padding=(1, 0),
|
||||||
activestyle="none",
|
activestyle="none",
|
||||||
selectmode="single",
|
selectmode="single",
|
||||||
@@ -1715,11 +1763,12 @@ class SettingsPanel:
|
|||||||
priority_frame, text="❌", command=self.priority_delete, width=3, style="Large.TButton"
|
priority_frame, text="❌", command=self.priority_delete, width=3, style="Large.TButton"
|
||||||
).grid(column=1, row=3, sticky="ns")
|
).grid(column=1, row=3, sticky="ns")
|
||||||
priority_frame.rowconfigure(3, weight=1)
|
priority_frame.rowconfigure(3, weight=1)
|
||||||
|
|
||||||
# Exclude section
|
# Exclude section
|
||||||
exclude_frame = ttk.LabelFrame(
|
exclude_frame = ttk.LabelFrame(
|
||||||
center_frame, padding=(4, 0, 4, 4), text=_("gui", "settings", "exclude")
|
center_frame, padding=(4, 0, 4, 4), text=_("gui", "settings", "exclude")
|
||||||
)
|
)
|
||||||
exclude_frame.grid(column=2, row=0, sticky="nsew")
|
exclude_frame.grid(column=2, row=0, rowspan=2, sticky="nsew")
|
||||||
self._exclude_entry = PlaceholderCombobox(
|
self._exclude_entry = PlaceholderCombobox(
|
||||||
exclude_frame, placeholder=_("gui", "settings", "game_name"), width=26
|
exclude_frame, placeholder=_("gui", "settings", "game_name"), width=26
|
||||||
)
|
)
|
||||||
@@ -1729,7 +1778,7 @@ class SettingsPanel:
|
|||||||
).grid(column=1, row=0)
|
).grid(column=1, row=0)
|
||||||
self._exclude_list = PaddedListbox(
|
self._exclude_list = PaddedListbox(
|
||||||
exclude_frame,
|
exclude_frame,
|
||||||
height=10,
|
height=12,
|
||||||
padding=(1, 0),
|
padding=(1, 0),
|
||||||
activestyle="none",
|
activestyle="none",
|
||||||
selectmode="single",
|
selectmode="single",
|
||||||
@@ -1743,9 +1792,10 @@ class SettingsPanel:
|
|||||||
ttk.Button(
|
ttk.Button(
|
||||||
exclude_frame, text="❌", command=self.exclude_delete, width=3, style="Large.TButton"
|
exclude_frame, text="❌", command=self.exclude_delete, width=3, style="Large.TButton"
|
||||||
).grid(column=0, row=2, columnspan=2, sticky="ew")
|
).grid(column=0, row=2, columnspan=2, sticky="ew")
|
||||||
|
|
||||||
# Reload button
|
# Reload button
|
||||||
reload_frame = ttk.Frame(center_frame)
|
reload_frame = ttk.Frame(center_frame)
|
||||||
reload_frame.grid(column=0, row=1, columnspan=3, pady=4)
|
reload_frame.grid(column=0, row=2, columnspan=3, pady=4)
|
||||||
ttk.Label(reload_frame, text=_("gui", "settings", "reload_text")).grid(column=0, row=0)
|
ttk.Label(reload_frame, text=_("gui", "settings", "reload_text")).grid(column=0, row=0)
|
||||||
ttk.Button(
|
ttk.Button(
|
||||||
reload_frame,
|
reload_frame,
|
||||||
@@ -1761,12 +1811,8 @@ class SettingsPanel:
|
|||||||
|
|
||||||
def update_dark_mode(self) -> None:
|
def update_dark_mode(self) -> None:
|
||||||
self._settings.dark_mode = bool(self._vars["dark_mode"].get())
|
self._settings.dark_mode = bool(self._vars["dark_mode"].get())
|
||||||
self._settings.alter()
|
|
||||||
self._manager.apply_theme(self._settings.dark_mode)
|
self._manager.apply_theme(self._settings.dark_mode)
|
||||||
|
|
||||||
def update_notifications(self) -> None:
|
|
||||||
self._settings.tray_notifications = bool(self._vars["tray_notifications"].get())
|
|
||||||
|
|
||||||
def _get_self_path(self) -> str:
|
def _get_self_path(self) -> str:
|
||||||
# NOTE: we need double quotes in case the path contains spaces
|
# NOTE: we need double quotes in case the path contains spaces
|
||||||
return f'"{SELF_PATH.resolve()!s}"'
|
return f'"{SELF_PATH.resolve()!s}"'
|
||||||
@@ -2629,6 +2675,8 @@ if __name__ == "__main__":
|
|||||||
autostart_tray=False,
|
autostart_tray=False,
|
||||||
exclude={"Lit Game"},
|
exclude={"Lit Game"},
|
||||||
tray_notifications=True,
|
tray_notifications=True,
|
||||||
|
enable_badges_emotes=False,
|
||||||
|
available_drops_check=False,
|
||||||
logging_level=LOGGING_LEVELS[0],
|
logging_level=LOGGING_LEVELS[0],
|
||||||
priority_mode=PriorityMode.PRIORITY_ONLY,
|
priority_mode=PriorityMode.PRIORITY_ONLY,
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-1
@@ -395,7 +395,9 @@ class DropsCampaign:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def eligible(self) -> bool:
|
def eligible(self) -> bool:
|
||||||
return self.linked or self.has_badge_or_emote
|
return self.linked or (
|
||||||
|
self._twitch.settings.enable_badges_emotes and self.has_badge_or_emote
|
||||||
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def has_badge_or_emote(self) -> bool:
|
def has_badge_or_emote(self) -> bool:
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ class SettingsFile(TypedDict):
|
|||||||
autostart_tray: bool
|
autostart_tray: bool
|
||||||
connection_quality: int
|
connection_quality: int
|
||||||
tray_notifications: bool
|
tray_notifications: bool
|
||||||
|
enable_badges_emotes: bool
|
||||||
|
available_drops_check: bool
|
||||||
priority_mode: PriorityMode
|
priority_mode: PriorityMode
|
||||||
|
|
||||||
|
|
||||||
@@ -32,6 +34,8 @@ default_settings: SettingsFile = {
|
|||||||
"connection_quality": 1,
|
"connection_quality": 1,
|
||||||
"language": DEFAULT_LANG,
|
"language": DEFAULT_LANG,
|
||||||
"tray_notifications": True,
|
"tray_notifications": True,
|
||||||
|
"enable_badges_emotes": False,
|
||||||
|
"available_drops_check": False,
|
||||||
"priority_mode": PriorityMode.PRIORITY_ONLY,
|
"priority_mode": PriorityMode.PRIORITY_ONLY,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +58,8 @@ class Settings:
|
|||||||
autostart_tray: bool
|
autostart_tray: bool
|
||||||
connection_quality: int
|
connection_quality: int
|
||||||
tray_notifications: bool
|
tray_notifications: bool
|
||||||
|
enable_badges_emotes: bool
|
||||||
|
available_drops_check: bool
|
||||||
priority_mode: PriorityMode
|
priority_mode: PriorityMode
|
||||||
|
|
||||||
PASSTHROUGH = ("_settings", "_args", "_altered")
|
PASSTHROUGH = ("_settings", "_args", "_altered")
|
||||||
|
|||||||
@@ -166,6 +166,12 @@ class GUISettingsGeneral(TypedDict):
|
|||||||
proxy: str
|
proxy: str
|
||||||
|
|
||||||
|
|
||||||
|
class GUISettingsAdvanced(TypedDict):
|
||||||
|
name: str
|
||||||
|
enable_badges_emotes: str
|
||||||
|
available_drops_check: str
|
||||||
|
|
||||||
|
|
||||||
class GUIPriorityModes(TypedDict):
|
class GUIPriorityModes(TypedDict):
|
||||||
priority_only: str
|
priority_only: str
|
||||||
ending_soonest: str
|
ending_soonest: str
|
||||||
@@ -174,6 +180,7 @@ class GUIPriorityModes(TypedDict):
|
|||||||
|
|
||||||
class GUISettings(TypedDict):
|
class GUISettings(TypedDict):
|
||||||
general: GUISettingsGeneral
|
general: GUISettingsGeneral
|
||||||
|
advanced: GUISettingsAdvanced
|
||||||
priority_modes: GUIPriorityModes
|
priority_modes: GUIPriorityModes
|
||||||
game_name: str
|
game_name: str
|
||||||
priority: str
|
priority: str
|
||||||
@@ -366,6 +373,11 @@ default_translation: Translation = {
|
|||||||
"priority_mode": "Priority mode: ",
|
"priority_mode": "Priority mode: ",
|
||||||
"proxy": "Proxy (requires restart):",
|
"proxy": "Proxy (requires restart):",
|
||||||
},
|
},
|
||||||
|
"advanced": {
|
||||||
|
"name": "Advanced",
|
||||||
|
"enable_badges_emotes": "Enable partial support for badges and emotes: ",
|
||||||
|
"available_drops_check": "Enable extra available drops check: ",
|
||||||
|
},
|
||||||
"priority_modes": {
|
"priority_modes": {
|
||||||
"priority_only": "Priority list only",
|
"priority_only": "Priority list only",
|
||||||
"ending_soonest": "Ending soonest",
|
"ending_soonest": "Ending soonest",
|
||||||
|
|||||||
@@ -1562,7 +1562,7 @@ class Twitch:
|
|||||||
async def bulk_check_online(self, channels: abc.Iterable[Channel]):
|
async def bulk_check_online(self, channels: abc.Iterable[Channel]):
|
||||||
"""
|
"""
|
||||||
Utilize batch GQL requests to check ONLINE status for a lot of channels at once.
|
Utilize batch GQL requests to check ONLINE status for a lot of channels at once.
|
||||||
Also handles the drops_enabled check.
|
Also handles the drops_enabled check (if enabled).
|
||||||
"""
|
"""
|
||||||
acl_streams_map: dict[int, JsonType] = {}
|
acl_streams_map: dict[int, JsonType] = {}
|
||||||
stream_gql_ops: list[GQLOperation] = [channel.stream_gql for channel in channels]
|
stream_gql_ops: list[GQLOperation] = [channel.stream_gql for channel in channels]
|
||||||
@@ -1587,29 +1587,30 @@ class Twitch:
|
|||||||
task.cancel()
|
task.cancel()
|
||||||
raise
|
raise
|
||||||
# for all channels with an active stream, check the available drops as well
|
# for all channels with an active stream, check the available drops as well
|
||||||
# acl_available_drops_map: dict[int, list[JsonType]] = {}
|
acl_available_drops_map: dict[int, list[JsonType]] = {}
|
||||||
# available_gql_ops: list[GQLOperation] = [
|
if self.settings.available_drops_check:
|
||||||
# GQL_OPERATIONS["AvailableDrops"].with_variables({"channelID": str(channel_id)})
|
available_gql_ops: list[GQLOperation] = [
|
||||||
# for channel_id, channel_data in acl_streams_map.items()
|
GQL_OPERATIONS["AvailableDrops"].with_variables({"channelID": str(channel_id)})
|
||||||
# if channel_data["stream"] is not None # only do this for ONLINE channels
|
for channel_id, channel_data in acl_streams_map.items()
|
||||||
# ]
|
if channel_data["stream"] is not None # only do this for ONLINE channels
|
||||||
# available_gql_tasks: list[asyncio.Task[list[JsonType]]] = [
|
]
|
||||||
# asyncio.create_task(self.gql_request(available_gql_chunk))
|
available_gql_tasks: list[asyncio.Task[list[JsonType]]] = [
|
||||||
# for available_gql_chunk in chunk(available_gql_ops, 20)
|
asyncio.create_task(self.gql_request(available_gql_chunk))
|
||||||
# ]
|
for available_gql_chunk in chunk(available_gql_ops, 20)
|
||||||
# try:
|
]
|
||||||
# for coro in asyncio.as_completed(available_gql_tasks):
|
try:
|
||||||
# response_list = await coro
|
for coro in asyncio.as_completed(available_gql_tasks):
|
||||||
# for response_json in response_list:
|
response_list = await coro
|
||||||
# available_info: JsonType = response_json["data"]["channel"]
|
for response_json in response_list:
|
||||||
# acl_available_drops_map[int(available_info["id"])] = (
|
available_info: JsonType = response_json["data"]["channel"]
|
||||||
# available_info["viewerDropCampaigns"] or []
|
acl_available_drops_map[int(available_info["id"])] = (
|
||||||
# )
|
available_info["viewerDropCampaigns"] or []
|
||||||
# except Exception:
|
)
|
||||||
# # asyncio.as_completed doesn't cancel tasks on errors
|
except Exception:
|
||||||
# for task in available_gql_tasks:
|
# asyncio.as_completed doesn't cancel tasks on errors
|
||||||
# task.cancel()
|
for task in available_gql_tasks:
|
||||||
# raise
|
task.cancel()
|
||||||
|
raise
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
channel_id = channel.id
|
channel_id = channel.id
|
||||||
if channel_id not in acl_streams_map:
|
if channel_id not in acl_streams_map:
|
||||||
@@ -1617,6 +1618,5 @@ class Twitch:
|
|||||||
channel_data = acl_streams_map[channel_id]
|
channel_data = acl_streams_map[channel_id]
|
||||||
if channel_data["stream"] is None:
|
if channel_data["stream"] is None:
|
||||||
continue
|
continue
|
||||||
# available_drops: list[JsonType] = acl_available_drops_map[channel_id]
|
available_drops: list[JsonType] = acl_available_drops_map.get(channel_id, [])
|
||||||
# channel.external_update(channel_data, available_drops)
|
channel.external_update(channel_data, available_drops)
|
||||||
channel.external_update(channel_data, [])
|
|
||||||
Reference in new issue
Block a user