Skip to content

Commit 5008982

Browse files
authored
Merge pull request #28 from seuyh/v2
V2
2 parents 34c966b + cdf8ce6 commit 5008982

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4198
-4444
lines changed

.banner/readme_banner.png

-2.07 MB
Binary file not shown.

.gitignore

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
/design/languages/installer_ru.ui
2-
/design/languages/installer_en.ui
3-
/design/resources.qrc
41
/venv/
52
/output/
63
/.idea/
7-
/unused_resources/
8-
/data.json
94
/.banner/
105
/.github/
11-
/gui/__pycache__
12-
/libs/__pycache__
13-
/design/languages/__pycache__
146
/presset.json
7+
README.md
8+
presset.json
9+
unlocker.log
10+
/UI/icons/1x/
11+
/UI/icons/social/
12+
/UI/icons/stellaris.ico
13+
/UI/recources.qrc
14+
/UI/ui_dialog.ui
15+
/UI/ui_main.ui
16+
/UI/ui_error.ui

Libs/ConnectionCheck.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import requests
2+
from PyQt5.QtCore import QThread, pyqtSignal
3+
4+
class ConnectionCheckThread(QThread):
5+
github_status_checked = pyqtSignal(bool)
6+
server_status_checked = pyqtSignal(bool)
7+
8+
def run(self):
9+
try:
10+
response = requests.get('https://github.com', timeout=10)
11+
if response.status_code == 200:
12+
self.github_status_checked.emit(True)
13+
else:
14+
raise Exception("GitHub no response")
15+
except:
16+
self.github_status_checked.emit(False)
17+
18+
try:
19+
response = requests.get('https://seuyh.ru', timeout=10)
20+
if response.status_code == 200:
21+
self.server_status_checked.emit(True)
22+
else:
23+
raise Exception("Server no response")
24+
except:
25+
self.server_status_checked.emit(False)

