mcpywrap 0.2.0__tar.gz → 0.2.2__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.
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/PKG-INFO +1 -2
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/AddonsPack.py +12 -6
- mcpywrap-0.2.2/mcpywrap/builders/MapPack.py +173 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/__init__.py +2 -2
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/dependency_manager.py +70 -35
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/file_merge.py +6 -0
- mcpywrap-0.2.2/mcpywrap/builders/project_builder.py +346 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/build_cmd.py +28 -22
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/run_cmd.py +89 -103
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/config.py +20 -3
- mcpywrap-0.2.2/mcpywrap/mcstudio/studio_server.py +719 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/studio_server_ui.py +280 -6
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/symlinks.py +4 -4
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/level_dat.py +318 -10
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/ui/project_ui.py +234 -22
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/PKG-INFO +1 -2
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/SOURCES.txt +1 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/requires.txt +0 -1
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/pyproject.toml +1 -2
- mcpywrap-0.2.0/mcpywrap/builders/project_builder.py +0 -195
- mcpywrap-0.2.0/mcpywrap/mcstudio/studio_server.py +0 -312
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/README.md +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/__init__.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/__main__.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/watcher.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/cli.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/__init__.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/add_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/default_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/dev_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/edit_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/init_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/mod_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/modsdk_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/publish_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/remove_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/ui_cmd.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/custom_packaging.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/__init__.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/editor.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/game.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/mcs.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/__init__.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/addons.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/map.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/netease_modsdk.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/template/mod_template.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/ui/__init__.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/__init__.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/print_guide.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/project_setup.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/py3to2_util.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/utils.py +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.2.0 → mcpywrap-0.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: A wrapper for Minecraft China Edition Addons management
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License: MIT
|
|
@@ -20,7 +20,6 @@ Requires-Dist: tomli-w
|
|
|
20
20
|
Requires-Dist: setuptools
|
|
21
21
|
Requires-Dist: PyQt5>=5.15.0
|
|
22
22
|
Requires-Dist: psutil>=5.8.0
|
|
23
|
-
Requires-Dist: amulet-nbt>=2.1.4
|
|
24
23
|
Requires-Dist: rich>=10.0.0
|
|
25
24
|
|
|
26
25
|
# 🧰 mcpywrap
|
|
@@ -78,10 +78,13 @@ class AddonsPack(object):
|
|
|
78
78
|
return True
|
|
79
79
|
return False
|
|
80
80
|
|
|
81
|
-
def copy_behavior_to(self, target_dir: str):
|
|
82
|
-
"""
|
|
81
|
+
def copy_behavior_to(self, target_dir: str, rename: str = None):
|
|
82
|
+
"""
|
|
83
|
+
复制行为包和资源包到目标目录
|
|
84
|
+
target_dir为父目录
|
|
85
|
+
"""
|
|
83
86
|
if self.behavior_pack_dir:
|
|
84
|
-
target_path = os.path.join(target_dir, os.path.basename(self.behavior_pack_dir))
|
|
87
|
+
target_path = os.path.join(target_dir, os.path.basename(self.behavior_pack_dir) if not rename else rename)
|
|
85
88
|
os.makedirs(target_path, exist_ok=True)
|
|
86
89
|
|
|
87
90
|
# 使用自定义复制函数而不是shutil.copytree
|
|
@@ -106,10 +109,13 @@ class AddonsPack(object):
|
|
|
106
109
|
else:
|
|
107
110
|
shutil.copy2(src_file, dest_file)
|
|
108
111
|
|
|
109
|
-
def copy_resource_to(self, target_dir: str):
|
|
110
|
-
"""
|
|
112
|
+
def copy_resource_to(self, target_dir: str, rename: str = None):
|
|
113
|
+
"""
|
|
114
|
+
复制资源包到目标目录
|
|
115
|
+
target_dir为父目录
|
|
116
|
+
"""
|
|
111
117
|
if self.resource_pack_dir:
|
|
112
|
-
target_path = os.path.join(target_dir, os.path.basename(self.resource_pack_dir))
|
|
118
|
+
target_path = os.path.join(target_dir, os.path.basename(self.resource_pack_dir) if not rename else rename)
|
|
113
119
|
os.makedirs(target_path, exist_ok=True)
|
|
114
120
|
|
|
115
121
|
# 使用自定义复制函数而不是shutil.copytree
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import click
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from ..mcstudio.symlinks import setup_map_packs_symlinks
|
|
9
|
+
|
|
10
|
+
class MapPack(object):
|
|
11
|
+
|
|
12
|
+
pkg_name: str
|
|
13
|
+
path: str
|
|
14
|
+
|
|
15
|
+
behavior_packs_dir: str
|
|
16
|
+
resource_packs_dir: str
|
|
17
|
+
|
|
18
|
+
behavior_packs = []
|
|
19
|
+
resource_packs = []
|
|
20
|
+
|
|
21
|
+
def __init__(self, pkg_name: str, path: str):
|
|
22
|
+
self.pkg_name = pkg_name
|
|
23
|
+
self.path = path
|
|
24
|
+
|
|
25
|
+
# 读取配置文件
|
|
26
|
+
self.behavior_packs_dir = os.path.join(self.path, "behavior_packs")
|
|
27
|
+
self.resource_packs_dir = os.path.join(self.path, "resource_packs")
|
|
28
|
+
|
|
29
|
+
if not os.path.exists(self.behavior_packs_dir):
|
|
30
|
+
os.makedirs(self.behavior_packs_dir)
|
|
31
|
+
|
|
32
|
+
# 遍历行为包目录
|
|
33
|
+
for pack in os.listdir(self.behavior_packs_dir):
|
|
34
|
+
pack_path = os.path.join(self.behavior_packs_dir, pack)
|
|
35
|
+
if os.path.isdir(pack_path):
|
|
36
|
+
self.behavior_packs.append(pack_path)
|
|
37
|
+
|
|
38
|
+
if not os.path.exists(self.resource_packs_dir):
|
|
39
|
+
os.makedirs(self.resource_packs_dir)
|
|
40
|
+
|
|
41
|
+
# 遍历资源包目录
|
|
42
|
+
for pack in os.listdir(self.resource_packs_dir):
|
|
43
|
+
pack_path = os.path.join(self.resource_packs_dir, pack)
|
|
44
|
+
if os.path.isdir(pack_path):
|
|
45
|
+
self.resource_packs.append(pack_path)
|
|
46
|
+
|
|
47
|
+
def copy_level_data_to(self, target_dir: str):
|
|
48
|
+
"""
|
|
49
|
+
复制地图数据到目标目录
|
|
50
|
+
"""
|
|
51
|
+
# level.dat
|
|
52
|
+
level_dat_path = os.path.join(target_dir, "level.dat")
|
|
53
|
+
if not os.path.exists(level_dat_path):
|
|
54
|
+
origin_level_dat_path = os.path.join(self.path, "level.dat")
|
|
55
|
+
if os.path.exists(origin_level_dat_path):
|
|
56
|
+
shutil.copy2(origin_level_dat_path, level_dat_path)
|
|
57
|
+
# levelname.txt
|
|
58
|
+
levelname_txt_path = os.path.join(target_dir, "levelname.txt")
|
|
59
|
+
if not os.path.exists(levelname_txt_path):
|
|
60
|
+
origin_levelname_txt_path = os.path.join(self.path, "levelname.txt")
|
|
61
|
+
if os.path.exists(origin_levelname_txt_path):
|
|
62
|
+
shutil.copy2(origin_levelname_txt_path, levelname_txt_path)
|
|
63
|
+
# db
|
|
64
|
+
level_db_dir = os.path.join(target_dir, "db")
|
|
65
|
+
if not os.path.exists(level_db_dir) and os.path.exists(os.path.join(self.path, "db")):
|
|
66
|
+
shutil.copytree(os.path.join(self.path, "db"), level_db_dir)
|
|
67
|
+
|
|
68
|
+
def copy_resource_packs_to(self, target_map_dir: str):
|
|
69
|
+
"""
|
|
70
|
+
复制资源包到目标目录
|
|
71
|
+
"""
|
|
72
|
+
for pack in self.resource_packs:
|
|
73
|
+
pack_name = os.path.basename(pack)
|
|
74
|
+
target_pack_dir = os.path.join(target_map_dir, "resource_packs", pack_name)
|
|
75
|
+
# 先删除目标目录下的包
|
|
76
|
+
if os.path.exists(target_pack_dir):
|
|
77
|
+
shutil.rmtree(target_pack_dir)
|
|
78
|
+
shutil.copytree(pack, target_pack_dir)
|
|
79
|
+
|
|
80
|
+
def copy_behavior_packs_to(self, target_map_dir: str):
|
|
81
|
+
"""
|
|
82
|
+
复制行为包到目标目录
|
|
83
|
+
"""
|
|
84
|
+
for pack in self.behavior_packs:
|
|
85
|
+
pack_name = os.path.basename(pack)
|
|
86
|
+
target_pack_dir = os.path.join(target_map_dir, "behavior_packs", pack_name)
|
|
87
|
+
# 先删除目标目录下的包
|
|
88
|
+
if os.path.exists(target_pack_dir):
|
|
89
|
+
shutil.rmtree(target_pack_dir)
|
|
90
|
+
shutil.copytree(pack, target_pack_dir)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def setup_packs_symlinks_to(self, level_id: str, target_dir: str):
|
|
94
|
+
"""
|
|
95
|
+
设置行为包和资源包的符号链接到目标目录(本体到远程)
|
|
96
|
+
"""
|
|
97
|
+
setup_map_packs_symlinks(self.path, level_id, target_dir)
|
|
98
|
+
|
|
99
|
+
def setup_world_packs_config(self):
|
|
100
|
+
"""根据当前path,生成世界需加载的pack的配置文件"""
|
|
101
|
+
behavior_packs_config = []
|
|
102
|
+
if os.path.exists(self.behavior_packs_dir):
|
|
103
|
+
behavior_packs_config = _find_and_extract_pack_info(self.behavior_packs_dir)
|
|
104
|
+
if behavior_packs_config:
|
|
105
|
+
world_behavior_packs_path = os.path.join(self.path, "world_behavior_packs.json")
|
|
106
|
+
with open(world_behavior_packs_path, 'w', encoding='utf-8') as f:
|
|
107
|
+
json.dump(behavior_packs_config, f, ensure_ascii=False, indent=4)
|
|
108
|
+
|
|
109
|
+
resource_packs_config = []
|
|
110
|
+
if os.path.exists(self.resource_packs_dir):
|
|
111
|
+
resource_packs_config = _find_and_extract_pack_info(self.resource_packs_dir)
|
|
112
|
+
if resource_packs_config:
|
|
113
|
+
world_resource_packs_path = os.path.join(self.path, "world_resource_packs.json")
|
|
114
|
+
with open(world_resource_packs_path, 'w', encoding='utf-8') as f:
|
|
115
|
+
json.dump(resource_packs_config, f, ensure_ascii=False, indent=4)
|
|
116
|
+
|
|
117
|
+
return behavior_packs_config, resource_packs_config
|
|
118
|
+
|
|
119
|
+
def _find_and_extract_pack_info(packs_dir):
|
|
120
|
+
"""
|
|
121
|
+
搜索指定目录中的所有包,并从manifest.json中提取信息
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
packs_dir: 包目录路径
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
list: 包配置列表
|
|
128
|
+
"""
|
|
129
|
+
packs_config = []
|
|
130
|
+
|
|
131
|
+
# 遍历目录下的所有子目录
|
|
132
|
+
for pack_name in os.listdir(packs_dir):
|
|
133
|
+
pack_path = os.path.join(packs_dir, pack_name)
|
|
134
|
+
|
|
135
|
+
# 只处理目录
|
|
136
|
+
if not os.path.isdir(pack_path):
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
# 查找manifest.json或pack_manifest.json
|
|
140
|
+
manifest_path = os.path.join(pack_path, "manifest.json")
|
|
141
|
+
if not os.path.exists(manifest_path):
|
|
142
|
+
manifest_path = os.path.join(pack_path, "pack_manifest.json")
|
|
143
|
+
if not os.path.exists(manifest_path):
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
with open(manifest_path, 'r', encoding='utf-8') as f:
|
|
148
|
+
manifest = json.load(f)
|
|
149
|
+
|
|
150
|
+
# 提取UUID和版本信息
|
|
151
|
+
if 'header' in manifest:
|
|
152
|
+
pack_id = manifest['header'].get('uuid')
|
|
153
|
+
version = manifest['header'].get('version', [0, 0, 1])
|
|
154
|
+
|
|
155
|
+
# 确保版本是列表格式
|
|
156
|
+
if isinstance(version, list):
|
|
157
|
+
version_array = version
|
|
158
|
+
elif isinstance(version, dict) and 'major' in version and 'minor' in version and 'patch' in version:
|
|
159
|
+
version_array = [version['major'], version['minor'], version['patch']]
|
|
160
|
+
else:
|
|
161
|
+
version_array = [0, 0, 1] # 默认版本
|
|
162
|
+
|
|
163
|
+
if pack_id:
|
|
164
|
+
pack_config = {
|
|
165
|
+
"pack_id": pack_id,
|
|
166
|
+
"type": "Addon",
|
|
167
|
+
"version": version_array
|
|
168
|
+
}
|
|
169
|
+
packs_config.append(pack_config)
|
|
170
|
+
except Exception as e:
|
|
171
|
+
click.echo(f"⚠️ 读取包配置失败: {pack_name} - {str(e)}", style="yellow")
|
|
172
|
+
|
|
173
|
+
return packs_config
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
构建模块包,提供项目构建、文件处理和监控功能
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from .project_builder import
|
|
6
|
+
from .project_builder import AddonProjectBuilder
|
|
7
7
|
from .watcher import ProjectWatcher, FileWatcher
|
|
8
8
|
from .dependency_manager import DependencyManager
|
|
9
9
|
|
|
10
|
-
__all__ = ['
|
|
10
|
+
__all__ = ['AddonProjectBuilder', 'ProjectWatcher', 'FileWatcher', 'DependencyManager']
|
|
@@ -11,13 +11,14 @@ from pathlib import Path
|
|
|
11
11
|
from typing import Dict, List, Optional, Set
|
|
12
12
|
import click
|
|
13
13
|
|
|
14
|
+
from ..config import CONFIG_FILE, check_has_mcpywrap_config
|
|
14
15
|
from .AddonsPack import AddonsPack
|
|
15
16
|
|
|
16
17
|
class DependencyNode:
|
|
17
18
|
"""依赖树节点"""
|
|
18
19
|
def __init__(self, name: str, addon_pack: AddonsPack, parent=None):
|
|
19
20
|
self.name = name
|
|
20
|
-
self.addon_pack = addon_pack
|
|
21
|
+
self.addon_pack: AddonsPack = addon_pack
|
|
21
22
|
self.parent = parent
|
|
22
23
|
self.children: List[DependencyNode] = []
|
|
23
24
|
|
|
@@ -30,6 +31,39 @@ class DependencyNode:
|
|
|
30
31
|
|
|
31
32
|
def __repr__(self):
|
|
32
33
|
return self.__str__()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _decode_direct_url(direct_url_path: str) -> Optional[str]:
|
|
37
|
+
with open(direct_url_path, 'r', encoding='utf-8') as f:
|
|
38
|
+
try:
|
|
39
|
+
direct_url = json.load(f)
|
|
40
|
+
# 读取其中的url
|
|
41
|
+
if "url" in direct_url:
|
|
42
|
+
url = direct_url["url"]
|
|
43
|
+
# 处理file://开头的路径
|
|
44
|
+
if url.startswith("file:///"):
|
|
45
|
+
# 移除file:/// 前缀
|
|
46
|
+
if sys.platform == "win32":
|
|
47
|
+
# Windows 路径处理 (例如 file:///D:/path)
|
|
48
|
+
url = url[8:] # 去除 file:///
|
|
49
|
+
else:
|
|
50
|
+
url = "/" + url[8:] # 保留根目录斜杠
|
|
51
|
+
url = os.path.abspath(url)
|
|
52
|
+
# 兼容处理旧格式 file://
|
|
53
|
+
elif url.startswith("file://"):
|
|
54
|
+
url = url[7:]
|
|
55
|
+
# 对URL进行解码,处理%编码的特殊字符
|
|
56
|
+
from urllib.parse import unquote
|
|
57
|
+
url = unquote(url)
|
|
58
|
+
url = os.path.abspath(url)
|
|
59
|
+
|
|
60
|
+
# 确保路径格式一致
|
|
61
|
+
if sys.platform == "win32":
|
|
62
|
+
url = url.replace("\\", "/")
|
|
63
|
+
|
|
64
|
+
return url
|
|
65
|
+
except json.JSONDecodeError:
|
|
66
|
+
logging.warning(f"无法解析 {direct_url_path} 的JSON内容")
|
|
33
67
|
|
|
34
68
|
class DependencyManager:
|
|
35
69
|
"""依赖管理器"""
|
|
@@ -37,7 +71,7 @@ class DependencyManager:
|
|
|
37
71
|
self.dependency_map: Dict[str, AddonsPack] = {}
|
|
38
72
|
self.root_node: Optional[DependencyNode] = None
|
|
39
73
|
self.processed_deps: Set[str] = set()
|
|
40
|
-
|
|
74
|
+
|
|
41
75
|
def find_dependency_path(self, package_name: str) -> Optional[str]:
|
|
42
76
|
"""
|
|
43
77
|
查找依赖包的真实路径,支持常规安装和pip install -e (编辑安装)
|
|
@@ -62,39 +96,13 @@ class DependencyManager:
|
|
|
62
96
|
# 处理direct_url.json获取包路径
|
|
63
97
|
direct_url_path = dist_info / "direct_url.json"
|
|
64
98
|
if direct_url_path.exists():
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
url = direct_url["url"]
|
|
71
|
-
# 处理file://开头的路径
|
|
72
|
-
if url.startswith("file:///"):
|
|
73
|
-
# 移除file:/// 前缀
|
|
74
|
-
if sys.platform == "win32":
|
|
75
|
-
# Windows 路径处理 (例如 file:///D:/path)
|
|
76
|
-
url = url[8:] # 去除 file:///
|
|
77
|
-
else:
|
|
78
|
-
url = "/" + url[8:] # 保留根目录斜杠
|
|
79
|
-
url = os.path.abspath(url)
|
|
80
|
-
# 兼容处理旧格式 file://
|
|
81
|
-
elif url.startswith("file://"):
|
|
82
|
-
url = url[7:]
|
|
83
|
-
# 对URL进行解码,处理%编码的特殊字符
|
|
84
|
-
from urllib.parse import unquote
|
|
85
|
-
url = unquote(url)
|
|
86
|
-
url = os.path.abspath(url)
|
|
87
|
-
|
|
88
|
-
# 确保路径格式一致
|
|
89
|
-
if sys.platform == "win32":
|
|
90
|
-
url = url.replace("\\", "/")
|
|
91
|
-
|
|
92
|
-
return url
|
|
93
|
-
except json.JSONDecodeError:
|
|
94
|
-
logging.warning(f"无法解析 {direct_url_path} 的JSON内容")
|
|
95
|
-
continue
|
|
99
|
+
origin_path = _decode_direct_url(str(direct_url_path))
|
|
100
|
+
if origin_path:
|
|
101
|
+
# 返回绝对路径
|
|
102
|
+
return origin_path
|
|
103
|
+
continue
|
|
96
104
|
return None
|
|
97
|
-
|
|
105
|
+
|
|
98
106
|
def build_dependency_tree(self, project_name: str, project_path: str, dependencies: List[str]) -> DependencyNode:
|
|
99
107
|
"""
|
|
100
108
|
构建依赖树
|
|
@@ -171,4 +179,31 @@ class DependencyManager:
|
|
|
171
179
|
|
|
172
180
|
def get_dependency_tree(self) -> Optional[DependencyNode]:
|
|
173
181
|
"""获取依赖树"""
|
|
174
|
-
return self.root_node
|
|
182
|
+
return self.root_node
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def find_all_mcpywrap_packages() -> List[str]:
|
|
186
|
+
packages = []
|
|
187
|
+
# 得到site-packages路径
|
|
188
|
+
for site_package_dir in __import__('site').getsitepackages():
|
|
189
|
+
site_packages = Path(site_package_dir)
|
|
190
|
+
for dist_info in site_packages.glob("*.dist-info"):
|
|
191
|
+
# 读取METADATA文件获取真实包名
|
|
192
|
+
metadata_path = dist_info / "METADATA"
|
|
193
|
+
if metadata_path.exists():
|
|
194
|
+
pkg_name = None
|
|
195
|
+
with open(metadata_path, 'r', encoding='utf-8') as f:
|
|
196
|
+
for line in f:
|
|
197
|
+
if line.startswith("Name:"):
|
|
198
|
+
pkg_name = line.split(":", 1)[1].strip()
|
|
199
|
+
break
|
|
200
|
+
if pkg_name is None:
|
|
201
|
+
continue
|
|
202
|
+
# 处理direct_url.json获取包路径
|
|
203
|
+
direct_url_path = dist_info / "direct_url.json"
|
|
204
|
+
if direct_url_path.exists():
|
|
205
|
+
origin_path = _decode_direct_url(str(direct_url_path))
|
|
206
|
+
if origin_path:
|
|
207
|
+
if check_has_mcpywrap_config(os.path.join(origin_path, CONFIG_FILE)):
|
|
208
|
+
packages.append(pkg_name)
|
|
209
|
+
return packages
|
|
@@ -121,6 +121,12 @@ def try_merge_file(source_file, target_file, source_dependency_name=None) -> Tup
|
|
|
121
121
|
_write_lang_file(target_file, merged_lang)
|
|
122
122
|
return True, f"成功合并 {base_name} 到 {os.path.basename(target_file)}"
|
|
123
123
|
|
|
124
|
+
if base_name.endswith('.png') or base_name.endswith('.jpg') or base_name.endswith('.jpeg') or base_name.endswith('.tga'):
|
|
125
|
+
# 直接覆盖,并给出警告
|
|
126
|
+
shutil.copy2(source_file, target_file)
|
|
127
|
+
click.secho(f"⚠️ 警告: {base_name} 图片文件重复,正在覆盖", fg="yellow")
|
|
128
|
+
return True, f"强制覆盖 {base_name} 到 {os.path.basename(target_file)}"
|
|
129
|
+
|
|
124
130
|
# 读取源文件和目标文件的JSON内容
|
|
125
131
|
source_json = _read_json_file(source_file)
|
|
126
132
|
target_json = _read_json_file(target_file)
|