← 素材库

API 文档

OpenAPI 3.0

同一份目录,网页与程序都能使用

公开接口无需密钥,可搜索、预览和下载。写入接口使用独立的管理密钥,上传后即可检索,不需要重新部署页面。文件存于 Cloudflare R2,目录存于 D1。

https://product-film-library-mxtec.pages.dev/api/v1
curl --get "https://product-film-library-mxtec.pages.dev/api/v1/assets" \
  --data-urlencode "q=收纳" \
  --data "type=video" --data "page_size=20"

鉴权与密钥

管理接口使用 Authorization: Bearer <管理密钥>。它是本素材库的密钥,与 Cloudflare 账号密钥分开。服务器只保存密钥摘要;浏览页面不会得到管理密钥。

本机已配置 Windows 用户级加密凭据,命令行客户端可自动读取。程序可从环境变量 MEDIA_LIBRARY_API_KEY 读取密钥。请在本机项目目录运行:

# PowerShell:赋值给当前进程,不打印密钥
$env:MEDIA_LIBRARY_API_KEY = & .\scripts\get-token.ps1
node .\scripts\library.mjs auth

其他设备需要自行配置此环境变量。不要把密钥写进网页、公开仓库或链接参数。GET /auth 可检查连接;GET /events 返回最近 100 条管理操作记录;使用 ?after_id=123&limit=100 按 ID 升序读取后续变更,以响应中的 next_after_id 继续读取。变更记录与目录修改在同一数据库事务内提交,包含归档、恢复和永久删除。

上传文件

先创建记录,再向返回的地址上传原始字节。服务端按 SHA-256 去重,R2 验证文件校验和,验证成功才会公开入库。失败记录保持待上传状态,可重新上传或归档。

POST /api/v1/assets
Authorization: Bearer <管理密钥>
Content-Type: application/json
Idempotency-Key: <本次操作的唯一值>

{
  "title": "产品结构演示",
  "category": "家居与家具",
  "tags": ["结构展示", "连续演示"],
  "source": {"url": "https://example.com/original"},
  "file": {
    "name": "demo.mp4",
    "mime_type": "video/mp4",
    "size_bytes": 1234567,
    "sha256": "<文件的 64 位 SHA-256>"
  }
}

首次返回 201;相同文件返回 200 和 duplicate: true,不会复制文件,也不会覆盖已有元数据。同一 Idempotency-Key 与不同请求体组合返回 409。

PUT /api/v1/assets/{id}/content
Authorization: Bearer <管理密钥>
Content-Type: video/mp4
Content-Length: 1234567

<原始二进制文件,不使用 multipart/form-data>

如果上传完成后连接中断,可调用 POST /assets/{id}/complete,服务端会检查已存文件并完成入库。原文件内容不可就地替换;不同文件应创建新素材。上传视频不会在云端转码,画幅、时长由客户端提供;管理页会尝试读取浏览器可识别的文件信息。

预览图片使用 PUT /assets/{id}/thumbnail 或 /storyboard,提供 If-Match、X-Content-SHA256、Content-Length 和图片 Content-Type。支持 JPEG、PNG、WebP,每张最大 4 MiB。浏览器不支持的媒体编码仍可下载原文件。

修改、归档与恢复

先读取详情,保存响应的 ETag,再在修改请求中传入 If-Match。缺少时返回 428,版本过期时返回 412,避免两个程序互相覆盖。

PATCH /api/v1/assets/{id}
Authorization: Bearer <管理密钥>
If-Match: "<id>:2"
Content-Type: application/json

{"title":"新的素材标题","tags":["开箱","功能演示"]}

顶层字段局部更新;tags 数组与 source、metrics 对象整体替换。修改嵌套对象前先读取并合并要保留的字段。

方法路径作用
DELETE/assets/{id}归档,保留文件
POST/assets/{id}/restore恢复归档
DELETE/assets/{id}/purge永久删除,必须先归档,并提供 If-Match 与值等于素材 ID 的 X-Confirm-Purge

归档从公开目录和受管读取接口隐藏素材;已有 R2 公开直链、缓存或已下载副本不会因此撤回。永久删除清除目录记录、R2 原文件和通过 API 写入的预览,不能恢复。待上传和归档记录仍占用本库容量预算,可按需明确执行永久删除。

可运行的客户端

import { MediaLibrary } from './client.js';

const library = new MediaLibrary();
const result = await library.list({ q: '收纳', type: 'video' });
for await (const asset of library.iterate({ category: '家居与家具' })) {
  console.log(asset.title, asset.urls.download);
}

// 写入时传 apiKey;先读取,再带 ETag 更新
const writer = new MediaLibrary({ apiKey: process.env.MEDIA_LIBRARY_API_KEY });
const current = await writer.get('<id>');
await writer.update(current.data.id, { tags: ['功能演示'] }, current.etag);
import os
from client import MediaLibrary

library = MediaLibrary()  # 读取无需密钥
for asset in library.iterate(q="收纳", type="video"):
    print(asset["title"], asset["urls"]["download"])

writer = MediaLibrary(api_key=os.environ["MEDIA_LIBRARY_API_KEY"])
writer.upload("demo.mp4", {"title": "产品结构演示", "tags": ["结构展示"]})

本机命令行

node scripts/library.mjs list "收纳"
node scripts/library.mjs get X11
node scripts/library.mjs upload "D:/素材/demo.mp4" metadata.json
node scripts/library.mjs update <id> patch.json
node scripts/library.mjs archive <id>
node scripts/library.mjs restore <id>

命令行写入自动读取已配置的加密凭据;环境变量 MEDIA_LIBRARY_URL 可覆盖服务地址。全部客户端源码可检查,不依赖 ChatGPT 登录。

限制与错误处理

单文件上传上限 90 MiB(94,371,840 字节),本库容量预算 8 GB,包含原文件、预览和待上传预留。请求 JSON 最多 64 KiB,每条最多 30 个标签。此版本使用单请求上传,不提供多段续传;网络中断可重试同一上传记录。

预算为本库写入保护,不能替代 Cloudflare 账号的全局计费上限。R2、D1 和 Pages Functions 额度按账号与套餐共享;当前使用免费额度内的配置,没有升级套餐。可在 Cloudflare 控制台检查用量。

HTTP处理建议
400 / 422检查参数、字段、大小与校验和
401提供有效的管理密钥
404资源不存在、尚未入库,或已归档
409状态或幂等键冲突;读取最新记录后决定下一步
411 / 413 / 415补充 Content-Length,缩小文件,或使用受支持格式
412 / 428重新读取 ETag,并携带 If-Match
503服务暂时不可用;稍后重试,创建操作保持同一幂等键
507库容量预算不足,先清理不需要的文件
{"error":{"code":"revision_conflict","message":"素材已被修改,请重新读取后再保存。","request_id":"..."}}

保留 request_id 便于排查。API 版本通过 /api/v1 固定;未来破坏性字段变化使用新版本,已有播放与下载路径继续兼容。