damon-devtools 0.2.0__tar.gz → 0.4.0__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.
Files changed (32) hide show
  1. damon_devtools-0.4.0/PKG-INFO +126 -0
  2. damon_devtools-0.4.0/README.md +115 -0
  3. damon_devtools-0.4.0/damon_devtools.egg-info/PKG-INFO +126 -0
  4. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/__main__.py +12 -2
  5. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/launcher.py +2 -1
  6. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/core.py +162 -5
  7. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/ui.py +35 -6
  8. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/pyproject.toml +1 -1
  9. damon_devtools-0.2.0/PKG-INFO +0 -46
  10. damon_devtools-0.2.0/README.md +0 -35
  11. damon_devtools-0.2.0/damon_devtools.egg-info/PKG-INFO +0 -46
  12. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/SOURCES.txt +0 -0
  13. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/dependency_links.txt +0 -0
  14. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/entry_points.txt +0 -0
  15. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/requires.txt +0 -0
  16. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/top_level.txt +0 -0
  17. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/__init__.py +0 -0
  18. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/__init__.py +0 -0
  19. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/__init__.py +0 -0
  20. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/__main__.py +0 -0
  21. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/models.py +0 -0
  22. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/__init__.py +0 -0
  23. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/__main__.py +0 -0
  24. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/core.py +0 -0
  25. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/models.py +0 -0
  26. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/ui.py +0 -0
  27. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/__init__.py +0 -0
  28. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/__main__.py +0 -0
  29. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/cli.py +0 -0
  30. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/core.py +0 -0
  31. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/types.py +0 -0
  32. {damon_devtools-0.2.0 → damon_devtools-0.4.0}/setup.cfg +0 -0
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: damon-devtools
3
+ Version: 0.4.0
4
+ Summary: Developer CLI tools — portkill, removebg, apkpull
5
+ Author: damon
6
+ License: MIT
7
+ Keywords: cli,devtools,android,apk,adb
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: psutil>=5.9
11
+
12
+ # devtools
13
+
14
+ Developer CLI tools: portkill, removebg, apkpull.
15
+
16
+ ---
17
+
18
+ ## Tools
19
+
20
+ ### portkill
21
+
22
+ Kill Node.js processes by port or scan all node processes.
23
+
24
+ ```bash
25
+ python -m devtools portkill
26
+ python -m devtools portkill --listen-only
27
+ ```
28
+
29
+ ### apkpull
30
+
31
+ Pull APK files from Android device via ADB.
32
+
33
+ Features:
34
+ - List and search third-party apps
35
+ - Fast app name lookup via aapt2
36
+ - Auto-download aapt2 if not found locally
37
+ - Multi-device support
38
+ - Label caching for faster subsequent runs
39
+
40
+ ```bash
41
+ # Interactive mode
42
+ python -m devtools apkpull
43
+
44
+ # Specify target directory
45
+ python -m devtools apkpull /path/to/output
46
+
47
+ # Search apps by keyword
48
+ python -m devtools apkpull -s wechat
49
+ ```
50
+
51
+ ### removebg
52
+
53
+ Remove image backgrounds using the remove.bg API.
54
+
55
+ ```bash
56
+ python -m devtools removebg input.jpg
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Install
62
+
63
+ ```bash
64
+ pip install damon-devtools
65
+ ```
66
+
67
+ Requires: Python >= 3.10
68
+
69
+ ---
70
+
71
+ # devtools
72
+
73
+ 开发者命令行工具集:portkill、removebg、apkpull。
74
+
75
+ ---
76
+
77
+ ## 工具
78
+
79
+ ### portkill
80
+
81
+ 根据端口查找并终止 Node.js 进程,或扫描所有 node 进程。
82
+
83
+ ```bash
84
+ python -m devtools portkill
85
+ python -m devtools portkill --listen-only
86
+ ```
87
+
88
+ ### apkpull
89
+
90
+ 通过 ADB 从 Android 设备拉取 APK 安装包。
91
+
92
+ 功能特点:
93
+ - 列出和搜索第三方应用
94
+ - 通过 aapt2 快速获取应用名
95
+ - 本地未找到 aapt2 时自动下载
96
+ - 多设备支持
97
+ - 应用名缓存,加速后续运行
98
+
99
+ ```bash
100
+ # 交互模式
101
+ python -m devtools apkpull
102
+
103
+ # 指定目标目录
104
+ python -m devtools apkpull /path/to/output
105
+
106
+ # 搜索应用
107
+ python -m devtools apkpull -s wechat
108
+ ```
109
+
110
+ ### removebg
111
+
112
+ 使用 remove.bg API 去除图片背景。
113
+
114
+ ```bash
115
+ python -m devtools removebg input.jpg
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 安装
121
+
122
+ ```bash
123
+ pip install damon-devtools
124
+ ```
125
+
126
+ 要求:Python >= 3.10
@@ -0,0 +1,115 @@
1
+ # devtools
2
+
3
+ Developer CLI tools: portkill, removebg, apkpull.
4
+
5
+ ---
6
+
7
+ ## Tools
8
+
9
+ ### portkill
10
+
11
+ Kill Node.js processes by port or scan all node processes.
12
+
13
+ ```bash
14
+ python -m devtools portkill
15
+ python -m devtools portkill --listen-only
16
+ ```
17
+
18
+ ### apkpull
19
+
20
+ Pull APK files from Android device via ADB.
21
+
22
+ Features:
23
+ - List and search third-party apps
24
+ - Fast app name lookup via aapt2
25
+ - Auto-download aapt2 if not found locally
26
+ - Multi-device support
27
+ - Label caching for faster subsequent runs
28
+
29
+ ```bash
30
+ # Interactive mode
31
+ python -m devtools apkpull
32
+
33
+ # Specify target directory
34
+ python -m devtools apkpull /path/to/output
35
+
36
+ # Search apps by keyword
37
+ python -m devtools apkpull -s wechat
38
+ ```
39
+
40
+ ### removebg
41
+
42
+ Remove image backgrounds using the remove.bg API.
43
+
44
+ ```bash
45
+ python -m devtools removebg input.jpg
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ pip install damon-devtools
54
+ ```
55
+
56
+ Requires: Python >= 3.10
57
+
58
+ ---
59
+
60
+ # devtools
61
+
62
+ 开发者命令行工具集:portkill、removebg、apkpull。
63
+
64
+ ---
65
+
66
+ ## 工具
67
+
68
+ ### portkill
69
+
70
+ 根据端口查找并终止 Node.js 进程,或扫描所有 node 进程。
71
+
72
+ ```bash
73
+ python -m devtools portkill
74
+ python -m devtools portkill --listen-only
75
+ ```
76
+
77
+ ### apkpull
78
+
79
+ 通过 ADB 从 Android 设备拉取 APK 安装包。
80
+
81
+ 功能特点:
82
+ - 列出和搜索第三方应用
83
+ - 通过 aapt2 快速获取应用名
84
+ - 本地未找到 aapt2 时自动下载
85
+ - 多设备支持
86
+ - 应用名缓存,加速后续运行
87
+
88
+ ```bash
89
+ # 交互模式
90
+ python -m devtools apkpull
91
+
92
+ # 指定目标目录
93
+ python -m devtools apkpull /path/to/output
94
+
95
+ # 搜索应用
96
+ python -m devtools apkpull -s wechat
97
+ ```
98
+
99
+ ### removebg
100
+
101
+ 使用 remove.bg API 去除图片背景。
102
+
103
+ ```bash
104
+ python -m devtools removebg input.jpg
105
+ ```
106
+
107
+ ---
108
+
109
+ ## 安装
110
+
111
+ ```bash
112
+ pip install damon-devtools
113
+ ```
114
+
115
+ 要求:Python >= 3.10
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: damon-devtools
3
+ Version: 0.4.0
4
+ Summary: Developer CLI tools — portkill, removebg, apkpull
5
+ Author: damon
6
+ License: MIT
7
+ Keywords: cli,devtools,android,apk,adb
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: psutil>=5.9
11
+
12
+ # devtools
13
+
14
+ Developer CLI tools: portkill, removebg, apkpull.
15
+
16
+ ---
17
+
18
+ ## Tools
19
+
20
+ ### portkill
21
+
22
+ Kill Node.js processes by port or scan all node processes.
23
+
24
+ ```bash
25
+ python -m devtools portkill
26
+ python -m devtools portkill --listen-only
27
+ ```
28
+
29
+ ### apkpull
30
+
31
+ Pull APK files from Android device via ADB.
32
+
33
+ Features:
34
+ - List and search third-party apps
35
+ - Fast app name lookup via aapt2
36
+ - Auto-download aapt2 if not found locally
37
+ - Multi-device support
38
+ - Label caching for faster subsequent runs
39
+
40
+ ```bash
41
+ # Interactive mode
42
+ python -m devtools apkpull
43
+
44
+ # Specify target directory
45
+ python -m devtools apkpull /path/to/output
46
+
47
+ # Search apps by keyword
48
+ python -m devtools apkpull -s wechat
49
+ ```
50
+
51
+ ### removebg
52
+
53
+ Remove image backgrounds using the remove.bg API.
54
+
55
+ ```bash
56
+ python -m devtools removebg input.jpg
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Install
62
+
63
+ ```bash
64
+ pip install damon-devtools
65
+ ```
66
+
67
+ Requires: Python >= 3.10
68
+
69
+ ---
70
+
71
+ # devtools
72
+
73
+ 开发者命令行工具集:portkill、removebg、apkpull。
74
+
75
+ ---
76
+
77
+ ## 工具
78
+
79
+ ### portkill
80
+
81
+ 根据端口查找并终止 Node.js 进程,或扫描所有 node 进程。
82
+
83
+ ```bash
84
+ python -m devtools portkill
85
+ python -m devtools portkill --listen-only
86
+ ```
87
+
88
+ ### apkpull
89
+
90
+ 通过 ADB 从 Android 设备拉取 APK 安装包。
91
+
92
+ 功能特点:
93
+ - 列出和搜索第三方应用
94
+ - 通过 aapt2 快速获取应用名
95
+ - 本地未找到 aapt2 时自动下载
96
+ - 多设备支持
97
+ - 应用名缓存,加速后续运行
98
+
99
+ ```bash
100
+ # 交互模式
101
+ python -m devtools apkpull
102
+
103
+ # 指定目标目录
104
+ python -m devtools apkpull /path/to/output
105
+
106
+ # 搜索应用
107
+ python -m devtools apkpull -s wechat
108
+ ```
109
+
110
+ ### removebg
111
+
112
+ 使用 remove.bg API 去除图片背景。
113
+
114
+ ```bash
115
+ python -m devtools removebg input.jpg
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 安装
121
+
122
+ ```bash
123
+ pip install damon-devtools
124
+ ```
125
+
126
+ 要求:Python >= 3.10
@@ -1,7 +1,11 @@
1
1
  import sys
