|
26 | 26 |
|
27 | 27 | __author__ = "Virtuous Flame & Gabriel" |
28 | 28 | __license__ = "GPL" |
29 | | -__version__ = "1.0.2" |
| 29 | +__version__ = "1.1.0" |
30 | 30 |
|
31 | 31 | import lz4.block |
32 | 32 | from struct import pack, unpack |
33 | 33 | from multiprocessing import Pool |
34 | | -from getopt import gnu_getopt, GetoptError |
| 34 | +from multiprocessing import Pool |
35 | 35 | import gettext |
36 | 36 |
|
37 | 37 | # Setup translation |
|
60 | 60 | ZISO_MAGIC = 0x4F53495A |
61 | 61 | DEFAULT_ALIGN = 0 |
62 | 62 | DEFAULT_BLOCK_SIZE = 0x800 |
63 | | -COMPRESS_THREHOLD_DEFAULT = 95 |
| 63 | +COMPRESS_THRESHOLD_DEFAULT = 95 |
64 | 64 | DEFAULT_PADDING = br'X' |
65 | 65 |
|
66 | 66 | MP_DEFAULT = False |
@@ -91,17 +91,7 @@ def lz4_decompress(compressed, block_size): |
91 | 91 | return decompressed |
92 | 92 |
|
93 | 93 |
|
94 | | -def usage(): |
95 | | - print("ziso-python (GTK4) 1.0 by Virtuous Flame & Gabriel") |
96 | | - print("Usage: ziso [-c level] [-m] [-t percent] [-h] infile outfile") |
97 | | - print(" -c level: 1-12 compress ISO to ZSO, 1 for standard compression, >1 for high compression") |
98 | | - print(" 0 decompress ZSO to ISO") |
99 | | - print(" -b size: 2048-8192, specify block size (2048 by default)") |
100 | | - print(" -m Use multiprocessing acceleration for compressing") |
101 | | - print(" -t percent Compression Threshold (1-100)") |
102 | | - print(" -a align Padding alignment 0=small/slow 6=fast/large") |
103 | | - print(" -p pad Padding byte") |
104 | | - print(" -h this help") |
| 94 | + |
105 | 95 |
|
106 | 96 |
|
107 | 97 | def open_input_output(fname_in, fname_out): |
@@ -251,10 +241,10 @@ def compress_zso(fname_in, fname_out, level, bsize, mp=False, threshold=95, alig |
251 | 241 | elif percent_cnt >= percent_period and percent_period != 0: |
252 | 242 | percent_cnt = 0 |
253 | 243 | if block == 0: |
254 | | - print("compress %3d%% avarage rate %3d%%\r" % ( |
| 244 | + print("compress %3d%% average rate %3d%%\r" % ( |
255 | 245 | block / percent_period, 0), file=sys.stderr, end='\r') |
256 | 246 | else: |
257 | | - print("compress %3d%% avarage rate %3d%%\r" % ( |
| 247 | + print("compress %3d%% average rate %3d%%\r" % ( |
258 | 248 | block / percent_period, 100*write_pos/(block*block_size)), file=sys.stderr, end='\r') |
259 | 249 |
|
260 | 250 | if mp: |
@@ -342,8 +332,6 @@ def __init__(self, app): |
342 | 332 | self.window = self.builder.get_object("window") |
343 | 333 | self.window.set_application(app) |
344 | 334 |
|
345 | | - self.window.set_application(app) |
346 | | - |
347 | 335 | self.file_list = self.builder.get_object("file_list") |
348 | 336 | self.stack_view = self.builder.get_object("stack_view") # GtkStack |
349 | 337 |
|
@@ -537,53 +525,19 @@ def update_ui_state(self): |
537 | 525 | if not self.processing: |
538 | 526 | self.convert_btn.set_sensitive(has_files and has_dest) |
539 | 527 |
|
540 | | - def process_queue(self, target_fmt, level, dest_folder): |
541 | | - |
542 | | - icon_path = os.path.join(os.path.dirname(__file__), "..", "data", "check-round-outline2-symbolic.svg") |
543 | | - |
544 | | - # Iterate using a list copy of children to avoid issues (though we aren't modifying structure) |
545 | | - # But we need to iterate in thread, safe way is to get items first? |
546 | | - # No, we cannot touch widgets in thread. We need to iterate in main thread or similar? |
547 | | - # Actually, `store` was thread-safe-ish or we were just lucky. |
548 | | - # GTK4 widgets are NOT thread safe. We must NOT access them in this thread. |
549 | | - # We need to gather data first or implement a safer queue mechanism. |
550 | | - |
551 | | - # However, for simplicity and matching previous structure: |
552 | | - # We will use GLib.idle_add to get the list of files to process *before* starting heavy work, |
553 | | - # OR we can just assume `process_queue` is running in a thread and we shouldn't touch widgets. |
554 | | - |
555 | | - # Refactor: Get all file data BEFORE thread start? |
556 | | - # The previous code iterated `self.store` in the thread. That was technically unsafe but worked sometimes. |
557 | | - # `self.store` is a GtkTreeModel. |
558 | | - # We MUST NOT iterate Gtk widgets in a background thread. |
559 | | - |
560 | | - # FIX: We passed `process_queue` to Thread. |
561 | | - # We should gather tasks first. |
562 | | - pass # Replaces below |
563 | | - |
564 | 528 | # New helper to gather tasks safely on main thread |
565 | 529 | def get_tasks(self): |
566 | 530 | tasks = [] |
567 | 531 | child = self.file_list.get_first_child() |
568 | 532 | while child: |
569 | 533 | tasks.append({ |
570 | 534 | "row": child, |
571 | | - "filepath": child.filepath, |
572 | | - "ext": child.file_ext |
| 535 | + "filepath": getattr(child, "filepath", ""), |
| 536 | + "ext": getattr(child, "file_ext", "") |
573 | 537 | }) |
574 | 538 | child = child.get_next_sibling() |
575 | 539 | return tasks |
576 | 540 |
|
577 | | - def process_queue(self, target_fmt, level, dest_folder): |
578 | | - # This is running in a thread. |
579 | | - # We can't access self.file_list children properties directly here safely if they were widgets. |
580 | | - # BUT `self.store` was data. `AdwActionRow` is a widget. |
581 | | - # We should rely on a pre-calculated list of tasks passed to this method, |
582 | | - # OR use GLib.invoke to get them (but that blocks). |
583 | | - |
584 | | - # Better approach: The `on_convert_clicked` should generate the list of tasks. |
585 | | - pass |
586 | | - |
587 | 541 | # Redefining on_convert_clicked to fix the thread safety issue introduced by moving to Widgets |
588 | 542 | def on_convert_clicked(self, btn): |
589 | 543 | if self.processing: return |
@@ -701,80 +655,13 @@ def on_about(self, action, param): |
701 | 655 | ).present() |
702 | 656 |
|
703 | 657 |
|
704 | | -def parse_args(): |
705 | | - try: |
706 | | - optlist, args = gnu_getopt(sys.argv[1:], "c:b:mt:a:p:h") |
707 | | - except GetoptError as err: |
708 | | - print(str(err)) |
709 | | - usage() |
710 | | - sys.exit(-1) |
711 | | - |
712 | | - level = None |
713 | | - bsize = DEFAULT_BLOCK_SIZE |
714 | | - mp = MP_DEFAULT |
715 | | - threshold = COMPRESS_THREHOLD_DEFAULT |
716 | | - align = None |
717 | | - padding = DEFAULT_PADDING |
718 | | - |
719 | | - for o, a in optlist: |
720 | | - if o == '-c': |
721 | | - level = int(a) |
722 | | - elif o == '-b': |
723 | | - bsize = int(a) |
724 | | - elif o == '-m': |
725 | | - mp = True |
726 | | - elif o == '-t': |
727 | | - threshold = min(int(a), 100) |
728 | | - elif o == '-a': |
729 | | - align = int(a) |
730 | | - elif o == '-p': |
731 | | - padding = bytes(a[0], encoding='utf8') |
732 | | - elif o == '-h': |
733 | | - usage() |
734 | | - sys.exit(0) |
735 | | - |
736 | | - if level is None: |
737 | | - print("Error: Nível de compressão (-c) é obrigatório no modo CLI.") |
738 | | - usage() |
739 | | - sys.exit(-1) |
740 | | - |
741 | | - try: |
742 | | - fname_in = args[0] |
743 | | - fname_out = args[1] |
744 | | - except IndexError: |
745 | | - print("Error: Você deve especificar os arquivos de entrada e saída.") |
746 | | - usage() |
747 | | - sys.exit(-1) |
748 | | - |
749 | | - if bsize % 2048 != 0: |
750 | | - print("Error: Tamanho do bloco inválido. Deve ser múltiplo de 2048.") |
751 | | - sys.exit(-1) |
752 | | - |
753 | | - return level, bsize, fname_in, fname_out, mp, threshold, align, padding |
754 | | - |
755 | | - |
756 | 658 | def main(): |
757 | | - if len(sys.argv) > 1: |
758 | | - # CLI Mode |
759 | | - level, bsize, fname_in, fname_out, mp, threshold, align, padding = parse_args() |
760 | | - |
761 | | - print(f"ziso-python 1.0 (CLI)") |
762 | | - if level == 0: |
763 | | - print(f"Decompressing {fname_in} to {fname_out}...") |
764 | | - decompress_zso(fname_in, fname_out) |
765 | | - else: |
766 | | - print(f"Compressing {fname_in} to {fname_out} (level {level})...") |
767 | | - compress_zso(fname_in, fname_out, level, bsize, mp, threshold, align, padding) |
768 | | - print("\nDone.") |
769 | | - |
770 | | - elif HAS_GUI: |
771 | | - # GUI Mode |
772 | | - app = ZisoApp() |
773 | | - sys.exit(app.run(sys.argv)) |
774 | | - else: |
775 | | - usage() |
776 | | - print("\nError: No GUI backend available (Gtk 4.0/Adw 1) and no CLI arguments provided.") |
| 659 | + if not HAS_GUI: |
| 660 | + print("Error: GTK4/Adwaita not available. This application requires a graphical environment.") |
777 | 661 | sys.exit(1) |
| 662 | + |
| 663 | + app = ZisoApp() |
| 664 | + sys.exit(app.run(sys.argv)) |
778 | 665 |
|
779 | 666 | if __name__ == "__main__": |
780 | 667 | main() |
0 commit comments