Libs/CreamApiMaker.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from idlelib.iomenu import errors
2+
3+
from requests import get
4+
from time import sleep
5+
from PyQt5 import QtCore
6+
import os
7+
8+
9+
class CreamAPI(QtCore.QThread):
10+
progress_signal = QtCore.pyqtSignal(int)
11+
12+
def __init__(self):
13+
super().__init__()
14+
# self.dlc_callback = dlc_callback
15+
# self.progress_callback = progress_callback
16+
self.parent_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17+
18+
def get_dlc_name(self, dlc_id, errors=0):
19+
print('CreamApi creating...')
20+
url = f"https://api.steamcmd.net/v1/info/{dlc_id}"
21+
headers = {
22+
"User-Agent": "Mozilla/5.0",
23+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
24+
"Accept-Encoding": "gzip, deflate, br",
25+
"Accept-Language": "en-US,en;q=0.9",
26+
"Cache-Control": "max-age=0",
27+
"Connection": "keep-alive",
28+
"Host": "api.steamcmd.net",
29+
"Upgrade-Insecure-Requests": "1"
30+
}
31+
try:
32+
response = get(url, headers=headers, timeout=3)
33+
print(response)
34+
if response.status_code == 200:
35+
data = response.json()
36+
dlc_name = data['data'][str(dlc_id)]['common']['name']
37+
print(dlc_name)
38+
return dlc_name
39+
else:
40+
return None
41+
except Exception:
42+
if errors >= 3:
43+
return False
44+
errors += 1
45+
print('Cant connect steamcmd. Rertying...')
46+
return self.get_dlc_name(dlc_id)
47+
48+
def get_dlc_list(self, app_id, errors=0):
49+
url = f"https://api.steamcmd.net/v1/info/{app_id}"
50+
headers = {
51+
"User-Agent": "Mozilla/5.0",
52+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
53+
"Accept-Encoding": "gzip, deflate, br",
54+
"Accept-Language": "en-US,en;q=0.9",
55+
"Cache-Control": "max-age=0",
56+
"Connection": "keep-alive",
57+
"Host": "api.steamcmd.net",
58+
"Upgrade-Insecure-Requests": "1"
59+
}
60+
try:
61+
response = get(url, headers=headers, timeout=3)
62+
data = response.json()
63+
dlc_list_json = data['data'][str(app_id)]['extended']['listofdlc']
64+
dlc_list = dlc_list_json.split(',')
65+
return dlc_list
66+
except Exception:
67+
if errors >= 3:
68+
return False
69+
errors += 1
70+
print('Cant connect steamcmd. Rertying...')
71+
return self.get_dlc_list(app_id, errors)
72+
73+
def run(self):
74+
print('Cream api creating...')
75+
dlc_list = self.get_dlc_list(281990)
76+
print(dlc_list)
77+
if dlc_list:
78+
self.check_and_update_dlc_list(dlc_list,
79+
os.path.join(self.parent_directory, 'creamapi_steam_files', 'cream_api.ini'))
80+
self.check_and_update_dlc_list(dlc_list,
81+
os.path.join(self.parent_directory, 'creamapi_launcher_files', 'cream_api.ini'))
82+
# self.launcher_creamapi(dlcs)
83+
self.progress_signal.emit(100)
84+
else:
85+
print('SteamCmd unavailable. Skipped')
86+
self.progress_signal.emit(100)
87+
return
88+
89+
90+
def check_and_update_dlc_list(self, dlc_list, path):
91+
with open(path, 'r+') as file:
92+
content = file.read()
93+
for dlc_id in dlc_list:
94+
if str(dlc_id) not in content:
95+
dlc_name = self.get_dlc_name(dlc_id)
96+
file.write(f"\n{dlc_id} = {dlc_name}")
97+
print('CreamApi writed')
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
class DownloaderThread(QtCore.QThread):
88
progress_signal = QtCore.pyqtSignal(int, bool)
99
progress_signal_2 = QtCore.pyqtSignal(int)
10-
text_signal = QtCore.pyqtSignal(str)
10+
# text_signal = QtCore.pyqtSignal(str)
1111
error_signal = QtCore.pyqtSignal(Exception)
1212
speed_signal = QtCore.pyqtSignal(float)
1313
finished = QtCore.pyqtSignal()
@@ -25,7 +25,7 @@ def run(self):
2525
request = urllib.request.Request(self.file_url, headers={"User-Agent": "Mozilla/5.0"})
2626
request.add_header('Range', f'bytes={self.downloaded_bytes}-')
2727
try:
28-
with urllib.request.urlopen(request, timeout=10) as response:
28+
with urllib.request.urlopen(request, timeout=25) as response:
2929
total_size = int(response.headers.get('content-length', 0)) + self.downloaded_bytes
3030
start_time = time()
3131

@@ -56,8 +56,8 @@ def run(self):
5656
finally:
5757
progress_percentage = int((self.dlc_downloaded / self.dlc_count) * 100)
5858
self.progress_signal.emit(progress_percentage, True)
59-
self.text_signal.emit(path.basename(self.save_path))
59+
# self.text_signal.emit(path.basename(self.save_path))
6060
self.finished.emit()
6161

6262
def cancel(self):
63-
self.cancelled = True
63+
self.cancelled = True
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,4 @@ def reg_search(vaule_data, vaule_name):
4949
winreg.CloseKey(key)
5050
return launcher_path
5151
except:
52-
return 0
52+
return 0
Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
from PyQt5 import QtCore
55
from time import sleep
66
from glob import glob
7+
from re import search
78

89

910
class ReinstallThread(QtCore.QThread):
10-
progress_signal = QtCore.pyqtSignal(int)
11+
# progress_signal = QtCore.pyqtSignal(int)
1112
error_signal = QtCore.pyqtSignal(Exception)
1213
continue_reinstall = QtCore.pyqtSignal(str)
1314

