打包插件
本文档介绍如何将开发完成的插件打包为正式发布版本。
一、开发目录 vs 正式目录
以 textwork 插件为例:
1.1 开发目录结构
plugins/textwork/
├── manifest.json
├── backend/
│ ├── main.py
│ ├── handlers.py # 源码
│ ├── utils/ # 源码
│ └── compile/
│ └── handlers.cp310-win_amd64.pyd
└── frontend/
├── src/ # 源码
├── node_modules/ # 依赖
└── dist/
└── index.html
1.2 正式目录结构
textwork/
├── manifest.json
├── backend/
│ ├── main.py
│ └── handlers.cp310-win_amd64.pyd
└── frontend/
└── dist/
├── index.html
└── logo.png
二、打包步骤
2.1 编译后端
cd plugins/my-plugin/backend
nuitka --include-module=utils --output-dir=compile --module handlers.py
2.2 编译前端
cd plugins/my-plugin/frontend
pnpm build
2.3 组织正式目录
创建正式插件目录,只保留必要文件:
| 需要保留 | 说明 |
|---|---|
manifest.json |
插件配置 |
backend/main.py |
入口文件 |
backend/handlers.*.pyd |
从 compile/ 复制 |
frontend/dist/ |
整个目录 |
| 不需要 | 说明 |
|---|---|
backend/handlers.py |
源码 |
backend/utils/ |
源码目录 |
backend/compile/handlers.build/ |
中间文件 |
frontend/src/ |
前端源码 |
frontend/node_modules/ |
前端依赖 |
2.4 压缩为 7z
使用任意压缩软件(如 360压缩、7-Zip、WinRAR、Bandizip 等)将正式目录压缩为 .7z 格式:
my-plugin.7z
注意:压缩时选择插件目录本身,确保解压后得到的是完整的插件目录,而不是目录里面的文件。
正确:
my-plugin.7z 解压后 → my-plugin/
├── manifest.json
├── backend/
└── frontend/
错误:
my-plugin.7z 解压后 → manifest.json
backend/
frontend/
三、批量导出脚本(可选)
项目根目录提供了 move_to_plugin.py 脚本,可批量将编译结果导出为正式插件。
3.1 功能说明
- 扫描
plugins/目录中的所有插件 - 复制到
out/xiaowo/plugins/目录 - 自动跳过
dev_mode: true的插件 - backend 只复制
main.py和.pyd文件 - frontend 只复制
dist/目录 - data 目录完整复制
3.2 使用方式
python move_to_plugin.py
3.3 脚本代码
"""
插件复制脚本
扫描 plugins 目录中的所有插件,复制到 out/xiaowo/plugins 目录
- backend 目录只复制 main.py 和 compile 目录中的 .pyd 文件
- frontend 目录只复制 dist 目录
- data 目录完整复制
- manifest.json 复制
"""
import json
import os
import shutil
from pathlib import Path
# 源目录和目标目录
SOURCE_DIR = Path(r"plugins").resolve()
TARGET_DIR = Path(r"out\xiaowo\plugins").resolve()
def get_backend_dir_from_manifest(plugin_path: Path, manifest_data: dict) -> Path:
"""
从 manifest.json 获取后端目录
优先读取 dev.backend.entry
"""
entry = None
# 优先读取 dev.backend.entry
dev = manifest_data.get('dev')
if dev:
dev_backend = dev.get('backend')
if dev_backend:
entry = dev_backend.get('entry')
# 如果没有从 dev 获取到 entry,则从 backend 获取
if not entry:
backend = manifest_data.get('backend')
if backend:
entry = backend.get('entry')
if not entry:
return plugin_path / "backend"
entry_path = Path(entry)
if entry_path.is_absolute():
return entry_path.parent
else:
return (plugin_path / entry).parent
def copy_plugin(plugin_name: str, plugin_path: Path, manifest_data: dict = None):
"""复制单个插件到目标目录(保留原有文件,覆盖同名文件)"""
target_plugin_path = TARGET_DIR / plugin_name
# 创建目标插件目录(如果不存在)
target_plugin_path.mkdir(parents=True, exist_ok=True)
# 复制 manifest.json
manifest_file = plugin_path / "manifest.json"
if manifest_file.exists():
shutil.copy2(manifest_file, target_plugin_path / "manifest.json")
print(f" 复制 manifest.json")
# 复制 backend 目录(只复制 main.py 和 compile 目录中的 .pyd 文件)
# 根据 manifest 获取实际的后端目录
backend_dir = get_backend_dir_from_manifest(plugin_path, manifest_data) if manifest_data else plugin_path / "backend"
if backend_dir.exists():
target_backend_dir = target_plugin_path / "backend"
target_backend_dir.mkdir(parents=True, exist_ok=True)
# 复制 main.py 和所有以 _main.py 结尾的文件
main_py = backend_dir / "main.py"
if main_py.exists():
shutil.copy2(main_py, target_backend_dir / "main.py")
print(f" 复制 backend/main.py (来源: {backend_dir})")
# 复制所有以 _main.py 结尾的文件
for main_file in backend_dir.glob("*_main.py"):
shutil.copy2(main_file, target_backend_dir / main_file.name)
print(f" 复制 backend/{main_file.name}")
# 复制 compile 目录中的 .pyd 文件到 backend 目录(与 main.py 同级)
compile_dir = backend_dir / "compile"
if compile_dir.exists():
pyd_files = list(compile_dir.glob("*.pyd"))
for pyd_file in pyd_files:
shutil.copy2(pyd_file, target_backend_dir / pyd_file.name)
print(f" 复制 backend/{pyd_file.name}")
if not pyd_files:
print(f" backend/compile 目录中没有找到 .pyd 文件")
# 复制 frontend/dist 目录(保留原有文件)
frontend_dist_dir = plugin_path / "frontend" / "dist"
if frontend_dist_dir.exists():
target_frontend_dir = target_plugin_path / "frontend"
target_frontend_dir.mkdir(parents=True, exist_ok=True)
shutil.copytree(frontend_dist_dir, target_frontend_dir / "dist", dirs_exist_ok=True)
print(f" 复制 frontend/dist 目录")
# 复制 data 目录(完整复制,排除 try_radio 目录)
data_dir = plugin_path / "data"
if data_dir.exists():
def ignore_try_radio(dir, files):
# 如果当前目录是 data 目录,排除 try_radio
if Path(dir) == data_dir:
return ['try_radio',"hotwords.json"]
return []
shutil.copytree(data_dir, target_plugin_path / "data",
ignore=ignore_try_radio, dirs_exist_ok=True)
print(f" 复制 data 目录 (排除 try_radio)")
def main():
"""主函数:扫描并复制所有插件"""
if not SOURCE_DIR.exists():
print(f"源目录不存在: {SOURCE_DIR}")
return
# 确保目标目录存在
TARGET_DIR.mkdir(parents=True, exist_ok=True)
# 扫描所有插件目录
plugins = [d for d in SOURCE_DIR.iterdir() if d.is_dir()]
if not plugins:
print("没有找到任何插件")
return
print(f"找到 {len(plugins)} 个插件")
print("-" * 40)
copied_count = 0
skipped_count = 0
for plugin_path in plugins:
plugin_name = plugin_path.name
# 检查 manifest.json 中的 dev_mode
manifest_file = plugin_path / "manifest.json"
if manifest_file.exists():
with open(manifest_file, "r", encoding="utf-8") as f:
manifest_data = json.load(f)
if manifest_data.get("dev_mode") is True:
print(f"跳过插件: {plugin_name} (dev_mode 为 true)")
skipped_count += 1
continue
print(f"正在复制插件: {plugin_name}")
copy_plugin(plugin_name, plugin_path, manifest_data)
copied_count += 1
print()
print("-" * 40)
print(f"复制完成! 共 {copied_count} 个插件, 跳过 {skipped_count} 个 (dev_mode)")
if __name__ == "__main__":
main()
四、注意事项
.pyd文件名包含 Python 版本(如cp310表示 Python 3.10)- 确保
manifest.json中的路径配置正确 - 如有
data/目录存放默认配置,也需要一并复制
0个回答默认排序 投票数排序
还没有回答~
请先登录