UtilityLibAPI 1.2510.22__tar.gz → 1.2510.23__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UtilityLibAPI
3
- Version: 1.2510.22
3
+ Version: 1.2510.23
4
4
  Summary: UtilityLibAPI Python package
5
5
  Author-email: James Lin <tylin123@ms27.hinet.net>
6
6
  License: Copyright (c) 2025 James Lin **UtilityLibAPI**
@@ -33,6 +33,10 @@ Dynamic: license-file
33
33
  #UtilityLibAPI Classes
34
34
 
35
35
  ## History of version
36
+ Version 1.2510.22: 2025/10/21<BR>
37
+ Add ExtWrapper7zLib --> 7z Archive tools library
38
+
39
+
36
40
  Version 1.2510.22: 2025/10/21<BR>
37
41
  Add MailSenderLib --> CLASS_MailSender library
38
42
 
@@ -1,6 +1,10 @@
1
1
  #UtilityLibAPI Classes
2
2
 
3
3
  ## History of version
4
+ Version 1.2510.22: 2025/10/21<BR>
5
+ Add ExtWrapper7zLib --> 7z Archive tools library
6
+
7
+
4
8
  Version 1.2510.22: 2025/10/21<BR>
5
9
  Add MailSenderLib --> CLASS_MailSender library
6
10
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "UtilityLibAPI"
7
- version = "1.2510.22"
7
+ version = "1.2510.23"
8
8
  description = "UtilityLibAPI Python package"
