├── master-record.txt ├── config.ini ├── main.png ├── README.md └── Infinite Cloud Storage via Discord.py /master-record.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | webhook_url = none -------------------------------------------------------------------------------- /main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/norangeflame/infinite-cloud-storage/HEAD/main.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Infinite Cloud Storage // Version 2.0 2 | ### currently broken/outdated 3 | ### Description 4 | - Infinite cloud storage utilises Discord servers for file storage. It also has a GUI to upload, download or delete files. 5 | - There are most likely still bugs, I will update over the coming weeks. 6 | - Inspired by DvorakDwarf's Infinite Storage Glitch. 7 | 8 | ### Features 9 | - [x] Upload and download any file or folder, regardless of filesize, via a simple graphical interface. 10 | - [x] Folders are automatically compressed (.tar.bz2) for easy upload. 11 | - [x] Files and folders larger than the 25MB Discord limit are automatically split when uploaded. 12 | - [x] Files and folders that are split are automatically joined back together when downloaded. This process _shouldn't_ corrupt any data. 13 | - [x] Delete files from the record. 14 | - [x] Status indicator that can show download speed. 15 | - [x] Proper config GUI, so you don't have to enter in URLS directly into the code. 16 | - [ ] PLANNED: Upload speed indicator. 17 | - [ ] PLANNED: Encryption. 18 | 19 | ### Instructions & Requirements 20 | - You require a Discord Webhook (and the URL). Tutorial [here](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks). 21 | - You need to install the python library "discord-webhook" via `pip install discord-webhook` in your command line (unless you run via compiled executable). 22 | - You must enter the webhook URL into the "config" section of the application. Press the "config" button to access this. 23 | 24 | 25 | ___ 26 | norangeflame 27 | -------------------------------------------------------------------------------- /Infinite Cloud Storage via Discord.py: -------------------------------------------------------------------------------- 1 | ### Unlimited Cloud Storage via Discord ### 2 | #------------------------------------------ 3 | # Usage: 4 | # - Enter the webhook URL into the "config" section of the application. Press the "config" button to access this. 5 | # - NOTE: Don't modify the TXT file, it acts as a log for files so they can be downloaded/uploaded correctly 6 | # 7 | # All coding by norangeflame 8 | 9 | from discord_webhook import DiscordWebhook #NEED TO INSTALL (run in command prompt: pip install discord-webhook) 10 | import tkinter as tk 11 | from tkinter import filedialog 12 | import os 13 | import requests 14 | import json 15 | import subprocess 16 | import time 17 | import configparser 18 | import re 19 | 20 | #variables 21 | r_config = configparser.ConfigParser() 22 | r_config.sections() 23 | r_config.read('config.ini') 24 | #token = r_config['DEFAULT' ]['token'] 25 | wbhkurl = r_config['DEFAULT' ]['webhook_url'] 26 | #channelId = r_config['DEFAULT' ]['channelId'] 27 | 28 | webhook = DiscordWebhook(url=wbhkurl, username="Cloud Storage Webhook") 29 | 30 | 31 | master = 'master-record.txt' 32 | limit = 100 33 | parts = 0 34 | chunk_size = 24 * 1024 * 1024 #24Mb; Discord limit = 25Mb, so I put 24 to be safe 35 | urls = [] 36 | ffi = 0 37 | g_progress = '' 38 | g_dwl_status = '' 39 | g_status = '' 40 | cs = 10 * 1024 * 1024 #10Mb 41 | units_size = 1024 * 1024 #1Mb 42 | units = 'Mb/s' 43 | config_token = '' 44 | config_wbhkurl = '' 45 | config_channelId = '' 46 | 47 | 48 | if not os.path.exists(master): 49 | f = open(master, "a") 50 | f.write('') 51 | f.close() 52 | 53 | 54 | #---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 55 | 56 | 57 | def update_main_status(labeltext): 58 | global g_status 59 | g_status.config(text=labeltext) 60 | g_status.update_idletasks() 61 | 62 | def upload_file_dialog(): 63 | update_main_status('Choosing file...') 64 | try: 65 | file = filedialog.askopenfilename() 66 | filename = os.path.basename(file) 67 | finfo = os.stat(file) 68 | print('Selected file:', file) 69 | except FileNotFoundError: 70 | tk.messagebox.showerror(title='Error', message='Invalid file name or file doesn\'t exist.') 71 | update_main_status('Ready') 72 | return 73 | update_main_status('Uploading...') 74 | if checkifduplicate(filename, master): 75 | print('The filename is already present in the master record.') 76 | tk.messagebox.showerror(title='Error', message='This file has already been uploaded.') 77 | 78 | else: 79 | print('The filename is not present in the master record.') 80 | 81 | if finfo.st_size <= chunk_size: #1024 * 1024 * 18 82 | upload_file(file) 83 | 84 | else: 85 | num_parts = split_file(file, chunk_size) #splits 86 | part_one = True 87 | #part_one is if the file being uploaded is the first part (part 1). 88 | #if it is, then the upload_file() function will add the filename to the master record, with the tag [SPLIT]. 89 | #after that, part_one is set to False, so the filename is not added to the record everytime another part is uploaded 90 | for part_no in range(num_parts): 91 | num = part_no + 1 92 | file_parts = f'{file}.part{num}' 93 | upload_file(file_parts) 94 | part_one = False 95 | del num 96 | labeltext = 'Ready' 97 | update_main_status('Ready') 98 | return 99 | 100 | def upload_folder_dialog(): 101 | update_main_status('Choosing folder...') 102 | foldername = filedialog.askdirectory() 103 | if foldername == '': 104 | tk.messagebox.showerror(title='Error', message='Invalid folder name or folder doesn\'t exist.') 105 | update_main_status('Ready') 106 | return 107 | print('Selected folder:', foldername) 108 | tarball_path = f'{foldername}.tar' 109 | update_main_status('Compressing folder...') 110 | tar_command = ['tar', '-cf', tarball_path, '-C', foldername, '.'] 111 | subprocess.run(tar_command) 112 | 113 | compressed_tarball_path = f'{foldername}.tar.bz2' 114 | 115 | bzip2_command = ['bzip2', tarball_path, '-c', '>', compressed_tarball_path] 116 | subprocess.run(' '.join(bzip2_command), shell=True) 117 | 118 | finfo = os.stat(compressed_tarball_path) 119 | update_main_status('Uploading folder...') 120 | if finfo.st_size <= chunk_size: #1024 * 1024 * 18 121 | upload_file(compressed_tarball_path) 122 | 123 | else: 124 | num_parts = split_file(compressed_tarball_path, chunk_size) #splits 125 | #part_one is if the file being uploaded is the first part (part 1). 126 | #if it is, then the upload_file() function will add the filename to the master record, with the tag [SPLIT]. 127 | #after that, part_one is set to False, so the filename is not added to the record everytime another part is uploaded 128 | for part_no in range(num_parts): 129 | num = part_no + 1 130 | file_parts = f'{compressed_tarball_path}.part{num}' 131 | upload_file(file_parts) 132 | del num 133 | #upload_file(compressed_tarball_path, False, False) 134 | update_main_status('Ready') 135 | tk.messagebox.showinfo(title='Message', message='Folders are compressed before uploading, and will have a ".tar.bz2" file extension. They are automatically decompressed when downloaded.') 136 | os.remove(tarball_path) 137 | os.remove(compressed_tarball_path) 138 | 139 | return 140 | 141 | 142 | 143 | def download_dialog(): 144 | #global since its used in another function 145 | global g_filebrowse 146 | global g_dwl_status 147 | 148 | dwl = tk.Tk() 149 | dwl.title('Download a file/folder') 150 | dwl.config(bg='#1c1c1c') 151 | dwl.resizable(False, False) 152 | g_frame = tk.Frame(dwl, bg='#1c1c1c') 153 | g_title = tk.Label(dwl, text='Download file', width=50, height=1, fg='#dedede', bg='#141414', font='fixedsys') 154 | g_title.pack() 155 | 156 | #for listbox 157 | lines = [] 158 | with open(master, "r") as file: 159 | lines = file.readlines() 160 | 161 | #NOTES: 162 | #Only add the .part1 to this list (so you dont have repeats of all the parts). Strip the ".part1" when displayed, and when downloading add the ".part1" back 163 | g_filebrowse = tk.Listbox(dwl, height=20, width=50, selectmode='SINGLE', fg='#dedede', bg='#141414', font='fixedsys') 164 | index = 0 165 | for line in lines: 166 | index = index + 1 167 | if '.tar.bz2' in line: 168 | if '.tar.bz2.part' in line: 169 | if '.tar.bz2.part1' in line: #.tar.bz2.part1 170 | foldername = line 171 | foldername = foldername.replace('.part1', '') 172 | foldername = os.path.basename(foldername.strip()) 173 | foldername = f'{foldername} ' 174 | g_filebrowse.insert(index, foldername) 175 | else: #.tar.bz2.part'x' 176 | foldername = line 177 | foldername = os.path.basename(foldername.strip()) 178 | else: #.tar.bz2 179 | foldername = os.path.basename(line.strip()) 180 | foldername = f'{foldername} ' 181 | g_filebrowse.insert(index, foldername) 182 | elif '.part1' in line: 183 | filename = line 184 | filename = filename.replace('.part1', '') 185 | filename = os.path.basename(filename.strip()) 186 | g_filebrowse.insert(index, filename) 187 | elif not '.part' in line: 188 | filename = os.path.basename(line.strip()) 189 | g_filebrowse.insert(index, filename) 190 | 191 | 192 | 193 | g_filebrowse.pack() 194 | 195 | g_dwl_status = tk.Label(g_frame, text='Ready', width=50, height=1, fg='#dedede', bg='#141414', font='fixedsys') 196 | 197 | 198 | g_dwl_file_sel = tk.Button(g_frame, text='Download', command=dwl_file_sel, fg='#dedede', bg='#262626', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 199 | g_dwl_file_sel.pack() 200 | g_dwl_status.pack() 201 | g_frame.pack() 202 | 203 | return 204 | 205 | def delete_file_folder_dialog(): 206 | global g_deletebrowse 207 | global delete 208 | 209 | delete = tk.Tk() 210 | delete.title('Delete a file/folder') 211 | 212 | delete.config(bg='#1c1c1c') 213 | delete.resizable(False, False) 214 | g_title = tk.Label(delete, text='Delete file', width=50, height=1, fg='#dedede', bg='#141414', font='fixedsys') 215 | g_title.pack() 216 | 217 | #listbox 218 | lines = [] 219 | with open(master, "r") as file: 220 | lines = file.readlines() 221 | 222 | g_deletebrowse = tk.Listbox(delete, height=20, width=50, selectmode='SINGLE', fg='#dedede', bg='#141414', font='fixedsys') 223 | index = 0 224 | 225 | for line in lines: 226 | index = index + 1 227 | if '.tar.bz2' in line: 228 | if '.tar.bz2.part' in line: 229 | if '.tar.bz2.part1' in line: #.tar.bz2.part1 230 | foldername = line 231 | foldername = foldername.replace('.part1', '') 232 | foldername = os.path.basename(foldername.strip()) 233 | foldername = f'{foldername} ' 234 | g_deletebrowse.insert(index, foldername) 235 | else: #.tar.bz2.part'x' 236 | foldername = line 237 | foldername = os.path.basename(foldername.strip()) 238 | else: #.tar.bz2 239 | foldername = os.path.basename(line.strip()) 240 | foldername = f'{foldername} ' 241 | g_deletebrowse.insert(index, foldername) 242 | 243 | elif '.part1' in line: 244 | filename = line 245 | filename = filename.replace('.part1', '') 246 | filename = os.path.basename(filename.strip()) 247 | g_deletebrowse.insert(index, filename) 248 | elif not '.part' in line: 249 | filename = os.path.basename(line.strip()) 250 | g_deletebrowse.insert(index, filename) 251 | 252 | 253 | 254 | g_deletebrowse.pack() 255 | 256 | g_del_file_sel = tk.Button(delete, text='Delete', command=del_file_sel, fg='#dedede', bg='#262626', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 257 | g_del_file_sel.pack() 258 | 259 | 260 | def checkifduplicate(filename, file_path): 261 | with open(file_path, "r") as file: 262 | for line in file: 263 | if line.strip() == filename: 264 | return True 265 | return False 266 | 267 | 268 | 269 | #---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 270 | 271 | 272 | def update_dwl_status(text): 273 | g_dwl_status.config(text=text) 274 | g_dwl_status.update_idletasks() 275 | 276 | 277 | #get selected FILE or FOLDER (STORED AS A FILE) to pass to the find function 278 | def dwl_file_sel(): 279 | update_dwl_status('Downloading...') 280 | update_main_status('Downloading...') 281 | for i in g_filebrowse.curselection(): 282 | print(g_filebrowse.get(i)) 283 | dwl_file_sel = g_filebrowse.get(i) 284 | if '' in dwl_file_sel: 285 | dwl_file_sel = dwl_file_sel.replace(' ', '') 286 | print(dwl_file_sel) 287 | find_split(dwl_file_sel) 288 | print(urls) 289 | for each_url in urls: 290 | download_file(each_url) 291 | num_parts = len(urls) 292 | if num_parts > 1: 293 | basename = dwl_file_sel 294 | join_files(basename, num_parts) 295 | 296 | update_dwl_status('Ready') 297 | update_main_status('Ready') 298 | return 299 | 300 | 301 | 302 | def upload_file(file): 303 | try: 304 | filename = os.path.basename(file) 305 | filename = filename.replace(' ', '_') #discord changes spaces to underscores _ 306 | filename = filename.replace('!', '') #discord strips most special characters 307 | filename = filename.replace('@', '') #discord strips most special characters 308 | filename = filename.replace('#', '') #discord strips most special characters 309 | filename = filename.replace('$', '') #discord strips most special characters 310 | filename = filename.replace('%', '') #discord strips most special characters 311 | filename = filename.replace('^', '') #discord strips most special characters 312 | filename = filename.replace('&', '') #discord strips most special characters 313 | filename = filename.replace('*', '') #discord strips most special characters 314 | filename = filename.replace('(', '') #discord strips most special characters 315 | filename = filename.replace(')', '') #discord strips most special characters 316 | filename = filename.replace('=', '') #discord strips most special characters 317 | filename = filename.replace('+', '') #discord strips most special characters 318 | filename = filename.replace('[', '') #discord strips most special characters 319 | filename = filename.replace(']', '') #discord strips most special characters 320 | filename = filename.replace('{', '') #discord strips most special characters 321 | filename = filename.replace('}', '') #discord strips most special characters 322 | filename = filename.replace(';', '') #discord strips most special characters 323 | filename = filename.replace(',', '') #discord strips most special characters 324 | 325 | 326 | print(f'Uploading "{file}"') 327 | with open(file, 'rb') as f: 328 | 329 | webhook = DiscordWebhook(url=wbhkurl, content=filename) 330 | webhook.add_file(file=f.read(), filename=filename) 331 | response = webhook.execute() 332 | 333 | print('Successfully uploaded') 334 | response = json.loads(response.text) 335 | print(response) 336 | url = response['attachments'][0]['url'] 337 | print(url) 338 | with open(master, 'a') as m: 339 | m.write(f'{url}\n') #write the full URL to the master record, and start a new line 340 | 341 | #OLD FUNCTION: 342 | #add to MASTER RECORD (which is a txt file located in the same directory as the script) 343 | #if multiple == True: 344 | #if p_one == True: 345 | #with open(master, 'a') as m: 346 | #filename = filename.replace('.part1', '') #assume its .part1, since its the first iteration (hence "if p_one == True:") 347 | #m.write(url + ' \n') #write the URL to the master record, add the 348 | #m.write(f'{url}\n') #write the full URL to the master record, and start a new line 349 | #else: 350 | #print('Skip writing to master record') 351 | #do nothing section. this is because it is uploading a part of a file which isnt the first, which means its already in the master record. 352 | #else: 353 | #with open(master, 'a') as m: 354 | #m.write(filename + '\n') 355 | return 356 | return 357 | 358 | 359 | except: 360 | print(f'There was an error uploading "{file}" to the cloud. Please check your connection and try again.') 361 | return 362 | 363 | 364 | def download_file(url): 365 | headers = { 366 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36' 367 | } 368 | response = requests.get(url, headers=headers, stream=True) 369 | urlfilename = os.path.basename(url) 370 | print(urlfilename) 371 | 372 | total_size = int(response.headers.get('content-length', 0)) 373 | downloaded_size = 0 374 | start_time = time.time() 375 | 376 | with open(urlfilename, 'wb') as f: 377 | for chunk in response.iter_content(chunk_size=cs): 378 | if chunk: 379 | f.write(chunk) 380 | downloaded_size = downloaded_size + len(chunk) 381 | elapsed_time = time.time() - start_time 382 | speed = downloaded_size / elapsed_time 383 | speed = speed / units_size 384 | speed = round(speed) 385 | mb_size = downloaded_size / units_size 386 | mb_tot_size = total_size / units_size 387 | mb_size = round(mb_size, 2) 388 | mb_tot_size = round(mb_tot_size, 2) 389 | print(f'{speed} {units} - {mb_size}/{mb_tot_size}MB') 390 | update_main_status(f'{speed}{units} ({mb_size}/{mb_tot_size}MB)') 391 | update_dwl_status(f'{speed}{units} ({mb_size}/{mb_tot_size}MB)') 392 | 393 | 394 | 395 | 396 | strippedname = urlfilename.replace('.tar.bz2', '') 397 | if '.tar.bz2' in urlfilename and not '.tar.bz2.part' in urlfilename: 398 | print('Decompressing folder') 399 | os.makedirs(strippedname, exist_ok=True) 400 | tarball_path = urlfilename 401 | 402 | tar_command = ['tar', '-xvjf', tarball_path, '-C', strippedname] 403 | subprocess.run(tar_command) 404 | os.remove(tarball_path) 405 | update_main_status('Ready') 406 | update_dwl_status('Ready') 407 | elif '.part' in urlfilename: 408 | print('Downloaded; not opening PART file') 409 | else: 410 | #os.system(urlfilename) 411 | print('File downloaded') 412 | update_main_status('Ready') 413 | update_dwl_status('Ready') 414 | return 415 | 416 | 417 | 418 | #finds the attachment URL to pass to the download_file function 419 | def find_split(filename): 420 | global urls #the URL array 421 | global ffi 422 | urls = [] 423 | file_found = False #file not found yet 424 | ffi = 0 425 | lines = [] 426 | with open(master, "r") as file: 427 | lines = file.readlines() 428 | 429 | index = 0 430 | for line in lines: 431 | index = index + 1 432 | var = os.path.basename(line.strip()) 433 | var = re.sub(r'\.part\d+', '', var) 434 | print(var + '== ' + filename.strip() + '?') 435 | if filename.strip() == var: 436 | urls.append(line.strip()) 437 | return urls 438 | 439 | def split_file(filename, chunk_size): 440 | with open(filename, 'rb') as f: 441 | part_num = 0 442 | while True: 443 | chunk = f.read(chunk_size) 444 | if not chunk: 445 | break 446 | part_num += 1 447 | part_filename = f'{filename}.part{part_num}' 448 | with open(part_filename, 'wb') as part_file: 449 | part_file.write(chunk) 450 | 451 | return part_num 452 | 453 | 454 | def join_files(filename, num_parts): 455 | update_main_status('Combining files...') 456 | update_dwl_status('Combining files...') 457 | with open(filename, 'wb') as f: 458 | for part_num in range(1, num_parts + 1): 459 | part_filename = f'{filename}.part{part_num}' 460 | with open(part_filename, 'rb') as part_file: 461 | f.write(part_file.read()) 462 | #Remove the part file after joining 463 | os.remove(part_filename) 464 | strippedname = filename.replace('.tar.bz2', '') 465 | filename = os.path.basename(filename.strip()) 466 | if '.tar.bz2' in filename: 467 | print('Decompressing folder') 468 | os.makedirs(strippedname, exist_ok=True) 469 | tarball_path = filename 470 | 471 | tar_command = ['tar', '-xvjf', tarball_path, '-C', strippedname] 472 | subprocess.run(tar_command) 473 | os.remove(tarball_path) 474 | update_main_status('Ready') 475 | update_dwl_status('Ready') 476 | return 477 | 478 | def del_file_sel(): 479 | for i in g_deletebrowse.curselection(): 480 | print(g_deletebrowse.get(i)) 481 | linetext = g_deletebrowse.get(i) 482 | 483 | try: 484 | print(linetext) 485 | except: 486 | tk.messagebox.showerror(title='Error', message='No file/folder selected.') 487 | return 488 | 489 | if '.tar.bz2' in linetext: 490 | linetext = linetext.replace(' ', '') 491 | with open(master, 'r') as file: 492 | lines = file.readlines() 493 | 494 | with open(master, 'w') as file: 495 | for line in lines: 496 | if linetext in line.strip() and linetext in line.rstrip("\n"): 497 | continue 498 | file.write(line) 499 | delete.destroy() 500 | delete_file_folder_dialog() 501 | 502 | def update_config(w): 503 | global wbhkurl 504 | 505 | #msg 506 | tk.messagebox.showinfo(title='Saved', message='Information saved. You will not need to re-enter it in the future, unless you wish to modify.') 507 | 508 | #set in case not restarted 509 | wbhkurl = w.strip() 510 | 511 | #writing 512 | w_config = configparser.ConfigParser() 513 | w_config['DEFAULT']['webhook_url'] = wbhkurl 514 | with open('config.ini', 'w') as configfile: 515 | w_config.write(configfile) 516 | return 517 | 518 | 519 | #---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 520 | #CONFIG GUI 521 | def config(): 522 | config_wbhkurl = tk.StringVar() 523 | 524 | def call_config_update(): 525 | update_config(g_config_webhook_entry.get()) 526 | config_window.destroy() 527 | 528 | #window config 529 | config_window = tk.Tk() 530 | config_window.title('Config Menu') 531 | config_window.geometry('500x220') 532 | config_window.config(bg='#1c1c1c') 533 | config_window.resizable(False, False) 534 | 535 | try: 536 | icon = tk.PhotoImage(file = 'main.png') 537 | config_window.iconphoto(False, icon) 538 | except: 539 | print('Icons not available') 540 | 541 | #framing 542 | configframeurl = tk.Frame(config_window, bg='#1c1c1c', pady=10) 543 | #main 544 | g_config_webhookurl_label = tk.Label(configframeurl, text='Discord webhook URL', fg='#dedede', bg='#1c1c1c', font='fixedsys') 545 | g_config_webhook_entry = tk.Entry(configframeurl, textvariable=config_wbhkurl, bg='#141414', fg='#dedede', width=60, exportselection=0, font='fixedsys', insertbackground='white') 546 | g_config_ok_btn = tk.Button(config_window, text='OK', command=call_config_update, width=10, fg='#dedede', bg='#262626', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 547 | #packing 548 | g_config_webhookurl_label.pack() 549 | g_config_webhook_entry.pack() 550 | 551 | configframeurl.pack() 552 | 553 | g_config_ok_btn.pack() 554 | return 555 | 556 | 557 | 558 | 559 | ###GUI### 560 | #this is the main screen with buttons etc. 561 | def main(): 562 | #global g_progress 563 | #window config 564 | global g_status 565 | window = tk.Tk() 566 | window.title('Infinite Cloud Storage') 567 | window.geometry('380x160') 568 | window.config(bg='#1c1c1c') 569 | window.resizable(False, False) 570 | 571 | #framing 572 | g_topframe = tk.Frame(window, bg='#1c1c1c') 573 | g_middleframe = tk.Frame(window, bg='#1c1c1c', pady=15) 574 | g_bottomframe = tk.Frame(window, bg='#1c1c1c', pady=2) 575 | g_statusframe = tk.Frame(window, bg='#141414') 576 | 577 | #main 578 | g_title = tk.Label(g_topframe, text='Infinite Cloud Storage via Discord', fg='#dedede', bg='#141414', width=60, height=3, font='fixedsys') 579 | 580 | g_upl_fil_btn = tk.Button(g_middleframe, text='Upload File', command=upload_file_dialog, width=20, fg='#dedede', bg='#262626', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 581 | 582 | g_upl_fol_btn = tk.Button(g_bottomframe, text='Upload Folder', command=upload_folder_dialog, width=20, fg='#dedede', bg='#262626', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 583 | 584 | g_dwl_fil_fol_btn = tk.Button(g_middleframe, text='Download File/Folder', command=download_dialog, width=20, fg='#dedede', bg='#262626', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 585 | 586 | g_del_fil_fol_btn = tk.Button(g_bottomframe, text='Delete File', command=delete_file_folder_dialog, width=20, height=1, fg='#dedede', bg='#262626', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 587 | 588 | g_config_btn = tk.Button(g_statusframe, text='Config', command=config, width=8, fg='#dedede', bg='#141414', activebackground='#363636', activeforeground='#dedede', relief='flat', font='fixedsys') 589 | 590 | g_status = tk.Label(g_statusframe, text='Ready', width=380, height=1, fg='#dedede', bg='#141414', font='fixedsys', anchor='w') 591 | 592 | #packing 593 | g_title.pack() 594 | g_upl_fil_btn.pack(side='left') 595 | g_upl_fol_btn.pack(side='left') 596 | g_dwl_fil_fol_btn.pack(side='right') 597 | g_del_fil_fol_btn.pack(side='right') 598 | g_config_btn.pack(side='right') 599 | g_status.pack(side='left') 600 | g_topframe.pack() 601 | g_middleframe.pack() 602 | g_bottomframe.pack() 603 | g_statusframe.pack() 604 | 605 | try: 606 | icon = tk.PhotoImage(file = 'main.png') 607 | window.iconphoto(False, icon) 608 | except: 609 | print('Icons not available') 610 | 611 | 612 | window.mainloop() 613 | 614 | 615 | 616 | 617 | #---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 618 | 619 | main() 620 | 621 | --------------------------------------------------------------------------------