播放 API
授权一名观众观看一个剧集,并获得可立即在 WebView 播放的托管链接,以及用于自有播放器的等效签名 HLS/DASH 清单。可选非 PII 偏好用于个性化 WebView。需要 playback:token。
端点
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /playback/tokens | 授权剧集 → WebView 链接及签名清单 |
| GET | /playback/sessions/{session_id} | 获取播放会话 |
| POST | /playback/sessions/{session_id}/revoke | 结束活动会话 |
令牌请求
curl -X POST "https://signal-partners.newunivers.ai/v1/playback/tokens" \
-H "Authorization: Bearer nsp_live_xxx" \
-H "X-NU-Partner-Id: org_acme" \
-H "X-NU-Request-Id: req_playback_0001" \
-H "Content-Type: application/json" \
-d '{
"title_id": "ttl_abc",
"episode_id": "ep_001",
"viewer_id_hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"country": "KR",
"device": "web",
"origin": "https://app.acme.example",
"preferences": {
"preferred_languages": ["ko", "en"],
"preferred_genres": ["romance", "thriller"],
"subtitle_language": "ko",
"audio_language": "ko",
"autoplay_next": true,
"maturity_rating": "15"
},
"expires_in": 1800
}'| 字段 | 必需 | 说明 |
|---|---|---|
title_id | 是 | |
episode_id | 是 | 必须属于该作品 |
viewer_id_hash | 是 | 合作伙伴侧 SHA-256(stable_viewer_id + salt)。Production 要求恰好 64 个小写十六进制字符;Sandbox 允许旧式 opaque ID。禁止原始 PII。 |
country | Production 必填 / Sandbox 非必填 | ISO 3166-1 alpha-2。Production 必填并与协议地区核对。Sandbox 可选;如提供则与可见权利地区核对。 |
device | 否 | 例如:web、ios、android |
origin | 有条件 | 密钥配置 allowed_origins 时必填,且必须匹配其中一项。 |
preferences | 否 | 用于托管 WebView 的非 PII 观众偏好信号;见下文。 |
expires_in | 否 | 令牌 TTL(秒)。默认 1,800;绝对上限 7,200;Production 默认上限 1,800。 |
preferences(观众信号)
可选对象,由托管 WebView 应用于字幕/音频默认值、自动播放和推荐,并存入会话用于分析。Schema 严格;未知键返回 422。值不得含原始 PII;包含 @ 或空格时返回 422 validation_failed。
| 字段 | 类型 | 说明 |
|---|---|---|
preferred_languages | string[] (≤20) | 按偏好顺序排列的 BCP-47 或 ISO 语言代码 |
preferred_genres | string[] (≤50) | 合作伙伴侧类型标签 |
subtitle_language | string | 默认字幕轨 |
audio_language | string | 默认音频或配音轨 |
autoplay_next | boolean | 自动播放下一集 |
maturity_rating | string | 观众最高分级 |
该端点通过可选 X-NU-Request-Id 实现幂等。重试相同 ID 返回原始响应;仍在处理的并发重复请求返回 conflict(409)。
响应
{
"data": {
"playback_session_id": "pbs_123",
"expires_at": "2026-06-24T00:30:00Z",
"webview_url": "https://signal-partners.newunivers.ai/w/pbs_123?t=...",
"manifest": {
"hls": "https://signal-partners.newunivers.ai/media/hls/pbs_123/signal-city/ep001/master.m3u8?token=...",
"dash": "https://signal-partners.newunivers.ai/media/dash/pbs_123/signal-city/ep001/manifest.mpd?token=..."
},
"tracking": {
"event_endpoint": "https://signal-partners.newunivers.ai/v1/events",
"required_events": [
"EPISODE_STARTED",
"PLAYBACK_PROGRESS",
"EPISODE_COMPLETED"
]
}
}
}提供两种等效方式播放同一已授权会话。请选择一种。
webview_url(推荐):由 NU 托管的仅播放页面。在 WebView(WKWebView/android.webkit.WebView)或<iframe>中打开。它在内部解析清单并应用已存preferences;无需播放器集成。链接包含会话 ID 和签名?t=;应视为密钥,且不得在expires_at后缓存。manifest(自有播放器):面向自营播放器合作伙伴的签名 HLS/DASH URL。每个清单或分段请求都会验证签名、TTL、会话 ID 和获批媒体路径。绝不返回源主文件。验证在应用的/media或等效 Cloudflare 边缘 Worker 中运行;URL 约定相同。
两种方式均绑定同一 playback_session_id;事件报告方式相同。
测试 WebView
获取真实会话前,可打开测试播放器验证 WebView 或 iframe 集成。无需令牌、会话或媒体配置,即可立即播放 Episode 009 Confession 的 9:16 预览。
https://signal-partners.newunivers.ai/w/test?subtitle=ko&audio=ko&lang=ko&autoplay=1测试使用与 webview_url 完全相同的播放器。若竖屏示例可在 WebView 播放,真实链接也使用相同 9:16 显示策略。
验证顺序
检查按顺序执行。缺少作品或剧集时返回 resource_not_found(404)。资源解析后,授权失败返回 403,并在 BLOCKED 会话记录 block_reason。
- API 密钥有效并具有
playback:token;其环境限定目录和会话访问范围。 - 作品在该环境中可见且剧集属于该作品;否则返回
resource_not_found。 - 权利包未过期,并满足
status ∈ {CLEAR, RESTRICTED}和apiStreamingAllowed = true。 - 剧集为
READY,具有已批准、未删除且 HLS 就绪的视频资产,其权利为CLEAR或RESTRICTED。 - Production:
country必填;ACTIVE协议覆盖作品和地区,具有production_api_enabled = true,并以accessLevel = stream包含就绪视频资产。Sandbox:无需协议;如提供country,会与可见权利地区核对。 - 密钥配置
allowed_origins时,origin必填且必须匹配。配置allowed_ips时,令牌签发请求 IP 必须匹配。这些限制仅在签发时应用;令牌不绑定观众 IP。 - 并发限制:对 Production 协议或 Sandbox 作品,每个
viewer_id_hash最多PLAYBACK_MAX_CONCURRENT_SESSIONS个活动会话(默认 3)。超额签发会创建BLOCKED会话并返回playback_concurrency_limit(429)。
错误原因
| error.code | HTTP | 原因 |
|---|---|---|
resource_not_found | 404 | 作品在环境中不可见,或剧集缺失/属于其他作品 |
missing_scope | 403 | 密钥缺少 playback:token |
license_not_active | 403 | 无 ACTIVE 协议、production_api_enabled 为 false,或无可见可流式播放权利包 |
territory_not_allowed | 403 | 缺少 Production country、country 不在协议/权利地区、缺少必需 origin,或 origin/IP 被阻止 |
episode_not_licensed | 403 | 剧集不是 READY、无就绪视频资产,或资产不在授权集合中 |
validation_failed | 422 | viewer_id_hash 疑似原始 PII,或 preferences 含未知键/含原始 PII('@' 或空格)的值 |
playback_concurrency_limit | 429 | 该观众与协议或作品的活动会话过多 |
conflict | 409 | 使用处理中 X-NU-Request-Id 的并发重复请求 |
会话
curl "https://signal-partners.newunivers.ai/v1/playback/sessions/pbs_123" \
-H "Authorization: Bearer nsp_live_xxx" -H "X-NU-Partner-Id: org_acme"{
"data": {
"playback_session_id": "pbs_123",
"title_id": "ttl_abc",
"episode_id": "ep_001",
"status": "issued",
"block_reason": null,
"preferences": { "subtitle_language": "ko", "autoplay_next": true },
"expires_at": "2026-06-24T00:30:00Z",
"started_at": null,
"completed_at": null
}
}API 以小写返回已存会话状态。流程:issued → started → completed | expired | revoked | blocked。令牌签发创建 issued;EPISODE_STARTED 推进到 started 并填充 started_at;EPISODE_COMPLETED 推进到 completed 并填充 completed_at。TTL 后读取会投影 expired,此 GET 不会写入数据库;授权失败创建含 block_reason 的 blocked。未知 ID 返回 invalid_playback_session(400)。
吊销会话
curl -X POST "https://signal-partners.newunivers.ai/v1/playback/sessions/pbs_123/revoke" \
-H "Authorization: Bearer nsp_live_xxx" -H "X-NU-Partner-Id: org_acme"将活动 issued/started 会话标记为 revoked,从并发计数中移除。响应返回真实最终状态;已结束会话保持 completed、expired 或现有状态。应用 /media 路径会在下一次清单请求检查吊销,但现有边缘 Worker 令牌可能在 TTL 内仍有效,因此 TTL 应保持较短。
viewer_id_hash 与到期
viewer_id_hash必须是稳定观众 ID 加服务端盐的 SHA-256。Production 仅接受 64 个小写十六进制字符;Sandbox 保留旧式 opaque ID。NU 从不接受或存储原始 PII,包括preferences;含@或空格的值会被拒绝。- 令牌有效期很短:默认 1,800 秒,绝对上限 7,200,Production 默认上限 1,800。到期后重新签发;绝不要在
expires_at后缓存webview_url或清单 URL。webview_url含签名令牌,不要记录日志或放入可分享链接。 - 吊销密钥或停用协议不会取消已签发的播放令牌。TTL 应保持较短,到期后重新签发。
使用返回的 playback_session_id 通过 事件 API 报告播放活动。