插件开发文档

照片流插件 API 参考

通过 Host API,插件可以读取项目素材、保存处理结果、管理任务和设置。这里列出了各接口的用途、所需权限和调用限制。

项目文件媒体处理后台任务插件设置
01

开始调用

如何调用 Host API

插件页面通常通过 RPC(页面与服务之间的方法调用)请求自己的服务,服务再调用 Host API。通知、面板标题和视频预览还提供专用页面方法。调用前,要在组件清单中声明所用接口和权限。

组件清单填写 componentHost.contractVersion:2,服务协议填写 protocolVersion:1。请求参数和返回类型可在 component-sdk/index.d.ts 中查阅。

选择接口

project.media.page 读取媒体列表,用 project.output 保存结果,用 tasks 管理任务。

组件私有 RPC

UI → 服务 RPC 属于组件自身实现,不等同于公开 Host 能力。

查阅参数类型

component-sdk 类型、运行时校验器与机器 schema 共同定义请求和结果。

申请权限

每项能力都有对应权限,清单中需要同时声明。下表列出了完整的对应关系。

02

接口权限

每个接口需要哪些权限

清单中的 capabilities 表示插件会调用哪些接口,permissions 表示它申请的权限,两者都要填写。项目、当前文件夹范围(scope)和所选文件由照片流提供,插件不能自行替换这些信息。

全局设置页 application.settings 只允许已授权的设置、生命周期、对话框、通知和凭据接口;应用命令 application.command 还允许网络请求。它们都不能读写项目文件。对话框的具体操作也会单独检查。

能力权限用途
project.media.pageproject.media.read分页列出项目中的媒体文件
project.media.variantsproject.media.read获取缩略图、预览图或原图
project.media.metadataproject.media.read读取尺寸、拍摄参数和视频信息
project.files.pageproject.files.read分页读取其他文件、目录和配套文件
project.files.searchproject.files.read搜索其他文件、目录和配套文件
project.versions.pageproject.versions.read分页读取版本记录
project.version.graphproject.versions.read读取版本和进度之间的关系
project.media.ratingsproject.media.ratings.read批量读取媒体评分
project.files.inputTokenproject.files.read + project.input.read取得任意格式项目文件的读取令牌
project.files.watchproject.files.read检查文件修改、改名、删除和版本变化
component.transferproject.input.read在插件页面与服务之间分块传输文件
project.previewproject.preview.read读取当前预览视频和播放位置
component.panelcomponent.panel读取或修改当前文件夹面板的标题
project.input.tokensproject.input.read用读取令牌获取插件私有文件副本
project.outputproject.output.write暂存、检查、保存或删除处理结果
version.createproject.version.create用已保存的处理结果创建版本
project.media.ratings.writeproject.media.ratings.write检查文件是否变化后逐项写入评分
project.version.updateproject.version.write检查记录是否变化后更新版本
project.version.deleteproject.version.delete删除版本,需要单独授权
project.progressproject.progress读取、创建进度并记录来源
project.progress.manageproject.progress.manage修改进度或取消登记和来源关系
project.importproject.import用读取令牌批量导入文件
project.files.mutateproject.files.write检查并执行文件操作,查询撤销结果
project.media.processproject.media.process视频时间线帧与 Office 图片提取
component.runtime.executecomponent.runtime.execute运行组件声明的命令,管理输入与后台任务
component.storagecomponent.storage组件私有数据和 SQLite 位置
component.settingscomponent.settings组件私有 JSON 设置
component.mediacomponent.media访问组件私有存储中的媒体
component.secretscomponent.secrets加密保存插件凭据,不返回明文
network.fetchnetwork.fetch向允许的网站发送 HTTPS 请求
taskstasks进度、检查点、取消与恢复
dialogsdialogs确认、受限选择与已提交输出操作
component.eventsevents发送已声明的组件事件
notificationsnotifications显示简短的纯文本通知
component.lifecyclecomponent.lifecycle.read查看插件权限、安装与运行状态