2
+ import argparse
2
3
  from importlib import import_module
4
+ from importlib.metadata import version as get_version
3
5
  from devtools.tools import get_tool
4
6
 
7
+ VERSION = get_version("damon-devtools")
8
+
5
9
 
6
10
  def run_tool(tool_name: str, args: list[str]):
7
11
  tool = get_tool(tool_name)
@@ -41,8 +45,14 @@ def run_tool(tool_name: str, args: list[str]):
41
45
 
42
46
 
43
47
  def main():
44
- if len(sys.argv) > 1:
45
- run_tool(sys.argv[1], sys.argv[2:])
48
+ parser = argparse.ArgumentParser(prog="devtools", description="Developer CLI tools")
49
+ parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {VERSION}")
50
+ parser.add_argument("tool", nargs="?", help="Tool name to run")
51
+ parser.add_argument("args", nargs="*", help="Arguments for the tool")
52
+ parsed = parser.parse_args()
53
+
54
+ if parsed.tool:
55
+ run_tool(parsed.tool, parsed.args)
46
56
  else:
47
57
  from devtools.launcher import render_launcher
48
58
  result = render_launcher()
@@ -3,6 +3,7 @@ if sys.platform == "win32":
3
3
  sys.stdout.reconfigure(encoding="utf-8")
