图片 API 与图片主机
图片接口以文件路径的形式列出作品的海报、背景图、标志、人物照片和剧照。随后从 img.amodb.dev 按固定尺寸加载每个文件,无需密钥,边缘缓存 30 天。路径和 JSON 与 TMDB v3 一致,换基础 URL 后现有图片代码即可使用。
获取电影的图片
GET / 以三个数组列出一部电影所有已知的图片:backdrops、logos 和 posters。每一项有 file_path、像素尺寸和语言标记。JSON 里不含文件本身,你要在第二步从图片主机加载它。
示例请求英文和无文字图片,选出第一张英文海报,然后以 500 像素宽度获取该文件。
curl -s "https://api.amodb.dev/3/movie/550/images?include_image_language=en,null" \
-H "Authorization: Bearer $AMODB_KEY"
curl -s -o poster.jpg -w "%{http_code}\n" \
"https://img.amodb.dev/t/p/w500/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg"const url = 'https://api.amodb.dev/3/movie/550/images?include_image_language=en,null';
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` } });
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { posters } = await res.json();
if (posters.length === 0) throw new Error('no posters');
const poster = posters.find((p) => p.iso_639_1 === 'en') ?? posters[0];
const posterUrl = `https://img.amodb.dev/t/p/w500${poster.file_path}`;
const img = await fetch(posterUrl);
console.log(img.status, img.headers.get('content-type'), posterUrl);import os
import requests
r = requests.get(
"https://api.amodb.dev/3/movie/550/images",
params={"include_image_language": "en,null"},
headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
timeout=10,
)
r.raise_for_status()
posters = r.json()["posters"]
if not posters:
raise SystemExit("no posters")
poster = next((p for p in posters if p["iso_639_1"] == "en"), posters[0])
poster_url = "https://img.amodb.dev/t/p/w500" + poster["file_path"]
img = requests.get(poster_url, timeout=10)
print(img.status_code, img.headers.get("content-type"), poster_url)<?php
$url = 'https://api.amodb.dev/3/movie/550/images?include_image_language=en,null';
$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");
}
$posters = json_decode($body, true)['posters'];
if (!$posters) {
throw new RuntimeException('no posters');
}
$poster = $posters[0];
foreach ($posters as $p) {
if ($p['iso_639_1'] === 'en') {
$poster = $p;
break;
}
}
$posterUrl = 'https://img.amodb.dev/t/p/w500' . $poster['file_path'];
$img = curl_init($posterUrl);
curl_setopt_array($img, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10]);
curl_exec($img);
echo curl_getinfo($img, CURLINFO_HTTP_CODE), ' ', curl_getinfo($img, CURLINFO_CONTENT_TYPE), ' ', $posterUrl, PHP_EOL;
curl_close($img);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
type Images struct {
Posters []struct {
FilePath string `json:"file_path"`
Language string `json:"iso_639_1"`
} `json:"posters"`
}
func main() {
url := "https://api.amodb.dev/3/movie/550/images?include_image_language=en,null"
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 images Images
if err := json.NewDecoder(res.Body).Decode(&images); err != nil || len(images.Posters) == 0 {
panic("no posters")
}
poster := images.Posters[0]
for _, p := range images.Posters {
if p.Language == "en" {
poster = p
break
}
}
posterURL := "https://img.amodb.dev/t/p/w500" + poster.FilePath
img, err := client.Get(posterURL)
if err != nil {
panic(err)
}
defer img.Body.Close()
fmt.Println(img.StatusCode, img.Header.Get("Content-Type"), posterURL)
}参数
movie_id路径必填数字形式的电影 ID,例如550。api_key查询二选一你的密钥。使用它,或使用Authorization: Bearer请求头。include_image_language查询用逗号分隔的 ISO 639-1 代码。加上null可包含无文字图片,例如en,null。只返回列出的语言。不传则返回所有语言。language查询此接口接受但会忽略它。请用include_image_language选择语言。
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
id | integer | 电影 ID。 |
backdrops、logos、posters | array | 每种图片一个数组,键按此顺序排列。每个都可能为空。 |
file_path | string | 以斜杠开头。把它追加到图片主机的某个尺寸后面。海报和背景图是 .jpg,标志是 .png 或 .svg。 |
width、height | integer | 原始文件的像素尺寸。尺寸未知时为 0。 |
aspect_ratio | number | 宽度除以高度。海报约为 0.667,背景图约为 1.778。尺寸未知时为 0。 |
iso_639_1 | string 或 null | 图片上文字的语言。null 表示没有文字。 |
vote_average、vote_count | number、integer | 电影和剧集的图片始终为 0,所以无法用来给图片排名。请保持 API 返回的顺序。 |
示例响应
每个数组显示两项。
{
"id": 550,
"backdrops": [
{
"aspect_ratio": 1.778,
"height": 1440,
"iso_639_1": null,
"file_path": "/c6OLXfKAk5BKeR6broC8pYiCquX.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2560
},
{
"aspect_ratio": 1.778,
"height": 1080,
"iso_639_1": "en",
"file_path": "/b9HyPoxwxjxkWEUL5ErZdhApQe2.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 1920
}
],
"logos": [
{
"aspect_ratio": 4.638,
"height": 389,
"iso_639_1": "en",
"file_path": "/7Uqhv24pGJs4Ns31NoOPWFJGWNG.png",
"vote_average": 0,
"vote_count": 0,
"width": 1804
},
{
"aspect_ratio": 0.848,
"height": 1295,
"iso_639_1": "en",
"file_path": "/4XkF0Rf7gvSfea8fYLFbU5tmuJw.png",
"vote_average": 0,
"vote_count": 0,
"width": 1098
}
],
"posters": [
{
"aspect_ratio": 0.667,
"height": 3000,
"iso_639_1": "en",
"file_path": "/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 2000
},
{
"aspect_ratio": 0,
"height": 0,
"iso_639_1": null,
"file_path": "/qkrH7rjYKU4KOwwIl6wF6H3ISvm.jpg",
"vote_average": 0,
"vote_count": 0,
"width": 0
}
]
}第二张海报展示了尺寸未知的条目。文件仍然可以加载。
构建图片 URL
把三部分拼在一起:https:/、一个尺寸段和 file_path。路径本身已经以斜杠开头,所以 https:/ 加上 / 就是一个完整的 URL。
尺寸是 w 加像素宽度、h 加像素高度,或表示存储文件的 original。主机提供十种尺寸:w92、w154、w185、w300、w342、w500、w780、w1280、h632 和 original。其他任何尺寸,例如 w400,都返回 404。/3/configuration 列出每种图片类型适用的尺寸。
| 类型 | 配置字段 | 尺寸 |
|---|---|---|
| 海报 | poster_sizes | w92、w154、w185、w342、w500、w780、original |
| 背景图 | backdrop_sizes | w300、w780、w1280、original |
| 标志 | logo_sizes | w92、w154、w185、w300、w500、original |
| 人物照片 | profile_sizes | w185、h632、original |
| 剧照 | still_sizes | w92、w185、w300、original |
主机不检查类型,所以海报也能以 w1280 加载。请使用该类型所列的尺寸,这样你的代码才与配置以及任何 TMDB 客户端一致。
图片主机无需密钥,边缘节点把每个文件缓存 30 天。因为不发送密钥,加载图片不消耗你每分钟的请求数。响应带有 Access-Control-Allow-Origin: *,所以浏览器可以从任何页面加载它们。未知的尺寸或未知的文件会返回 404 和一个 JSON 响应体。
用 srcset 实现响应式图片
选择仍能覆盖显示宽度乘以屏幕密度的最小尺寸。一张显示为 171 个 CSS 像素宽的海报,在 2x 屏幕上需要 w342。下面的辅助函数根据保存的路径构建 srcset。
const WIDTHS = [185, 342, 500, 780];
function posterSrcset(filePath) {
return WIDTHS.map((w) => `https://img.amodb.dev/t/p/w${w}${filePath} ${w}w`).join(', ');
}
const path = '/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg';
console.log(`<img src="https://img.amodb.dev/t/p/w342${path}" srcset="${posterSrcset(path)}" sizes="(max-width: 600px) 50vw, 342px" alt="Fight Club poster">`);其他图片接口
下面所有路径都使用基础地址 https:/,并返回第二列所列的数组。
| 方法和路径 | 返回的数组 | 参数 |
|---|---|---|
GET / | backdrops、logos、posters | include_image_language |
GET / | posters,始终为空 | 无 |
GET / | stills,一项,width 和 height 设为 0 | 无 |
GET / | profiles | 无 |
对于电影和剧集,你也可以在详情调用上加 append_。include_image_language 在那里同样有效。追加的数据块没有自己的 id。
人物照片是 vote_average 带有真实评分的唯一地方。季或集响应的 id 是该季或该集的 ID。父级记录见人物和电视剧。
amodb 不提供合集、公司和电视网的图片路径。它们的记录本身已包含一个 poster_path、backdrop_path 或 logo_path,见系列合集和公司与电视网。
配置接口
GET /3/configuration 返回一个 images 块,其中 secure_base_url 设为 https:/,base_url 是纯 http 形式,还有尺寸列表 backdrop_sizes、logo_sizes、poster_sizes、profile_sizes 和 still_sizes。启动时读取一次,使用安全形式,并据此构建 URL,而不是把尺寸写死。边缘节点缓存它 24 小时。
错误
| HTTP | status_code | 何时出现 |
|---|---|---|
| 401 | 7 | JSON 调用的密钥缺失或错误。 |
| 402 | 36 | 套餐周期已结束。 |
| 404 | 34 | 没有该 ID 的人物、季或集,或图片主机上的尺寸或文件未知。 |
| 405 | 37 | 你对图片路径使用了写入方法。 |
| 429 | 25 | 你超过了每分钟请求数。请等待 Retry-After 秒。 |
不存在的电影或剧集 ID 在这里不算错误:图片路径返回 200 和空数组,而详情路径返回 404。完整列表见错误。图片文件请求是普通的 HTTP 加载,所以请检查响应的状态码,而不是解析 JSON 响应体。
实用说明
- 在数据库中保存
file_path,在渲染时构建 URL。这样新增尺寸或更换主机只需改一处代码。 - 把
include_image_language设为你的显示语言加null,例如ru,null,这样带外语文字的海报就不会出现。 - 空数组是正常的 200。请回退到电影详情记录上的
poster_path。 - 全宽主视觉背景图用
w1280,卡片用w780。original是完整分辨率的存储文件,比w780大得多。 - 边缘副本保存 30 天且是共享的,所以一个客户端请求过的文件会从缓存提供给下一个客户端。