1010
1111import os
1212import requests
13+ import urllib .request
14+ import shutil
15+ import tempfile
1316
1417
1518def download_predbat_file_from_github (tag , filename , new_filename ):
1619 """
1720 Downloads a predbat source file from github and returns the contents
21+ Now supports files in subdirectories.
1822
1923 Args:
2024 tag (str): The tag to download from (e.g. v1.0.0)
21- filename (str): The filename to download (e.g. predbat.py)
25+ filename (str): The filename to download (e.g. predbat.py or utils/battery_manager.py )
2226 new_filename (str): The new filename to save the file as
2327 Returns:
2428 str: The contents of the file
2529 """
26- url = "https://raw.githubusercontent.com/springfall2008/batpred/" + tag + "/apps/predbat/{}" .format (filename )
30+ # Handle both flat files and files in directories
31+ url_path = filename .replace (os .sep , "/" ) # Ensure forward slashes for URL
32+ url = "https://raw.githubusercontent.com/springfall2008/batpred/" + tag + "/apps/predbat/{}" .format (url_path )
2733 print ("Downloading {}" .format (url ))
2834 r = requests .get (url , headers = {})
2935 if r .ok :
3036 data = r .text
3137 print ("Got data, writing to {}" .format (new_filename ))
3238 if new_filename :
39+ # Create directory if needed
40+ dir_path = os .path .dirname (new_filename )
41+ if dir_path and not os .path .exists (dir_path ):
42+ os .makedirs (dir_path )
3343 with open (new_filename , "w" ) as han :
3444 han .write (data )
3545 return data
@@ -38,19 +48,66 @@ def download_predbat_file_from_github(tag, filename, new_filename):
3848 return None
3949
4050
41- def predbat_update_move (version , files ):
51+ def predbat_update_move (version , backup_path_or_files ):
4252 """
43- Move the updated files into place
53+ Move the updated files into place.
54+ Handles both zip-based (backup_path) and individual file approaches.
4455 """
4556 tag_split = version .split (" " )
4657 if tag_split :
4758 tag = tag_split [0 ]
4859 this_path = os .path .dirname (__file__ )
49- cmd = ""
50- for file in files :
51- cmd += "mv -f {} {} && " .format (os .path .join (this_path , file + "." + tag ), os .path .join (this_path , file ))
52- cmd += "echo 'Update complete'"
53- os .system (cmd )
60+
61+ # Check if we have a backup path (zip method) or file list (individual method)
62+ if isinstance (backup_path_or_files , str ) and os .path .isdir (backup_path_or_files ):
63+ # Zip method - copy from backup directory
64+ backup_path = backup_path_or_files
65+ print ("Moving files from backup directory: {}" .format (backup_path ))
66+
67+ # Copy all files from backup to current directory
68+ for root , dirs , files in os .walk (backup_path ):
69+ for file in files :
70+ source = os .path .join (root , file )
71+ # Calculate relative path from backup_path
72+ rel_path = os .path .relpath (source , backup_path )
73+ dest = os .path .join (this_path , rel_path )
74+
75+ # Create destination directory if needed
76+ dest_dir = os .path .dirname (dest )
77+ if dest_dir and dest_dir != this_path and not os .path .exists (dest_dir ):
78+ os .makedirs (dest_dir )
79+
80+ # Copy the file
81+ shutil .copy2 (source , dest )
82+ print ("Copied {} to {}" .format (rel_path , dest ))
83+
84+ # Clean up backup directory
85+ shutil .rmtree (backup_path )
86+ print ("Cleaned up backup directory" )
87+
88+ else :
89+ # Individual file method (backward compatibility)
90+ files = backup_path_or_files
91+ print ("Moving individual files with version suffix" )
92+
93+ # Process files, creating directories as needed
94+ for file in files :
95+ source = os .path .join (this_path , file + "." + tag )
96+ dest = os .path .join (this_path , file )
97+
98+ # Create destination directory if needed
99+ dest_dir = os .path .dirname (dest )
100+ if dest_dir and dest_dir != this_path and not os .path .exists (dest_dir ):
101+ os .makedirs (dest_dir )
102+
103+ # Move the file
104+ if os .path .exists (source ):
105+ os .rename (source , dest )
106+ print ("Moved {} to {}" .format (source , dest ))
107+ else :
108+ print ("Warning: Source file {} not found" .format (source ))
109+
110+ print ("Update complete" )
54111 return True
55112 return False
56113
@@ -72,6 +129,7 @@ def get_files_from_predbat(predbat_code):
72129def check_install ():
73130 """
74131 Check if Predbat is installed correctly
132+ Now supports files in subdirectories.
75133 """
76134 this_path = os .path .dirname (__file__ )
77135 predbat_file = os .path .join (this_path , "predbat.py" )
@@ -91,10 +149,93 @@ def check_install():
91149 return False
92150
93151
152+ def predbat_update_download_zip (version ):
153+ """
154+ Download the defined version of Predbat from Github using zip method (like addon).
155+ This supports directory structures automatically.
156+ """
157+ this_path = os .path .dirname (__file__ )
158+ tag_split = version .split (" " )
159+ if tag_split :
160+ tag = tag_split [0 ]
161+
162+ print ("Downloading Predbat {} using zip method..." .format (version ))
163+
164+ # Download entire repository as zip
165+ download_url = "https://github.com/springfall2008/batpred/archive/refs/tags/{}.zip" .format (tag )
166+
167+ with tempfile .TemporaryDirectory () as temp_dir :
168+ zip_path = os .path .join (temp_dir , "predbat_{}.zip" .format (tag ))
169+
170+ try :
171+ print ("Downloading {}" .format (download_url ))
172+ urllib .request .urlretrieve (download_url , zip_path )
173+ print ("Predbat downloaded successfully" )
174+ except Exception as e :
175+ print ("Error: Unable to download Predbat - {}" .format (e ))
176+ return None
177+
178+ print ("Extracting Predbat..." )
179+ extract_path = os .path .join (temp_dir , "extract" )
180+ os .makedirs (extract_path )
181+ shutil .unpack_archive (zip_path , extract_path )
182+
183+ # Find the extracted directory (batpred-X.Y.Z format)
184+ repo_path = os .path .join (extract_path , "batpred-{}" .format (tag .replace ("v" , "" )))
185+ predbat_source = os .path .join (repo_path , "apps" , "predbat" )
186+
187+ if not os .path .exists (predbat_source ):
188+ print ("Error: Could not find predbat source at {}" .format (predbat_source ))
189+ return None
190+
191+ # Copy only Python files selectively (safe approach)
192+ backup_path = os .path .join (this_path , "backup_{}" .format (tag ))
193+ if os .path .exists (backup_path ):
194+ shutil .rmtree (backup_path )
195+ os .makedirs (backup_path )
196+
197+ print ("Copying Python files to {}..." .format (backup_path ))
198+
199+ # Copy main *.py files from root directory
200+ for item in os .listdir (predbat_source ):
201+ source_path = os .path .join (predbat_source , item )
202+ dest_path = os .path .join (backup_path , item )
203+
204+ if os .path .isfile (source_path ) and item .endswith (".py" ):
205+ # Copy only Python files
206+ shutil .copy2 (source_path , dest_path )
207+ print (" Copied file: {}" .format (item ))
208+ elif os .path .isdir (source_path ) and item not in ["config" , "__pycache__" , ".ruff_cache" , ".git" ]:
209+ # Copy subdirectories but only *.py files within them
210+ os .makedirs (dest_path )
211+ print (" Created directory: {}" .format (item ))
212+ for subitem in os .listdir (source_path ):
213+ if subitem .endswith (".py" ):
214+ sub_source = os .path .join (source_path , subitem )
215+ sub_dest = os .path .join (dest_path , subitem )
216+ shutil .copy2 (sub_source , sub_dest )
217+ print (" Copied: {}/{}" .format (item , subitem ))
218+
219+ # Note: Only *.py files copied, no config or other file types
220+
221+ print ("Download and extraction completed successfully" )
222+ return backup_path
223+
224+ return None
225+
226+
94227def predbat_update_download (version ):
95228 """
96- Download the defined version of Predbat from Github
229+ Download the defined version of Predbat from Github.
230+ Uses zip method for better directory support.
97231 """
232+ # Use zip method (more reliable with directories)
233+ backup_path = predbat_update_download_zip (version )
234+ if backup_path :
235+ return backup_path
236+
237+ # Fallback to original method for backward compatibility
238+ print ("Zip method failed, trying individual file download..." )
98239 this_path = os .path .dirname (__file__ )
99240 tag_split = version .split (" " )
100241 if tag_split :
0 commit comments