播放 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。
countryProduction 必填 / 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_languagesstring[] (≤20)按偏好顺序排列的 BCP-47 或 ISO 语言代码
preferred_genresstring[] (≤50)合作伙伴侧类型标签
subtitle_languagestring默认字幕轨
audio_languagestring默认音频或配音轨
autoplay_nextboolean自动播放下一集
maturity_ratingstring观众最高分级

该端点通过可选 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。

  1. API 密钥有效并具有 playback:token;其环境限定目录和会话访问范围。
  2. 作品在该环境中可见且剧集属于该作品;否则返回 resource_not_found。
  3. 权利包未过期,并满足 status ∈ {CLEAR, RESTRICTED} 和 apiStreamingAllowed = true。
  4. 剧集为 READY,具有已批准、未删除且 HLS 就绪的视频资产,其权利为 CLEAR 或 RESTRICTED。
  5. Production:country 必填;ACTIVE 协议覆盖作品和地区,具有 production_api_enabled = true,并以 accessLevel = stream 包含就绪视频资产。Sandbox:无需协议;如提供 country,会与可见权利地区核对。
  6. 密钥配置 allowed_origins 时,origin 必填且必须匹配。配置 allowed_ips 时,令牌签发请求 IP 必须匹配。这些限制仅在签发时应用;令牌不绑定观众 IP。
  7. 并发限制:对 Production 协议或 Sandbox 作品,每个 viewer_id_hash 最多 PLAYBACK_MAX_CONCURRENT_SESSIONS 个活动会话(默认 3)。超额签发会创建 BLOCKED 会话并返回 playback_concurrency_limit(429)。

错误原因

error.codeHTTP原因
resource_not_found404作品在环境中不可见,或剧集缺失/属于其他作品
missing_scope403密钥缺少 playback:token
license_not_active403无 ACTIVE 协议、production_api_enabled 为 false,或无可见可流式播放权利包
territory_not_allowed403缺少 Production country、country 不在协议/权利地区、缺少必需 origin,或 origin/IP 被阻止
episode_not_licensed403剧集不是 READY、无就绪视频资产,或资产不在授权集合中
validation_failed422viewer_id_hash 疑似原始 PII,或 preferences 含未知键/含原始 PII('@' 或空格)的值
playback_concurrency_limit429该观众与协议或作品的活动会话过多
conflict409使用处理中 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 报告播放活动。