66import sys
77import threading
88import time
9-
109from helper .config_operations import get_library_path , get_debug_mode
1110from helper .file_operations import create_temp_folder , delete_temp_folder , create_logger
1211from patoolib .util import PatoolError
1312import content_database
1413import patches
1514
15+ # Create a logger instance
1616logger = create_logger ()
1717
18- TEMP_FOLDER = pathlib .Path ("temp" ) # Temporary folder for extraction
19-
20-
21- lock = threading .Lock ()
18+ # Path to the temporary extraction folder
19+ TEMP_FOLDER = pathlib .Path ("temp" )
2220
21+ # Folders to target during extraction
2322TARGET_FOLDERS = [
24- "aniBlocks" , "data" , "Environments" , "Light Presets" , "People" ,
25- "Props" , " ReadMe's" , "Render Presets" , "Render Settings" , "Runtime" ,
26- "Scenes" , " Scripts" , "Shader Presets" , "Cameras" , "Documentation"
23+ "aniBlocks" , "data" , "Environments" , "Light Presets" , "People" , "Props" ,
24+ "ReadMe's" , "Render Presets" , "Render Settings" , "Runtime" , "Scenes " ,
25+ "Scripts" , "Shader Presets" , "Cameras" , "Documentation"
2726]
2827
28+ # Determine base path based on execution context
2929if getattr (sys , "frozen" , False ):
3030 BASE_PATH = pathlib .Path (sys ._MEIPASS )
3131else :
3232 BASE_PATH = pathlib .Path (__file__ ).parent
3333
34+ # Path to the 7z executable
3435SEVEN_ZIP_PATH = BASE_PATH / "7z/7z.exe"
3536
37+ # Create a threading lock
38+ lock = threading .Lock ()
39+
3640
3741def get_relative_path (full_path : str ) -> str :
3842 """
39- Extract the relative path starting from the first occurrence of a target folder.
43+ Get the relative path based on target folders.
44+ If the path contains a target folder, return the sub-path starting
45+ from the target folder.
4046 """
4147 pattern = r'|' .join ([re .escape (folder ) for folder in TARGET_FOLDERS ])
4248 match = re .search (pattern , full_path )
4349 return full_path [match .start ():] if match else full_path
4450
51+
4552def clean_temp_folder () -> None :
4653 """
47- Clean and recreate the temporary folder.
54+ Clean the temporary folder by removing its contents .
4855 """
4956 if TEMP_FOLDER .exists ():
5057 shutil .rmtree (TEMP_FOLDER )
5158 TEMP_FOLDER .mkdir (parents = True , exist_ok = True )
5259
60+
5361def extract_archive (item_path : pathlib .Path , is_debug_mode : bool ) -> bool :
5462 """
55- Extract the archive to the temporary folder.
63+ Extract an archive into the temporary folder.
5664 """
5765 base_item_name = item_path .name
5866 if base_item_name .lower ().endswith (('.zip' , '.rar' , '7z' , '.tar' )):
@@ -73,26 +81,22 @@ def extract_archive(item_path: pathlib.Path, is_debug_mode: bool) -> bool:
7381 return False
7482 return False
7583
84+
7685def clean_folder (folder_path : pathlib .Path ) -> None :
7786 """
78- Remove all files that are not part of the target folders .
87+ Remove all files in the specified folder .
7988 """
8089 for item in folder_path .iterdir ():
8190 if item .is_file ():
8291 item .unlink ()
8392
93+
8494def add_to_database (root_path : pathlib .Path , item : pathlib .Path ) -> bool :
8595 """
86- Add the archive's content to the database.
96+ Add the extracted files to the content database.
8797 """
8898 archive_name = item .stem .split ("." )[0 ]
89- file_list = []
90-
91- # Collect all files in the directory
92- for file_path in root_path .rglob ("*" ):
93- if file_path .is_file ():
94- relative_path = get_relative_path (str (file_path ))
95- file_list .append (relative_path )
99+ file_list = [get_relative_path (str (file_path )) for file_path in root_path .rglob ("*" ) if file_path .is_file ()]
96100
97101 if content_database .does_archive_exist (archive_name , file_list ):
98102 logger .info (f"Archive '{ archive_name } ' already exists in the database." )
@@ -104,89 +108,71 @@ def add_to_database(root_path: pathlib.Path, item: pathlib.Path) -> bool:
104108 return False
105109
106110
107- # Searching the content of extracted archive for target folders
108- def traverse_directory (
109- folder_path : pathlib .Path ,
110- current_item : pathlib .Path ,
111- progressbar ,
112- is_debug_mode : bool ,
113- ) -> bool :
111+ def handle_nested_archives (root_path , files , is_debug_mode ):
114112 """
115- Process a directory, search for nested archives, and add valid content to the library.
116-
117- Args:
118- folder_path (pathlib.Path): The path of the folder to process.
119- current_item (pathlib.Path): The current archive being processed.
120- progressbar: A GUI progress bar object to track progress.
121- is_debug_mode (bool): Whether debug mode is enabled for verbose logs.
122-
123- Returns:
124- bool: True if the content was successfully added to the library, False otherwise.
113+ Handle and extract nested archives within the main archive.
125114 """
126115 archive_extracted = False
127- manifest_exists = False
116+ for file in files :
117+ file_path = root_path / file
118+ if file .lower ().endswith (('.zip' , '.rar' , '7z' , '.tar' )):
119+ logger .info (f"Extracting nested archive: { file } " )
120+ try :
121+ verbosity = 2 if is_debug_mode else - 1
122+ patoolib .extract_archive (
123+ str (file_path ),
124+ outdir = str (root_path ),
125+ verbosity = verbosity ,
126+ interactive = False ,
127+ program = str (SEVEN_ZIP_PATH ),
128+ )
129+ time .sleep (0.5 )
130+ file_path .unlink () # Delete the nested archive after extraction
131+ archive_extracted = True
132+ except PatoolError as e :
133+ logger .error (f"Failed to extract nested archive { file } : { e } " )
134+ return archive_extracted
135+
136+
137+ def process_manifest_and_target_folders (root_path , dirs , files , progressbar , current_item ):
138+ """
139+ Check for manifest files and target folders, and process them accordingly.
140+ """
141+ manifest_exists = any (file .lower ().endswith ("manifest.dsx" ) for file in files )
142+
143+ if manifest_exists or any (target in dirs for target in TARGET_FOLDERS ):
144+ for folder in dirs :
145+ if manifest_exists and folder .lower ().startswith ("content" ):
146+ content_path = root_path / folder
147+ clean_folder (content_path )
148+ if add_to_database (content_path , current_item ):
149+ return False
150+ shutil .copytree (content_path , get_library_path (), dirs_exist_ok = True )
151+ return True
152+
153+ if any (target .lower () == folder .lower () for target in TARGET_FOLDERS ):
154+ clean_folder (root_path )
155+ if add_to_database (root_path , current_item ):
156+ return False
157+ shutil .copytree (root_path , get_library_path (), dirs_exist_ok = True )
158+ return True
159+ return False
128160
129- # Traverse the directory structure
161+
162+ def traverse_directory (folder_path : pathlib .Path , current_item : pathlib .Path , progressbar , is_debug_mode : bool ):
163+ """
164+ Traverse the directory structure and handle nested archives and target folders.
165+ """
130166 for root , dirs , files in folder_path .walk ():
131167 root_path = pathlib .Path (root )
132-
133- for file in files :
134- file_path = root_path / file
135-
136- # Handle nested archives
137- if file .lower ().endswith (('.zip' , '.rar' , '7z' , '.tar' )):
138- logger .info (f"Extracting nested archive: { file } " )
139- try :
140- verbosity = 2 if is_debug_mode else - 1
141- patoolib .extract_archive (
142- str (file_path ),
143- outdir = str (root_path ),
144- verbosity = verbosity ,
145- interactive = False ,
146- program = str (SEVEN_ZIP_PATH ),
147- )
148- time .sleep (0.5 )
149- file_path .unlink () # Delete the nested archive after extraction
150- archive_extracted = True
151- except PatoolError as e :
152- logger .error (f"Failed to extract nested archive { file } : { e } " )
153-
154- # Check for manifest files
155- if file .lower ().endswith ("manifest.dsx" ):
156- manifest_exists = True
157-
158- progressbar .set (progressbar .get () + 0.1 )
159-
160- # If a nested archive was extracted, restart traversal
161- if archive_extracted :
162- progressbar .set (progressbar .get () + 0.1 )
163- return traverse_directory (folder_path , current_item , progressbar , is_debug_mode , is_nested_archive = True )
164-
165- if manifest_exists :
166- for folder in dirs :
167- if folder .lower ().startswith ("content" ):
168- content_path = root_path / folder
169- progressbar .set (0.9 )
170- clean_folder (content_path )
171- if add_to_database (content_path , current_item ):
172- return False
173- shutil .copytree (content_path , get_library_path (), dirs_exist_ok = True )
174- return True
175-
176- if any (target in dirs for target in TARGET_FOLDERS ):
177- progressbar .set (0.9 )
178- clean_folder (root_path )
179- if add_to_database (root_path , current_item ):
180- return False
181- shutil .copytree (root_path , get_library_path (), dirs_exist_ok = True )
168+ if handle_nested_archives (root_path , files , is_debug_mode ):
169+ return traverse_directory (folder_path , current_item , progressbar , is_debug_mode )
170+ if process_manifest_and_target_folders (root_path , dirs , files , progressbar , current_item ):
182171 return True
183-
184172 return False
185173
186174
187- def start_installer_gui (
188- file_path : str , progressbar , is_delete_archive : bool = False
189- ) -> bool :
175+ def start_installer_gui (file_path : str , progressbar , is_delete_archive : bool = False ) -> bool :
190176 """
191177 Main function to handle the installation process via the GUI.
192178
@@ -199,39 +185,34 @@ def start_installer_gui(
199185 bool: True if the archive was successfully imported, False otherwise.
200186 """
201187 is_archive_imported = False
202- file_path = pathlib .Path (file_path ) # Convert the input string to a pathlib.Path object
203-
204- with lock : # Ensure thread safety
188+ file_path = pathlib .Path (file_path )
189+ with lock :
205190 logger .info (f"Installing { file_path } " )
206191 create_temp_folder ()
207192 clean_temp_folder ()
208193 progressbar .set (0.1 )
209194
210- # Step 1: Extract the archive
211195 if not extract_archive (file_path , get_debug_mode ()):
212196 clean_temp_folder ()
213197 return is_archive_imported
214198
215199 progressbar .set (0.4 )
216200
217- # Step 2: Traverse the extracted directory
218201 if traverse_directory (TEMP_FOLDER , file_path , progressbar , get_debug_mode ()):
219202 is_archive_imported = True
220203 logger .info (f"Successfully imported: { file_path } " )
221204 else :
222205 is_archive_imported = False
223206 logger .warning (f"Failed to import { file_path } . Invalid folder structure or asset already exists." )
224207
225- # Cleanup
226208 clean_temp_folder ()
227209 delete_temp_folder ()
228210
229- # Step 3: Delete the original archive if requested
230211 if is_delete_archive :
231212 try :
232213 file_path .unlink ()
233214 logger .info (f"Deleted archive: { file_path } " )
234215 except Exception as e :
235216 logger .error (f"Failed to delete archive { file_path } : { e } " )
236217
237- return is_archive_imported
218+ return is_archive_imported
0 commit comments