Skip to content

Commit

Permalink
version 0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
FriendsOfGalaxy committed Nov 26, 2019
1 parent ecc8778 commit 299bc98
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 92 deletions.
2 changes: 1 addition & 1 deletion requirements/app.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
galaxy.plugin.api==0.53
galaxy.plugin.api==0.55
python-dateutil==2.8.0
requests==2.21.0
psutil==5.6.1
18 changes: 2 additions & 16 deletions src/backend.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
import logging as log
import asyncio


class ParadoxClient:
def __init__(self, http_client):
self.http_client = http_client
self.paradox_launcher_skus = None

async def prepare_sku(self):
data = {'token': self.http_client.token}
log.info('Starting skus retrieve')
response = await self.http_client.do_request('GET', 'https://accounts.paradoxplaza.com/api/skus', headers=data)
response = await response.json()
log.info('Finished skus retrieve')
paradox_launcher_skus = set()
for sku in response:
await asyncio.sleep(0.01)
if 'paradoxLauncher' in response[sku]['platform']:
paradox_launcher_skus.add(sku)
self.paradox_launcher_skus = paradox_launcher_skus

async def get_account_id(self):
data = {'Authorization': f'{{"session":{{"token":"{self.http_client.token}"}}}}',
Expand All @@ -34,13 +19,14 @@ async def get_owned_games(self):
headers=data)

response = await response.json()

owned_products = []
if 'products' in response:
for platforms in response['products']:
for platform in platforms:
for game in platforms[platform]:
log.info(game)
if game['sku'] and game['title'] and game['product_type']:
if game['sku'] and game['title'] and game['product_type'] and "paradox builds" in game['platforms']:
owned_products.append({'sku': game['sku'],
'title': game['title'],
'type': game['product_type']})
Expand Down
16 changes: 15 additions & 1 deletion src/consts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import re
import sys
from enum import EnumMeta


AUTH_URL = r"https://accounts.paradoxplaza.com/login"
Expand All @@ -9,9 +10,22 @@
PARADOX_LAUNCHER_EXE = "Paradox Launcher.exe"


class System(EnumMeta):
WINDOWS = 1
MACOS = 2
LINUX = 3


if sys.platform == 'win32':
SYSTEM = System.WINDOWS
elif sys.platform == 'darwin':
SYSTEM = System.MACOS


def regex_pattern(regex):
return ".*" + re.escape(regex) + ".*"