project.files.inputToken 同时需要两项读取权限;project.files.watch 使用 includeVersions:true 时还需 project.versions.readproject.preview 跳转播放位置时还需 project.preview.control

生命周期管理describe 使用 component.lifecycle.read;执行 preflight、install、repair 或 uninstall 还需要 component.lifecycle.manage
03

页面入口

如何添加插件入口、设置和通知

插件可以添加工具栏按钮、侧面板、右键动作、导入/导出入口和应用命令。每个入口只开放清单中列出的 RPC 方法;解码器等仅供宿主调用的方法,不能开放给普通页面。

工具菜单支持 workspace.videoTools(视频工具)、workspace.imageTools(图片工具)和 workspace.officeTools(Office 文档)。component.sidePanelproject.contextAction 可以选择这些位置,同时出现在文件选择右键菜单的对应分组中。菜单不会自动按后缀筛选输入,插件需要自己检查。

常驻文件夹面板

component.sidePanel 设置 placement:"workspace.folderPanel" 后,会加入文件页的面板菜单。用户可以固定、排序和拖动边界调整宽度。

普通设置表单

application.settingsForm 可声明开关、选项、文本、数字和滑块,照片流负责显示并保存这些设置。

自定义设置页面

需要授权登录或诊断等复杂交互时,可给表单添加 customPage,或使用独立的 application.settingsPage

页面样式

component-sdk/ui.css 提供统一的颜色、间距,以及表单、按钮、卡片和对话框样式。

灵感库入口的 contentKindinspiration,项目入口为 project。灵感库不提供项目版本树和进度操作。目录或选择变化时,通过 onContextChange 更新页面内容。

只提供设置的插件

如果插件只声明 application.settingsForm,且所有表单都没有 customPage,可以省略服务、工具页面和工具栏按钮。照片流直接显示并保存表单,不启动插件服务。名称、入口、图标和专属许可说明都应由插件清单提供。

在设置页显示许可说明

表单可声明 notices,每条包含 title、description、license、sourceUrl 和 licenseUrl,最多 64 条。两个链接必须使用 HTTPS,且不能带用户名或密码。只展示许可时可用 groups:[],但至少要有一条 notice。

绑定视频播放偏好

提供标准视频播放后端的插件,可在表单中设置 preferenceScope:"videoPlayback",直接读取和保存共享播放器的 HDR 偏好。只开放 hdrMode、toneMapping 和 targetPeakNits,不允许访问其他应用配置。

hdrMode 和 toneMapping 使用 select;targetPeakNits 使用 number 或 range,范围限制在 100–4000。选项还必须符合清单中播放后端声明的能力,例如 HDR 直通或对应的色调映射算法。普通表单仍保存到插件自己的设置中。

更新面板标题

声明 component.panel 接口和同名权限后,页面可调用 host.setPanelInfo({title, subtitle})。服务也可调用 component.panel 的 get/update。标题为 1–160 个字符,副标题最多 240 个字符,均为纯文本;只能修改当前文件夹面板。固定、关闭和排序按钮由照片流提供。

浮动工具面板由宿主测量正文高度,最高为 90vh;文件夹常驻面板则使用文件页分配的空间。不要在插件正文中重复绘制标题栏或关闭按钮。

发送通知

页面使用 host.notify,服务使用 notifications

通知格式为 {tone,message,dedupeKey?}tone 可选 info/success/warning/error,消息最多 360 个字符;可用 dedupeKey 避免重复提示。错误通知由用户关闭,其他通知约 3.5 秒后消失。内容必须是纯文本,不能包含 HTML、路径、URL 或执行命令,也不能设置 durationMs

04

读取项目

如何读取项目文件和媒体信息

读取媒体列表、缩略图和原图

project.media.page 每页返回 1–200 项,继续翻页时使用返回的 cursor(分页游标),5 分钟内有效。project.media.variants 可获取最长边 320 像素的缩略图、1600 像素的预览图或原图。传入 variants:[] 只取媒体信息;请求原图时还会得到 10 分钟有效、只能使用一次的读取令牌。

查找其他文件和读取拍摄信息

