按范围和预算选择

TVmaze 只提供电视剧。它是可免费使用的公开 API,按 IP 地址设置速率限制,数据遵循署名许可,详情请查阅其文档。amodb 是付费的,涵盖电视剧、电影和人物。如果你的应用是电视剧追踪器,而免费数据源能满足需求,那么 TVmaze 是不错的起点。如果你还要展示电影、演员页面或多语言海报,一个 amodb 密钥就能取代多个数据源。

不同范围对你的应用意味着什么

你的应用展示TVmazeamodb
电视剧页面支持GET /3/tv/{series_id}
剧集列表支持GET /3/tv/{series_id}/season/{season_number}
电影页面没有电影GET /3/movie/{movie_id}
演员页面请查阅其文档GET /3/person/{person_id} 和合并的演职员记录
今日播出的剧集请查阅其文档GET /3/tv/airing_today
翻译后的简介请查阅其文档language 参数,14 种语言
多个网站的评分请查阅其文档/ratings 扩展

电影这一行决定了大多数情况。如果你没有电影页面,额外的范围对你就没有价值。

迁移前要检查的缺口

季和集接口遵循 TMDB 的结构,其中少数部分返回为空。请测试用到它们的页面。

  • 单集演员。 在单集上,cast 是空数组。guest_stars 和 crew 有内容,主要演员在剧集的 /credits 调用中。
  • 季的附加内容。 季的 images、videos 和 translations 为空,因此季海报来自季记录,而不是图片列表。
  • 外部 ID。 季和集的 external_ids 全为 null。请按 season_number 和 episode_number 把剧集映射到你自己的记录。
  • 创作者。 大多数剧集的 created_by 为空。
  • 没有变更动态。 amodb 没有列出自某个日期以来有变化的剧集的接口。

功能对比

功能TVmazeamodb
定价模式可免费使用的公开 API预付,每次购买 1 至 24 个月
免费额度有,整个公开 API无,也没有试用
认证方式请查阅其文档api_key 或 Bearer 请求头
响应格式JSON,请查阅其文档TMDB v3 格式的 JSON
语言请查阅其文档14 种界面语言
电影无支持
电视剧支持,唯一的定位支持
人物请查阅其文档支持
图片请查阅其文档img.amodb.dev,十种尺寸标识
评分请查阅其文档已知时提供五个来源
速率限制按 IP 地址,具体数字请查阅其文档每个密钥每分钟 20、40 或 120 次
商业使用数据遵循署名许可,请查阅其文档付费套餐,见定价
兼容性自有 APITMDB v3 读取接口和 OMDb

amodb 的不同之处

  • 限制跟着密钥走。 你所有的服务器共用一个计数器,窗口为 60 秒滑动窗口,响应带有 X-RateLimit-Remaining。请求从哪个地址发出并不影响计数。
  • 电影、电视剧和人物集中在一处。 一个客户端、一种错误格式和一组响应头就能覆盖整个应用。
  • 语言。 片名和简介可以用 14 种语言中的任意一种返回,original_name 保留原生标题。
  • 成熟的协议。 为 TMDB v3 读取编写的库,换一个新的基础 URL 就能使用。剧集接口见电视剧 API页面。

TVmaze 更适合的场景

  • 没有预算。 它可免费使用,学生项目或周末做的追剧工具无需付费。amodb 没有免费额度,也没有试用。
  • 只做电视剧,保持简单。 如果你的数据模型里只有剧集和单集,聚焦电视剧的 API 更贴合。
  • 它自己的约定。 已经围绕 TVmaze 模型构建的应用,留在原地可以省事。

如果继续使用它,请遵守其署名许可。具体措辞请查阅其文档。

迁移到 amodb

从一个映射任务开始。读取你保存的 TVmaze 记录所列出的外部 ID(字段名请查阅其文档),并通过 /3/find 解析。下面的程序把一个 IMDb ID 映射到一部剧集,并打印下一集的日期。

Go
package main

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

type findResult struct {
	TVResults []struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	} `json:"tv_results"`
}

type episode struct {
	AirDate       string `json:"air_date"`
	SeasonNumber  int    `json:"season_number"`
	EpisodeNumber int    `json:"episode_number"`
}

type series struct {
	Name        string   `json:"name"`
	Status      string   `json:"status"`
	NextEpisode *episode `json:"next_episode_to_air"`
}

var client = &http.Client{Timeout: 10 * time.Second}

func get(path string, out any) error {
	req, err := http.NewRequest(http.MethodGet, "https://api.amodb.dev/3"+path, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	res, err := client.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		return fmt.Errorf("amodb returned %s", res.Status)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

func main() {
	var found findResult
	if err := get("/find/tt0903747?external_source=imdb_id", &found); err != nil {
		fmt.Println(err)
		return
	}
	if len(found.TVResults) == 0 {
		fmt.Println("no match")
		return
	}
	var s series
	if err := get(fmt.Sprintf("/tv/%d", found.TVResults[0].ID), &s); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(s.Name, s.Status)
	if s.NextEpisode != nil {
		fmt.Println("next episode:", s.NextEpisode.AirDate)
	}
}

NextEpisode 的指针类型用来处理已完结剧集返回的 null。运行一次映射,把 amodb ID 与你的每条记录一起保存,之后改为调用 /3/tv/{id}。429 响应会带有 Retry-After,其他 ID 来源见按外部 ID 查找页面。

估算夜间同步的规模

假设你每晚刷新 300 部剧。由于一次剧集调用返回一部剧,这就是 300 个请求。每分钟 20 个请求时任务需要 15 分钟,40 个时需要 7.5 分钟,120 个时需要 2.5 分钟。请在每次调用中加上 append_to_response=credits,ratings,让演职员和评分在同一个请求中返回,而不是每部剧再多花两次请求。读取 X-RateLimit-Remaining,降到零时暂停,然后在 X-RateLimit-Reset 秒之后继续。

定价

amodb 采用预付方式,从不自动续费。可选择 1、3、6、12 或 24 个月,以及每分钟 20、40 或 120 个请求的套餐。较长的周期便宜 10、20、30 或 40%,每个套餐都包含全部数据。具体金额见定价。