Skip to content

Commit a43d1c7

Browse files
committed
huge performance improvemnets and code optimizations & progressbar added
1 parent d77c978 commit a43d1c7

File tree

3 files changed

+99
-39
lines changed

3 files changed

+99
-39
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "pydvpl"
33
description = "A CLI Tool Coded In Python3 To Convert WoTB ( Dava ) SmartDLC DVPL File Based On LZ4 High Compression."
44
readme = "README.md"
5-
version = "0.1.0"
5+
version = "0.2.0"
66
authors = [{ name = "RifsxD", email = "support@rxd-mods.xyz" }]
77
license = { text = "MIT License" }
88
requires-python = ">=3.10"

src/pydvpl/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99

1010
__all__ = ['CompressDVPL', 'DecompressDVPL','ConvertDVPLFiles', 'VerifyDVPLFiles', 'readDVPLFooter', 'createDVPLFooter']
1111

12-
__version__ = '0.1.0'
12+
__version__ = '0.2.0'

src/pydvpl/converter.py

Lines changed: 97 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import os
21
import argparse
32
import time
43
import os
54
import lz4.block
65
import zlib
76
import threading
8-
from concurrent.futures import ThreadPoolExecutor
7+
import sys
8+
import multiprocessing
9+
import queue
10+
from functools import partial
11+
912

1013
class Color:
1114
RED = '\033[31m'
@@ -14,28 +17,35 @@ class Color:
1417
YELLOW = '\033[33m'
1518
RESET = '\033[0m'
1619

20+
1721
class Meta:
1822
NAME = 'PyDVPL'
19-
VERSION = '0.1.0'
20-
DATE = '13/03/2024'
23+
VERSION = '0.2.0'
24+
DATE = '14/03/2024'
2125
DEV = 'RifsxD'
2226
REPO = 'https://github.com/rifsxd/pydvpl'
2327
INFO = 'A CLI Tool Coded In Python3 To Convert WoTB ( Dava ) SmartDLC DVPL File Based On LZ4 High Compression.'
2428

29+
2530
output_lock = threading.Lock()
2631

32+
# Define a thread-safe queue to store processed files
33+
processed_queue = queue.Queue()
34+
2735
DVPL_FOOTER_SIZE = 20
2836
DVPL_TYPE_NONE = 0
2937
DVPL_TYPE_LZ4 = 2
3038
DVPL_FOOTER = b"DVPL"
3139

40+
3241
class DVPLFooter:
3342
def __init__(self, original_size, compressed_size, crc32, type_val):
3443
self.original_size = original_size
3544
self.compressed_size = compressed_size
3645
self.crc32 = crc32
3746
self.type = type_val
3847

48+
3949
def createDVPLFooter(input_size, compressed_size, crc32_val, type_val):
4050
result = bytearray(DVPL_FOOTER_SIZE)
4151
result[:4] = input_size.to_bytes(4, 'little')
@@ -45,6 +55,7 @@ def createDVPLFooter(input_size, compressed_size, crc32_val, type_val):
4555
result[16:] = DVPL_FOOTER
4656
return result
4757

58+
4859
def readDVPLFooter(buffer):
4960
if len(buffer) < DVPL_FOOTER_SIZE:
5061
raise ValueError(Color.RED + "InvalidDVPLFooter: Buffer size is smaller than expected" + Color.RESET)
@@ -61,11 +72,13 @@ def readDVPLFooter(buffer):
6172

6273
return DVPLFooter(original_size, compressed_size, crc32_val, type_val)
6374

75+
6476
def CompressDVPL(buffer):
6577
compressed_block = lz4.block.compress(buffer, store_size=False)
6678
footer_buffer = createDVPLFooter(len(buffer), len(compressed_block), zlib.crc32(compressed_block), DVPL_TYPE_LZ4)
6779
return compressed_block + footer_buffer
6880

81+
6982
def DecompressDVPL(buffer):
7083
footer_data = readDVPLFooter(buffer)
7184
target_block = buffer[:-DVPL_FOOTER_SIZE]
@@ -88,22 +101,39 @@ def DecompressDVPL(buffer):
88101
else:
89102
raise ValueError(Color.RED + "UNKNOWN DVPL FORMAT" + Color.RESET)
90103

91-
def ConvertDVPLFiles(directory_or_file, config):
104+
105+
def print_progress_bar(processed_files, total_files):
106+
with output_lock:
107+
progress = min(processed_files.value / total_files, 1.0) # Ensure progress doesn't exceed 100%
108+
bar_length = 50
109+
filled_length = int(bar_length * progress)
110+
bar = '=' * filled_length + '-' * (bar_length - filled_length)
111+
percentage = progress * 100
112+
sys.stdout.write('\rProcessing: [{:<50}] {:.2f}%'.format(bar, percentage))
113+
sys.stdout.flush()
114+
115+
116+
def count_total_files(directory):
117+
total_files = 0
118+
for root, dirs, files in os.walk(directory):
119+
total_files += len(files)
120+
return total_files
121+
122+
123+
def ConvertDVPLFiles(directory_or_file, config, total_files, processed_files):
92124
success_count = 0
93125
failure_count = 0
94126
ignored_count = 0
95127