project.files.pageproject.files.search 用于其他文件、目录和配套文件。project.media.metadata 返回尺寸、相机、镜头、曝光,以及视频编码、时长和帧率等信息;没有的数据返回 null。结果使用项目相对路径,不返回电脑上的绝对路径。

读取版本关系和媒体评分

project.versions.pageproject.version.graph 通过只读快照返回结果,不修改项目数据或索引。评分读取一次接受 1–100 个媒体引用;supported.labelssupported.selectionStatefalse,对应字段为 null

可读取的文件范围读取范围由打开插件的文件页决定。不要通过快捷方式、符号链接或目录联接访问范围以外的文件,也不要把电脑上的绝对路径提交给项目接口。
05

文件资源

读取任意格式文件,并跟踪文件变化

取得文件副本

project.files.inputToken 接受 relativePath,可读取当前范围内任何格式的普通文件,包括没有后缀的文件。返回读取令牌、文件大小、SHA-256 摘要和 fileId。文件解析和编辑由插件负责。

随后调用 project.input.tokens 的 materialize,用令牌取得服务可读取的私有副本。令牌 10 分钟有效且只能使用一次,副本路径不能传给页面。可传入 expectedDigest 核对文件内容;文件发生变化时返回冲突,需重新读取。

检查文件是否被修改

project.files.watch 使用 subscribe → poll → unsubscribe。一次订阅 1–256 个已有文件;可见页面建议每 2–5 秒检查一次,每次保存返回的 cursor,同一订阅不要同时发起多个 poll。

事件包括 modified、renamed、deleted 和 versionChanged。按 sequence 去重;收到 rescanRequired:true 时重新核对文件。它反映文件状态,两次检查之间的多次变化可能合并,并不是完整操作记录。要读取变化后的内容,还需重新取得读取令牌。

每组件最多 16 个订阅,全宿主最多 256 个;5 分钟不调用就会过期。超过 30 秒没有轮询、游标不匹配或扫描达到上限时,都可能要求重新检查。页面释放或插件卸载时清理订阅。

分块传输大文件

component.transfer 用于插件页面与服务之间的二进制传输,不会上传到互联网。上传按 create → write → finish → close 执行;write 按 offset 顺序发送 base64,finish 用 expectedDigest 检查 SHA-256 并返回读取令牌。

下载时用 openInput 消费读取令牌,再用 read 按段读取,最后 close。上传每块原始数据最多 1 MiB,单文件及每组件活动上传合计最多 2 GiB;每组件最多 8 个会话,全宿主最多 64 个。10 分钟无操作会过期,宿主重启后需重新建立会话。

每块数据用独立 RPC 转发,避免超过单次 RPC 的 128 次嵌套接口调用限制。协议单帧仍限 2 MiB。服务已经能直接读取的文件,优先使用读取令牌或输出暂存区,减少不必要的数据传输。

06

预览扩展

让插件跟随视频播放,并支持更多文件预览

读取播放位置和跳转

声明 project.previewproject.preview.read 后,可调用 host.getPreview(),或用 await host.onPreviewChange(callback) 接收状态变化。返回的 video 包含 sessionId、relativePath、time、duration、paused 和 canSeek,时间单位为秒。没有可用视频时,video 为 null。

播放位置约每 250 毫秒更新一次,适合字幕高亮,不保证逐帧同步。页面隐藏或退出时,调用订阅返回的取消函数。接口仅对绑定来源文件页的项目或灵感库页面开放。

跳转还需 project.preview.control。调用 host.seekPreview(sessionId, time) 时使用刚读取的会话 ID,并先检查 canSeek。视频已切换、时间越界或不可跳转时会失败;accepted:true 表示请求被接受,实际位置以随后收到的状态为准。

为其他文件格式提供预览

componentHost.service.previewDecoders 中声明 id、label、extensions、method 和可选 priority。后缀支持自定义格式,也可用 * 匹配未知格式;插件仍需检查真实文件内容。解码方法必须列在 service.rpcMethods 中,只能由宿主调用。