4
4
 
5
5
  from devtools.tools import TOOLS
6
+ from devtools.__main__ import VERSION
6
7
 
7
8
 
8
9
  def render_launcher():
@@ -16,7 +17,7 @@ def render_launcher():
16
17
 
17
18
  print()
18
19
  print("=" * 50)
19
- print(" devtools — 开发者工具箱")
20
+ print(f" devtools {VERSION} — 开发者工具箱")
20
21
  print("=" * 50)
21
22
  print()
22
23
  for i, tool in enumerate(TOOLS):
@@ -17,6 +17,15 @@ AAPT2_REMOTE_URL = "https://github.com/JonForShort/android-tools/raw/refs/heads/
17
17
  # Device-side aapt2 path
18
18
  AAPT2_DEVICE_PATH = "/data/local/tmp/aapt2"
19
19
 
20
+ # Apktool download URL and local path
21
+ APKTOOL_VERSION = "2.12.1"
22
+ APKTOOL_REMOTE_URL = f"https://github.com/iBotPeaches/Apktool/releases/download/v{APKTOOL_VERSION}/apktool_{APKTOOL_VERSION}.jar"
23
+ APKTOOL_LOCAL_DIR = os.path.join(
24
+ os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
25
+ "deps", "apktool"
26
+ )
27
+ APKTOOL_JAR = os.path.join(APKTOOL_LOCAL_DIR, f"apktool_{APKTOOL_VERSION}.jar")
28
+
20
29
  # App name cache