@@ -23,22 +24,46 @@ def __init__(self, msi_path, paradox_folder1, paradox_folder2, paradox_folder3,
2324
self.downloaded_launcher_dir = downloaded_launcher_dir
2425

2526
def run(self):
27+
28+
latest_file = None
29+
latest_version = (0, 0)
30+
def extract_version(filename):
31+
match = search(r'launcher-installer-windows_(\d+\.\d+)', filename)
32+
if match:
33+
return tuple(map(int, match.group(1).split('.')))
34+
return None
2635
msi_files = glob(os.path.join(self.msi_path, "launcher-installer-windows_*.msi"))
36+
if self.launcher_downloaded:
37+
print('Alt unlock. Deleting all other launches')
38+
try:
39+
for file_path in msi_files:
40+
try:
41+
os.remove(file_path)
42+
print(f"Delete {file_path}")
43+
except Exception as e:
44+
print(f"Unable to delete {file_path}: {e}")
45+
except:
46+
pass
47+
try:
48+
move(self.downloaded_launcher_dir, self.msi_path)
49+
print(f"Launcher moved: {self.msi_path}")
50+
except Exception as e:
51+
print(f"Unable to move launcher: {e}")
52+
self.error_signal.emit(e)
53+
msi_files = glob(os.path.join(self.msi_path, "launcher-installer-windows_*.msi"))
54+
2755
if msi_files:
2856
msi_path = msi_files[0]
57+
2958
else:
3059
msi_path = os.path.join(self.msi_path, "launcher-installer-windows.msi")
3160
if os.path.exists(msi_path):
3261
pass
3362
else:
3463
self.error_signal.emit('launcher_installer not found!')
35-
print(f'Требуется ли подмена лаунчера: {self.launcher_downloaded}')
36-
if self.launcher_downloaded:
37-
os.remove(msi_path)
38-
move(self.downloaded_launcher_dir, msi_path)
39-
print(f'Путь к игре: {self.msi_path}')
40-
print(f'Путь к лаунчеру: {msi_path}\nСуществует ли путь: {os.path.exists(msi_path)}')
41-
print(f'Выполнение удаления')
64+
print(f'Game path: {self.msi_path}')
65+
print(f'Launcher Path: {msi_path}\nPath exists: {os.path.exists(msi_path)}')
66+
print(f'Deleting launcher...')
4267
try:
4368
self.paradox_remove(self.paradox_folder1, self.paradox_folder2, self.paradox_folder3, self.paradox_folder4)
4469

@@ -49,17 +74,16 @@ def run(self):
4974
# print("Произошла ошибка при удалении:", error.decode())
5075

5176
uninstall.wait()
52-
self.progress_signal.emit(33)
77+
# self.progress_signal.emit(33)
5378
sleep(1)
54-
print(f'Выполнение установки')
79+
print(f'Installing launcher...')
5580
install = Popen(
5681
['cmd.exe', '/c', 'msiexec', '/package', msi_path,
5782
'/quiet', 'CREATE_DESKTOP_SHORTCUT=0'], shell=True)
5883
# output, error = install.communicate()
5984
# if uninstall.returncode != 0:
6085
# print("Произошла ошибка при установке:", error.decode())
6186
install.wait()
62-
self.progress_signal.emit(66)
6387
self.continue_reinstall.emit(self.paradox_folder1)
6488
except Exception as e:
6589
self.error_signal.emit(e)
@@ -73,15 +97,20 @@ def paradox_remove(paradox_folder1, paradox_folder2, paradox_folder3, paradox_fo
7397
# paradox_folder4 = os.path.join(user_home, "AppData", "Roaming", "paradox-launcher-v2")
7498
try:
7599
if os.path.exists(paradox_folder1):
100+
print(f'Removing {paradox_folder1}')
76101
rmtree(paradox_folder1)
77102

78103
if os.path.exists(paradox_folder2):
104+
print(f'Removing {paradox_folder2}')
79105
rmtree(paradox_folder2)
80106

81107
if os.path.exists(paradox_folder3):
108+
print(f'Removing {paradox_folder3}')
82109
rmtree(paradox_folder3)
83110

84111
if os.path.exists(paradox_folder4):
112+
print(f'Removing {paradox_folder4}')
85113
rmtree(paradox_folder4)
86-
except Exception:
87-
pass
114+
except Exception as e:
115+
print(f'Cant delete {e}')
116+
pass

Libs/ServerData.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import requests
2+
from requests.packages.urllib3.exceptions import InsecureRequestWarning
3+
import sys
4+
5+
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
6+
headers = {'Cache-Control': 'no-cache', 'Pragma': 'no-cache'}
7+
try:
8+
response_dlc_data = requests.get(
9+
'https://raw.githubusercontent.com/seuyh/stellaris-dlc-unlocker/main/dlc_data.json', headers=headers).json()
10+
dlc_data = response_dlc_data
11+
except Exception:
12+
sys.exit(2)

Libs/logger.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import sys
2+
from datetime import datetime
3+
from functools import partial
4+
import io
5+
import traceback
6+
import atexit
7+
from PyQt5.QtCore import QTimer
8+
from UI_logic.ErrorWindow import errorUi
9+
10+
class Logger:
11+
def __init__(self, log_file_path, log_widget):
12+
self.log_widget = log_widget
13+
self.stdout_buffer = []
14+
self.stderr_buffer = []
15+
self.log_file = open(log_file_path, 'w', encoding='utf-8')
16+
self.error = errorUi()
17+
18+
if sys.stdout is None:
19+
sys.stdout = io.StringIO()
20+
if sys.stderr is None:
21+
sys.stderr = io.StringIO()
22+
23+
self.orig_stdout_write = sys.stdout.write
24+
self.orig_stderr_write = sys.stderr.write
25+
26+
sys.stdout.write = partial(self.log_print, orig_write=self.orig_stdout_write, is_stderr=False)
27+
sys.stderr.write = partial(self.log_print, orig_write=self.orig_stderr_write, is_stderr=True)
28+
29+
sys.excepthook = self.handle_exception
30+
atexit.register(self.close)
31+
32+
def log_print(self, text, orig_write, is_stderr=False):
33+
if is_stderr:
34+
self.stderr_buffer.append(text)
35+
else:
36+
self.stdout_buffer.append(text)
37+
38+
if text.endswith('\n'):
39+
if is_stderr:
40+
full_message = ''.join(self.stderr_buffer)
41+
self.stderr_buffer.clear()
42+
self.handle_logging(full_message)
43+
else:
44+
full_message = ''.join(self.stdout_buffer)
45+
self.stdout_buffer.clear()
46+
self.handle_logging(full_message)
47+
48+
orig_write(text)
49+
50+
def handle_logging(self, full_message):
51+
if full_message.strip():
52+
log_text = f'[{datetime.now().strftime("%H:%M:%S")}] {full_message.strip()}'
53+
self.log_file.write(log_text + '\n')
54+
self.log_file.flush()
55+
self.log_widget.addItem(log_text)
56+
self.log_widget.scrollToBottom()
57+
58+
def handle_exception(self, exc_type, exc_value, exc_traceback):
59+
if issubclass(exc_type, KeyboardInterrupt):
60+
sys.__excepthook__(exc_type, exc_value, exc_traceback)
61+
return
62+
63+
error_message = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
64+
self.handle_logging(f"Unhandled exception: {error_message}")
65+
66+
print(f"Error: {error_message}")
67+
QTimer.singleShot(0, lambda: self.errorexec('Crashed!\nSee unlocker.log', 'Exit', exitApp=True))
68+
69+
def close(self):
70+
if self.log_file and not self.log_file.closed:
71+
self.log_file.close()
72+
73+
def errorexec(self, heading, btnOk, icon=":/icons/icons/1x/closeAsset 43.png", exitApp=False):
74+
print(f'Call errorexec: {heading, btnOk, icon, exitApp}')
75+
errorUi.errorConstrict(self.error, heading, icon, btnOk, None, exitApp)
76+
self.error.exec_()

UI/icons/stellaris.png

16.6 KB
Loading

0 commit comments

Comments
 (0)