只提供解码器的插件可使用空的 contributions:[]。至少声明 project.input.tokenscomponent.transfer 两项接口,以及 project.input.read 权限。每组件最多 16 个解码器,每个最多 64 条后缀规则,priority 范围为 -100~100。

宿主传入文件读取令牌、name、extension、从 0 开始的 pageIndex 和 maxEdge(64–4096)。服务取得输入副本后解码一页 PNG,通过分块传输接口获得输出令牌,返回 {inputToken, mimeType:"image/png", pageIndex, pageCount}

PNG 宽高均不能超过 maxEdge,文件最多 32 MiB,总页数为 1–10,000。宿主检查并重新编码后显示图片,不接收 HTML、脚本、任意 URL 或本地路径。此接口用于静态图片预览,不提供文档编辑或交互式 3D。

安装并启用对应解码插件后,文件才会获得预览能力。解码失败时显示错误、重试和外部打开按钮;每文件页同时处理一个解码请求,全宿主最多 8 个,普通服务超时为 60 秒。

07

修改与保存

如何修改项目数据和保存处理结果

project.media.ratings.write

逐项写入图片或 RAW 星级,每个文件单独返回结果;不支持视频评分、标签或选择状态写入。

project.version.update / project.version.delete

传入 expectedUpdatedAt,确认记录自读取后没有变化再修改。删除版本需要单独权限。

project.progress.manage

修改进度和来源关系时,检查节点属于当前项目范围,并防止出现循环关系。

project.import

用一次性输入令牌导入文件,先暂存和检查,再保存到项目。处理中的令牌仍受原有到期时间限制。

project.files.mutate

先用 preflight 检查计划,再用 commit 执行改名、移动、新建目录或移入回收站。每一步都有操作记录。

project.media.process

仅支持 video.timelineFramesoffice.extractImages。前者传入相对视频路径和时间点;后者传入稳定幂等键、文档相对路径和输出目录。

调用视频处理组件转码、切割等算法由视频处理组件实现。服务通过 component.runtime.execute 调用组件运行时,具体用法见下方说明。

保存新文件时,依次调用 project.output 的 stage、write、validate 和 commit;放弃未提交结果时调用 rollback。stage 最多 2,000 文件/2 GiB,24 小时后过期;项目目标必须是相对路径,绝对路径与 .. 无效。

保存新结果遇到同名文件时,默认返回冲突。commit 可传入 onConflict:"rename",由宿主另取文件名并保留已有文件。请使用返回的 relativePath 作为实际保存位置,requestedRelativePath 是原先请求的名称;已拥有文件的替换或摘要冲突仍需单独处理。

替换已提交文件时,提供 replace:true、该文件的 commit/artifact ID 和 expectedDigest。宿主核对文件摘要后再执行;发生冲突时保留用户改动并停止操作。

文件变更提交后应检查 undoAvailable;进入回收站不代表一定能自动撤销。遇到 outcomeUnknown 应停止并核实实际文件状态,不可重复执行删除或恢复。

08

数据与网络

如何保存插件数据、凭据和设置

component.storage

获取组件专属的数据和 SQLite 存储位置。写入项目文件仍需使用项目输出接口。

component.settings

get 读取,replace 整体替换,merge 只合并第一层字段。JSON 对象最大 256 KiB,保存后返回新的修订号。

component.secrets

用 Electron safeStorage 加密保存凭据。list 只返回凭据记录,不返回明文;无法加密或数据损坏时拒绝操作。

network.fetch

只能请求清单允许的 HTTPS 网站。origin 是协议、主机名和端口;凭据通过已声明的请求头绑定注入,由宿主检查目标地址和重定向。

网络超时从读取凭据前开始计时,覆盖连接和响应全过程。卸载插件会中止正在执行的网络请求;关闭一个页面不会打断同插件其他页面的网络状态。

09

运行任务

如何运行组件程序和管理后台任务

component.runtime.execute 需要同名 capability 和 permission。execute 指定组件自身声明的 runtimeCapability 和参数;顶层 capabilitiesruntimeCommandCapabilities 与平台入口共同定义可运行命令,不能传入任意可执行文件路径。

输入预览

