11from tkinter import *
22from tkinter .ttk import *
33from tkinter .scrolledtext import ScrolledText
4- from tkinter import messagebox
4+ from tkinter import messagebox , filedialog
55from PIL import ImageTk , Image
66from urllib .request import urlretrieve
77from zipfile import ZipFile
@@ -12,6 +12,8 @@ import requests
1212import subprocess
1313import threading
1414import re
15+ import shutil
16+ import datetime
1517
1618class SettingsPage (Frame ):
1719
@@ -62,6 +64,8 @@ class App:
6264 self .tabs .add (self .mainframe , text = "Versions" , sticky = "nsew" )
6365 if not os .path .isdir ("assets" ):
6466 os .mkdir ("assets" )
67+ if not os .path .isdir ("temp" ):
68+ os .mkdir ("temp" )
6569 urlretrieve ("https://raw.github.com/SpacePython12/AP-Launcher/main/assets/background.png" , "assets/background.png" )
6670 self .background = ImageTk .PhotoImage (Image .open ("assets/background.png" ))
6771 self .background2 = Label (self .mainframe , image = self .background )
@@ -83,12 +87,16 @@ class App:
8387 "id" : None ,
8488 "expiresAt" : None
8589 },
86- "premium" : True
90+ "premium" : True ,
91+ "selectedVersion" : None
8792 }
88- try :
93+ if type ( self . cache [ "selectedVersion" ]) is type ( list ()) :
8994 self .versionvar .set (f'{ self .cache ["selectedVersion" ][0 ]} ({ self .cache ["selectedVersion" ][1 ]} )' )
90- except KeyError :
91- self .versionvar .set (self .versions [0 ])
95+ elif type (self .cache ["selectedVersion" ]) is type (None ):
96+ try :
97+ self .versionvar .set (self .versions [0 ])
98+ except IndexError :
99+ pass
92100 self .versionlabel = Label (self .buttonframe , text = "Version: " )
93101 self .versionlabel .grid (column = 3 , row = 0 )
94102 self .versionlist = Combobox (self .buttonframe , textvariable = self .versionvar , width = 30 )
@@ -105,15 +113,18 @@ class App:
105113 self .processtext = ScrolledText (self .processframe , state = "disabled" , height = 35 , width = 120 )
106114 self .processtext .grid (column = 0 , row = 2 , sticky = "nsew" )
107115 self .profileframe = Frame (self .win )
108- self .tabs .add (self .profileframe , text = "Edit Profiles" )
116+ self .tabs .add (self .profileframe , text = "Profiles" )
109117 self .profileselect = Frame (self .profileframe )
110118 self .profileselect .grid (column = 0 , row = 0 , sticky = "nsew" )
111119 self .profilelabel = Label (self .profileselect , text = "Profile: " )
112120 self .profilelabel .grid (column = 0 , row = 0 , sticky = "nsew" )
113121 self .profilelist = Combobox (self .profileselect , textvariable = self .versionvar )
114122 self .profilelist .grid (column = 1 , row = 0 , sticky = "nsew" )
115123 self .profilelist ["values" ] = self .versions
116- self .profname = LabeledEntry (self .profileframe , "Name: " , self .accounts ["profiles" ][self .nametoprofile [self .versionvar .get ()]]["name" ])
124+ try :
125+ self .profname = LabeledEntry (self .profileframe , "Name: " , self .accounts ["profiles" ][self .nametoprofile [self .versionvar .get ()]]["name" ])
126+ except KeyError :
127+ self .profname = LabeledEntry (self .profileframe , "Name: " , "N/A" )
117128 self .profname .grid (column = 0 , row = 1 , sticky = "nsew" )
118129 self .profgamedir = LabeledEntry (self .profileframe , "Game Directory: " , os .path .join (os .getenv ('APPDATA' ), '.minecraft' ), elength = 30 )
119130 self .profgamedir .grid (column = 0 , row = 2 , sticky = "nsew" )
@@ -128,6 +139,10 @@ class App:
128139 self .profjavargs .grid (column = 0 , row = 4 , sticky = "nsew" )
129140 self .profsave = Button (self .profileframe , text = "Save" )
130141 self .profsave .grid (column = 0 , row = 5 , sticky = "nsew" )
142+ self .proftrans = Label (self .profileframe , text = "OR" )
143+ self .proftrans .grid (column = 0 , row = 6 , sticky = "nsew" )
144+ self .profadd = Button (self .profileframe , text = "Import new version" , command = lambda : self .open_install_archive ())
145+ self .profadd .grid (column = 0 , row = 7 , sticky = "nsew" )
131146 self .update_profiles (self .versionvar .get ())
132147 self .profilelist .bind ("<<ComboboxSelected>>" , lambda x : self .update_profiles (self .versionvar .get ()))
133148 self .accesstoken = self .cache ["accessid" ]["id" ]
@@ -171,7 +186,6 @@ class App:
171186
172187 def on_closing (self ):
173188 """Saves info before the window is closed"""
174- self .accounts ["selectedProfile" ] = self .versionvar .get ()
175189 json .dump (self .accounts , open (os .path .join (self .minecraftdir , "launcher_profiles.json" ), "w" ), indent = 2 )
176190 json .dump (self .cache , open ("cache.json" , "w" ), indent = 2 )
177191 self .win .withdraw ()
@@ -248,7 +262,10 @@ class App:
248262
249263 def update_profiles (self , name ):
250264 """Updates special game arguments"""
251- self .profname .set (self .accounts ["profiles" ][self .nametoprofile [name ]]["name" ])
265+ try :
266+ self .profname .set (self .accounts ["profiles" ][self .nametoprofile [name ]]["name" ])
267+ except :
268+ pass
252269 try :
253270 self .profgamedir .set (self .accounts ["profiles" ][self .nametoprofile [name ]]["gameDir" ])
254271 except :
@@ -312,6 +329,7 @@ class App:
312329 self .currentversion = self .get_latest_version ("snapshot" )
313330 else :
314331 self .currentversion = self .accounts ["profiles" ][self .nametoprofile [self .versionvar .get ()]]["lastVersionId" ]
332+ self .accounts ["profiles" ][self .nametoprofile [self .versionvar .get ()]]["lastUsed" ] = datetime .datetime .now ().strftime ("%Y-%m-%dT%H:%M:%S.%f" )[:- 3 ] + "Z"
315333 thread = threading .Thread (None , lambda : self .sbloop ())
316334 thread .start ()
317335
@@ -341,29 +359,13 @@ class App:
341359 shell = True ,
342360 text = True ,
343361 stdout = subprocess .PIPE ,
344- stderr = subprocess .DEVNULL ,
362+ stderr = subprocess .STDOUT ,
345363 stdin = subprocess .DEVNULL
346364 )
347- line = sb .stdout .readline ().rstrip ()
348- while True :
349- if sb .poll () is None :
350- if line .startswith ("\t " ):
351- self .update_procscreen (line )
352- else :
353- try :
354- title , text = line .split (": " , 1 )
355- title = title .replace ("[" , "" )
356- title = title .replace ("]" , "" )
357- text = text .replace ("ERROR : " , "" )
358- time , rinfo = title .split (" " , 1 )
359- name , info = rinfo .split ("/" )
360- self .update_procscreen (f"[{ time } ] { info } from { name } : { text } " )
361- except ValueError :
362- if not line .startswith ("ERROR : " ):
363- self .update_procscreen (line )
364- line = sb .stdout .readline ().rstrip ()
365- else :
366- break
365+ while sb .poll () is None :
366+ line = sb .stdout .readline ().rstrip ()
367+ if not line == "" :
368+ self .update_procscreen (line )
367369 print ("Process finished" )
368370 self .playbutton .config (state = "normal" , text = "\n Play\n " )
369371 self .playcontext .entryconfigure (0 , state = "disabled" )
@@ -408,6 +410,36 @@ class App:
408410 zf .close ()
409411 os .remove ("java/java.zip" )
410412
413+ def open_install_archive (self ):
414+ filepath = filedialog .askopenfilename (master = self .win , title = "Open version file" , filetypes = [("ZIP files" , "*.zip" )])
415+ if filepath == "" :
416+ return
417+ with ZipFile (open (filepath , "rb" )) as zf :
418+ folders = list (set ([os .path .dirname (x ).split ("/" )[0 ] for x in zf .namelist ()]))
419+ folders .remove ("indexes" )
420+ for folder in folders :
421+ infofile = zf .open (f"{ folder } /manifest.json" )
422+ info = json .load (infofile )
423+ if os .path .isdir (os .path .join (self .minecraftdir , "versions" , folder )):
424+ if not messagebox .askyesno ("Confirm" , "There is already a version by this name. Would you like to overwrite it?" ):
425+ zf .close ()
426+ return
427+ else :
428+ shutil .rmtree (os .path .join (self .minecraftdir , "versions" , folder ))
429+ os .mkdir (os .path .join (self .minecraftdir , "versions" , folder ))
430+ for file_ in zf .namelist ():
431+ if file_ .startswith (folder ) and not file_ .endswith ("manifest.json" ):
432+ zf .extract (file_ , os .path .join (self .minecraftdir , "versions" ))
433+ info ["profile" ][list (info ["profile" ].keys ())[0 ]]["created" ] = datetime .datetime .now ().strftime ("%Y-%m-%dT%H:%M:%S.%f" )[:- 3 ] + "Z"
434+ self .accounts ["profiles" ][list (info ["profile" ].keys ())[0 ]] = info ["profile" ][list (info ["profile" ].keys ())[0 ]]
435+ for file_ in zf .namelist ():
436+ if file_ .startswith ("indexes" ):
437+ zf .extract (file_ , os .path .join (self .minecraftdir , "assets" ))
438+ messagebox .showinfo ("Success" , "The version was successfully imported. Restart AP Launcher to see changes." )
439+ zf .close ()
440+ return
441+
442+
411443if __name__ == "__main__" :
412444 main = App ()
413445 main .win .mainloop ()
0 commit comments