Postavte si vlastní databázi filmů z API

amodb je nezávislé API, které mluví protokolem TMDB v3. Filmy, osoby, společnosti, kolekce, klíčová slova i žánry mají číselná ID a odkazují na sebe, takže z něj můžete plnit SQL tabulky a udržovat je aktuální. Tabulky naplňte stránkováním discover, obnovujte je podle stáří a vlastní identifikátory přiřazujte přes find. amodb nemá feed změn, takže obnova je prosté opětovné stažení řádků, které ponecháváte.

Co dostanete:

  • Šest propojených typů entit se stálými číselnými ID.
  • Stránkování discover pro první načtení: až 20 výsledků na stránku, 500 stránek na dotaz.
  • Okna dat premiér v discover, kterými rozdělíte první načtení a vypíšete nové premiéry.
  • Vyhledání podle externích ID imdb_id, tvdb_id, wikidata_id a kinopoisk_id.

Jaká data dostanete

Toto je datový model. Každý řádek je jedna tabulka v relační kopii.

EntitaČteníKlíčOdkazuje na
FilmGET /3/movie/{movie_id}id, imdb_idgenres[].id, production_companies[].id, belongs_to_collection.id
OsobaGET /3/person/{person_id}id, imdb_idFilmy přes /3/movie/{movie_id}/credits
SpolečnostGET /3/company/{company_id}idFilmy přes with_companies v discover. parent_company je vždy null a description je prázdné
KolekceGET /3/collection/{collection_id}idparts[], seznam filmů
Klíčové slovoGET /3/keyword/{keyword_id}idFilmy přes /3/keyword/{keyword_id}/movies nebo with_keywords
ŽánrGET /3/genre/movie/listidgenres[].id u filmů

Vazby mnoho k mnoha potřebují spojovací tabulky: movie_genre, movie_company, movie_keyword a movie_credit. Jako klíč poslední použijte credit_id ze stránky obsazení a štáb. Seznamy polí jsou na stránkách filmy, osoby a kolekce.

Začněte ve 3 krocích

  1. Porovnejte tři rychlosti požadavků na stránce ceník. Vyšší rychlost zkrátí první načtení, nic jiného se nemění.
  2. Vytvořte klíč v panelu účtu a držte ho v proměnné prostředí.
  3. Zavolejte /3/discover/movie pro jednodenní okno dat premiér a pak načtěte každé ID ze seznamu.

Příklad požadavku

Skript v PHP vypíše filmy vydané včera a dnes přes discover a každý záznam uloží (upsert) do tabulky SQLite. Mezi čteními detailů čeká 3 sekundy, aby zůstal pod 20 požadavky za minutu, po Retry-After zopakuje 429 a maže řádky, jejichž ID nyní vrací 404.

bashphp
curl -s "https://api.amodb.dev/3/discover/movie?primary_release_date.gte=2026-10-17&primary_release_date.lte=2026-10-18&sort_by=primary_release_date.asc&page=1" \
  -H "Authorization: Bearer $AMODB_KEY"
<?php
const BASE = 'https://api.amodb.dev/3';

function amodb(string $path, array $query = []): ?array
{
    $url = BASE . $path . '?' . http_build_query($query);
    for ($attempt = 0; $attempt < 3; $attempt++) {
        $retryAfter = 1;
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
            CURLOPT_HEADERFUNCTION => function ($handle, $line) use (&$retryAfter) {
                if (stripos($line, 'Retry-After:') === 0) {
                    $retryAfter = max(1, (int) trim(substr($line, 12)));
                }
                return strlen($line);
            },
        ]);
        $body = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($status === 429) {
            sleep($retryAfter);
            continue;
        }
        if ($status === 404) {
            return null;
        }
        if ($status !== 200) {
            throw new RuntimeException("amodb returned $status for $path");
        }
        return json_decode($body, true);
    }
    throw new RuntimeException("Still rate limited on $path");
}

$db = new PDO('sqlite:movies.sqlite');
$db->exec('CREATE TABLE IF NOT EXISTS movie (id INTEGER PRIMARY KEY, imdb_id TEXT, title TEXT, release_date TEXT, raw TEXT)');
$save = $db->prepare('INSERT OR REPLACE INTO movie (id, imdb_id, title, release_date, raw) VALUES (?, ?, ?, ?, ?)');
$drop = $db->prepare('DELETE FROM movie WHERE id = ?');

$from = date('Y-m-d', strtotime('-1 day'));
$to = date('Y-m-d');
$page = 1;
do {
    $list = amodb('/discover/movie', [
        'primary_release_date.gte' => $from,
        'primary_release_date.lte' => $to,
        'sort_by' => 'primary_release_date.asc',
        'page' => $page,
    ]);
    foreach ($list['results'] as $row) {
        $movie = amodb('/movie/' . $row['id'], ['language' => 'en-US']);
        if ($movie === null) {
            $drop->execute([$row['id']]);
            continue;
        }
        $save->execute([$movie['id'], $movie['imdb_id'] ?? null, $movie['title'], $movie['release_date'] ?? null, json_encode($movie)]);
        sleep(3);
    }
    $page++;
} while ($page <= min($list['total_pages'], 500));