9
9
  authors = [
10
10
  { name="James Lin", email="tylin123@ms27.hinet.net" }
@@ -0,0 +1,166 @@
1
+ import os
2
+ import threading
3
+ import subprocess
4
+ import time
5
+ from typing import Callable, Dict, Optional
6
+
7
+ class CLASS_ExtWrapper7z:
8
+ def __init__(self, Pms_7z_path: Optional[str] = None):
9
+ self.Pms_7z_path = Pms_7z_path or self.CUF_Find7zPath()
10
+ self.tasks: Dict[int, Dict] = {}
11
+ self.task_counter = 0
12
+ self.lock = threading.Lock()
13
+
14
+ # 🔍 自動搜尋 7z.exe
15
+ def CUF_Find7zPath(self) -> Optional[str]:
16
+ common_paths = [
17
+ r"C:\\Program Files\\7-Zip\\7z.exe",
18
+ r"C:\\Program Files (x86)\\7-Zip\\7z.exe",
19
+ ]
20
+ for path in common_paths:
21
+ if os.path.exists(path):
22
+ return path
23
+
24
+ for root, dirs, files in os.walk("C:\\"):
25
+ if "7z.exe" in files:
26
+ return os.path.join(root, "7z.exe")
27
+ return None
28
+
29
+ # 📦 建立壓縮或解壓縮任務
30
+ def CUF_AddTask(
31
+ self,
32
+ Pms_Mode: str, # "compress" 或 "extract"
33
+ Pms_InputPath: str,
34
+ Pms_OutputPath: str,
35
+ Pmi_VolumeSizeMB: int = 0,
36
+ Pms_Password: str = "",
37
+ Pmf_Callback: Optional[Callable[[int, float, str], None]] = None,
38
+ ) -> int:
39
+ with self.lock:
40
+ self.task_counter += 1
41
+ mi_TaskID = self.task_counter
42
+ self.tasks[mi_TaskID] = {
43
+ "thread": None,
44
+ "progress": 0.0,
45
+ "status": "queued",
46
+ "cancel": False,
47
+ "mode": Pms_Mode,
48
+ "input": Pms_InputPath,
49
+ "output": Pms_OutputPath,
50
+ "callback": Pmf_Callback,
51
+ }
52
+
53
+ thread = threading.Thread(
54
+ target=self._CUF_RunTask,
55
+ args=(mi_TaskID, Pms_Mode, Pms_InputPath, Pms_OutputPath, Pmi_VolumeSizeMB, Pms_Password),
56
+ daemon=True,
57
+ )
58
+ self.tasks[mi_TaskID]["thread"] = thread
59
+ thread.start()
60
+ return mi_TaskID
61
+
62
+ # 🧩 執行壓縮/解壓縮
63
+ def _CUF_RunTask(self, TaskID, Mode, InputPath, OutputPath, VolumeSizeMB, Password):
64
+ task = self.tasks[TaskID]
65
+ task["status"] = "running"
66
+ Pms_7z_path = self.Pms_7z_path
67
+ if not Pms_7z_path or not os.path.exists(Pms_7z_path):
68
+ task["status"] = "error"
69
+ if task["callback"]:
70
+ task["callback"](TaskID, 0, "7z.exe not found")
71
+ return
72
+
73
+ if Mode == "compress":
74
+ cmd = [Pms_7z_path, "a", OutputPath, "-y"]
75
+ # 🔹 支援多個檔案/資料夾;以 ; 分隔
76
+ paths = [p.strip() for p in InputPath.split(";") if p.strip()]
77
+ cmd.extend(paths)
78
+ if Password:
79
+ cmd.append(f"-p{Password}")
80
+ if VolumeSizeMB > 0:
81
+ cmd.append(f"-v{VolumeSizeMB}m")
82
+
83
+ elif Mode == "extract":
84
+ cmd = [Pms_7z_path, "x", InputPath, f"-o{OutputPath}", "-y"]
85
+ if Password:
86
+ cmd.append(f"-p{Password}")
87
+ else:
88
+ task["status"] = "error"
89
+ if task["callback"]:
90
+ task["callback"](TaskID, 0, "Invalid mode")
91
+ return
92
+
93
+ proc = subprocess.Popen(
94
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
95
+ )
96
+
97
+ for line in proc.stdout:
98
+ if task["cancel"]:
99
+ proc.terminate()
100
+ task["status"] = "cancelled"
101
+ if task["callback"]:
102
+ task["callback"](TaskID, task["progress"], "Cancelled")
103
+ return
104
+
105
+ if "%" in line:
106
+ try:
107
+ percent = float(line.strip().split("%")[0].split()[-1])
108
+ task["progress"] = percent
109
+ if task["callback"]:
110
+ task["callback"](TaskID, percent, "running")
111
+ except:
112
+ pass
113
+
114
+ proc.wait()
115
+ if proc.returncode == 0 and not task["cancel"]:
116
+ task["status"] = "done"
117
+ task["progress"] = 100.0
118
+ if task["callback"]:
119
+ task["callback"](TaskID, 100.0, "done")
120
+ elif not task["cancel"]:
121
+ task["status"] = "error"
122
+ if task["callback"]:
123
+ task["callback"](TaskID, task["progress"], "error")
124
+
125
+ # ❌ 取消任務
126
+ def CUF_CancelTask(self, Pmi_TaskID: int):
127
+ if Pmi_TaskID in self.tasks:
128
+ self.tasks[Pmi_TaskID]["cancel"] = True
129
+
130
+ # 📊 查詢所有任務總進度
131
+ def CUF_GetTotalProgress(self) -> float:
132
+ with self.lock:
133
+ if not self.tasks:
134
+ return 0.0
135
+ total = sum(task["progress"] for task in self.tasks.values())
136
+ return total / len(self.tasks)
137
+
138
+ # 🔍 查詢任務狀態
139
+ def CUF_GetTaskStatus(self, Pmi_TaskID: int) -> str:
140
+ return self.tasks.get(Pmi_TaskID, {}).get("status", "unknown")
141
+
142
+
143
+
144
+
145
+ # ================================================================================
146
+ # === 測試範例 ===
147
+ if(__name__ == "__main__"):
148
+ def my_callback(TaskID, progress, status):
149
+ print(f"[Task {TaskID}] {status} - {progress:.1f}%")
150
+
151
+ wrapper = CLASS_ExtWrapper7z()
152
+ print("7z 路徑:", wrapper.Pms_7z_path)
153
+
154
+ # 壓縮多個檔案 (同時支援萬用字元與多個資料夾)
155
+ task1 = wrapper.CUF_AddTask(
156
+ "compress",
157
+ Pms_InputPath=r"C:\\Temp\\Rubber\b1*.bmp;C:\\boost\\more",
158
+ Pms_OutputPath=r"C:\\TEMP\\OKOKO_GOOD.7z",
159
+ Pmi_VolumeSizeMB=50,
160
+ Pmf_Callback=my_callback,
161
+ )
162
+
163
+ while True:
164
+ total_progress = wrapper.CUF_GetTotalProgress()
165
+ print(f"總進度: {total_progress:.2f}%")
166
+ time.sleep(2)
@@ -0,0 +1,10 @@
1
+ from .EnDeCodeLib import CLASS_EnDeCodeLib
2
+ from .SessionVARLib import CLASS_SessionVAR
3
+ from .MailSenderLib import CLASS_MailSender
4
+ from .ExtWrapper7zLib import CLASS_ExtWrapper7z
5
+
6
+ __all__ = ["CLASS_EnDeCodeLib",
7
+ "CLASS_SessionVAR",
8
+ "CLASS_MailSender",
9
+ "CLASS_ExtWrapper7z"
10
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UtilityLibAPI
3
- Version: 1.2510.22
3
+ Version: 1.2510.23
4
4
  Summary: UtilityLibAPI Python package
5
5
  Author-email: James Lin <tylin123@ms27.hinet.net>
6
6
  License: Copyright (c) 2025 James Lin **UtilityLibAPI**
@@ -33,6 +33,10 @@ Dynamic: license-file
33
33
  #UtilityLibAPI Classes
34
34
 
35
35
  ## History of version
36
+ Version 1.2510.22: 2025/10/21<BR>
37
+ Add ExtWrapper7zLib --> 7z Archive tools library
38
+
39
+
36
40
  Version 1.2510.22: 2025/10/21<BR>
37
41
  Add MailSenderLib --> CLASS_MailSender library
38
42
 
@@ -3,6 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  setup.cfg
5
5
  src/UtilityLibAPI/EnDeCodeLib.py
6
+ src/UtilityLibAPI/ExtWrapper7zLib.py
6
7
  src/UtilityLibAPI/MailSenderLib.py
7
8
  src/UtilityLibAPI/SessionVARLib.py
8
9
  src/UtilityLibAPI/__init__.py
@@ -1,8 +0,0 @@
1
- from .EnDeCodeLib import CLASS_EnDeCodeLib
2
- from .SessionVARLib import CLASS_SessionVAR
3
- from .MailSenderLib import CLASS_MailSender
4
-
5
- __all__ = ["CLASS_EnDeCodeLib",
6
- "CLASS_SessionVAR",
7
- "CLASS_MailSender"
8
- ]