21
30
  LABEL_CACHE_FILE = os.path.expanduser("~/.devtools/cache/apkpull/label_cache.json")
22
31
 
@@ -215,19 +224,25 @@ def scan_packages() -> list[str]:
215
224
  return pkgs
216
225
 
217
226
  def get_apk_path(pkg: str) -> str | None:
218
- """执行 pm path,返回 APK 物理路径"""
227
+ """执行 pm path,返回 APK 物理路径(仅返回第一个)"""
228
+ paths = get_apk_paths(pkg)
229
+ return paths[0] if paths else None
230
+
231
+ def get_apk_paths(pkg: str) -> list[str]:
232
+ """执行 pm path,返回所有 APK 物理路径(split APK 会返回多个)"""
219
233
  serial = get_device_serial()
220
234
  try:
221
235
  result = subprocess.run(
222
236
  adb_cmd(["shell", "pm", "path", pkg], serial),
223
237
  capture_output=True, text=True, encoding="utf-8", errors="replace", check=True
224
238
  )
239
+ paths = []
225
240
  for line in result.stdout.strip().split("\n"):
226
241
  if line.startswith("package:"):
227
- return line[8:].strip()
228
- return None
242
+ paths.append(line[8:].strip())
243
+ return paths
229
244
  except (subprocess.CalledProcessError, FileNotFoundError):
230
- return None
245
+ return []
231
246
 
232
247
  def get_app_name_pm_dump(pkg: str) -> str:
233
248
  """通过设备端 pm dump 提取 application label(失败时回退包名)"""
@@ -300,4 +315,146 @@ def pull_apk(apk_path: str, dest_path: str) -> bool:
300
315
  )
301
316
  return True
302
317
  except (subprocess.CalledProcessError, FileNotFoundError):
