Skip to content

Commit a7bbc89

Browse files
committed
Added Qt GUI, server integration, migrate to pyinstaller & more
1 parent de4ea71 commit a7bbc89

File tree

11 files changed

+536
-64
lines changed

11 files changed

+536
-64
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
__pycache__/
22
build/
3+
dist/

asInvoker.manifest

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3+
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
4+
<security>
5+
<requestedPrivileges>
6+
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
7+
</requestedPrivileges>
8+
</security>
9+
</trustInfo>
10+
</assembly>

build.spec

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# -*- mode: python ; coding: utf-8 -*-
2+
3+
block_cipher = None
4+
5+
6+
a = Analysis(['update.py'],
7+
binaries=[],
8+
datas=[],
9+
hiddenimports=[],
10+
hookspath=[],
11+
runtime_hooks=[],
12+
excludes=['matplotlib', 'numpy'],
13+
win_no_prefer_redirects=False,
14+
win_private_assemblies=False,
15+
cipher=block_cipher,
16+
noarchive=False)
17+
pyz = PYZ(a.pure, a.zipped_data,
18+
cipher=block_cipher)
19+
exe = EXE(pyz,
20+
a.scripts,
21+
a.binaries,
22+
a.zipfiles,
23+
a.datas,
24+
[],
25+
name='FlashpointUpdater',
26+
debug=False,
27+
manifest='asInvoker.manifest',
28+
bootloader_ignore_signals=False,
29+
strip=False,
30+
upx=True,
31+
upx_exclude=[],
32+
runtime_tmpdir=None,
33+
console=True , icon='icon.ico')
34+
35+
b = Analysis(['update_gui.py'],
36+
binaries=[],
37+
datas=[('icon.png', '.')],
38+
hiddenimports=[],
39+
hookspath=[],
40+
runtime_hooks=[],
41+
excludes=['matplotlib', 'numpy'],
42+
win_no_prefer_redirects=False,
43+
win_private_assemblies=False,
44+
cipher=block_cipher,
45+
noarchive=False)
46+
pyz = PYZ(b.pure, b.zipped_data,
47+
cipher=block_cipher)
48+
exe = EXE(pyz,
49+
b.scripts,
50+
b.binaries,
51+
b.zipfiles,
52+
b.datas,
53+
[],
54+
name='FlashpointUpdaterQt',
55+
debug=False,
56+
manifest='asInvoker.manifest',
57+
bootloader_ignore_signals=False,
58+
strip=False,
59+
upx=True,
60+
upx_exclude=[],
61+
runtime_tmpdir=None,
62+
console=False , icon='icon.ico')

config.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"index_endpoint": "https://unstable.life/fp-index",
3-
"file_endpoint": "https://unstable.life/fp"
2+
"index_endpoint": "https://unstable.life/fp-index/",
3+
"file_endpoint": "https://unstable.life/fp/"
44
}