AUTH_PARAMS = {
"window_title": "Login to Paradox\u2122",
"window_width": 700,
Expand Down
172 changes: 99 additions & 73 deletions src/plugin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import logging as log
import sys

Expand All @@ -7,9 +6,9 @@
from galaxy.api.types import NextStep, Authentication, Game, LicenseInfo, LicenseType, LocalGame, LocalGameState
from version import __version__

from consts import AUTH_PARAMS
from backend import ParadoxClient
from http_client import AuthenticatedHttpClient
from consts import AUTH_PARAMS, System, SYSTEM

import pickle
import asyncio
Expand All @@ -19,8 +18,6 @@
import webbrowser

from typing import Any, List, Optional


from local import LocalClient


Expand All @@ -30,7 +27,6 @@ def __init__(self, reader, writer, token):
self._http_client = AuthenticatedHttpClient(self.store_credentials)
self.paradox_client = ParadoxClient(self._http_client)
self.local_client = LocalClient()
self.prepare_sku = None
self.owned_games_cache = None

self.local_games_cache = {}
Expand All @@ -39,17 +35,20 @@ def __init__(self, reader, writer, token):
self.tick_counter = 0

self.local_games_called = None
self.owned_games_called = None

self.update_installed_games_task = None
self.update_running_games_task = None
self.update_owned_games_task = None



async def authenticate(self, stored_credentials=None):
if stored_credentials:
stored_cookies = pickle.loads(bytes.fromhex(stored_credentials['cookie_jar']))
self._http_client.authenticate_with_cookies(stored_cookies)
self._http_client.set_auth_lost_callback(self.lost_authentication)
acc_id = await self.paradox_client.get_account_id()
self.prepare_sku = asyncio.create_task(self.paradox_client.prepare_sku())
return Authentication(str(acc_id), 'Paradox')
if not stored_credentials:
return NextStep("web_session", AUTH_PARAMS)
Expand All @@ -58,76 +57,85 @@ async def pass_login_credentials(self, step, credentials, cookies):
self._http_client.authenticate_with_cookies(cookies)
self._http_client.set_auth_lost_callback(self.lost_authentication)
acc_id = await self.paradox_client.get_account_id()
self.prepare_sku = asyncio.create_task(self.paradox_client.prepare_sku())
return Authentication(str(acc_id), 'Paradox')

async def get_owned_games(self):
owned_games = await self.paradox_client.get_owned_games()
await self.prepare_sku
games_to_send = []
sent_titles = set()
for game in owned_games:
log.info(game)
if game['sku'] in self.paradox_client.paradox_launcher_skus and 'game' in game['type']:
title = game['title'].replace(' (Paradox)', '')
title = title.split(':')[0]
if title in sent_titles:
continue
sent_titles.add(title)
games_to_send.append(Game(title.lower().replace(' ', '_'), title, None, LicenseInfo(LicenseType.SinglePurchase)))
self.owned_games_cache = games_to_send
return games_to_send

async def get_local_games(self):
games_path = self.local_client.games_path
if not games_path:
return []
local_games = os.listdir(games_path)

games_to_send = []
local_games_cache = {}
for local_game in local_games:
game_folder = os.path.join(games_path, local_game)
game_cpatch = os.path.join(game_folder, '.cpatch', local_game)
try:
with open(os.path.join(game_cpatch, 'version'))as game_cp:
version = game_cp.readline()
with open(os.path.join(game_cpatch, 'repository.json'), 'r') as js:
game_repository = json.load(js)
exe_path = game_repository['content']['versions'][version]['exePath']
except FileNotFoundError:
continue
except Exception as e:
log.error(f"Unable to parse local game {local_game} {repr(e)}")
continue

local_games_cache[local_game] = os.path.join(game_folder, exe_path)
games_to_send.append(LocalGame(local_game, LocalGameState.Installed))
self.local_games_cache = local_games_cache
self.local_games_called = True
try:
owned_games = await self.paradox_client.get_owned_games()
sent_titles = set()
for game in owned_games:
log.info(game)
if 'game' in game['type']:
title = game['title'].replace(' (Paradox)', '')
title = title.split(':')[0]
if title in sent_titles:
continue
sent_titles.add(title)
games_to_send.append(Game(title.lower().replace(' ', '_'), title, None, LicenseInfo(LicenseType.SinglePurchase)))
self.owned_games_cache = games_to_send
self.owned_games_called = True
except Exception as e:
log.error(f"Encountered exception while retriving owned games {repr(e)}")
self.owned_games_called = True
raise e
return games_to_send

async def launch_game(self, game_id):
exe_path = self.local_games_cache.get(game_id)
log.info(f"Launching {exe_path}")
game_dir = os.path.join(self.local_client.games_path, game_id)
subprocess.Popen(exe_path,cwd=game_dir)
if SYSTEM == System.WINDOWS:
async def get_local_games(self):
games_path = self.local_client.games_path
if not games_path:
self.local_games_called = True
return []
local_games = os.listdir(games_path)

games_to_send = []
local_games_cache = {}
for local_game in local_games:
game_folder = os.path.join(games_path, local_game)
game_cpatch = os.path.join(game_folder, '.cpatch', local_game)
try:
with open(os.path.join(game_cpatch, 'version'))as game_cp:
version = game_cp.readline()
with open(os.path.join(game_cpatch, 'repository.json'), 'r') as js:
game_repository = json.load(js)
exe_path = game_repository['content']['versions'][version]['exePath']
except FileNotFoundError:
continue
except Exception as e:
log.error(f"Unable to parse local game {local_game} {repr(e)}")
continue

async def install_game(self, game_id):
bootstraper_exe = self.local_client.bootstraper_exe
if bootstraper_exe:
subprocess.Popen(bootstraper_exe)
return
log.info("Local client not installed")
webbrowser.open('https://play.paradoxplaza.com')

async def uninstall_game(self, game_id):
bootstraper_exe = self.local_client.bootstraper_exe
if bootstraper_exe:
subprocess.call(bootstraper_exe)
return
log.info("Local client not installed")
webbrowser.open('https://play.paradoxplaza.com')
local_games_cache[local_game] = os.path.join(game_folder, exe_path)
games_to_send.append(LocalGame(local_game, LocalGameState.Installed))
self.local_games_cache = local_games_cache
self.local_games_called = True
return games_to_send

if SYSTEM == System.WINDOWS:
async def launch_game(self, game_id):
exe_path = self.local_games_cache.get(game_id)
log.info(f"Launching {exe_path}")
game_dir = os.path.join(self.local_client.games_path, game_id)
subprocess.Popen(exe_path,cwd=game_dir)

if SYSTEM == System.WINDOWS:
async def install_game(self, game_id):
bootstraper_exe = self.local_client.bootstraper_exe
if bootstraper_exe:
subprocess.Popen(bootstraper_exe)
return
log.info("Local client not installed")
webbrowser.open('https://play.paradoxplaza.com')

if SYSTEM == System.WINDOWS:
async def uninstall_game(self, game_id):
bootstraper_exe = self.local_client.bootstraper_exe
if bootstraper_exe:
subprocess.call(bootstraper_exe)
return
log.info("Local client not installed")
webbrowser.open('https://play.paradoxplaza.com')

async def update_installed_games(self):
games_path = self.local_client.games_path
Expand Down Expand Up @@ -165,18 +173,36 @@ async def update_running_games(self):

self.running_game = running_game

async def update_owned_games(self):
owned_games_cache = self.owned_games_cache
owned_games = await self.get_owned_games()
log.info("Looking for new games")
for game in owned_games:
if game not in owned_games_cache:
log.info(f"Adding game {game}")
self.add_game(game)


def tick(self):
if not self.local_games_called or sys.platform != 'win32':
return
self.tick_counter += 1

if not self.owned_games_called or (sys.platform == 'win32' and not self.local_games_called):
return

if self.tick_counter % 60 == 0:
if not self.update_owned_games_task or self.update_owned_games_task.done():
self.update_owned_games_task = asyncio.create_task(self.update_owned_games())

if sys.platform != 'win32':
return

if not self.update_installed_games_task or self.update_installed_games_task.done():
self.update_installed_games_task = asyncio.create_task(self.update_installed_games())
if not self.update_running_games_task or self.update_running_games_task.done():
self.update_running_games_task = asyncio.create_task(self.update_running_games())

def shutdown(self):
asyncio.create_task(self._http_client.close())
async def shutdown(self):
await self._http_client.close()

async def prepare_os_compatibility_context(self, game_ids: List[str]) -> Any:
return None
Expand Down
2 changes: 1 addition & 1 deletion src/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3"
__version__ = "0.4"

0 comments on commit 299bc98

Please sign in to comment.