调用概览
页面用 window.photoFlowComponent.rpc() 请求组件服务;服务调用授权的 Host capability。组件 RPC / 事件带 .vN,Host 能力不带版本后缀。
contractVersion: 2服务 protocolVersion: 1页面桥接 contractVersion: 1能力与权限
同时声明 capabilities 与 permissions,每次调用仍校验组件、项目和 scope。全局设置页只开放应用级设置、生命周期、对话框、通知和凭据;应用命令及首页卡片还可使用已授权网络能力,不能访问项目文件。
项目读取 13 项
| 能力 | 权限 | 用途 |
|---|---|---|
project.media.page | project.media.read | 分页列出项目中的媒体文件 |
project.media.variants | project.media.read | 获取缩略图、预览图或原图 |
project.media.metadata | project.media.read | 读取尺寸、拍摄参数和视频信息 |
project.files.page | project.files.read | 分页读取其他文件、目录和配套文件 |
project.files.search | project.files.read | 搜索其他文件、目录和配套文件 |
project.versions.page | project.versions.read | 分页读取版本记录 |
project.version.graph | project.versions.read | 读取版本和进度之间的关系 |
project.media.ratings | project.media.ratings.read | 批量读取媒体评分 |
project.files.inputToken | project.files.read + project.input.read | 取得任意格式项目文件的读取令牌 |
project.files.watch | project.files.read | 检查文件修改、改名、删除和版本变化 |
component.transfer | project.input.read | 在插件页面与服务之间分块传输文件 |
project.preview | project.preview.read | 读取当前预览视频和播放位置 |
project.input.tokens | project.input.read | 用读取令牌获取插件私有文件副本 |
项目写入与工作流 10 项
| 能力 | 权限 | 用途 |
|---|---|---|
project.output | project.output.write | 暂存、检查、保存或删除处理结果 |
version.create | project.version.create | 用已保存的处理结果创建版本 |
project.media.ratings.write | project.media.ratings.write | 检查文件是否变化后逐项写入评分 |
project.version.update | project.version.write | 检查记录是否变化后更新版本 |
project.version.delete | project.version.delete | 删除版本,需要单独授权 |
project.progress | project.progress | 读取、创建进度并记录来源 |
project.progress.manage | project.progress.manage | 修改进度或取消登记和来源关系 |
project.import | project.import | 用读取令牌批量导入文件 |
project.files.mutate | project.files.write | 检查并执行文件操作,查询撤销结果 |
project.media.process | project.media.process | 视频时间线帧与 Office 图片提取 |
插件数据与网络 5 项
| 能力 | 权限 | 用途 |
|---|---|---|
component.storage | component.storage | 组件私有数据和 SQLite 位置 |
component.settings | component.settings | 组件私有 JSON 设置 |
component.media | component.media | 访问组件私有存储中的媒体 |
component.secrets | component.secrets | 加密保存插件凭据,不返回明文 |
network.fetch | network.fetch | 向允许的网站发送 HTTPS 请求 |
界面、任务与运行 7 项
| 能力 | 权限 | 用途 |
|---|---|---|
component.panel | component.panel | 读取或修改当前文件夹面板的标题 |
component.runtime.execute | component.runtime.execute | 运行组件声明的命令,管理输入与后台任务 |
tasks | tasks | 进度、检查点、取消与恢复 |
dialogs | dialogs | 确认、受限选择与已提交输出操作 |
component.events | events | 发送已声明的组件事件 |
notifications | notifications | 显示简短的纯文本通知 |
component.lifecycle | component.lifecycle.read | 查看插件权限、安装与运行状态 |
任意文件令牌同时需要 project.files.read 与 project.input.read;订阅 includeVersions 另需 project.versions.read;预览跳转另需 project.preview.control;生命周期动作另需 component.lifecycle.manage。
入口、菜单与设置
插件可以添加工具栏按钮、侧面板、右键动作、导入/导出入口、应用命令和首页卡片。每个入口只开放清单中列出的 RPC 方法;解码器等仅供宿主调用的方法,不能开放给普通页面。
按文件类型打开插件
在 component.sidePanel 中声明 fileHandler.extensions。用户打开匹配文件时,宿主按单击/双击偏好打开标准工具面板,并使用清单中的插件图标。
{
"type": "component.sidePanel",
"id": "open-document",
"label": "打开文档",
"pageId": "document-page",
"contextMenu": "none",
"fileHandler": {
"extensions": [
".example"
]
},
"rpcMethods": [
"document.open.v1"
]
}| 规则 | 说明 |
|---|---|
| 声明 | 仅限 component.sidePanel;不能与 placement 同用。contextMenu:"none" 可隐藏右键入口。 |
| 后缀 | 1–64 项,不可重复;点开头的小写字母或数字,点后 1–16 位。实际匹配不区分大小写。 |
| 范围 | 仅可写普通文件;不接管目录、快捷方式和只读条目。读取仍需 InputToken 与对应权限。 |
| 优先级 | 主程序支持的压缩包优先进入内置浏览;插件间按组件 ID、入口 ID 稳定排序。 |
“用默认方式打开”始终绕过插件;此声明不改变系统文件关联。停用或卸载插件后,接管与图标自动撤销。
只有 UI context 包含 contributionId;服务公开 context 不包含。操作类型通过 RPC payload 显式传递,服务按已声明操作再次校验。
将侧面板打开为独立窗口
component.sidePanel 页面可以直接调用 window.photoFlowComponent.openPanelWindow()。它返回 Promise<{ windowId: number }>,在独立原生窗口创建同一入口的第二个实例,原面板继续保留。
const host = window.photoFlowComponent;
if (typeof host.openPanelWindow === 'function') {
const { windowId } = await host.openPanelWindow();
console.log('已打开插件窗口', windowId);
}新实例继承来源面板的项目、目录和安全选择上下文,沿用组件沙箱与该入口的 RPC 白名单。窗口显示插件图标、名称和系统最小化、最大化、关闭按钮,正文只加载插件面板。该方法只用于侧面板实例,不向普通应用页面开放,也不额外授予文件访问权限。
从首页卡片打开插件页面
在 componentHost.contributions 中声明 application.homeCard,可在首页内置卡片旁提供插件入口。pageId 必须引用同一清单里的 component.fullPage;rpcMethods 必须属于服务已声明的方法。
{
"type": "application.homeCard",
"id": "open-studio",
"label": "插件名称",
"description": "打开插件工作区",
"pageId": "studio-page",
"rpcMethods": [
"studio.open.v1"
]
}顶层 icon 和 displayName 提供卡片图标及窗口标题。每次点击会创建独立页面实例;只声明首页卡片和 fullPage 的页面插件也有效,不必再添加工具栏或侧面板入口。
首页卡片使用 context.surface === 'application.homeCard',没有项目或文件选择。RPC 受该入口的白名单限制,Host 能力沿用应用命令权限:可以使用已授权的应用级能力及网络请求,不能读写项目文件。
工具菜单支持 workspace.videoTools(视频工具)、workspace.imageTools(图片工具)和 workspace.officeTools(Office 文档)。component.sidePanel 和 project.contextAction 可以选择这些位置,同时出现在文件选择右键菜单的对应分组中。可通过 selection 声明数量、类型和后缀筛选,服务仍需校验输入。
component.sidePanel 设置 placement:"workspace.folderPanel" 后,会加入文件页的面板菜单。用户可以固定、排序和拖动边界调整宽度。
application.settingsForm 可声明开关、选项、文本、数字和滑块,照片流负责显示并保存这些设置。
需要授权登录或诊断等复杂交互时,可给表单添加 customPage,或使用独立的 application.settingsPage。
component-sdk/ui.css 提供统一的颜色、间距,以及表单、按钮、卡片和对话框样式。
灵感库入口的 contentKind 为 inspiration,项目入口为 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。
文件读取与预览
读取媒体列表、缩略图和原图
project.media.page 每页返回 1–200 项,继续翻页时使用返回的 cursor(分页游标),5 分钟内有效。project.media.variants 可获取最长边 320 像素的缩略图、1600 像素的预览图或原图。传入 variants:[] 只取媒体信息;请求原图时还会得到 10 分钟有效、只能使用一次的读取令牌。
导入的外部项目与文件范围
外部文件夹可通过复制、移动或引用原位置导入为项目。引用原位置时,该文件夹就是绑定项目的物理根目录,文件使用普通项目相对路径。项目内创建文件或文件夹外链的功能已删除;旧 External/... 记录、快捷方式和符号链接不能扩大读取范围,以 photoId/versionId 查询也会检查项目归属。
查找其他文件和读取拍摄信息
project.files.page 与 project.files.search 用于其他文件、目录和配套文件。project.media.metadata 返回尺寸、相机、镜头、曝光,以及视频编码、时长和帧率等信息;没有的数据返回 null。结果使用项目相对路径,不返回电脑上的绝对路径。
读取版本关系和媒体评分
project.versions.page 和 project.version.graph 通过只读快照返回结果,不修改项目数据或索引。评分读取一次接受 1–100 个媒体引用;supported.labels 和 supported.selectionState 为 false,对应字段为 null。
文件资源与传输
取得文件副本
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。服务已经能直接读取的文件,优先使用读取令牌或输出暂存区,减少不必要的数据传输。
播放联动与预览解码
读取播放位置和跳转
声明 project.preview 和 project.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 和 thumbnailMethod。后缀支持自定义格式,也可用 * 匹配无后缀或未知格式;插件仍需检查真实文件内容。明确后缀匹配优先于通配符。解码和缩略图方法都必须列在 service.rpcMethods 中,只能由宿主调用,不能加入普通页面的 RPC 白名单。
只提供解码器的插件可使用空的 contributions:[]。至少声明 project.input.tokens、component.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。
返回 MOV / MP4 动态预览
插件自行解包或生成视频,通过 component.transfer 获得输出令牌。使用同一解码方法返回以下结果,无需创建插件页面:
{
inputToken: videoToken,
mimeType: 'video/quicktime', // MP4 使用 video/mp4
pageIndex: 0,
pageCount: 1
}视频必须为第 0 页、共 1 页,容器仅支持 MOV/MP4,最大 256 MiB。maxEdge 不要求视频缩小;实际编码能否播放由公共播放器及已安装后端决定。普通 RPC 最多调用 128 次 capability,分块上传时需将 create、write、finish、close 一并计入预算,不能把容器大小上限当作单次解码请求的可上传大小。
宿主在主预览区挂载公共播放器,提供播放、暂停、跳转及播放位置通知。视频保留在私有预览缓存,不写入项目,暂不提供派生预览的截图保存。切换文件、关闭预览或停用插件后,宿主释放媒体授权和播放会话;取消等待会丢弃迟到结果,插件仍需为自身解码算法设置时间与资源上限。
照片式动态预览
视频结果可额外提供 posterInputToken(PNG 封面令牌)和 presentation: 'live-photo'。封面与视频必须使用不同的一次性令牌,封面尺寸仍受 maxEdge 限制。
{
inputToken: videoToken,
mimeType: 'video/mp4',
pageIndex: 0,
pageCount: 1,
posterInputToken: coverToken,
presentation: 'live-photo'
}宿主验证封面后,以照片样式自动静音播放一次,结束后恢复封面;左上角 LIVE 按钮用于重播,不显示普通视频工具条。照片与视频的解析和配对由插件负责;没有可解码封面时,省略这两个字段,返回普通视频预览。
文件图标缩略图
{
"id": "photos",
"label": "照片预览",
"extensions": [".custom"],
"method": "photo.preview.v1",
"thumbnailMethod": "photo.thumbnail.v1"
}thumbnailMethod 接收相同的输入令牌、pageIndex 和 maxEdge 字段;pageIndex 固定为 0,maxEdge 为 64–640,只接受 PNG 结果。若主预览方法也返回 PNG,可以复用同一方法。
文件列表、网格和版本树按可见区域请求缩略图,失败时保留系统图标。缩略图最多两个并发请求,同组件最多一个;宿主缓存最多 256 项、64 MiB,每次读取仍校验文件身份与授权,源文件改变会丢弃旧结果。缓存不写入项目,插件无需自行维护宿主缓存。
安装并启用对应解码插件后,文件才会获得预览能力。非媒体文件有匹配解码器时,点击显示预览;普通视频优先走播放器,播放失败后才尝试匹配解码器。解码失败时显示错误、重试和外部打开按钮,不自动打开外部程序;每文件页同时处理一个主预览解码请求,全宿主最多 8 个,普通服务超时为 60 秒。
输出事务与项目写入
基本流程为 stage → write → validate → commit。项目目标使用相对路径;跨项目、绝对路径和上级跳转均拒绝。
| 动作 | 用途 |
|---|---|
stage | 创建私有暂存区,可填写 maxBytes。 |
write | 登记暂存文件、复制输入令牌或接收内联 base64,返回 artifact ID。 |
validate | 检查路径、文件和容量;拒绝空、链接、缺失或超限暂存区。 |
commit | 用稳定 idempotencyKey 保存结果并返回 commit/artifact 引用。 |
status | 按原幂等键只读查询收据并校验当前输出,不触发发布。 |
rollback | 清理本组件已验证的私有暂存区。 |
delete | 按已拥有的 commit/artifact ID 和期望摘要删除输出。 |
materializeOwned | 校验已提交结果并复制到组件私有存储。 |
adopt | 仅用于明确的一次性迁移授权,不是通用文件系统访问。 |
暂存容量与有效期
默认 2 GiB,stage.maxBytes 接受 1 字节至 64 GiB 的安全整数,按字节填写;最多 2,000 文件,有效期固定为创建后 24 小时。内联 base64 最大 8 MiB。提高暂存容量不会改变传输会话或输入快照的独立限制。
await hostCapability("project.output", {
action: "stage",
maxBytes: 16 * 1024 ** 3
});hostCapability 代表组件服务自己的 Host 能力调用封装,不是页面 SDK 方法。
查询提交结果与重启恢复
中断后先按原幂等键查询。该动作绑定当前组件、工作区和项目,不发布或修改文件。
await hostCapability("project.output", {
action: "status",
idempotencyKey: savedOperationKey
});| state | 含义 |
|---|---|
missing | 没有对应的持久提交收据。 |
prepared | 准备收据存在,尚未完成提交。 |
changed | 收据已提交,但当前输出大小或摘要已变化。 |
committed | 输出仍匹配;返回 commitId、idempotencyKey 和项目相对输出引用,不返回物理路径。 |
missing / prepared 不表示可以直接重复不可逆操作。核对暂存区、输出和任务记录再恢复;changed 时停止覆盖并处理冲突。
同名文件、替换与撤销
新文件重名默认冲突;onConflict: "rename" 由宿主另取名称,使用返回的 relativePath。替换必须同时提供 replace: true、previousCommitId、previousArtifactId 和 expectedDigest,摘要变化时保留用户改动。
文件变更后检查 undoAvailable;遇到 outcomeUnknown 先核实实际状态,不重复删除或恢复。
其他项目写入能力
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.timelineFrames 与 office.extractImages。前者传入相对视频路径和时间点;后者传入稳定幂等键、文档相对路径和输出目录。
插件数据与网络
component.storage获取组件专属的数据和 SQLite 存储位置。写入项目文件仍需使用项目输出接口。
component.settingsget 读取,replace 整体替换,merge 只合并第一层字段。JSON 对象最大 256 KiB,保存后返回新的修订号。
component.secrets用 Electron safeStorage 加密保存凭据。list 只返回凭据记录,不返回明文;无法加密或数据损坏时拒绝操作。
network.fetch只能请求清单允许的 HTTPS 网站。origin 是协议、主机名和端口;凭据通过已声明的请求头绑定注入,由宿主检查目标地址和重定向。
网络超时从读取凭据前开始计时,覆盖连接和响应全过程。卸载插件会中止正在执行的网络请求;关闭一个页面不会打断同插件其他页面的网络状态。
组件运行时与播放
component.runtime.execute 需要同名 capability 和 permission。execute 指定组件自身声明的 runtimeCapability 和参数;顶层 capabilities、runtimeCommandCapabilities 与平台入口共同定义可运行命令,不能传入任意可执行文件路径。
inputs.preview 接受当前范围内的 relativePaths 或受限 inputTokens,配合 input.extensions 筛选来源。
task.background:true 必须提供稳定的 operationKey 与 idempotencyKey。任务进入宿主任务中心;调用仍可能等待完成,UI 应独立监听进度。
status、cancel、pause、resume 使用相同 runtime capability 和操作键。取消、暂停需要运行时支持对应的 control 参数;resume 用于继续暂停任务。
eventName 必须在服务事件白名单中。projectArtifacts 登记预览或转码目录时另需 project.progress 能力及权限,并依赖已登记的项目来源节点。
项目输出和私有预览缓存
output.relativeDirectory 相对绑定项目根目录,"." 表示项目根目录。从选中子文件夹执行任务时,可以输出到项目内同级目录;输入仍受来源 scope 限制,输出拒绝绝对路径、上级跳转和逃出项目的链接。
output: {private:true, argument:"--preview-root"} 会分配绑定组件、项目和来源范围的独立预览缓存目录。播放时的 privateOutputPath 必须由组件服务从自己的预览记录解析,宿主拒绝跨范围路径及链接;页面不获得任意私有文件访问权。
组件页面内嵌播放
使用 action:"playback",通过 relativePaths 或保留的 inputGrants 指定输入,并传入 playback 请求。source、backends、start 和 frames 均要求恰好一个已授权媒体文件;服务记录的输出还须经过项目与真实路径校验。
source 返回当前页面可用的媒体 URL;backends 返回宿主播放后端描述;start 创建属于当前页面的会话。control、bounds、stop 只操作自己的会话;frames 最多接受两个有限且非负的秒数时间点,并复用已安装的时间线帧后端。
页面通过 host.onPlaybackState(callback) 监听播放事件,并保存返回的取消订阅函数。host.setPlaybackBounds(sessionId, bounds) 是已有授权会话的单向布局更新,bounds 包含 x、y、width、height、visible;宿主裁切到组件视图并处理缩放,不会因此授予新的媒体访问权。
页面停用、被对话框遮挡或进入裁剪编辑时,应暂停并隐藏播放;销毁视图会关闭原生会话。其他播放操作仍通过服务的 capability 请求执行。
运行时执行默认超时 20 分钟,可在 1 秒至 4 小时内指定 timeoutMs。清单授权、scope、输入令牌和组件归属仍由宿主复核;灵感库任务不应请求项目产物关系登记。
统一播放器与帧时间线
插件页面使用宿主提供的播放器资源,复用主程序的播放界面、快捷键、Chromium 播放和原生后端选择。不要在插件包内复制播放器实现;每次 mount 创建独立实例。
<link rel="stylesheet" href="photoflow-player://runtime/v1/player.css">
<script src="photoflow-player://runtime/v1/player.js"></script>在插件入口脚本之前加载资源;有 CSP 时,将 photoflow-player: 加入 script-src 和 style-src。初始化前检查 window.PhotoFlowPlayback?.apiVersion === 1,不匹配时提示更新主程序。
const player = PhotoFlowPlayback.mount(container, {
apiVersion: 1,
filePath: authorizedRelativeName,
electronApi: scopedPlaybackFacade,
onState: ({ time, duration, paused }) => updateTimeline(time, duration, paused),
onError: message => showError(message),
});
player.playFrom(12.5);
const stoppedAt = await player.pauseAtFrame();
// 页面销毁时释放播放器及插件自己的监听器。
player.close();示例中的 scopedPlaybackFacade 由插件适配已有授权 RPC 和 Host 播放通道,不能替换全局 window.electronAPI。它提供 getVideoPlaybackSource、getVideoPlaybackBackends、startVideoPlayer、stopVideoPlayer、controlVideoPlayer、setVideoPlayerBounds、onVideoPlayerState、getVideoDisplayCapabilities、captureVideoPlayerFrame、publishVideoPlayerFrame、chooseVideoSubtitle 和 setHostSurfaceSuspended。资源 URL 本身不授予文件或媒体访问权限。
原生窗口 bounds 使用设备像素,并附带 viewportDip 矩形;适配层应转换到组件本地 DIP,处理页面可见性、对话框遮挡与裁切后再提交。
实例方法与扩展
实例提供 control、playFrom、pauseAtFrame、fullscreen、refreshBounds、frameAt、backendId、update 和可重复调用的 close。update(patch) 可更新参数,但不能替换授权适配层;媒体授权范围改变时应关闭并创建新实例。暂停并标记位置请等待 pauseAtFrame 完成,它会等待原生后端确认。
frameAt(time) 暂停并定位已授权的 Chromium 视频,返回解码帧的 PNG data URL;原生后端或无法捕获时返回 null,此时可改用已授权的宿主取帧能力。关闭播放器或替换来源会取消待完成的帧读取。
可传入 timeline、surfaceOverlay、toolbar DOM 节点扩展业务界面,覆盖层不得遮挡公共控制条。playbackEnabled 控制是否打开媒体,controlsVisible 控制工具条显示;editorState 提供外部时间线状态。onControlRequest 仅在业务已处理用户操作时返回 true,否则返回 false 使用正常播放;显式调用 control 不经过该回调。
公共帧时间线
可选加载 photoflow-player://runtime/v1/timeline.js 和 photoflow-player://runtime/v1/timeline.css,通过 PhotoFlowTimeline.mount 接入帧时间码、进度拖动、裁剪区间和标记交互。省略 #trim-slider 可禁用裁剪区间,保留标记编辑。
#timeline-current 使用时、分、秒、帧四个输入框。Enter 或移出整组时提交,Escape 恢复播放位置,上下键调整当前单位,左右键切换单位。已索引的视频按真实时间戳逐帧移动,支持可变帧率;忙碌时使用 setEditorEnabled 禁用编辑及逐帧按钮。
视频播放后端协议
顶层 runtimeContributions 可声明 media.playbackBackend 协议版本 1,包括 backend ID、原生进程 transport、优先级、容器/编码探测和 transforms、HDR、统计、字幕、硬解、截图能力矩阵。
清单 priority 用于插件后端之间的排序,宿主还会参考 Chromium 的实际播放能力。
主程序提供统一播放器界面。插件可以另外声明原生设置表单,用于 HDR 偏好和许可说明,不需要创建自定义页面。
插件返回自己的 HWND;主程序验证所属 PID 后负责嵌入、DPI、定位与裁切。
普通 JSON 帧最大 256 KiB,禁止传输图像、像素或音视频帧;会话关闭后,其命令授权随之失效。
任务、协议与错误
tasks 提供 start/report/status/cancel/resume/complete/fail,用于开始任务、报告进度、查询状态、取消和结束。进度范围为 0–100;checkpoint(检查点)记录继续任务所需的信息,插件需要自己实现恢复步骤。收到取消请求后应停止工作并清理未提交结果。
页面 RPC 与服务 JSONL(每行一个 JSON 对象)消息最多 2 MiB。stdout 只输出协议消息,日志写 stderr。方法名、字段、权限、令牌和文件范围都会检查;不符合声明的请求会失败。
普通请求 60 秒超时。媒体处理和组件运行时执行可获得宿主批准的更长等待时间,其他 RPC 不会自动延长。
统一任务进度样式
使用 SDK 的 ui.css,同步更新进度填充宽度、可见百分比与 aria-valuenow;任务结束时将 aria-busy 设为 false。
<div class="pf-task-progress" aria-busy="true">
<div class="pf-task-progress-header">
<p class="pf-task-progress-message">正在处理</p>
<span class="pf-task-progress-percentage">42%</span>
</div>
<div class="pf-progress-track" role="progressbar"
aria-valuemin="0" aria-valuemax="100" aria-valuenow="42">
<div class="pf-progress-fill" style="width:42%"></div>
</div>
</div>外框的 data-state 支持 completed、failed 和 publication-failed;取消、重试等操作放在进度旁。CSS 只负责展示,执行、取消、恢复及进度上报仍由插件与 Host tasks 完成。
错误码列表
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_INTERNALretryable 或接口明确支持安全重试时重试。幂等键(idempotencyKey)用于识别同一次操作,重试时保持不变,避免重复导入或保存。嵌套能力调用预算
| 项目 | 限制 |
|---|---|
| 普通父 RPC | 最多 128 次嵌套能力调用。 |
| 已启动的长任务 | 成功 tasks.start / tasks.resume 返回未取消的 Host 任务后,该父请求最多 262,144 次调用,期限四小时。 |
| 速率与并发 | 长任务每秒最多 128 次;每个父请求同时最多 8 次能力调用。 |
| 协议帧 | JSON 对象最多 2 MiB;日志写 stderr,stdout 只写协议帧。 |
数据归属与执行边界
沙箱隔离只适用于组件 UI。service、生命周期脚本和 executable 是用户安装的受信本机代码,以当前用户的系统权限运行,可访问该用户的文件、网络和进程。Host API 权限约束正常接口调用,受监管进程不等于操作系统沙箱;应只安装可信来源的组件。
项目、媒体索引/变体、版本、文件安全、任务中心、组件生命周期、权限账本与受控发布。
私有存储、设置结构、算法、UI 状态、业务实体和自带的独立运行时。
插件应通过 Host API 修改项目数据,并自行管理插件数据库。卸载插件只移除代码,不自动删除组件数据。
从完整示例开始,了解清单、页面和服务如何配合工作。
云端插件目录(源码契约)
这是软件读取插件公开元数据的独立 HTTP 契约,不是组件 Host capability。以下按当前源码整理;线上部署和可用性需另行确认。
目录字段与刷新规则
GET /v1/plugins 返回 { plugins: [...] }。公开读取不包含管理员凭据、文件名或本地路径。
| 字段 | 类型与用途 |
|---|---|
| id | string;稳定插件 ID。 |
| name | string;显示名称。 |
| homepage | string;允许的 HTTPS 官方主页。 |
| latestVersion | string;日期版本。 |
| capabilities | string[];预览插件包含 preview。 |
| formats | string[];完整文件后缀,大小写不敏感。 |
| previewPromptEnabled | boolean;严格为 true 才允许预览下载提示。 |
只返回已发布记录。缓存和活动页面刷新周期为一分钟;失败清除提示且不阻止本地预览,最短重试间隔 15 秒。已有启用的解码器时不提示,快捷方式不触发提示。目录独立于遥测开关。
管理写入使用 POST /v1/admin/plugins 和管理员 Bearer Token;记录另含 published 与 updatedAt。凭据只在管理端使用,不放进插件、前端或示例。