inputs.preview 接受当前范围内的 relativePaths 或受限 inputTokens,配合 input.extensions 筛选来源。

后台执行

task.background:true 必须提供稳定的 operationKeyidempotencyKey。任务进入宿主任务中心;调用仍可能等待完成,UI 应独立监听进度。

控制任务

statuscancelpauseresume 使用相同 runtime capability 和操作键。取消、暂停需要运行时支持对应的 control 参数;resume 用于继续暂停任务。

进度与产物

eventName 必须在服务事件白名单中。projectArtifacts 登记预览或转码目录时另需 project.progress 能力及权限,并依赖已登记的项目来源节点。

运行时执行默认超时 20 分钟,可在 1 秒至 4 小时内指定 timeoutMs。清单授权、scope、输入令牌和组件归属仍由宿主复核;灵感库任务不应请求项目产物关系登记。

10

视频播放

如何添加视频播放后端

顶层 runtimeContributions 可声明 media.playbackBackend 协议版本 1,包括 backend ID、原生进程 transport、优先级、容器/编码探测和 transforms、HDR、统计、字幕、硬解、截图能力矩阵。

后端选择顺序

清单 priority 用于插件后端之间的排序,宿主还会参考 Chromium 的实际播放能力。

播放器界面

主程序提供统一播放器界面。插件可以另外声明原生设置表单,用于 HDR 偏好和许可说明,不需要创建自定义页面。

视频窗口

插件返回自己的 HWND;主程序验证所属 PID 后负责嵌入、DPI、定位与裁切。

通信限制

普通 JSON 帧最大 256 KiB,禁止传输图像、像素或音视频帧;会话关闭后,其命令授权随之失效。

11

任务与错误

如何处理任务取消、失败和重试

tasks 提供 start/report/status/cancel/resume/complete/fail,用于开始任务、报告进度、查询状态、取消和结束。进度范围为 0–100;checkpoint(检查点)记录继续任务所需的信息,插件需要自己实现恢复步骤。收到取消请求后应停止工作并清理未提交结果。

页面 RPC 与服务 JSONL(每行一个 JSON 对象)消息最多 2 MiB。stdout 只输出协议消息,日志写 stderr。方法名、字段、权限、令牌和文件范围都会检查;不符合声明的请求会失败。

普通请求 60 秒超时。媒体处理和组件运行时执行可获得宿主批准的更长等待时间,其他 RPC 不会自动延长。

COMPONENT_HOST_INVALID_REQUEST
COMPONENT_HOST_PERMISSION_DENIED
COMPONENT_HOST_NOT_FOUND
COMPONENT_HOST_TOKEN_EXPIRED
COMPONENT_HOST_TOKEN_SCOPE
COMPONENT_HOST_LIMIT_EXCEEDED
COMPONENT_HOST_VARIANT_UNAVAILABLE
COMPONENT_HOST_CONFLICT
COMPONENT_HOST_CANCELLED
COMPONENT_HOST_TIMEOUT
COMPONENT_HOST_SERVICE_EXITED
COMPONENT_HOST_INTERNAL
重试规则只在错误允许 retryable 或接口明确支持安全重试时重试。幂等键(idempotencyKey)用于识别同一次操作,重试时保持不变,避免重复导入或保存。
12

数据管理

主程序和插件分别管理哪些数据

沙箱隔离只适用于组件 UI。service、生命周期脚本和 executable 是用户安装的受信本机代码,以当前用户的系统权限运行,可访问该用户的文件、网络和进程。Host API 权限约束正常接口调用,受监管进程不等于操作系统沙箱;应只安装可信来源的组件。

主程序管理

项目、媒体索引/变体、版本、文件安全、任务中心、组件生命周期、权限账本与受控发布。

插件管理

私有存储、设置结构、算法、UI 状态、业务实体和自带的独立运行时。

插件应通过 Host API 修改项目数据,并自行管理插件数据库。卸载插件只移除代码,不自动删除组件数据。

动手开发一个插件

从完整示例开始,了解清单、页面和服务如何配合工作。

查看开发教程