96128
if os.path.isdir(directory_or_file):
97129
dir_list = os.listdir(directory_or_file)
98-
with ThreadPoolExecutor() as executor:
99-
future_results = []
100-
for dir_item in dir_list:
101-
future_results.append(executor.submit(ConvertDVPLFiles, os.path.join(directory_or_file, dir_item), config))
102-
for future in future_results:
103-
succ, fail, ignored = future.result()
104-
success_count += succ
105-
failure_count += fail
106-
ignored_count += ignored
130+
for dir_item in dir_list:
131+
succ, fail, ignored = ConvertDVPLFiles(os.path.join(directory_or_file, dir_item), config, total_files, processed_files)
132+
success_count += succ
133+
failure_count += fail
134+
ignored_count += ignored
135+
processed_files.value += 1
136+
print_progress_bar(processed_files, total_files)
107137
else:
108138
is_decompression = config.mode == "decompress" and directory_or_file.endswith(".dvpl")
109139
is_compression = config.mode == "compress" and not directory_or_file.endswith(".dvpl")
@@ -147,22 +177,24 @@ def ConvertDVPLFiles(directory_or_file, config):
147177

148178
return success_count, failure_count, ignored_count
149179

150-
def VerifyDVPLFiles(directory_or_file, config):
180+
181+
182+
def VerifyDVPLFiles(directory_or_file, config, total_files, processed_files):
151183
success_count = 0
152184
failure_count = 0
153185
ignored_count = 0
154186

155187
if os.path.isdir(directory_or_file):
156188
dir_list = os.listdir(directory_or_file)
157-
with ThreadPoolExecutor() as executor:
158-
future_results = []
159-
for dir_item in dir_list:
160-
future_results.append(executor.submit(VerifyDVPLFiles, os.path.join(directory_or_file, dir_item), config))
161-
for future in future_results:
162-
succ, fail, ignored = future.result()
163-
success_count += succ
164-
failure_count += fail
165-
ignored_count += ignored
189+
for dir_item in dir_list:
190+
succ, fail, ignored = VerifyDVPLFiles(os.path.join(directory_or_file, dir_item), config, total_files,
191+
processed_files)
192+
success_count += succ
193+
failure_count += fail
194+
ignored_count += ignored
195+
if processed_files.value < total_files: # Ensure processed files count does not exceed total files
196+
processed_files.value += 1
197+
print_progress_bar(processed_files, total_files)
166198
else:
167199
is_dvpl_file = directory_or_file.endswith(".dvpl")
168200

@@ -187,7 +219,8 @@ def VerifyDVPLFiles(directory_or_file, config):
187219

188220
if config.verbose:
189221
with output_lock:
190-
print(f"{Color.GREEN}\nFile{Color.RESET} {file_path} has been successfully {Color.GREEN}verified.{Color.RESET}")
222+
print(
223+
f"{Color.GREEN}\nFile{Color.RESET} {file_path} has been successfully {Color.GREEN}verified.{Color.RESET}")
191224

192225
success_count += 1
193226
except Exception as e:
@@ -203,13 +236,18 @@ def VerifyDVPLFiles(directory_or_file, config):
203236

204237
return success_count, failure_count, ignored_count
205238

239+
206240
def ParseCommandLineArgs():
207241
parser = argparse.ArgumentParser()
208-
parser.add_argument("-m", "--mode", help="mode can be 'compress' / 'decompress' / 'help' (for an extended help guide).")
209-
parser.add_argument("-k", "--keep-originals", action="store_true", help="keep original files after compression/decompression.")
210-
parser.add_argument("-v", "--verbose", action="store_true", help="shows verbose information for all processed files.")
242+
parser.add_argument("-m", "--mode",
243+
help="mode can be 'compress' / 'decompress' / 'help' (for an extended help guide).")
244+
parser.add_argument("-k", "--keep-originals", action="store_true",
245+
help="keep original files after compression/decompression.")
246+
parser.add_argument("-v", "--verbose", action="store_true",
247+
help="shows verbose information for all processed files.")
211248
parser.add_argument("-p", "--path", help="directory/files path to process. Default is the current directory.")
212-
parser.add_argument("-i", "--ignore", default="", help="Comma-separated list of file extensions to ignore during compression.")
249+
parser.add_argument("-i", "--ignore", default="",
250+
help="Comma-separated list of file extensions to ignore during compression.")
213251
args = parser.parse_args()
214252