303
- return False
318
+ return False
319
+
320
+ def download_apktool() -> str | None:
321
+ """Download apktool jar and return local path, or None on failure."""
322
+ if os.path.isfile(APKTOOL_JAR):
323
+ return APKTOOL_JAR
324
+ try:
325
+ os.makedirs(APKTOOL_LOCAL_DIR, exist_ok=True)
326
+ print(f"正在下载 apktool {APKTOOL_VERSION}...")
327
+ urllib.request.urlretrieve(APKTOOL_REMOTE_URL, APKTOOL_JAR)
328
+ return APKTOOL_JAR
329
+ except Exception as e:
330
+ print(f"apktool 下载失败: {e}")
331
+ return None
332
+
333
+ def ensure_apktool() -> str | None:
334
+ """Ensure apktool is available, download if missing, and initialize framework."""
335
+ # Check if apktool.jar exists locally
336
+ if not os.path.isfile(APKTOOL_JAR):
337
+ if not download_apktool():
338
+ return None
339
+
340
+ # Initialize apktool framework (one-time setup)
341
+ # --force ensures the framework is updated to support newer Android SDK attributes
342
+ try:
343
+ subprocess.run(
344
+ ["java", "-jar", APKTOOL_JAR, "empty-framework-dir"],
345
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60
346
+ )
347
+ except Exception:
348
+ pass # Non-fatal, continue even if cf fails
349
+
350
+ return APKTOOL_JAR
351
+
352
+ def merge_split_apks(apk_paths: list[str], output_apk: str, java_path: str | None = None) -> bool:
353
+ """
354
+ Merge multiple split APKs into a single APK using apktool.
355
+ apk_paths: list of local APK file paths (first is treated as base)
356
+ output_apk: path to write the merged APK
357
+ Returns True on success.
358
+ """
359
+ import shutil
360
+
361
+ java = java_path or "java"
362
+ apktool_jar = ensure_apktool()
363
+ if not apktool_jar:
364
+ print("错误: apktool 不可用")
365
+ return False
366
+
367
+ # Check java
368
+ try:
369
+ subprocess.run([java, "-version"], capture_output=True, check=True)
370
+ except (subprocess.CalledProcessError, FileNotFoundError):
371
+ print("错误: 未找到 Java,请确保已安装 JDK 并加入 PATH")
372
+ return False
373
+
374
+ base_apk = apk_paths[0]
375
+ split_apks = apk_paths[1:]
376
+
377
+ # Create temp working directory
378
+ work_dir = tempfile.mkdtemp(prefix="apkmerge_")
379
+ base_dir = os.path.join(work_dir, "base")
380
+ decoded_dir = base_dir # apktool will create this
381
+
382
+ try:
383
+ # Decode base APK
384
+ print(f"正在解码基础 APK...")
385
+ result = subprocess.run(
386
+ [java, "-jar", apktool_jar, "d", base_apk, "-o", base_dir, "-f"],
387
+ capture_output=True, text=True, encoding="utf-8", errors="replace"
388
+ )
389
+ if result.returncode != 0:
390
+ print(f"apktool 解码失败: {result.stderr}")
391
+ return False
392
+
393
+ # Decode and merge each split APK
394
+ for split_apk in split_apks:
395
+ print(f"正在解码分片 APK: {os.path.basename(split_apk)}")
396
+ split_dir = tempfile.mkdtemp(prefix="split_")
397
+ result = subprocess.run(
398
+ [java, "-jar", apktool_jar, "d", split_apk, "-o", split_dir, "-f"],
399
+ capture_output=True, text=True, encoding="utf-8", errors="replace"
400
+ )
401
+ if result.returncode != 0:
402
+ print(f"apktool 解码分片失败: {result.stderr}")
403
+ return False
404
+
405
+ # Copy dex files from split to base (apktool names them classes.dex, classes2.dex, etc.)
406
+ split_root = split_dir
407
+ for fname in os.listdir(split_root):
408
+ if fname.endswith(".dex"):
409
+ src = os.path.join(split_root, fname)
410
+ dst = os.path.join(base_dir, fname)
411
+ # If already exists (from another split), rename (classes3.dex, etc.)
412
+ if os.path.exists(dst):
413
+ base_name = fname.rsplit(".", 1)[0]
414
+ ext = fname.rsplit(".", 1)[1]
415
+ counter = 2
416
+ while os.path.exists(dst):
417
+ dst = os.path.join(base_dir, f"{base_name}{counter}.{ext}")
418
+ counter += 1
419
+ shutil.copy2(src, dst)
420
+ print(f" 合并: {fname} -> {os.path.basename(dst)}")
421
+
422
+ # Copy lib directories if present (architecture-specific libs)
423
+ for lib_name in ["lib", "assets"]:
424
+ split_lib = os.path.join(split_dir, lib_name)
425
+ if os.path.isdir(split_lib):
426
+ base_lib = os.path.join(base_dir, lib_name)
427
+ if not os.path.isdir(base_lib):
428
+ shutil.copytree(split_lib, base_lib)
429
+ else:
430
+ # Merge contents
431
+ for item in os.listdir(split_lib):
432
+ src_item = os.path.join(split_lib, item)
433
+ dst_item = os.path.join(base_lib, item)
434
+ if os.path.isdir(src_item):
435
+ if not os.path.exists(dst_item):
436
+ shutil.copytree(src_item, dst_item)
437
+ else:
438
+ if not os.path.exists(dst_item):
439
+ shutil.copy2(src_item, dst_item)
440
+ shutil.rmtree(split_dir)
441
+
442
+ # Build merged APK
443
+ print("正在构建合并 APK...")
444
+ result = subprocess.run(
445
+ [java, "-jar", apktool_jar, "b", base_dir, "-o", output_apk],
446
+ capture_output=True, text=True, encoding="utf-8", errors="replace"
447
+ )
448
+ if result.returncode != 0:
449
+ print(f"apktool 构建失败: {result.stderr}")
450
+ return False
451
+
452
+ print(f"合并完成: {output_apk}")
453
+ return True
454
+
455
+ except Exception as e:
456
+ print(f"合并过程出错: {e}")
457
+ return False
458
+ finally:
459
+ # Cleanup
460
+ shutil.rmtree(work_dir, ignore_errors=True)
@@ -158,21 +158,50 @@ def run(args: list[str] | None = None):
158
158
  continue
