| 
									
										
										
										
											2022-09-21 11:28:28 +08:00
										 |  |  | import os | 
					
						
							|  |  |  | import optparse | 
					
						
							|  |  |  | from hashlib import md5 | 
					
						
							|  |  |  | import brotli | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # file compress level(0-11) | 
					
						
							|  |  |  | compress_level = 11 | 
					
						
							|  |  |  | # 4GB maximum | 
					
						
							|  |  |  | length_count = 4 | 
					
						
							|  |  |  | # encoding | 
					
						
							|  |  |  | encoding = 'utf-8' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # output: {path: (compressed_data, file_md5)} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def generate_md5_table(folder: str) -> dict: | 
					
						
							|  |  |  |     res: dict = dict() | 
					
						
							|  |  |  |     curdir = os.curdir | 
					
						
							|  |  |  |     os.chdir(folder) | 
					
						
							|  |  |  |     for root, _, files in os.walk('.'): | 
					
						
							|  |  |  |         # remove ./ | 
					
						
							|  |  |  |         for f in files: | 
					
						
							|  |  |  |             md5_generator = md5() | 
					
						
							|  |  |  |             full_path = os.path.join(root, f) | 
					
						
							|  |  |  |             print(f"processing {full_path}...") | 
					
						
							|  |  |  |             f = open(full_path, "rb") | 
					
						
							|  |  |  |             content = f.read() | 
					
						
							|  |  |  |             content_compressed = brotli.compress( | 
					
						
							|  |  |  |                 content, quality=compress_level) | 
					
						
							|  |  |  |             md5_generator.update(content) | 
					
						
							|  |  |  |             md5_code = md5_generator.hexdigest().encode(encoding=encoding) | 
					
						
							|  |  |  |             res[full_path] = (content_compressed, md5_code) | 
					
						
							|  |  |  |     os.chdir(curdir) | 
					
						
							|  |  |  |     return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def write_metadata(md5_table: dict, output_folder: str, exe: str): | 
					
						
							|  |  |  |     output_path = os.path.join(output_folder, "data.bin") | 
					
						
							|  |  |  |     with open(output_path, "wb") as f: | 
					
						
							|  |  |  |         f.write("rustdesk".encode(encoding=encoding)) | 
					
						
							|  |  |  |         for path in md5_table.keys(): | 
					
						
							|  |  |  |             (compressed_data, md5_code) = md5_table[path] | 
					
						
							|  |  |  |             data_length = len(compressed_data) | 
					
						
							|  |  |  |             path = path.encode(encoding=encoding) | 
					
						
							|  |  |  |             # path length & path | 
					
						
							|  |  |  |             f.write((len(path)).to_bytes(length=length_count, byteorder='big')) | 
					
						
							|  |  |  |             f.write(path) | 
					
						
							|  |  |  |             # data length & compressed data | 
					
						
							| 
									
										
										
										
											2023-10-28 10:57:50 +02:00
										 |  |  |             f.write(data_length.to_bytes( | 
					
						
							| 
									
										
										
										
											2022-09-21 11:28:28 +08:00
										 |  |  |                 length=length_count, byteorder='big')) | 
					
						
							|  |  |  |             f.write(compressed_data) | 
					
						
							|  |  |  |             # md5 code | 
					
						
							|  |  |  |             f.write(md5_code) | 
					
						
							|  |  |  |         # end | 
					
						
							|  |  |  |         f.write("rustdesk".encode(encoding=encoding)) | 
					
						
							|  |  |  |         # executable | 
					
						
							|  |  |  |         f.write(exe.encode(encoding='utf-8')) | 
					
						
							|  |  |  |     print(f"metadata had written to {output_path}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def build_portable(output_folder: str): | 
					
						
							|  |  |  |     os.chdir(output_folder) | 
					
						
							|  |  |  |     os.system("cargo build --release") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Linux: python3 generate.py -f ../rustdesk-portable-packer/test -o . -e ./test/main.py | 
					
						
							|  |  |  | # Windows: python3 .\generate.py -f ..\rustdesk\flutter\build\windows\runner\Debug\ -o . -e ..\rustdesk\flutter\build\windows\runner\Debug\rustdesk.exe | 
					
						
							| 
									
										
										
										
											2023-10-28 10:57:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-21 11:28:28 +08:00
										 |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     parser = optparse.OptionParser() | 
					
						
							|  |  |  |     parser.add_option("-f", "--folder", dest="folder", | 
					
						
							|  |  |  |                       help="folder to compress") | 
					
						
							|  |  |  |     parser.add_option("-o", "--output", dest="output_folder", | 
					
						
							|  |  |  |                       help="the root of portable packer project") | 
					
						
							|  |  |  |     parser.add_option("-e", "--executable", dest="executable", | 
					
						
							|  |  |  |                       help="specify startup file") | 
					
						
							|  |  |  |     (options, args) = parser.parse_args() | 
					
						
							|  |  |  |     folder = options.folder | 
					
						
							|  |  |  |     output_folder = os.path.abspath(options.output_folder) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     exe: str = os.path.abspath(options.executable) | 
					
						
							|  |  |  |     if not exe.startswith(os.path.abspath(folder)): | 
					
						
							|  |  |  |         print("the executable must locate in source folder") | 
					
						
							|  |  |  |         exit(-1) | 
					
						
							|  |  |  |     exe = '.' + exe[len(os.path.abspath(folder)):] | 
					
						
							|  |  |  |     print("executable path: " + exe) | 
					
						
							|  |  |  |     md5_table = generate_md5_table(folder) | 
					
						
							|  |  |  |     write_metadata(md5_table, output_folder, exe) | 
					
						
							|  |  |  |     build_portable(output_folder) |