Discover vrací řádky seznamu, ne plné záznamy. Toto jsou 2 z 20 výsledků na stránce 1. Ostatní pole načtěte z /3/movie/{id}.

JSON
{
  "page": 1,
  "results": [
    {
      "adult": false,
      "backdrop_path": "/siepLRGVjM8yLKppApkD3uSh8Y0.jpg",
      "genre_ids": [
        35
      ],
      "id": 1800227,
      "original_language": "en",
      "original_title": "Sly's Pack",
      "overview": "Nesmith, Gary, & Tony are smoking weed on the roof of a parking garage, but a...",
      "popularity": 0.0071,
      "poster_path": "/3HpIWZUI7jmpKZkfZNdchS2WsvN.jpg",
      "release_date": "2026-10-17",
      "title": "Sly's Pack",
      "video": false,
      "vote_average": 0,
      "vote_count": 0
    },
    {
      "adult": false,
      "backdrop_path": null,
      "genre_ids": [
        99
      ],
      "id": 1800168,
      "original_language": "en",
      "original_title": "The Good Life",
      "overview": "The Good LIfe is based on the best-selling book by Drs. Robert Waldinger and...",
      "popularity": 0.0143,
      "poster_path": null,
      "release_date": "2026-10-17",
      "title": "The Good Life",
      "video": false,
      "vote_average": 0,
      "vote_count": 0
    }
  ],
  "total_pages": 15,
  "total_results": 299
}

Plnění a obnova tabulek

ÚlohaVoláníLimity
První načteníGET /3/discover/movie se sort_by a oknem datpage 1 až 500, až 20 výsledků na stránku, tedy 10 000 na dotaz. K překonání dělte podle primary_release_date.gte a .lte.
Nové premiéryGET /3/discover/movie s primary_release_date.gte nastaveným na poslední běh a GET /3/movie/upcomingCache 30 minut.
Obnova řádkůGET /3/movie/{movie_id} pro řádky starší než 6 hodinNeexistuje feed změn. Obnovujte tituly, které uživatelé otevírají, a ostatní méně často.
Spojení vlastních IDGET /3/find/{external_id}Jedno ID na volání, zdroj se volí přes external_source.
Líné plněníGET /3/movie/{movie_id}, když uživatel otevře titulOdpověď uložte s časovým razítkem.

Okno stránkování je hlavní návrhové rozhodnutí. Když je total_pages v odpovědi nad 500, okno obsahuje víc než 10 000 řádků, rozpůlte ho tedy, dokud se nevejde. Poslední rok potřebuje měsíce a jeden den premiér jsou několik set titulů. Ukládejte dokončený úsek, aby se spadlý běh mohl obnovit. Pravidla párování pro spojení jsou na stránce hledání podle externího ID.

Dimenzování synchronizace

Každý obnovovaný řádek stojí jedno čtení detailu. Tarif s 20 požadavky za minutu dává 1 200 volání za hodinu, 40 dává 2 400 a 120 dává 7 200. Stránka discover vypíše 20 ID za jedno volání. Obnovu pouštějte mimo špičku, pokud aplikace sdílí klíč, protože limit platí na klíč, ne na skript. Hlavičky, které hlásí, co v okně zbývá, jsou na stránce limity požadavků.

Cache ve dvou vrstvách

VrstvaObsahujeTTL
Cache na edgeDetaily a obsazení6 hodin
Cache na edgeSeznamy discover, trending a populární30 minut
Cache na edgeVyhledávání10 minut
Cache na edgeKonfigurace24 hodin
Cache na edgeObrázky30 dní
Vaše tabulkyŘádky, které ponecháváteDokud pravidlo stáří neřekne znovu načíst, nikdy dřív než za 6 hodin

Cache na edge je sdílená mezi klíči, ale limit požadavků se vyhodnocuje před ní, takže zásah cache stále spotřebuje jeden požadavek z vaší minuty. Čtení řádku častěji než každých 6 hodin vrátí stejnou kopii a plýtvá kvótou. O novém načtení nechte rozhodnout pravidlo stáří.

Jak si stojí v porovnání

SchopnostScrapování stránek vlastními silamiStatické soubory datasetuamodb
AktuálnostTak čerstvé jako poslední crawlTak čerstvé jako poslední zveřejněný souborDetaily v cache 6 hodin, obnova podle stáří
Přírůstkové aktualizaceZnovu procházet a porovnávatStáhnout nový souborDiscover s oknem dat premiér, bez feedu změn
ID napříč entitamiURL stránekUrčuje vydavatelČíselná ID, imdb_id a /3/find
Co provozujeteCrawlery a úložištěÚložiště a importní úlohaÚložiště a synchronizační skript
Limit požadavkůUrčují weby, které procházíteNetýká se20, 40 nebo 120 požadavků za minutu, počítáno na klíč
Jazyky na záznamCo nabízí která stránkaZáleží na souborech14, vybírané přes language

Ceník

Nákladné je první načtení a omezuje ho váš počet požadavků za minutu. Vyberte 20, 40 nebo 120 za minutu na stránce ceník. Platíte za 1, 3, 6, 12 nebo 24 měsíců se slevou 0, 10, 20, 30 nebo 40 procent. Nic se neobnovuje automaticky, bezplatný tarif neexistuje a každý tarif čte stejná data.