Update proxy branch with the new websocket implementation

This commit is contained in:
DevilXD committed 2022-04-08 22:07:41 +02:00
commit fa59cde737
7 files changed
+214 -102

No files matched your search

+65 -54
View File
@@ -72,7 +72,9 @@ class Twitch:
# Maintenance task
self._mnt_task: asyncio.Task[None] | None = None
def initialize(self) -> None:
async def get_session(self) -> aiohttp.ClientSession:
if self._session is not None:
return self._session
cookie_jar = aiohttp.CookieJar()
if COOKIES_PATH.exists():
cookie_jar.load(COOKIES_PATH)
@@ -81,6 +83,7 @@ class Twitch:
headers={"User-Agent": USER_AGENT},
timeout=aiohttp.ClientTimeout(connect=5, total=10),
)
return self._session
async def shutdown(self) -> None:
start_time = time()
@@ -135,7 +138,7 @@ class Twitch:
def prevent_close(self):
"""
Called when the application window has to be prevented from closing, even after the user
closes it with X. Usually used solely to display tracebacks drom the closing sequence.
closes it with X. Usually used solely to display tracebacks from the closing sequence.
"""
self.gui.prevent_close()
@@ -203,6 +206,7 @@ class Twitch:
self._state_change.clear()
elif self._state is State.INVENTORY_FETCH:
await self.fetch_inventory()
self.gui.set_games(set(campaign.game for campaign in self.inventory))
self.change_state(State.GAMES_UPDATE)
elif self._state is State.GAMES_UPDATE:
# Figure out which games to watch, and claim the drops we can
@@ -222,14 +226,15 @@ class Twitch:
game = campaign.game
if (
game not in self.games # isn't already there
and game.name not in exclude # isn't excluded
# isn't excluded by priority_only
and game.name not in exclude # and isn't excluded
# and isn't excluded by priority_only
and (not priority_only or game.name in priority)
and campaign.can_earn() # campaign can be progressed
and campaign.can_earn() # and can be progressed (active required)
):
# non-excluded games with no priority, are placed last, below priority ones
self.games[game] = priorities.get(game.name, 0)
self.gui.set_games(self.games.keys())
full_cleanup = True
self.restart_watching()
self.change_state(State.CHANNELS_CLEANUP)
elif self._state is State.CHANNELS_CLEANUP:
if not self.games or full_cleanup:
@@ -330,13 +335,14 @@ class Twitch:
])
# relink watching channel after cleanup,
# or stop watching it if it no longer qualifies
# NOTE: this replaces 'self.watching_channel's internal value with the new object
watching_channel = self.watching_channel.get_with_default(None)
if watching_channel is not None:
new_watching = channels.get(watching_channel.id)
if new_watching is not None and self.can_watch(new_watching):
self.watch(new_watching)
else:
# we're removing a channel we're watching
# we've removed a channel we were watching
self.stop_watching()
# pre-display the active drop with a substracted minute
for channel in channels.values():
@@ -349,36 +355,34 @@ class Twitch:
elif self._state is State.CHANNEL_SWITCH:
# Change into the selected channel, stay in the watching channel,
# or select a new channel that meets the required conditions
priority_channels: list[Channel] = []
selected_channel = self.gui.channels.get_selection()
if selected_channel is not None:
self.gui.channels.clear_selection()
priority_channels.append(selected_channel)
watching_channel = self.watching_channel.get_with_default(None)
if watching_channel is not None:
priority_channels.append(watching_channel)
# If there's no selected channel, change into a channel we can watch
new_watching = None
for channel in priority_channels:
if self.can_watch(channel):
new_watching = channel
break
if new_watching is None:
selected_channel = self.gui.channels.get_selection()
if selected_channel is not None and self.can_watch(selected_channel):
# selected channel is checked first, and set as long as we can watch it
new_watching = selected_channel
else:
# other channels additionally need to have a good reason
# for a switch (including the watching one)
# NOTE: we need to sort the channels every time because one channel
# can end up streaming any game, since channels aren't game-tied
# can end up streaming any game - channels aren't game-tied
for channel in sorted(channels.values(), key=self._game_key):
if self.can_watch(channel):
if self.can_watch(channel) and self.should_switch(channel):
new_watching = channel
break
if new_watching is not None:
self.watch(channel)
# break the state change chain by clearing the flag
self._state_change.clear()
else:
watching_channel = self.watching_channel.get_with_default(None)
if watching_channel is None and new_watching is None:
# not watching anything and there isn't anything to watch either
self.gui.print(
"No available channels to watch. Waiting for an ONLINE channel..."
)
self.change_state(State.IDLE)
else:
if new_watching is not None:
# if we have a better switch target - do so
# otherwise, continue watching what we had before
self.watch(new_watching)
# break the state change chain by clearing the flag
self._state_change.clear()
elif self._state is State.EXIT:
# we've been requested to exit the application
break
@@ -386,8 +390,7 @@ class Twitch:
# post-main-loop code goes here
async def _watch_sleep(self, delay: float) -> None:
# we use wait_for here to allow an asyncio.sleep that can be ended prematurely,
# without cancelling the containing task
# we use wait_for here to allow an asyncio.sleep-like that can be ended prematurely
self._watching_restart.clear()
with suppress(asyncio.TimeoutError):
await asyncio.wait_for(self._watching_restart.wait(), timeout=delay)
@@ -432,6 +435,8 @@ class Twitch:
else:
drop.update_minutes(drop_data["currentMinutesWatched"])
drop.display()
else:
use_active = True
if use_active:
# Sometimes, even GQL fails to give us the correct drop.
# In that case, we can use the locally cached inventory to try
@@ -461,6 +466,9 @@ class Twitch:
await asyncio.sleep(60)
def can_watch(self, channel: Channel) -> bool:
"""
Determines if the given channel qualifies as a watching candidate.
"""
if not self.games:
return False
return (
@@ -472,6 +480,28 @@ class Twitch:
and any(campaign.can_earn(channel) for campaign in self.inventory)
)
def should_switch(self, channel: Channel) -> bool:
"""
Determines if the given channel qualifies as a switch candidate.
"""
watching_channel = self.watching_channel.get_with_default(None)
channel_order = self._game_key(channel)
if watching_channel is not None:
watching_order = self._game_key(watching_channel)
else:
# stub it with some high value, it doesn't really matter
# since 'is None' check returns earlier anyway
watching_order = 1
return (
watching_channel is None # there's no current watching channel
# or this channel's game is higher order than the watching one's
# NOTE: order is tied to the priority list position, so lower == higher
or channel_order < watching_order
or channel_order == watching_order # or the order is the same
# and this channel has priority over the watching channel
and channel.priority > watching_channel.priority
)
def watch(self, channel: Channel):
self.gui.channels.set_watching(channel)
self.watching_channel.set(channel)
@@ -516,23 +546,9 @@ class Twitch:
Called by a Channel when it goes online (after pending).
"""
logger.debug(f"{channel.name} goes ONLINE")
channel_order = self._game_key(channel)
watching_channel = self.watching_channel.get_with_default(None)
if watching_channel is not None:
watching_order = self._game_key(watching_channel)
else:
watching_order = 1
if (
(
self._state is State.IDLE # we're currently idle
or watching_channel is None # or aren't watching anything
# or this channel is higher order than the watching one
# NOTE: order is tied to the list position, so lower == higher
or channel_order < watching_order
or channel_order == watching_order # or the order is the same
and channel.priority # and this channel has priority
and not watching_channel.priority # and we're watching a non-priority channel
) and self.can_watch(channel)
self.can_watch(channel) # we can watch the channel that just got ONLINE
and self.should_switch(channel) # and we should!
):
self.gui.print(f"{channel.name} goes ONLINE, switching...")
self.watch(channel)
@@ -763,12 +779,10 @@ class Twitch:
logger.debug("Checking login")
login_form.update("Logging in...", None)
# NOTE: We need this here because of the jar being accessed
if self._session is None:
self.initialize()
assert self._session is not None
session = await self.get_session()
url = URL(BASE_URL)
assert url.host is not None
jar = cast(aiohttp.CookieJar, self._session.cookie_jar)
jar = cast(aiohttp.CookieJar, session.cookie_jar)
for attempt in range(2):
cookie = jar.filter_cookies(url)
if not cookie:
@@ -810,10 +824,7 @@ class Twitch:
async def request(
self, method: str, url: str, *, attempts: int = 5, **kwargs
) -> abc.AsyncIterator[aiohttp.ClientResponse]:
session = self._session
if session is None:
self.initialize()
assert session is not None
session = await self.get_session()
method = method.upper()
if self.settings.proxy and "proxy" not in kwargs:
kwargs["proxy"] = self.settings.proxy