159
159
 
160
160
  # Pull APKs
161
- from devtools.tools.apkpull.core import get_apk_path, pull_apk, sanitize_filename
161
+ from devtools.tools.apkpull.core import get_apk_paths, pull_apk, merge_split_apks, sanitize_filename
162
162
  succeeded: list[str] = []
163
163
  failed: list[tuple] = []
164
164
 
165
165
  for p in selected:
166
- apk_path = get_apk_path(p.pkg)
167
- if not apk_path:
166
+ apk_paths = get_apk_paths(p.pkg)
167
+ if not apk_paths:
168
168
  failed.append((p.label, "未找到 APK 路径"))
169
169
  continue
170
170
  file_base = p.label if p.label and p.label != p.pkg else p.pkg
171
171
  dest_apk = os.path.join(parsed.dest_dir, f"{sanitize_filename(file_base)}.apk")
172
- if pull_apk(apk_path, dest_apk):
173
- succeeded.append(p.label)
172
+
173
+ if len(apk_paths) == 1:
174
+ # Single APK - direct pull
175
+ if pull_apk(apk_paths[0], dest_apk):
176
+ succeeded.append(p.label)
177
+ else:
178
+ failed.append((p.label, "拉取失败"))
174
179
  else:
175
- failed.append((p.label, "拉取失败"))
180
+ # Split APK - pull all then merge
181
+ print(f"\n检测到 split APK (共 {len(apk_paths)} 个分片),正在拉取并合并...")
182
+ tmp_apks: list[str] = []
183
+ tmp_dir = os.path.join(parsed.dest_dir, f".tmp_{sanitize_filename(file_base)}")
184
+ try:
185
+ os.makedirs(tmp_dir, exist_ok=True)
186
+ all_ok = True
187
+ for i, apk_path in enumerate(apk_paths):
188
+ tmp_file = os.path.join(tmp_dir, f"split_{i}.apk")
189
+ if not pull_apk(apk_path, tmp_file):
190
+ failed.append((p.label, f"拉取分片 {i+1} 失败"))
191
+ all_ok = False
192
+ break
193
+ tmp_apks.append(tmp_file)
194
+
195
+ if all_ok:
196
+ if merge_split_apks(tmp_apks, dest_apk):
197
+ succeeded.append(p.label)
198
+ else:
199
+ failed.append((p.label, "合并 APK 失败"))
200
+ finally:
201
+ # Cleanup temp files
202
+ import shutil
203
+ if os.path.isdir(tmp_dir):
204
+ shutil.rmtree(tmp_dir, ignore_errors=True)
176
205
 