215253
if not args.mode:
@@ -220,6 +258,7 @@ def ParseCommandLineArgs():
220258

221259
return args
222260

261+
223262
def PrintHelpMessage():
224263
print('''pydvpl [--mode] [--keep-originals] [--path]
225264
@@ -263,12 +302,13 @@ def PrintHelpMessage():
263302
$ pydvpl --mode compress --path /path/to/decompress --ignore exe,dll
264303
265304
$ pydvpl --mode compress --path /path/to/decompress --ignore test.exe,test.txt
266-
305+
267306
$ pydvpl --mode verify -path /path/to/verify
268307
269308
$ pydvpl --mode verify -path /path/to/verify/verify.yaml.dvpl
270309
''')
271310

311+
272312
def PrintElapsedTime(elapsed_time):
273313
if elapsed_time < 1:
274314
print(f"\nProcessing took {Color.GREEN}{int(elapsed_time * 1000)} ms{Color.RESET}\n")
@@ -277,6 +317,7 @@ def PrintElapsedTime(elapsed_time):
277317
else:
278318
print(f"\nProcessing took {Color.RED}{int(elapsed_time / 60)} min{Color.RESET}\n")
279319

320+
280321
def main():
281322
print(f"\n{Color.BLUE}• Name:{Color.RESET} {Meta.NAME}")
282323
print(f"{Color.BLUE}• Version:{Color.RESET} {Meta.VERSION}")
@@ -288,22 +329,41 @@ def main():
288329
start_time = time.time()
289330
config = ParseCommandLineArgs()
290331

332+
total_files = count_total_files(config.path)
333+
manager = multiprocessing.Manager()
334+
processed_files = manager.Value('i', 0) # Define processed_files using a Manager
335+
291336
try:
292337
if config.mode in ["compress", "decompress"]:
293-
success_count, failure_count, ignored_count = ConvertDVPLFiles(config.path, config)
294-
print(f"\n{Color.GREEN}{config.mode.upper()} FINISHED{Color.RESET}. Successful conversions: {Color.GREEN}{success_count}{Color.RESET}, Failed conversions: {Color.RED}{failure_count}{Color.RESET}, Ignored files: {Color.YELLOW}{ignored_count}{Color.RESET}")
338+
process_func = partial(ConvertDVPLFiles, config=config, total_files=total_files,
339+
processed_files=processed_files)
295340
elif config.mode == "verify":
296-
success_count, failure_count, ignored_count = VerifyDVPLFiles(config.path, config)
297-
print(f"\n{Color.GREEN}{config.mode.upper()} FINISHED{Color.RESET}. Successful verifications: {Color.GREEN}{success_count}{Color.RESET}, Failed verifications: {Color.RED}{failure_count}{Color.RESET}, Ignored files: {Color.YELLOW}{ignored_count}{Color.RESET}")
298-
elif config.mode == "help":
299-
PrintHelpMessage()
341+
process_func = partial(VerifyDVPLFiles, config=config, total_files=total_files,
342+
processed_files=processed_files)
300343
else:
301344
raise ValueError("Incorrect mode selected. Use '--help' for information.")
345+
346+
with multiprocessing.Pool() as pool:
347+
results = pool.map(process_func, [config.path])
348+
349+
success_count = sum(result[0] for result in results)
350+
failure_count = sum(result[1] for result in results)
351+
ignored_count = sum(result[2] for result in results)
352+
353+
if config.mode in ["compress", "decompress"]:
354+
print(
355+
f"\n\n{Color.GREEN}{config.mode.upper()} FINISHED{Color.RESET}. Successful conversions: {Color.GREEN}{success_count}{Color.RESET}, Failed conversions: {Color.RED}{failure_count}{Color.RESET}, Ignored files: {Color.YELLOW}{ignored_count}{Color.RESET}")
356+
elif config.mode == "verify":
357+
print(
358+
f"\n\n{Color.GREEN}{config.mode.upper()} FINISHED{Color.RESET}. Successful verifications: {Color.GREEN}{success_count}{Color.RESET}, Failed verifications: {Color.RED}{failure_count}{Color.RESET}, Ignored files: {Color.YELLOW}{ignored_count}{Color.RESET}")
359+
elif config.mode == "help":
360+
PrintHelpMessage()
302361
except Exception as e:
303-
print(f"\n{Color.RED}{config.mode.upper()} FAILED{Color.RESET}: {e}\n")
362+
print(f"\n\n{Color.RED}{config.mode.upper()} FAILED{Color.RESET}: {e}\n")
304363

305364
elapsed_time = time.time() - start_time
306365
PrintElapsedTime(elapsed_time)
307366

367+
308368
if __name__ == "__main__":
309369
main()

0 commit comments

Comments
 (0)