core.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
from collections import namedtuple
2+
from urllib.parse import urljoin
3+
import threading
4+
import requests
5+
import datetime
6+
import logging
7+
import queue
8+
import time
9+
import lzma
10+
import json
11+
import math
12+
13+
# Allows you to retrieve the arguments passed to a function and
14+
# an arbitrary value by passing a 'store' or '_store' argument
15+
# as its return value. Good for use with concurrent.futures.
16+
def wrap_call(function, *args, **kwargs):
17+
store = kwargs.pop('_store', kwargs.pop('store', None))
18+
Wrap = namedtuple('Wrap', ['result', 'store', 'args', 'kwargs'])
19+
return Wrap(function(*args, **kwargs), store, args, kwargs)
20+
21+
class IndexServer(object):
22+
# Index metadata schema:
23+
# { name.str: { path.str, lzma.bool, info.str }, ... }
24+
def __init__(self, endpoint):
25+
self.endpoint = endpoint
26+
r = requests.get(urljoin(self.endpoint, 'meta.json'))
27+
r.raise_for_status()
28+
self.meta = r.json()
29+
30+
def available_indexes(self):
31+
return self.meta['indexes'].keys()
32+
33+
def get_latest(self):
34+
return self.meta['latest']
35+
36+
def get_anchor(self):
37+
return self.meta.get('anchor', None)
38+
39+
def autodetect_anchor(self, anchor_hash):
40+
anchor = self.get_anchor()
41+
autodetect = dict()
42+
if anchor:
43+
autodetect = anchor['autodetect']
44+
return autodetect.get(anchor_hash, None)
45+
46+
def info(self, name):
47+
return self.meta['indexes'][name]['info']
48+
49+
def fetch(self, name, reporter, block_size=2048):
50+
index_meta = self.meta['indexes'][name]
51+
r = requests.get(urljoin(self.endpoint, index_meta['path']), stream=True)
52+
r.raise_for_status()
53+
data = bytearray()
54+
total_size = int(r.headers.get('content-length', 0))
55+
for chunk, report in reporter.task_it('Fetching index %s' % name, r.iter_content(block_size), length=math.ceil(total_size / block_size)):
56+
report()
57+
data += chunk
58+
if index_meta['lzma']:
59+
data = lzma.decompress(data)
60+
return json.loads(data)
61+
62+
class PoisonPill(object):
63+
pass
64+
65+
class Task(object):
66+
def __init__(self, title, unit, length):
67+
self.title = title
68+
self.unit = unit
69+
self.length = length
70+
71+
class ProgressReporter(object):
72+
def __init__(self, logger='reporter'):
73+
self._start = None
74+
self._stopped = False
75+
self._task = None
76+
self._report_event = threading.Event()
77+
self._step_queue = queue.Queue()
78+
self._task_queue = queue.Queue()
79+
self.logger = logging.getLogger(logger)
80+
81+
def stop(self):
82+
self._stopped = True
83+
self._step_queue.put(PoisonPill())
84+
self._task_queue.put(PoisonPill())
85+
86+
def is_stopped(self):
87+
return self._stopped
88+
89+
def task(self, title, unit=None, length=None):
90+
if self._stopped:
91+
raise ValueError('operation on stopped reporter')
92+
self._task = Task(title, unit, length)
93+
self._task_queue.put(self._task)
94+
if not self._start:
95+
self._start = time.time()
96+
else:
97+
self._step_queue.put(PoisonPill())
98+
99+
def task_it(self, title, iterator, unit=None, length=None):
100+
if not length:
101+
length = len(iterator) if iterator else None
102+
self.task(title, unit=unit, length=length)
103+
for item in iterator:
104+
if self._stopped:
105+
raise ValueError('operation on stopped reporter')
106+
yield item, self.report
107+
if not self._report_event.isSet():
108+
raise RuntimeError('report not called in previous iteration')
109+
self._report_event.clear()
110+
111+
def report(self, payload=None):
112+
if not self._report_event.isSet():
113+
self._step_queue.put(payload)
114+
self._report_event.set()
115+
116+
def get_current_task(self):
117+
return self._task
118+
119+
def tasks(self):
120+
while True:
121+
payload = self._task_queue.get()
122+
if isinstance(payload, PoisonPill):
123+
break
124+
self.logger.info('*** Task Start: %s ***' % payload.title)
125+
yield payload
126+
self.logger.info('*** Task End: %s ***' % payload.title)
127+
128+
def steps(self):
129+
while True:
130+
payload = self._step_queue.get()
131+
if isinstance(payload, PoisonPill):
132+
break
133+
#self.logger.info('Step > %s' % payload)
134+
yield payload
135+
136+
def step(self, payload=None):
137+
if self._stopped:
138+
raise ValueError('operation on stopped reporter')
139+
self._step_queue.put(payload)
140+
141+
def elapsed(self):
142+
elapsed = 0
143+
if self._start:
144+
elapsed = time.time() - self._start
145+
return str(datetime.timedelta(seconds=elapsed))

icon.ico

167 KB
Binary file not shown.

icon.png

28.4 KB
Loading

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
requests
22
backoff
33
tqdm
4+
pyqt5

setup.py

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)