API d'images et hôte d'images
Les points de terminaison d'images listent les affiches, fonds, logos, profils et images d'épisodes d'un titre en chemins de fichiers. Vous chargez ensuite chaque fichier depuis img.amodb.dev dans une taille fixe, sans clé et avec un cache de périphérie de 30 jours. Chemins et JSON suivent TMDB v3 : le code existant fonctionne après un changement d'URL de base.
Obtenir les images d'un film
GET / liste toutes les images connues d'un film sous forme de trois tableaux : backdrops, logos et posters. Chaque entrée a un file_path, sa taille en pixels et une balise de langue. Le fichier lui-même n'est pas dans le JSON. Vous le chargez depuis l'hôte d'images dans un second temps.
L'exemple demande les images en anglais et sans texte, choisit la première affiche anglaise, puis récupère ce fichier en 500 pixels de large.
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)
}Paramètres
movie_idcheminobligatoireIdentifiant numérique du film, par exemple550.api_keyrequêtel'un des deuxVotre clé. Utilisez-la ou l'en-têteAuthorization: Bearer.include_image_languagerequêteCodes ISO 639-1 séparés par des virgules. Ajouteznullpour inclure les images sans texte, par exempleen,null. Seules les langues listées reviennent. Sans lui, vous obtenez toutes les langues.languagerequêteAccepté mais ignoré sur ce point de terminaison. Utilisezinclude_image_languagepour choisir les langues.
Champs de la réponse
| Champ | Type | Remarques |
|---|---|---|
id | entier | L'identifiant du film. |
backdrops, logos, posters | tableau | Un tableau par type d'image, dans cet ordre de clés. Chacun peut être vide. |
file_path | chaîne | Commence par une barre oblique. À ajouter à une taille sur l'hôte d'images. Les affiches et les images de fond sont en .jpg, les logos en .png ou .svg. |
width, height | entier | Taille en pixels du fichier d'origine. 0 quand la taille n'est pas connue. |
aspect_ratio | nombre | Largeur divisée par hauteur. Les affiches sont proches de 0.667, les images de fond de 1.778. 0 quand la taille n'est pas connue. |
iso_639_1 | chaîne ou null | Langue du texte sur l'image. null signifie qu'il n'y a pas de texte. |
vote_average, vote_count | nombre, entier | Toujours 0 pour les images de films et de séries : elles ne peuvent donc pas classer les images. Gardez l'ordre que renvoie l'API. |
Exemple de réponse
Deux entrées par tableau sont montrées.
{
"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
}
]
}La seconde affiche montre une entrée dont la taille n'est pas connue. Le fichier se charge quand même.
Construire une URL d'image
Assemblez trois parties : https:/, un segment de taille et le file_path. Le chemin commence déjà par une barre oblique : https:/ plus / est donc une URL complète.
Une taille est w plus une largeur en pixels, h plus une hauteur en pixels, ou original pour le fichier stocké. L'hôte sert dix tailles : w92, w154, w185, w300, w342, w500, w780, w1280, h632 et original. Toute autre taille, comme w400, répond 404. /3/configuration liste les tailles qui conviennent à chaque type d'image.
| Type | Champ de configuration | Tailles |
|---|---|---|
| Affiche | poster_sizes | w92, w154, w185, w342, w500, w780, original |
| Image de fond | backdrop_sizes | w300, w780, w1280, original |
| Logo | logo_sizes | w92, w154, w185, w300, w500, original |
| Profil | profile_sizes | w185, h632, original |
| Image d'épisode | still_sizes | w92, w185, w300, original |
L'hôte ne vérifie pas le type : une affiche se charge donc aussi en w1280. Restez sur les tailles listées pour le type, afin que votre code corresponde à la configuration et à tout client TMDB.
L'hôte d'images n'exige aucune clé, et la périphérie garde chaque fichier 30 jours. Comme aucune clé n'est envoyée, les chargements d'images n'utilisent pas vos requêtes par minute. Les réponses portent Access-Control-Allow-Origin: * : un navigateur peut donc les charger depuis n'importe quelle page. Une taille inconnue ou un fichier inconnu répond 404 avec un corps JSON.
Images adaptatives avec srcset
Choisissez la plus petite taille qui couvre encore la largeur affichée multipliée par la densité d'écran. Une affiche affichée sur 171 pixels CSS de large demande w342 sur un écran 2x. Cette fonction d'aide construit un srcset à partir d'un chemin stocké.
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">`);Autres points de terminaison d'images
Tous les chemins ci-dessous utilisent la base https:/ et renvoient les tableaux nommés dans la deuxième colonne.
| Méthode et chemin | Tableaux renvoyés | Paramètres |
|---|---|---|
GET / | backdrops, logos, posters | include_image_language |
GET / | posters, toujours vide | aucun |
GET / | stills, une entrée avec width et height à 0 | aucun |
GET / | profiles | aucun |
Pour les films et les séries, vous pouvez aussi ajouter append_ à un appel de détails. include_image_language y fonctionne aussi. Le bloc ajouté n'a pas d'id propre.
Les profils de personnes sont le seul endroit où vote_average porte une vraie note. L'id d'une réponse de saison ou d'épisode est l'identifiant de la saison ou de l'épisode. Voir personnes et séries TV pour les fiches parentes.
amodb ne propose pas de chemins d'images pour les collections, les sociétés et les chaînes. Leurs fiches contiennent déjà un poster_path, un backdrop_path ou un logo_path, décrits sur collections et sociétés et chaînes.
Le point de terminaison de configuration
GET /3/configuration renvoie un bloc images avec secure_base_url réglé sur https:/, base_url (la forme http simple), et les listes de tailles backdrop_sizes, logo_sizes, poster_sizes, profile_sizes et still_sizes. Lisez-le une fois au démarrage, utilisez la forme sécurisée et construisez les URL à partir de lui au lieu d'écrire les tailles en dur. La périphérie le met en cache 24 heures.
Erreurs
| HTTP | status_code | Quand |
|---|---|---|
| 401 | 7 | La clé est absente ou fausse sur un appel JSON. |
| 402 | 36 | La période de l'offre est terminée. |
| 404 | 34 | Aucune personne, saison ou épisode avec cet identifiant, ou une taille ou un fichier inconnu sur l'hôte d'images. |
| 405 | 37 | Vous avez utilisé une méthode d'écriture sur un chemin d'images. |
| 429 | 25 | Vous avez dépassé vos requêtes par minute. Attendez les secondes de Retry-After. |
Un identifiant de film ou de série qui n'existe pas n'est pas une erreur ici : le chemin d'images répond 200 avec des tableaux vides, alors que le chemin de détails répond 404. Voir erreurs pour la liste complète. Les requêtes de fichiers d'images sont de simples chargements HTTP : vérifiez donc le code de statut de la réponse plutôt que d'analyser un corps JSON.
Remarques pratiques
- Stockez
file_pathdans votre base de données et construisez l'URL au moment de l'affichage. Une nouvelle taille ou un nouvel hôte ne coûte alors qu'un changement de code. - Passez
include_image_languageavec votre langue d'affichage plusnull, par exempleru,null, pour que les affiches avec du texte étranger n'apparaissent pas. - Un tableau vide est un 200 normal. Repliez-vous sur le
poster_pathde la fiche de détails du film. - Utilisez
w1280pour les images de fond pleine largeur etw780pour les cartes.originalest le fichier stocké en pleine résolution et il est bien plus lourd quew780. - Les copies de la périphérie vivent 30 jours et sont partagées : un fichier demandé par un client est donc servi depuis le cache au suivant.