-
Notifications
You must be signed in to change notification settings - Fork 0
/
Local Network File Share.py
339 lines (289 loc) · 14 KB
/
Local Network File Share.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
338
339
'''
A simple UI application for transfering files inside a localized network. Uses a simple pyhton http server, or any other http server
to download any files from it. Works on Android phones too, even though the UI is a bit less optimized for it.
'''
import customtkinter
import tkinter
import requests
from requests.exceptions import ConnectTimeout
import wget
import json
import os
import sys
from bs4 import BeautifulSoup
customtkinter.set_appearance_mode("dark")
customtkinter.set_default_color_theme("dark-blue")
class UI:
SERVER_IP = None
SERVER_PORT = None
FULL_LINK = None
SYSTEM = None
SCROLLABLE_FRAME = None
IP_LABEL = None
PORT_LABEL = None
SELECT_ALL_BUTTON = None
BACK_BUTTON = None
OPTIONS_WINDOW = None
REQUEST_TIMEOUT = 1.0
SELECTED_ALL = False
NEW_FOLDER_LOADED = False
DEFAULT_LINK = None
variable_list = []
checkboxes = []
checked_boxes = []
links = []
download_location = '.'
new_ip_textbox = None
new_port_textbox = None
request_time_limit_textbox = None
def __init__(self):
super().__init__()
self.ROOT = customtkinter.CTk()
self.ROOT.title("Local Network File Sharing Client")
self.ROOT.geometry("1000x450")
self.ROOT.resizable(False, False)
self.ROOT.protocol("WM_DELETE_WINDOW", self.safe_close)
self.system_info()
if os.path.exists('config'):
self.load_configuration()
else:
self.write_default_configuration()
self.create_widgets()
def __repr__(self):
try:
return f"Local Network File Sharing Class:\n\tIP:{self.SERVER_IP}\n\tPORT:{self.SERVER_PORT}\n\tSYSTEM:{self.SYSTEM}"
except:
raise SystemError
# Writes default config file if it isn't present
def write_default_configuration(self):
with open('config', 'a+') as f:
data = {
"SERVER_IP": "localhost",
"SERVER_PORT": 8080
}
json.dump(data, f)
return
# Loads from the config file
def load_configuration(self):
with open('config', 'r') as f:
data = json.load(f)
self.SERVER_IP = data['SERVER_IP']
self.SERVER_PORT = data['SERVER_PORT']
return
# Assigns System info
def system_info(self):
if sys.platform.startswith('linux'):
self.SYSTEM = 'Lin'
elif sys.platform.startswith('win32'):
self.SYSTEM = 'Win'
self.ROOT.after(501, lambda :self.ROOT.iconbitmap('Logo.ico'))
elif sys.platform.startswith('darwin'):
self.SYSTEM = 'mOS'
elif hasattr(sys, 'getandroidapilevel'):
self.SYSTEM = 'And'
else:
self.SYSTEM = None
raise SystemExit
# Creates main window widgets
def create_widgets(self):
self.IP_LABEL = customtkinter.CTkLabel(self.ROOT, text=f"Current Session IP: {self.SERVER_IP}")
self.IP_LABEL.place(relx=0.02, rely=0.05, anchor=customtkinter.W)
self.PORT_LABEL = customtkinter.CTkLabel(self.ROOT, text=f"Current Session PORT: {self.SERVER_PORT}")
self.PORT_LABEL.place(relx=0.02, rely=0.1, anchor=customtkinter.W)
load_content_button = customtkinter.CTkButton(self.ROOT, width=200, text='Load Content', command=self.load_content)
load_content_button.place(relx=0.02, rely=0.2, anchor=customtkinter.W)
self.SCROLLABLE_FRAME = customtkinter.CTkScrollableFrame(self.ROOT, width=450, height=420)
self.SCROLLABLE_FRAME.place(relx=0.5, rely=0.01, anchor=customtkinter.NW)
download_button = customtkinter.CTkButton(self.ROOT, width=200, text='Download', command=self.download)
download_button.place(relx=0.02, rely=0.3, anchor=customtkinter.W)
location_button = customtkinter.CTkButton(self.ROOT, width=200, text='Set Download Location', command=self.set_download_location)
location_button.place(relx=0.02, rely=0.4, anchor=customtkinter.W)
self.SELECT_ALL_BUTTON = customtkinter.CTkButton(self.ROOT, width=200, text='Select All', command=self.select_all)
self.SELECT_ALL_BUTTON.place(relx=0.02, rely=0.5, anchor=customtkinter.W)
options_button = customtkinter.CTkButton(self.ROOT, width=200, text='Options', command=self.options)
options_button.place(relx=0.02, rely=0.6, anchor=customtkinter.W)
self.BACK_BUTTON = customtkinter.CTkButton(self.ROOT, width=200, text='Back', command=self.load_back)
# Sends the request to the server, and retrieves its contents
def request_content(self):
try:
r = requests.get(self.FULL_LINK, timeout=self.REQUEST_TIMEOUT)
if r.status_code == 200:
return r.text
else:
return None
except ConnectTimeout:
return None
# Loads, sorts, and figures out the data received from the server
def load_content(self, suffix="", new_full_link=None):
self.clear_loaded_content()
if new_full_link == None:
if self.DEFAULT_LINK == None:
self.DEFAULT_LINK = "http://"+str(self.SERVER_IP)+":"+str(self.SERVER_PORT)
self.FULL_LINK = "http://"+str(self.SERVER_IP)+":"+str(self.SERVER_PORT)+suffix
else:
if new_full_link[-1] == '/':
new_full_link = new_full_link[:-1]
self.FULL_LINK = new_full_link+suffix
if self.FULL_LINK[-1] != '/':
self.FULL_LINK = self.FULL_LINK+'/'
text = self.request_content()
if text == None:
return
soup = BeautifulSoup(text, features='html.parser')
links_list = soup.find_all('a')
self.links = []
for link in links_list:
self.links.append(link.text)
self.display_links()
# Displays links in the Scrollable frame after its loaded
def display_links(self):
for link in self.links:
check_var = tkinter.IntVar()
checkbox = customtkinter.CTkCheckBox(self.SCROLLABLE_FRAME, text=link, variable=check_var, onvalue=1, offvalue=0, command=self.folder_or_file)
checkbox.pack(padx=5, pady=5)
self.variable_list.append(check_var)
self.checkboxes.append(checkbox)
self.SCROLLABLE_FRAME._parent_canvas.yview_moveto(0)
# Clears loaded content, this is neccessary for reloading
def clear_loaded_content(self):
for checkbox,var in zip(self.checkboxes, self.variable_list):
checkbox.destroy()
del(var)
self.checkboxes.clear()
self.variable_list.clear()
self.checked_boxes.clear()
# Changes the source link, and loads new data from it
def reload_new_link(self, link):
self.links.clear()
self.clear_loaded_content()
self.load_content(suffix='/'+link, new_full_link=self.FULL_LINK)
self.NEW_FOLDER_LOADED = True
self.back_button_show()
# Displays the back button, after you move to a folder down the tree
def back_button_show(self):
if self.NEW_FOLDER_LOADED == True:
self.BACK_BUTTON.place(relx=0.02, rely=0.8, anchor=customtkinter.W)
# Figures out if you selected a file or a folder, and does the appropriate action
def folder_or_file(self):
for var in self.variable_list:
if var.get():
index = self.variable_list.index(var)
if self.links[index].endswith('/'):
self.reload_new_link(self.links[index])
return
if self.links[index].endswith('@'):
self.reload_new_link(self.links[index].replace('@', ''))
return
self.update_checked()
# Goes back after you click the back button
def load_back(self):
if self.NEW_FOLDER_LOADED == False:
return
else:
if self.FULL_LINK == self.DEFAULT_LINK:
self.NEW_FOLDER_LOADED = False
return
if self.FULL_LINK.endswith('/'):
self.FULL_LINK = self.FULL_LINK[:-1].rsplit('/', 1)[0]
if self.FULL_LINK == self.DEFAULT_LINK:
self.NEW_FOLDER_LOADED = False
self.load_content(suffix='',new_full_link=self.FULL_LINK)
# This just keeps track of the clicked checkboxes and updates the list of them accordingly
def update_checked(self):
self.checked_boxes = []
for var in self.variable_list:
if var.get():
index = self.variable_list.index(var)
if self.links[index] not in self.checked_boxes and not self.links[index].endswith('/'):
self.checked_boxes.append(self.links[index])
# Used for setting the download location
def set_download_location(self):
new_location = customtkinter.filedialog.askdirectory(mustexist=True)
if new_location != None and new_location != ():
self.download_location = new_location
# Downloads the selected files after you press the Download button
def download(self):
for checked_item in self.checked_boxes:
if checked_item.endswith('/'):
continue
item_link = self.FULL_LINK+'/'+checked_item
wget.download(item_link, self.download_location)
# Selects all * files *. This only selects downloadable content, and will skip any and all folders
def select_all(self):
if len(self.checkboxes) > 0:
if self.SELECTED_ALL == False:
for checkbox in self.checkboxes:
if not checkbox.cget('text').endswith('/') and not checkbox.cget('text').endswith('@'):
checkbox.select()
self.SELECT_ALL_BUTTON.configure(text='Deselect All')
self.SELECTED_ALL = True
self.update_checked()
else:
for checkbox in self.checkboxes:
checkbox.deselect()
self.SELECT_ALL_BUTTON.configure(text='Select All')
self.SELECTED_ALL = False
self.update_checked()
# Initiator function for the Options window
def options(self):
self.OPTIONS_WINDOW = customtkinter.CTkToplevel(self.ROOT)
self.OPTIONS_WINDOW.title("Options")
self.OPTIONS_WINDOW.geometry("400x200")
self.OPTIONS_WINDOW.resizable(False, False)
new_ip_label = customtkinter.CTkLabel(self.OPTIONS_WINDOW, text="Enter the new IP Address:")
new_ip_label.place(relx=0.02, rely=0.1, anchor=customtkinter.W)
self.new_ip_textbox = customtkinter.CTkTextbox(self.OPTIONS_WINDOW, height=20, width=130)
self.new_ip_textbox.place(relx=0.02, rely=0.25, anchor=customtkinter.W)
self.new_ip_textbox.bind('<KeyRelease>', lambda event:self.limit_input(self.new_ip_textbox, type="IP"))
new_port_label = customtkinter.CTkLabel(self.OPTIONS_WINDOW, text="Enter the new PORT:")
new_port_label.place(relx=0.02, rely=0.4, anchor=customtkinter.W)
self.new_port_textbox = customtkinter.CTkTextbox(self.OPTIONS_WINDOW, height=20, width=55)
self.new_port_textbox.place(relx=0.02, rely=0.55, anchor=customtkinter.W)
self.new_port_textbox.bind('<KeyRelease>', lambda event:self.limit_input(self.new_port_textbox, type='PORT'))
options_button_update = customtkinter.CTkButton(self.OPTIONS_WINDOW, width=200, text="Update", command=self.update_source)
options_button_update.place(relx=0.02, rely=0.8, anchor=customtkinter.W)
request_time_limit_label = customtkinter.CTkLabel(self.OPTIONS_WINDOW, text="Request Time Limit (Default is 1)")
request_time_limit_label.place(relx=0.4, rely=0.4, anchor=customtkinter.W)
self.request_time_limit_textbox = customtkinter.CTkTextbox(self.OPTIONS_WINDOW, height=20, width=50)
self.request_time_limit_textbox.place(relx=0.4, rely=0.55, anchor=customtkinter.W)
self.request_time_limit_textbox.bind('<KeyRelease>', lambda event:self.limit_input(self.request_time_limit_textbox, type='RATE'))
# Limits the allowed characters that can be entered in the textboxes in options window
def limit_input(self, textbox, type=None):
if type == "PORT":
if len(textbox.get('0.0', customtkinter.END)) > 5:
textbox.delete('end-2c', 'end')
elif type == "IP":
if len(textbox.get('0.0', customtkinter.END)) > 16:
textbox.delete('end-2c', 'end')
elif type == "RATE":
if len(textbox.get('0.0', customtkinter.END)) > 4:
textbox.delete('end-2c', 'end')
# Used to apply changes from the options window
def update_source(self):
if len(self.new_ip_textbox.get('0.0', customtkinter.END).strip()) == 0:
self.SERVER_IP = self.SERVER_IP
else:
self.SERVER_IP = self.new_ip_textbox.get('0.0', customtkinter.END).strip()
self.update_labels()
if len(self.new_port_textbox.get('0.0', customtkinter.END).strip()) == 0:
self.SERVER_PORT = self.SERVER_PORT
else:
self.SERVER_PORT = self.new_port_textbox.get('0.0', customtkinter.END).strip()
self.update_labels()
if len(self.request_time_limit_textbox.get('0.0', customtkinter.END).strip()) == 0:
self.REQUEST_TIMEOUT = self.REQUEST_TIMEOUT
else:
self.REQUEST_TIMEOUT = float(self.request_time_limit_textbox.get('0.0', customtkinter.END).strip())
# Updates labels in the original window after changes happen in the options window
def update_labels(self):
self.IP_LABEL.configure(text=f"Current Session IP: {self.SERVER_IP}")
self.PORT_LABEL.configure(text=f"Current Session PORT: {self.SERVER_PORT}")
# Maybe not a neccessary function but I found it makes closing the app much safer.
def safe_close(self):
if self.OPTIONS_WINDOW != None:
self.OPTIONS_WINDOW.destroy()
self.ROOT.destroy()
raise SystemExit
if __name__ == "__main__":
defaultUI = UI()
defaultUI.ROOT.mainloop()