177
206
  print()
178
207
  if succeeded:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "damon-devtools"
3
- version = "0.2.0"
3
+ version = "0.4.0"
4
4
  description = "Developer CLI tools — portkill, removebg, apkpull"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,46 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: damon-devtools
3
- Version: 0.2.0
4
- Summary: Developer CLI tools — portkill, removebg, apkpull
5
- Author: damon
6
- License: MIT
7
- Keywords: cli,devtools,android,apk,adb
8
- Requires-Python: >=3.10
9
- Description-Content-Type: text/markdown
10
- Requires-Dist: psutil>=5.9
11
-
12
- # devtools
13
-
14
- Developer CLI tools: portkill, removebg, apkpull.
15
-
16
- ## Tools
17
-
18
- ### portkill
19
- Kill Node.js processes by port or scan all node processes.
20
-
21
- ```bash
22
- python -m devtools portkill
23
- python -m devtools portkill --listen-only
24
- ```
25
-
26
- ### apkpull
27
- Pull APK files from Android device via ADB.
28
-
29
- ```bash
30
- python -m devtools apkpull [target_dir]
31
- ```
32
-
33
- ### removebg
34
- Remove image backgrounds using the remove.bg API.
35
-
36
- ```bash
37
- python -m devtools removebg input.jpg
38
- ```
39
-
40
- ## Install
41
-
42
- ```bash
43
- pip install damon-devtools
44
- ```
45
-
46
- Requires: Python >=3.10
@@ -1,35 +0,0 @@
1
- # devtools
2
-
3
- Developer CLI tools: portkill, removebg, apkpull.
4
-
5
- ## Tools
6
-
7
- ### portkill
8
- Kill Node.js processes by port or scan all node processes.
9
-
10
- ```bash
11
- python -m devtools portkill
12
- python -m devtools portkill --listen-only
13
- ```
14
-
15
- ### apkpull
16
- Pull APK files from Android device via ADB.
17
-
18
- ```bash
19
- python -m devtools apkpull [target_dir]
20
- ```
21
-
22
- ### removebg
23
- Remove image backgrounds using the remove.bg API.
24
-
25
- ```bash
26
- python -m devtools removebg input.jpg
27
- ```
28
-
29
- ## Install
30
-
31
- ```bash
32
- pip install damon-devtools
33
- ```
34
-
35
- Requires: Python >=3.10
@@ -1,46 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: damon-devtools
3
- Version: 0.2.0
4
- Summary: Developer CLI tools — portkill, removebg, apkpull
5
- Author: damon
6
- License: MIT
7
- Keywords: cli,devtools,android,apk,adb
8
- Requires-Python: >=3.10
9
- Description-Content-Type: text/markdown
10
- Requires-Dist: psutil>=5.9
11
-
12
- # devtools
13
-
14
- Developer CLI tools: portkill, removebg, apkpull.
15
-
16
- ## Tools
17
-
18
- ### portkill
19
- Kill Node.js processes by port or scan all node processes.
20
-
21
- ```bash
22
- python -m devtools portkill
23
- python -m devtools portkill --listen-only
24
- ```
25
-
26
- ### apkpull
27
- Pull APK files from Android device via ADB.
28
-
29
- ```bash
30
- python -m devtools apkpull [target_dir]
31
- ```
32
-
33
- ### removebg
34
- Remove image backgrounds using the remove.bg API.
35
-
36
- ```bash
37
- python -m devtools removebg input.jpg
38
- ```
39
-
40
- ## Install
41
-
42
- ```bash
43
- pip install damon-devtools
44
- ```
45
-
46
- Requires: Python >=3.10
File without changes