SDK 1.0.0

插件开发指南

面向为 PixiuCut 开发草稿工具的第三方开发者。按本文的方法名、字段与错误码实现即可,无需阅读应用源码。

一、插件能做什么

插件是一个草稿转换器:读取用户草稿,写出一个或多个新草稿。需要成片时,可以委托应用把已保存的草稿送进任务中心导出。

输入 = 草稿(以及用户自己的素材文件)
输出 = 新草稿(一个或多个)
可选 = 委托应用把草稿导出到用户指定的目录

适合的场景:

  • 用一条模板草稿批量替换素材,生成多个变体,再排队导出
  • 读取视频信息或自备音频,调用自己的语音识别服务,写回带字幕的新草稿
  • 把其它软件的工程描述转成 PixiuCut 草稿
  • 把草稿同步到自己的存储
插件页面跑在独立窗口里,不进入剪辑器时间线,也不提供自定义滤镜/转场/编码器。媒体探测、抽音轨、批量切片、导出均由应用代为完成,插件不要自己连接剪辑引擎。

二、五分钟示例

一个最小插件只需要两个文件:

hello-plugin/
├── manifest.json
└── index.html

manifest.json

{
  "id": "com.example.hello",
  "name": "Hello Plugin",
  "version": "1.0.0",
  "description": "我的第一个插件",
  "author": "Your Name",
  "main": "index.html",
  "sdkVersion": "^1.0.0",
  "permissions": ["draft.read", "draft.write", "ui.notify"],
  "slots": [{ "id": "home.shortcut", "label": "Hello" }],
  "window": { "width": 640, "height": 480 }
}

在页面里直接使用全局对象 PixiuCutPlugin

const sdk = PixiuCutPlugin

async function init() {
  const drafts = await sdk.draft.list()
  console.log(`已连接,当前有 ${drafts.length} 个草稿`)
  await sdk.ready()
}

async function makeVariant() {
  const drafts = await sdk.draft.list()
  if (!drafts.length) return sdk.ui.notify('请先创建一个草稿', 'warning')
  const { blob } = await sdk.draft.readBlob(drafts[0].id)
  await sdk.draft.create(drafts[0].name + '-副本', blob)
  await sdk.ui.notify('已生成副本', 'success')
}

init().catch((e) => console.error(e))
插件包不要自带 SDK,HTML 里也不要写 <script src="sdk.js">。应用的本地页面服务会在返回插件 HTML 时自动注入 /sdk.js。页面运行在沙箱 iframe(sandbox="allow-scripts allow-forms allow-modals",不含 allow-same-origin)中,因此不能用 localStorage,配置请写到 fs.dataDir()。

三、插件包与 manifest

没有规定的构建工具。原生 HTML,或 React / Vue / Svelte 构建出的单个入口页都可以。构建时把 PixiuCutPlugin 当作外部全局变量,不要打进包里。静态资源使用相对插件根的路径。

manifest 字段

字段要求
id必填。反向域名,全局唯一,安装目录名与之相同
name / version / author必填。version 为 semver
main入口 HTML,默认 index.html
sdkVersion目标 SDK,当前写 ^1.0.0
appVersion可选。兼容的应用版本范围
permissions见第四节,安装时展示给用户
slots首页入口,id 固定 home.shortcut,可带相对路径 icon
window可选。默认 800×600、可调整,下限 480×360
mcp可选。向本机助手暴露工具

四、权限模型

安装时用户会看到权限说明,标为高危的项会单独提示。只声明完成功能所需要的权限。

权限风险可调用
draft.read低list、readBlob、draft.changed
draft.write中create、writeBlob、remove、rename、stageMedia
fs.read / fs.write高文件读写
net.fetch / net.local高外网 / 本机回环请求
media.probe低probe、extractAudio、getEncodeCaps
media.render高批量切片 startSplitJob / cancelSplitJob
export.render高enqueue、cancel、getStatus 及导出事件
process.spawn高启动外部进程
ui.notify / ui.pickFiles / ui.pickDirectory低通知、选文件/目录

以下方法默认开放,不必声明权限:plugin.ready/resize/close/log/packageDir/setBusy、ui.getAppInfo、license.isActive/trialDaysLeft、events.subscribe、fs.dataDir 及其读写。

五、SDK API 速查

全局对象 window.PixiuCutPlugin,异步方法返回 Promise,失败抛出 PluginError(err.code 为错误码)。默认单次调用 60 秒超时。

草稿

const drafts = await PixiuCutPlugin.draft.list()
// { id, name, dir, created, modified, durationUs?, thumbnail, type }

