API de imagens e host de imagens

Os endpoints de imagens listam pôsteres, backdrops, logos, perfis e stills de um título como caminhos de arquivo. Você carrega cada arquivo de img.amodb.dev em um tamanho fixo, sem chave e com cache de borda de 30 dias. Caminhos e JSON seguem o TMDB v3, então o código de imagens existente funciona após trocar a URL base.

Atualizado em Ver em Markdown

Obter as imagens de um filme

GET /3/movie/{movie_id}/images lista todas as imagens conhecidas de um filme em três arrays: backdrops, logos e posters. Cada entrada tem um file_path, o tamanho em pixels e uma tag de idioma. O arquivo em si não vem no JSON. Você o carrega do host de imagens em uma segunda etapa.

O exemplo pede imagens em inglês e sem texto, escolhe o primeiro pôster em inglês e depois busca esse arquivo com 500 pixels de largura.

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)
}

Parâmetros

  • movie_idcaminhoobrigatório
    Id numérico do filme, por exemplo 550.
  • api_keyconsultaum dos dois
    Sua chave. Use este parâmetro ou o header Authorization: Bearer.
  • include_image_languageconsulta
    Códigos ISO 639-1 separados por vírgulas. Acrescente null para incluir imagens sem texto, por exemplo en,null. Só os idiomas listados voltam. Sem ele, você recebe todos os idiomas.
  • languageconsulta
    Aceito, mas ignorado neste endpoint. Use include_image_language para escolher idiomas.

Campos da resposta

CampoTipoObservações
idinteiroO id do filme.
backdrops, logos, postersarrayUm array por tipo de imagem, nessa ordem de chaves. Cada um pode vir vazio.
file_pathstringComeça com uma barra. Acrescente-o a um tamanho no host de imagens. Pôsteres e backdrops são .jpg, logos são .png ou .svg.
width, heightinteiroTamanho em pixels do arquivo original. 0 quando o tamanho não é conhecido.
aspect_rationúmeroLargura dividida pela altura. Pôsteres ficam perto de 0.667, backdrops perto de 1.778. 0 quando o tamanho não é conhecido.
iso_639_1string ou nullIdioma do texto na imagem. null significa sem texto.
vote_average, vote_countnúmero, inteiroSempre 0 em imagens de filmes e séries, então não servem para ordenar imagens. Mantenha a ordem que a API retorna.

Exemplo de resposta

Duas entradas por array aparecem aqui.

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
    }
  ]
}

O segundo pôster mostra uma entrada cujo tamanho não é conhecido. O arquivo carrega normalmente.

Montar a URL de uma imagem

Junte três partes: https://img.amodb.dev/t/p/, um segmento de tamanho e o file_path. O caminho já começa com uma barra, então https://img.amodb.dev/t/p/w500 mais /jSziioSwPVrOy9Yow3XhWIBDjq1.jpg é uma URL completa.

Um tamanho é w mais uma largura em pixels, h mais uma altura em pixels, ou original para o arquivo armazenado. O host serve dez tamanhos: w92, w154, w185, w300, w342, w500, w780, w1280, h632 e original. Qualquer outro tamanho, como w400, responde 404. /3/configuration lista os tamanhos que servem para cada tipo de imagem.

TipoCampo na configuraçãoTamanhos
Pôsterposter_sizesw92, w154, w185, w342, w500, w780, original
Backdropbackdrop_sizesw300, w780, w1280, original
Logologo_sizesw92, w154, w185, w300, w500, original
Perfilprofile_sizesw185, h632, original
Still (foto de cena)still_sizesw92, w185, w300, original

O host não verifica o tipo, então um pôster também carrega em w1280. Fique nos tamanhos listados para o tipo, para que seu código corresponda à configuração e a qualquer cliente do TMDB.

O host de imagens não exige chave, e a borda guarda cada arquivo por 30 dias. Como nenhuma chave é enviada, o carregamento de imagens não consome o seu limite de requisições por minuto. As respostas trazem Access-Control-Allow-Origin: *, então um navegador pode carregá-las de qualquer página. Um tamanho desconhecido ou um arquivo desconhecido responde 404 com um corpo JSON.

Imagens responsivas com srcset

Escolha o menor tamanho que ainda cubra a largura exibida vezes a densidade da tela. Um pôster exibido com 171 pixels CSS de largura precisa de w342 em uma tela 2x. Esta função auxiliar monta um srcset a partir de um caminho guardado.

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">`);

Outros endpoints de imagens

Todos os caminhos abaixo usam a base https://api.amodb.dev/3 e retornam os arrays citados na segunda coluna.

Método e caminhoArrays retornadosParâmetros
GET /tv/{series_id}/imagesbackdrops, logos, postersinclude_image_language
GET /tv/{series_id}/season/{season_number}/imagesposters, sempre vazionenhum
GET /tv/{series_id}/season/{season_number}/episode/{episode_number}/imagesstills, uma entrada com width e height definidos como 0nenhum
GET /person/{person_id}/imagesprofilesnenhum

Para filmes e séries, você também pode acrescentar append_to_response=images a uma chamada de detalhes. include_image_language funciona ali também. O bloco anexado não tem um id próprio.

Os perfis de pessoas são o único lugar em que vote_average traz uma nota real. O id da resposta de uma temporada ou de um episódio é o id da temporada ou do episódio. Veja pessoas e séries de TV para os registros principais.

O amodb não oferece caminhos de imagens para coleções, produtoras e emissoras. Os registros delas já têm um poster_path, backdrop_path ou logo_path, descritos em coleções e em produtoras e emissoras.

O endpoint de configuração

GET /3/configuration retorna um bloco images com secure_base_url definido como https://img.amodb.dev/t/p/, base_url (a forma http simples) e as listas de tamanhos backdrop_sizes, logo_sizes, poster_sizes, profile_sizes e still_sizes. Leia-o uma vez na inicialização, use a forma segura e monte as URLs a partir dele em vez de fixar os tamanhos no código. A borda o guarda em cache por 24 horas.

Erros

HTTPstatus_codeQuando
4017A chave está ausente ou incorreta em uma chamada JSON.
40236O período do plano terminou.
40434Nenhuma pessoa, temporada ou episódio com esse id, ou um tamanho ou arquivo desconhecido no host de imagens.
40537Você usou um método de escrita em um caminho de imagens.
42925Você passou do seu limite de requisições por minuto. Espere os segundos indicados em Retry-After.

Um id de filme ou série que não existe não é um erro aqui: o caminho de imagens responde 200 com arrays vazios, enquanto o caminho de detalhes responde 404. Veja erros para a lista completa. As requisições de arquivos de imagem são cargas HTTP simples, então confira o código de status da resposta em vez de interpretar um corpo JSON.

Notas práticas

  • Guarde o file_path no seu banco de dados e monte a URL na hora de renderizar. Assim, um novo tamanho ou host custa uma só mudança de código.
  • Passe include_image_language com o seu idioma de exibição mais null, por exemplo ru,null, para que pôsteres com texto em outros idiomas não apareçam.
  • Um array vazio é um 200 normal. Use como alternativa o poster_path do registro de detalhes do filme.
  • Use w1280 para backdrops de destaque em largura total e w780 para cards. original é o arquivo armazenado em resolução máxima e é bem maior que w780.
  • As cópias na borda duram 30 dias e são compartilhadas, então um arquivo que um cliente pediu é servido do cache para o próximo.