5 分钟创建第一个插件
从最小骨架开始,再逐步加入设置页、后台任务和商城能力。
目录与打包规范
推荐目录
tynvr_module_example/
├── manifest.json
├── plugin.py
├── config.example.yml
├── README.md
└── assets/
└── plugin.css
打包要求
- ZIP 顶层只放一个插件根目录。
manifest.json与plugin.py必须存在。- 普通插件 ZIP 建议不超过 16 MB。
- 大型模型、PyTorch、CUDA 等运行时不要塞进普通插件包。
manifest.json
描述插件身份、版本、设置 UI、菜单挂载和可执行操作。
{
"id": "tynvr_module_example",
"name": "Example Plugin",
"version": "1.0.0",
"description": "Example Securex Nvr plugin",
"enabled": true,
"kind": "tynvr_core_module",
"module_id": "example",
"order": 50,
"config_apply": "hot",
"first_install_restart": true,
"navigation_user_toggle": true,
"navigation_default_visible": true,
"actions": [
{"id": "reload", "label": "Reload"}
],
"navigation": {
"key": "example",
"mount": "settings.sidebar.plugins",
"label": "Example Plugin",
"url": "/api/example/?embed=1",
"view": "iframe",
"order": 50,
"icon": "puzzle"
}
}id稳定唯一 ID;建议 tynvr_module_xxx。version每次商城升级必须递增。navigation菜单由 manifest 动态声明;建议显式提供稳定 key。navigation_user_toggle允许用户控制侧栏显示;升级必须保留用户选择。config_apply推荐 hot:普通设置保存后立即应用,不要求重启 Frigate。first_install_restart首次安装/代码升级可要求一次重载;不要把普通配置保存也做成重启。actions暴露给插件管理页的明确操作。plugin.py / class Plugin
当前 Universal 插件以 FrigatePlugin 为基类。推荐把路由、配置、后台线程与停止逻辑都封装在插件实例内。
from pathlib import Path
from typing import Any
from fastapi import APIRouter, Depends
from frigate.api.auth import require_role
from frigate.plugins.base import FrigatePlugin
class Plugin(FrigatePlugin):
def __init__(self, frigate_config: Any, plugin_dir: Path, stop_event=None):
super().__init__(frigate_config, plugin_dir, stop_event)
self._router = APIRouter(tags=["example"])
self._build_routes()
@property
def router(self):
return self._router
def _build_routes(self):
@self._router.get(
"/api/example/status",
dependencies=[Depends(require_role(["admin"]))],
)
def status():
return {"ok": True}
def get_public_config(self):
return {"enabled": True}
def update_config(self, payload):
return {"success": True}
def run_action(self, action, payload):
return {"success": action == "reload"}
def stop(self):
pass配置、状态与用户数据
权限、Secret 与敏感操作
最小权限:读取使用已登录权限;修改配置、Token、模型部署、硬件控制等使用管理员权限。
Secret:完整 Token、密码、密钥只保留在服务器端。UI 只显示“已配置”或掩码。
CSRF / POST:更改状态的操作不要使用 GET。Frigate 同源 POST/PUT/PATCH/DELETE 显式发送 X-CSRF-TOKEN: 1(建议同时 X-CACHE-BYPASS: 1);Universal 有通用桥接,但插件自身仍应正确实现。
输入校验:路径、URL、命令参数、上传文件名都要白名单/规范化,避免把用户输入直接拼进 shell。
后台任务、网络与恢复
不要阻塞请求
训练、下载、同步、扫描等长任务放入后台线程/Worker,并提供状态、进度、停止与错误信息。
异步路由不要做同步回环 HTTP
async FastAPI 路由里不要直接用 urllib/requests 同步访问 Frigate 自己的 127.0.0.1 API,否则可能把事件循环自锁到 timeout。需要回读本机 API 时使用线程池/async 客户端,或直接复用已有数据。
网络必须可恢复
设置合理 timeout;断网后退避重试;上传大文件使用分块与校验;重复请求尽量做到幂等。
日志与运行时要可维护
日志提供清理按钮但采用原地 truncate,不停止 Worker/训练;大型 runtime 与 artifacts 放在插件代码目录之外,代码升级只替换插件本体。
官方 AI 插件参考
以下为当前正式基线,用于验证第三方插件与平台约定是否一致。
Securex AI Cloud v1.1.7
- Shadow A/B:同帧比较生产模型与候选模型,不切换生产 detector。
- Explore 手动确认:是/否即提交;只有 Store 云端确认 correct/incorrect 后才显示“已提交”。
- 人工确认样本使用 sample_reason=manual,并在图片页显示“正确/错误 · 手动确认”。
- 手动提交读取 Frigate event/snapshot 时必须在线程池执行,避免 async 路由自锁。
Securex AI Training Worker v1.0.11
- 训练 runtime 与插件代码分离,插件升级/重启不删除训练环境和 artifacts。
- 支持正在运行 Job 重新附着,不重复训练。
- 安装日志与 Worker 日志可原地清空,不停止安装器/Worker。
- 独立页面跟随 Frigate 主题,状态操作使用 Frigate CSRF 规则。
简体 / 繁體 / English 与响应式
商城中的插件名称、描述、按钮、错误、设置字段都应完整提供三种语言;不要只翻译标题。
- 英文通常更长,按钮和卡片不能依赖固定中文宽度。
- 在 1366px、平板宽度和窄侧栏嵌入场景下测试。
- 日志、长 ID、URL 使用换行/省略规则,避免撑破布局。
版本、升级与卸载
升级覆盖插件代码时,应保留用户配置、侧栏显示偏好和持久数据;普通配置保存采用热应用。卸载时要区分“删除插件代码”和“删除用户数据”;破坏性清理必须明确提示。
发布前检查
ZIP 结构正确manifest.json / plugin.py
安装与启用正常全新安装测试
重启自动恢复Frigate 重启后状态正确
升级保留数据config / db / status
卸载无残留菜单动态菜单正常
三语言完整简 / 繁 / EN
窄屏不溢出1366 / tablet / embed
网络失败可恢复timeout / retry / resume
Secret 不泄露浏览器与日志检查
写请求 CSRF 正确X-CSRF-TOKEN / POST / PUT / DELETE
热保存无需重启config_apply=hot
升级保留侧栏偏好navigation_visible
异步路由无阻塞回环threadpool / async client
版本与商城资料一致manifest / catalog / ZIP
插件准备好了?
完成检查后上传 ZIP。商城审核重点包括安全、兼容性、升级保留、多语言和界面完整性。