const { blob, version } = await PixiuCutPlugin.draft.readBlob(draftId)
const summary = await PixiuCutPlugin.draft.create(name, blob?)  // 传 blob 则以它为模板
const res = await PixiuCutPlugin.draft.writeBlob(id, blob, version) // version 用于冲突检测
await PixiuCutPlugin.draft.remove(id)
await PixiuCutPlugin.draft.rename(id, newName)
const { path } = await PixiuCutPlugin.draft.stageMedia(draftId, srcPath)

媒体探测

const info = await PixiuCutPlugin.media.probe(filePath)
// { success, type, width, height, durationUs, frameRate, codec }
const audio = await PixiuCutPlugin.media.extractAudio(filePath, { format: 'wav' })

委托导出

const { queueId } = await PixiuCutPlugin.export.enqueue(
  [{ draftId: 'proj_…', outputDir: 'D:/exports', fileName: '成片标题' }],
  { config: { format: 'mp4', vcodec: 'libx264', resolution: 1080, frameRate: 30 } },
)
PixiuCutPlugin.on('export.progress', ({ draftId, percent }) => {})
PixiuCutPlugin.on('export.queueIdle', ({ queueId }) => {})

界面与生命周期

await PixiuCutPlugin.ui.notify('操作完成', 'success')
const files = await PixiuCutPlugin.ui.pickFiles({ multiple: true })
const dir = await PixiuCutPlugin.ui.pickDirectory()
await PixiuCutPlugin.ready()
await PixiuCutPlugin.setBusy(true)   // 长任务期间
const root = await PixiuCutPlugin.packageDir()
const data = await PixiuCutPlugin.fs.dataDir()  // 免权限读写
还提供 fs.* 文件操作、net.fetch 网络代理、process.* 外部进程、media.startSplitJob 批量切片、mcp.* 向本机助手登记工具等能力,完整签名见随软件分发的《插件开发指南》原文。

六、草稿格式

草稿 blob 是 JSON 字符串,format_version 固定为 "1.2"。时间字段单位一律是微秒(µs);project_info.created/modified/last_opened 是秒级 Unix 时间。

{
  "format_version": "1.2",
  "project_info": { "id": "proj_…", "name": "我的视频", "created": 1700000000,
                    "modified": 1700000000, "duration": 0, "status": "editing" },
  "project_settings": {
    "video": { "width": 1080, "height": 1920, "frame_rate": 30 },
    "audio": { "sample_rate": 48000, "channels": 2 }
  },
  "tracks": [
    { "id": "track_1", "type": "video", "name": "主视频轨",
      "main_track": 1, "clips": [] }
  ]
}

clip.type 为整数:1 视频、2 音频、3 图片、4 文本、5 纯色、6 字幕。媒体片段需备齐 name / type / start_time / in_point / out_point / media_duration / duration,其中 duration = (out_point - in_point) / speed_factor。字幕轨只放一个容器片段(type:6,subtitle_container:true),每句台词是 subtitle_track.items 里的一条。

七、错误码

错误码含义
PERMISSION_DENIED未声明或用户未授予该权限
UNKNOWN_METHOD方法或事件名不存在
NOT_FOUND草稿、文件或进程句柄不存在
DRAFT_BUSY / EXPORT_BUSY草稿被剪辑器打开 / 剪辑器已打开不能入队
CONFLICTetag 冲突,或目标文件已存在
INVALID_DRAFTJSON 无法解析或格式非法
PAYLOAD_TOO_LARGE超过单次体积上限
ENGINE_UNAVAILABLE剪辑引擎未就绪
TIMEOUT超过 SDK 默认 60 秒
INTERNAL应用内部错误

八、发布

打成一个 zip:解压后的根目录就是 manifest.json 和入口 HTML,不要多包一层,体积不超过 80MB。

  • 从 zip 安装:插件管理 → 已安装 →「从 zip 安装」
  • 本地目录:以 manifest.id 为文件夹名放到应用插件目录,点「刷新」
  • 发现目录:向官方目录维护者提供记录(id、name、latestVersion、downloadUrl 等),用户在「插件 → 发现」中安装

安装后默认启用,首页出现快捷入口,不必重启。更新时安装目录被替换,fs.dataDir() 数据目录默认保留。

九、常见问题

可以用 React / Vue / Svelte 吗?

可以。构建产物是一个入口 HTML。不要把 SDK 打进包,将 PixiuCutPlugin 标为外部全局变量。

可以调用 Node.js 吗?

不可以。系统能力都走 SDK 方法。需要 ffmpeg 或自带程序时,用 process.spawn 启动绝对路径,或把程序放进插件包内。

能直接连剪辑引擎吗?

不能。探测、抽音轨、切片和导出都通过 SDK,由应用代为调用。

插件之间能互相调用吗?

不能。交接方式是插件 A 创建草稿,用户再打开插件 B 读取它。

本页为速查版,完整 SDK(含 fs/net/process/media/mcp 全部方法签名与约束)随软件分发。下载 PixiuCut 后即可查阅并在插件窗口按 F12 调试。