第三方应用和服务如何调用 VidHub API播放影片
第三方应用可以通过 URL Scheme 调用 VidHub 播放单个视频。VidHub 目前提供两种调用方式:
- 旧版
/open:保留给已有的 Apple 平台接入,支持打开视频和外部字幕。 - 新版
/play:基于 x-callback-url,支持指定起播位置,并在退出播放时返回播放进度和状态。
新版 /play 支持以下平台:
- iPhone 和 iPad
- Apple TV
- Mac
- Android Mobile
- Android TV
新接入请优先使用
/play。已有 Apple 平台的/open调用仍可继续使用;Android Mobile 和 Android TV 目前只支持新版/play。
平台支持
| 平台 | 旧版 /open |
新版 /play |
|---|---|---|
| iPhone / iPad | 支持 | 支持 |
| Apple TV | 支持 | 支持 |
| Mac | 支持 | 支持 |
| Android Mobile | 不支持 | 支持 |
| Android TV | 不支持 | 支持 |
两种调用方式的区别
| 功能 | 旧版 /open |
新版 /play |
|---|---|---|
| 播放单个视频 | 支持 | 支持 |
| 加载外部字幕 | 支持 | 支持 |
| 指定起播位置 | 不支持 | 支持 |
| 指定显示文件名 | 不支持 | 支持 |
| 返回最终播放位置 | 不支持 | 支持 |
| 区分播放完成和中途退出 | 不支持 | 支持 |
| 标准成功、失败、取消回调 | 不完整 | 支持 |
| Android Mobile / TV | 不支持 | 支持 |
旧版方式:/open
旧版调用地址:
open-vidhub://x-callback-url/open
旧版 /open 用于兼容已有的 Apple 平台接入,不适用于 Android。
参数
| 参数 | 是否必填 | 说明 |
|---|---|---|
url |
是 | 视频地址 |
sub |
否 | 外部字幕地址 |
on-success |
否 | VidHub 成功打开播放页时调用的 URL |
on-failed |
否 | VidHub 无法打开时调用的 URL |
基础示例
open-vidhub://x-callback-url/open?url=http%3A%2F%2Flocalhost%3A8080%2Fsample.mp4&on-success=some-app%3A%2F%2Fx-callback-url%2Fsuccess&on-failed=some-app%3A%2F%2Fx-callback-url%2Ffailed
带字幕的示例
open-vidhub://x-callback-url/open?url=http%3A%2F%2Flocalhost%3A8080%2Fsample.mp4&sub=http%3A%2F%2Flocalhost%3A8080%2Fsample.srt&on-success=some-app%3A%2F%2Fx-callback-url%2Fsuccess&on-failed=some-app%3A%2F%2Fx-callback-url%2Ffailed
/open不会在用户退出播放时返回当前播放位置,也无法通过回调区分“播放完成”和“中途退出”。
新版方式:/play
新版调用地址:
open-vidhub://x-callback-url/play
/play 在 iPhone、iPad、Apple TV、Mac、Android Mobile 和 Android TV 上使用相同的地址、参数和回调字段。
它适合将 VidHub 作为外部播放器使用。第三方应用可以传入上次的播放位置,VidHub 退出播放时再通过回调返回最新进度。
一次调用只支持一个视频地址。
请求参数
| 参数 | 是否必填 | 说明 |
|---|---|---|
url |
是 | 要播放的视频地址 |
position |
否 | 起播位置,单位为秒;必须是 0 到 31536000 之间的有效数字 |
filename |
否 | 播放器中显示的文件名或标题 |
sub |
否 | 外部字幕地址 |
x-success |
否 | 播放结束或用户退出播放后的回调 URL |
x-error |
否 | 请求参数无效、播放器忙碌或播放失败时的回调 URL |
x-cancel |
否 | 正式开始播放前,用户取消操作时的回调 URL |
x-source |
否 | 调用方名称,用于标识请求来源;不影响播放 |
request-id |
否 | 由调用方生成的请求标识,VidHub 会在回调中原样返回 |
url、sub 和回调 URL 都必须是包含 Scheme 的完整 URL。回调 URL 不能使用 open-vidhub Scheme,否则会被视为无效回调。
Apple 平台 Swift 调用示例
使用 URLComponents 和 URLQueryItem 生成调用地址,可以避免手动拼接参数时出现编码错误:
func playWithVidHub(
mediaURL: URL,
subtitleURL: URL? = nil,
position: Double = 0,
filename: String? = nil,
requestID: String = UUID().uuidString
) {
var components = URLComponents()
components.scheme = "open-vidhub"
components.host = "x-callback-url"
components.path = "/play"
var queryItems = [
URLQueryItem(name: "url", value: mediaURL.absoluteString),
URLQueryItem(name: "position", value: String(max(0, position))),
URLQueryItem(name: "request-id", value: requestID),
URLQueryItem(name: "x-source", value: "My App"),
URLQueryItem(name: "x-success", value: "myapp://x-callback-url/success"),
URLQueryItem(name: "x-error", value: "myapp://x-callback-url/error"),
URLQueryItem(name: "x-cancel", value: "myapp://x-callback-url/cancel")
]
if let filename, !filename.isEmpty {
queryItems.append(
URLQueryItem(name: "filename", value: filename)
)
}
if let subtitleURL {
queryItems.append(
URLQueryItem(name: "sub", value: subtitleURL.absoluteString)
)
}
components.queryItems = queryItems
guard let vidHubURL = components.url else {
return
}
#if os(macOS)
NSWorkspace.shared.open(vidHubURL)
#else
UIApplication.shared.open(
vidHubURL,
options: [:],
completionHandler: nil
)
#endif
}
Android Mobile / TV Kotlin 调用示例
Android Mobile 和 Android TV 使用相同的 ACTION_VIEW Intent。以下代码可放在两端共用模块中:
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import java.util.UUID
fun playWithVidHub(
context: Context,
mediaUrl: Uri,
subtitleUrl: Uri? = null,
position: Double = 0.0,
filename: String? = null,
requestId: String = UUID.randomUUID().toString(),
) {
val callbackBase = "myapp://x-callback-url"
val vidHubUri = Uri.Builder()
.scheme("open-vidhub")
.authority("x-callback-url")
.appendPath("play")
.appendQueryParameter("url", mediaUrl.toString())
.appendQueryParameter("position", position.coerceAtLeast(0.0).toString())
.appendQueryParameter("request-id", requestId)
.appendQueryParameter("x-source", "My App")
.appendQueryParameter("x-success", "$callbackBase/success")
.appendQueryParameter("x-error", "$callbackBase/error")
.appendQueryParameter("x-cancel", "$callbackBase/cancel")
.apply {
filename?.takeIf { it.isNotBlank() }?.let {
appendQueryParameter("filename", it)
}
subtitleUrl?.let {
appendQueryParameter("sub", it.toString())
}
}
.build()
val intent = Intent(Intent.ACTION_VIEW, vidHubUri).apply {
if (context !is Activity) {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
}
try {
context.startActivity(intent)
} catch (_: ActivityNotFoundException) {
// VidHub 未安装,或当前版本尚不支持该调用方式。
}
}
调用示例:
open-vidhub://x-callback-url/play?url=https%3A%2F%2Fexample.com%2Fvideos%2Fmovie.m3u8&sub=https%3A%2F%2Fexample.com%2Fsubtitles%2Fmovie-zh.srt&position=120&filename=%E7%A4%BA%E4%BE%8B%E5%BD%B1%E7%89%87.mp4&request-id=movie-123&x-source=My%20App&x-success=myapp%3A%2F%2Fx-callback-url%2Fsuccess&x-error=myapp%3A%2F%2Fx-callback-url%2Ferror&x-cancel=myapp%3A%2F%2Fx-callback-url%2Fcancel
实际使用时应由 URLComponents、URLQueryItem 或 Android Uri.Builder 完成百分号编码,不建议手工拼接完整 URL。
回调规则
每次 /play 请求最多触发一次终态回调:
- 正常播放完成或进入播放器后退出:调用
x-success。 - 请求或播放失败:调用
x-error。 - 正式开始播放前取消:调用
x-cancel。
如果调用方没有提供某类回调 URL,VidHub 会直接结束对应流程,不会尝试调用其他类型的回调。
x-success
VidHub 会在视频自然播放完成,或用户在播放过程中退出播放器时调用 x-success。
| 参数 | 说明 |
|---|---|
lastPlayedUrl |
实际播放的视频地址 |
position |
退出时的播放位置,单位为秒,向下取整 |
duration |
视频总时长,单位为秒;无法取得时可能不返回 |
status |
finished 或 stopped |
request-id |
请求时传入的 request-id;未传入时不返回 |
视频自然播放完成:
myapp://x-callback-url/success?lastPlayedUrl=https%3A%2F%2Fexample.com%2Fvideo.m3u8&position=599&duration=600&status=finished&request-id=movie-123
用户中途退出:
myapp://x-callback-url/success?lastPlayedUrl=https%3A%2F%2Fexample.com%2Fvideo.m3u8&position=38&duration=600&status=stopped&request-id=movie-123
建议调用方根据 status 处理进度:
finished:将内容标记为已播放,并按自身规则清除或保留续播位置。stopped:保存position,下次调用/play时将它传回 VidHub。
x-error
请求参数无效、播放器正忙、无法打开播放器或播放失败时,VidHub 会调用 x-error。
| 参数 | 说明 |
|---|---|
errorCode |
错误码 |
errorMessage |
错误说明 |
failedUrl |
失败的视频地址;有可用地址时返回 |
request-id |
请求时传入的 request-id;未传入时不返回 |
示例:
myapp://x-callback-url/error?errorCode=200&errorMessage=Playback%20failed&failedUrl=https%3A%2F%2Fexample.com%2Fvideo.m3u8&request-id=movie-123
错误码:
| 错误码 | 说明 |
|---|---|
100 |
缺少 url 参数 |
101 |
视频 URL 无效 |
102 |
position 无效 |
103 |
不支持的视频 URL Scheme |
104 |
回调 URL 无效 |
200 |
播放失败 |
201 |
播放器正忙,当前无法接受新的外部播放请求 |
202 |
无法显示播放器 |
x-cancel
x-cancel 只用于正式开始播放前的取消操作。回调不会附带播放进度,只会在请求中包含 request-id 时将其返回:
myapp://x-callback-url/cancel?request-id=movie-123
进入播放器后再退出不属于取消,此时会调用 x-success,并返回 status=stopped。
接收回调
调用方需要注册自己的 URL Scheme。以下示例使用:
myapp://
请将 myapp 替换为你的应用实际注册的 Scheme,并确保 x-success、x-error 和 x-cancel 使用该 Scheme。
Apple 平台
使用 Scene 的 UIKit 应用可以在 SceneDelegate 中接收回调:
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
guard let url = URLContexts.first?.url else {
return
}
handleVidHubCallback(url)
}
SwiftUI 应用可以使用:
.onOpenURL { url in
handleVidHubCallback(url)
}
Android Mobile / TV
Android 调用方需要为自己的回调 Activity 注册 Intent Filter:
<activity
android:name=".VidHubCallbackActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="x-callback-url" />
</intent-filter>
</activity>
然后从 intent.data 读取回调路径和查询参数:
val callbackUri = intent?.data ?: return
when (callbackUri.path) {
"/success" -> {
val position = callbackUri.getQueryParameter("position")?.toDoubleOrNull()
val duration = callbackUri.getQueryParameter("duration")?.toDoubleOrNull()
val status = callbackUri.getQueryParameter("status")
val requestId = callbackUri.getQueryParameter("request-id")
}
"/error" -> {
val errorCode = callbackUri.getQueryParameter("errorCode")
val errorMessage = callbackUri.getQueryParameter("errorMessage")
}
"/cancel" -> {
val requestId = callbackUri.getQueryParameter("request-id")
}
}
Android TV 通常没有网页浏览器,因此 TV 端建议使用调用方应用自己注册的回调 Scheme,而不是依赖 HTTPS 网页回调。
URL 编码注意事项
新版 /play 应把原始参数传给系统 URL 构造工具,由系统统一完成百分号编码:
URLQueryItem(name: "url", value: mediaURL.absoluteString)
uriBuilder.appendQueryParameter("url", mediaUrl.toString())
不要先手动编码,再交给 URLQueryItem 或 Uri.Builder,否则可能发生二次编码。
旧版 /open 继续保留原有编码方式,以兼容已有 Apple 平台接入。
从 /open 迁移到 /play
已有 Apple 平台接入可以按以下步骤迁移:
- 将路径从
/open改为/play。 - 将
on-success改为x-success。 - 将
on-failed改为x-error。 - 根据需要增加
x-cancel和request-id。 - 将本地保存的播放进度通过
position传给 VidHub。 - 在回调中读取
position、duration和status。 - 新接口使用系统 URL 构造工具统一编码。
Android Mobile 和 Android TV 应直接使用 /play,无需兼容 /open。
使用注意事项
- 每次调用只支持一个视频 URL。
/play在 iPhone、iPad、Apple TV、Mac、Android Mobile 和 Android TV 上使用相同的协议字段。- 视频和字幕地址必须能从运行 VidHub 的设备访问。
- 视频地址可以使用 VidHub 支持的网络或本地 URL Scheme,但不能使用
open-vidhub作为媒体地址的 Scheme。 - 回调 URL 必须属于第三方应用自身注册的 Scheme,不能使用
open-vidhub。 - 如果第三方应用未安装,或没有应用能处理回调 Scheme,VidHub 不会持续重试。
- 应用被强制结束、系统关机或进程异常退出时,无法保证一定发送最终回调。
- 这套回调用于第三方应用与 VidHub 之间同步本次播放进度,不依赖 Trakt 登录或 Trakt Scrobbling。