-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.py
337 lines (260 loc) · 12.3 KB
/
client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
import customtkinter
import PIL
import data_backend
import client_details
import pgwrapper
import sys
import json
import os
import cache_utils
import imagetools
import webbrowser
customtkinter.set_appearance_mode("dark")
customtkinter.set_default_color_theme("blue")
app = customtkinter.CTk()
app.geometry("1490x700")
last_geometry = app.winfo_geometry()
scrollable_frame = customtkinter.CTkScrollableFrame(app, width=app.winfo_width(), height=app.winfo_height())
buttons = []
details_elements = []
non_disabled_entry_color = None
db = None # app data-backend (i.e. LocalFS or FTP)
CONFIG_FILE = "gamevault_config.json"
def close_input_window(input_window):
'''Close the config window and save the settings'''
# retrieve values #
entries = list(filter(lambda x: isinstance(x, (customtkinter.CTkEntry, customtkinter.CTkOptionMenu)), input_window.winfo_children()))
labels = [ input_window.grid_slaves(row=e.grid_info()["row"], column=e.grid_info()["column"]-1)[0] for e in entries ]
ret_dict = dict()
for e, l in zip(entries, labels):
ret_dict.update({ l.cget("text") : e.get() })
# dump config and write to file #
print(json.dumps(ret_dict))
with open(CONFIG_FILE, "w") as f:
json.dump(ret_dict, f, indent=2)
# quit input window #
input_window.update()
input_window.quit()
input_window.withdraw()
def dropdown_changed(dropdown_var, user_entry, password_entry, server_path_entry,
install_dir_entry):
global non_disabled_entry_color
# FIXME or not idfk #
if type(dropdown_var) != str:
dropdown_var = dropdown_var.get()
# Check the current value of the dropdown and enable/disable user and password inputs accordingly
if dropdown_var == "Local Filesystem":
user_entry.configure(state=customtkinter.DISABLED)
password_entry.configure(state=customtkinter.DISABLED)
non_disabled_entry_color = password_entry.cget("fg_color")
print(non_disabled_entry_color)
password_entry.configure(fg_color="#CCCCCC")
user_entry.configure(fg_color="#CCCCCC")
server_path_entry.delete(0, customtkinter.END)
server_path_entry.insert(0, "C:/path/to/game/mount/")
else:
user_entry.configure(state=customtkinter.NORMAL)
password_entry.configure(state=customtkinter.NORMAL)
if non_disabled_entry_color: # else first run and nothing to do
password_entry.configure(fg_color=non_disabled_entry_color)
user_entry.configure(fg_color=non_disabled_entry_color)
server_path_entry.delete(0, customtkinter.END)
server_path_entry.insert(0, "ftp://server:port/path or ftps://server:port/path")
install_dir_entry.delete(0, customtkinter.END)
install_dir_entry.insert(0, "./install-dir")
# Create a frame to hold the inputs
def get_config_inputs():
'''Create a config input window and use the settings from it'''
input_window = customtkinter.CTk()
input_window.geometry("500x250")
input_window.title("Vault Configuration")
# Server/Path
server_path_label = customtkinter.CTkLabel(input_window, text="Server/Path:")
server_path_label.grid(row=1, column=0, sticky="w", padx=10, pady=5)
server_path_entry = customtkinter.CTkEntry(input_window)
server_path_entry.grid(row=1, column=1, padx=10, pady=5, sticky="ew", columnspan=2)
# User
user_label = customtkinter.CTkLabel(input_window, text="User:")
user_label.grid(row=2, column=0, sticky="w", padx=10, pady=5)
user_entry = customtkinter.CTkEntry(input_window)
user_entry.grid(row=2, column=1, padx=10, pady=5)
# Password
password_label = customtkinter.CTkLabel(input_window, text="Password:")
password_label.grid(row=3, column=0, sticky="w", padx=10, pady=5)
password_entry = customtkinter.CTkEntry(input_window, show="*")
password_entry.grid(row=3, column=1, padx=10, pady=5)
# Install dir
install_dir_label = customtkinter.CTkLabel(input_window, text="Install dir:")
install_dir_label.grid(row=4, column=0, sticky="w", padx=10, pady=5)
install_dir_entry = customtkinter.CTkEntry(input_window)
install_dir_entry.grid(row=4, column=1, padx=10, pady=5, sticky="ew", columnspan=2)
# Dropdown
dropdown_var = customtkinter.StringVar(value="Local Filesystem")
dropdown_label = customtkinter.CTkLabel(input_window, text="Select option:")
dropdown_label.grid(row=0, column=0, sticky="w", padx=10, pady=5)
dropdown = customtkinter.CTkOptionMenu(input_window, variable=dropdown_var, values=["FTP/FTPS", "Local Filesystem"],
command=lambda dropdown_var=dropdown_var, user_entry=user_entry, password_entry=password_entry,
server_path_entry=server_path_entry, install_dir_entry=install_dir_entry:
dropdown_changed(dropdown_var, user_entry, password_entry, server_path_entry, install_dir_entry))
# set dropdown & field defaults
dropdown_changed(dropdown_var, user_entry, password_entry, server_path_entry, install_dir_entry)
dropdown.grid(row=0, column=1, padx=10, pady=5)
# Button to abort & close #
abort_button = customtkinter.CTkButton(input_window, text="Abort", command=lambda: input_window.quit(), fg_color="red")
abort_button.grid(row=5, column=0, padx=10, pady=20)
# Button to save & close #
save_button = customtkinter.CTkButton(input_window, text="Save & Close", command=lambda: close_input_window(input_window))
save_button.grid(row=5, column=2, padx=10, pady=20)
input_window.update()
input_window.mainloop()
def create_navbar():
'''Create basic navigation bar'''
# spawn frame at very top #
# add "Home" button
def switch_to_main():
'''Switch back to main view from details'''
global details_elements
global last_geometry
last_geometry = (0,0)
# destroy details elements #
for el in details_elements:
el.destroy()
details_elements = []
load_main()
def switch_to_game_details(software):
'''Switch to the details of the clicked tile'''
destroy_main()
load_details(app, software)
def load_main():
'''Load the main page overview'''
app.title("Lan Vault: Overview")
# navbar should not expand when window is resized
app.grid_rowconfigure(0, weight=0)
# buttongrid (scrollable frame) should expand when window is resized
app.grid_rowconfigure(1, weight=1)
app.grid_columnconfigure(0, weight=1)
# place scrollable frame
scrollable_frame.grid(row=1, column=0, sticky="nsew", columnspan=2)
# create tiles from meta files #
cache_dir_size = 0
for software in db.find_all_metadata():
create_main_window_tile(software, scrollable_frame)
# retrieve cache dir from any software #
if not cache_dir_size:
cache_dir_size = cache_utils.get_cache_size()
label = customtkinter.CTkLabel(app, text="Cache Size: {:.2f} GB".format(cache_dir_size))
GITHUB_URL = "https://github.com/FAUSheppy/homelab_gamevault"
github = customtkinter.CTkButton(app, text="Star on Github",
command=lambda: webbrowser.open_new(GITHUB_URL))
label.grid(row=0, column=0)
github.grid(row=0, column=1)
# set update listener & update positions #
update_button_positions()
app.bind("<Configure>", update_button_positions)
def destroy_main():
'''Destroy all elements in the main view'''
global buttons
scrollable_frame.grid_remove()
app.unbind("<Configure>")
for b in buttons:
b.destroy()
buttons = []
app.update()
def load_details(app, software):
'''Load the details page for a software'''
global details_elements
app.title("Lan Vault: {}".format(software.title))
details_elements = client_details.create_details_page(app, software, switch_to_main)
def create_main_window_tile(software, parent):
'''Create the main window tile'''
if software.get_thumbnail():
try:
print("Loading thumbnail:", software.get_thumbnail())
img = PIL.Image.open(software.get_thumbnail())
img = imagetools.smart_resize(img, 200, 300)
except PIL.UnidentifiedImageError:
print("Failed to load thumbnail:", software.get_thumbnail())
img = PIL.Image.new('RGB', (200, 300))
else:
img = PIL.Image.new('RGB', (200, 300))
img = PIL.ImageTk.PhotoImage(img)
button = customtkinter.CTkButton(parent, image=img,
width=200, height=300,
command=lambda: switch_to_game_details(software),
border_width=0, corner_radius=0, border_spacing=0,
text=software.title,
fg_color="transparent", compound="top", anchor="s")
buttons.append(button)
return button
def update_button_positions(event=None):
'''Sets the tile positions initially and on resize'''
global last_geometry
# check vs old location #
new_geometry = app.winfo_geometry()
if last_geometry[0] == new_geometry[0] and last_geometry[1] == new_geometry[1]:
return
else:
last_geometry = new_geometry
scrollable_frame.configure(width=app.winfo_width(), height=app.winfo_height())
# Calculate the number of columns based on the current width of the window #
num_columns = app.winfo_width() // 201 # Adjust 100 as needed for button width
# window became too small #
if num_columns == 0:
return
# calculated and set positions
for i, button in enumerate(buttons):
grid_info_current = button.grid_info()
column_new = i % num_columns
row_new = i // num_columns
if(grid_info_current.get("row") and grid_info_current["row"] == row_new
and grid_info_current["column"] == column_new):
continue
else:
DOWNSHIFT = 1 # FIXME make real navbar
button.grid(row=(i // num_columns)+ DOWNSHIFT, column=i % num_columns, sticky="we")
if __name__ == "__main__":
pgw = pgwrapper.ProgressBarWrapper()
pgw.new(app)
# define data backend #
#db = data_backend.LocalFS(None, None, "./install/", remote_root_dir="example_software_root")
# db = data_backend.FTP(None, None, "./install/", server="ftp://192.168.1.132:2121",
# remote_root_dir="/", progress_bar_wrapper=pgw, tkinter_root=app)
if not os.path.isfile(CONFIG_FILE):
get_config_inputs()
print("wtf")
# load config #
with open(CONFIG_FILE) as f:
config_loaded = json.load(f)
# load login & install dir #
user = config_loaded.get("User:")
password = config_loaded.get("Password:")
install_dir = config_loaded["Install dir:"]
backend_type = config_loaded["Select option:"]
# fix abs path if not set #
if os.path.abspath(install_dir):
install_dir = os.path.join(os.getcwd(), install_dir)
# get the secod part of the server string, then split once at first / to get path and prepend / again#
if backend_type == "FTP/FTPS":
remote_root_dir = "/" + config_loaded["Server/Path:"].split("://")[1].split("/", 1)[1]
server = config_loaded["Server/Path:"][:-len(remote_root_dir)]
elif backend_type == "Local Filesystem":
remote_root_dir = config_loaded["Server/Path:"]
server = None
else:
raise NotImplementedError("Unsupported Backend")
# debug output #
print(user, password, install_dir, remote_root_dir, server, config_loaded["Server/Path:"])
# add db backend #
if backend_type == "FTP/FTPS":
db = data_backend.FTP(user, password, install_dir, server=server,
remote_root_dir=remote_root_dir, progress_bar_wrapper=pgw, tkinter_root=app)
elif backend_type == "Local Filesystem":
db = data_backend.LocalFS(user, password, install_dir, server=server,
remote_root_dir=remote_root_dir, progress_bar_wrapper=pgw, tkinter_root=app)
else:
raise NotImplementedError("Unsupported Backend")
# geometry is set at the very beginning #
app.update()
# fill and run app #
load_main() # TODO add button to reopen config # TODO add button to purge cache/purge cache window # TODO show game size on remote
app.mainloop()