视频与预告片 API

视频接口列出电影或剧集的预告片、先导预告、片段和花絮。每条记录给出视频平台名称、视频 key、类型、是否官方、分辨率和发布日期。季与单集路径存在但不返回视频。请在你自己的代码里按语言和类型筛选。

更新于 以 Markdown 查看

获取电影的视频

GET /3/movie/{movie_id}/videos 返回挂在一部电影上的视频记录。记录只有元数据:名称、提供该视频的平台、视频在该平台上的 key、类型和几个标记。amodb 不返回视频文件。

示例打印第一条英文官方预告片,找不到时回退到任意英文预告片。

bashjspyphpgo
curl -s "https://api.amodb.dev/3/movie/550/videos" \
  -H "Authorization: Bearer $AMODB_KEY"
const url = 'https://api.amodb.dev/3/movie/550/videos';
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` } });
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { results } = await res.json();
const trailers = results.filter((v) => v.type === 'Trailer' && v.iso_639_1 === 'en');
const pick = trailers.find((v) => v.official) ?? trailers[0];
console.log(pick ? `${pick.name} (${pick.site}, key ${pick.key})` : 'no trailer');
import os
import requests

r = requests.get(
    "https://api.amodb.dev/3/movie/550/videos",
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
r.raise_for_status()
trailers = [v for v in r.json()["results"] if v["type"] == "Trailer" and v["iso_639_1"] == "en"]
official = [v for v in trailers if v["official"]]
pick = (official or trailers or [None])[0]
print(f"{pick['name']} ({pick['site']}, key {pick['key']})" if pick else "no trailer")
<?php
$url = 'https://api.amodb.dev/3/movie/550/videos';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
    throw new RuntimeException("amodb returned $status");
}
$trailers = array_values(array_filter(
    json_decode($body, true)['results'],
    fn($v) => $v['type'] === 'Trailer' && $v['iso_639_1'] === 'en'
));
$official = array_values(array_filter($trailers, fn($v) => $v['official']));
$pick = $official[0] ?? $trailers[0] ?? null;
echo $pick ? "{$pick['name']} ({$pick['site']}, key {$pick['key']})" : 'no trailer', PHP_EOL;
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

type Videos struct {
	Results []struct {
		Name     string `json:"name"`
		Key      string `json:"key"`
		Site     string `json:"site"`
		Type     string `json:"type"`
		Language string `json:"iso_639_1"`
		Official bool   `json:"official"`
	} `json:"results"`
}

func main() {
	url := "https://api.amodb.dev/3/movie/550/videos"
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	client := &http.Client{Timeout: 10 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		panic(res.Status)
	}
	var v Videos
	if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
		panic(err)
	}
	for _, r := range v.Results {
		if r.Type == "Trailer" && r.Language == "en" && r.Official {
			fmt.Printf("%s (%s, key %s)\n", r.Name, r.Site, r.Key)
			return
		}
	}
	fmt.Println("no official trailer")
}

参数

参数位置必填说明
movie_id路径是数字形式的电影 ID,例如 550。
api_key查询二选一你的密钥。使用它,或使用 Authorization: Bearer 请求头。
language、include_video_language查询否接受但被忽略。响应始终包含该作品的全部视频。请在你的代码里按 iso_639_1 筛选。

响应字段

字段类型说明
idinteger电影 ID。
resultsarray视频记录,不按类型或日期排序。没有已知视频时为空。
results[].iso_639_1、results[].iso_3166_1string视频的语言和国家,例如 en 和 US。
results[].namestring视频标题,例如 #TBT Trailer。
results[].keystring视频在该平台上的 ID。
results[].sitestring视频平台名称。目前始终是 YouTube。
results[].sizeinteger垂直分辨率。目前始终为 1080。
results[].typestringTrailer、Teaser、Clip、Featurette、Behind the Scenes、Bloopers、Opening Credits 或 Recap。
results[].officialboolean由制片方或版权方发布时为 true。
results[].published_atstringISO 8601 时间戳,带或不带毫秒。
results[].idstring视频记录的 ID。与 key 相同。

示例响应

这里显示了电影 550 的 19 条记录中的 3 条。

JSON
{
  "id": 550,
  "results": [
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "Theatrical Trailer (HD Fan Remaster)",
      "key": "6JnN1DmbqoU",
      "site": "YouTube",
      "size": 1080,
      "type": "Trailer",
      "official": false,
      "published_at": "2015-02-26T03:19:25Z",
      "id": "6JnN1DmbqoU"
    },
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "#TBT Trailer",
      "key": "BdJKm16Co6M",
      "site": "YouTube",
      "size": 1080,
      "type": "Trailer",
      "official": true,
      "published_at": "2014-10-02T19:20:22Z",
      "id": "BdJKm16Co6M"
    },
    {
      "iso_639_1": "en",
      "iso_3166_1": "US",
      "name": "Mark Kermode introduces Fight Club | Film4 Interview",
      "key": "tiWCNtGDGEY",
      "site": "YouTube",
      "size": 1080,
      "type": "Featurette",
      "official": true,
      "published_at": "2015-05-08T15:59:05Z",
      "id": "tiWCNtGDGEY"
    }
  ]
}

视频类型

type 字段告诉你视频是什么。请严格匹配,包括所示的大写字母。

类型用途
Trailer主要宣传片。在详情页展示它。
Teaser在预告片之前发布的简短预告。
Clip从成片或单集中截取的场景。
Featurette讲述作品如何制作的短片。
Behind the Scenes片场画面和采访。
Bloopers剪掉的镜头和搞笑集锦。
Opening Credits剧集的片头。
Recap前几集的回顾。

以后可能出现其他取值,所以把未知的 type 当作一般的附加内容,不要因此报错。

挑选合适的视频

对每部作品使用同一套顺序。按顺序尝试你的语言。在同一种语言内,先官方预告片,然后任意预告片,再是先导预告,最后是片段。在同一组内按 published_at 排序:升序得到第一条预告片,降序得到最新的一条。

JavaScript
function pickVideo(results, langs = ['en']) {
  const order = ['Trailer', 'Teaser', 'Clip'];
  for (const lang of langs) {
    for (const type of order) {
      const group = results
        .filter((v) => v.iso_639_1 === lang && v.type === type)
        .sort((a, b) => Number(b.official) - Number(a.official) || Date.parse(a.published_at) - Date.parse(b.published_at));
      if (group.length > 0) return group[0];
    }
  }
  return null;
}

// Russian first, English as the fallback.
// const video = pickVideo(results, ['ru', 'en']);

语言

language 和 include_video_language 不会改变响应,所以一次调用就能得到该作品的全部视频。每条记录都带有 iso_639_1 和 iso_3166_1。电影 550 有标记为 fr、en、es、pt、it、cs、pl 和 ru 的视频。如上所示在你的代码里按 iso_639_1 筛选,然后告诉用户他们将听到哪种语言。一部作品可能没有你想要语言的视频,所以请保留英文或任意语言的回退。14 种提供语言见语言。

其他视频接口

下面的路径返回相同的 id 和 results 结构。

方法和路径返回内容
GET /tv/{series_id}/videos一部剧的视频。
GET /tv/{series_id}/season/{season_number}/videos始终是空的 results 数组。
GET /tv/{series_id}/season/{season_number}/episode/{episode_number}/videos始终是空的 results 数组。

你也可以在电影或剧集详情调用中加 append_to_response=videos。追加的数据块包含 results,没有 id。剧集和季的页面分别在电视剧和季与单集中说明。

错误

HTTPstatus_code何时出现
4017密钥缺失或错误。
40236套餐周期已结束。
40434没有该 ID 的季或单集,或 ID 不是数字。
40537你对视频路径使用了写入方法。
42925超过了每分钟请求数。请等待 Retry-After 秒。

不存在的电影或剧集 ID 在这里不算错误:视频路径返回 200 和 "results": [],与没有视频的已知作品相同。详情路径对未知 ID 返回 404。更多见错误和速率限制。

实用说明

  • 把选中的 site 和 key 与作品一起保存,而不是完整地址,这样以后可以更换播放器。
  • 视频列表遵循边缘 6 小时的详情缓存。几小时的客户端缓存与之匹配。
  • 目前每条记录的 size 都是 1080,所以它无法告诉你哪个片段更轻。让播放器自己选择画质。
  • 不要指望一定存在某条预告片。当你的挑选函数返回 null 时,隐藏播放按钮。
  • results 的顺序没有意义。请在你的代码里排序。
  • 海报和背景图文件见图片。用例视角见电影预告片 API。