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.

Mis à jour le Voir en Markdown

Obtenir les images d'un film

GET /3/movie/{movie_id}/images 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.

bashjspyphpgo
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_idcheminobligatoire
    Identifiant numérique du film, par exemple 550.
  • api_keyrequêtel'un des deux
    Votre clé. Utilisez-la ou l'en-tête Authorization: Bearer.
  • include_image_languagerequête
    Codes ISO 639-1 séparés par des virgules. Ajoutez null pour inclure les images sans texte, par exemple en,null. Seules les langues listées reviennent. Sans lui, vous obtenez toutes les langues.
  • languagerequête
    Accepté mais ignoré sur ce point de terminaison. Utilisez include_image_language pour choisir les langues.

Champs de la réponse

ChampTypeRemarques
identierL'identifiant du film.
backdrops, logos, posterstableauUn tableau par type d'image, dans cet ordre de clés. Chacun peut être vide.
file_pathchaîneCommence 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, heightentierTaille en pixels du fichier d'origine. 0 quand la taille n'est pas connue.
aspect_rationombreLargeur 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_1chaîne ou nullLangue du texte sur l'image. null signifie qu'il n'y a pas de texte.
vote_average, vote_countnombre, entierToujours 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.

JSON
{
  "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://img.amodb.dev/t/p/, un segment de taille et le file_path. Le chemin commence déjà par une barre oblique : https://img.amodb.dev/t/p/w500 plus /jSziioSwPVrOy9Yow3XhWIBDjq1.jpg 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.

TypeChamp de configurationTailles
Afficheposter_sizesw92, w154, w185, w342, w500, w780, original
Image de fondbackdrop_sizesw300, w780, w1280, original
Logologo_sizesw92, w154, w185, w300, w500, original
Profilprofile_sizesw185, h632, original
Image d'épisodestill_sizesw92, 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é.

JavaScript
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://api.amodb.dev/3 et renvoient les tableaux nommés dans la deuxième colonne.

Méthode et cheminTableaux renvoyésParamètres
GET /tv/{series_id}/imagesbackdrops, logos, postersinclude_image_language
GET /tv/{series_id}/season/{season_number}/imagesposters, toujours videaucun
GET /tv/{series_id}/season/{season_number}/episode/{episode_number}/imagesstills, une entrée avec width et height à 0aucun
GET /person/{person_id}/imagesprofilesaucun

Pour les films et les séries, vous pouvez aussi ajouter append_to_response=images à 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://img.amodb.dev/t/p/, 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

HTTPstatus_codeQuand
4017La clé est absente ou fausse sur un appel JSON.
40236La période de l'offre est terminée.
40434Aucune personne, saison ou épisode avec cet identifiant, ou une taille ou un fichier inconnu sur l'hôte d'images.
40537Vous avez utilisé une méthode d'écriture sur un chemin d'images.
42925Vous 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_path dans 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_language avec votre langue d'affichage plus null, par exemple ru,null, pour que les affiches avec du texte étranger n'apparaissent pas.
  • Un tableau vide est un 200 normal. Repliez-vous sur le poster_path de la fiche de détails du film.
  • Utilisez w1280 pour les images de fond pleine largeur et w780 pour les cartes. original est le fichier stocké en pleine résolution et il est bien plus lourd que w780.
  • 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.