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.
- damon_devtools-0.4.0/PKG-INFO +126 -0
- damon_devtools-0.4.0/README.md +115 -0
- damon_devtools-0.4.0/damon_devtools.egg-info/PKG-INFO +126 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/__main__.py +12 -2
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/launcher.py +2 -1
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/core.py +162 -5
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/ui.py +35 -6
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/pyproject.toml +1 -1
- damon_devtools-0.2.0/PKG-INFO +0 -46
- damon_devtools-0.2.0/README.md +0 -35
- damon_devtools-0.2.0/damon_devtools.egg-info/PKG-INFO +0 -46
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/SOURCES.txt +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/dependency_links.txt +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/entry_points.txt +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/requires.txt +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/damon_devtools.egg-info/top_level.txt +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/__init__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/__init__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/__init__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/__main__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/apkpull/models.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/__init__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/__main__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/core.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/models.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/portkill/ui.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/__init__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/__main__.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/cli.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/tools/removebg/core.py +0 -0
- {damon_devtools-0.2.0 → damon_devtools-0.4.0}/devtools/types.py +0 -0
- {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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
228
|
-
return
|
|
242
|
+
paths.append(line[8:].strip())
|
|
243
|
+
return paths
|
|
229
244
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
230
|
-
return
|
|
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
|
|
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
|
-
|
|
167
|
-
if not
|
|
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
|
-
|
|
173
|
-
|
|
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
|
-
|
|
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:
|
damon_devtools-0.2.0/PKG-INFO
DELETED
|
@@ -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
|
damon_devtools-0.2.0/README.md
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|