Construiește-ți propria bază de date de filme dintr-un API
amodb este un API independent care vorbește protocolul TMDB v3. Filmele, persoanele, companiile, colecțiile, cuvintele-cheie și genurile au toate ID-uri numerice și trimit unele la altele, așa că poți umple tabele SQL din el și le poți ține la zi. Încarcă tabelele cu paginarea discover, reîmprospătează-le după vechime și potrivește-ți propriii identificatori cu find. amodb nu are flux de modificări, așa că o reîmprospătare este pur și simplu o nouă preluare a rândurilor pe care le păstrezi.
Ce primești:
- Șase tipuri de entități legate, cu ID-uri numerice stabile.
- Paginare discover pentru prima încărcare: până la 20 de rezultate pe pagină, 500 de pagini pe interogare.
- Ferestre de date de lansare în discover, ca să tai prima încărcare în felii și să listezi lansările noi.
- Căutări după ID extern pentru
imdb_id,tvdb_id,wikidata_idșikinopoisk_id.
Ce date primești
Acesta este modelul de date. Fiecare rând este un tabel într-o copie relațională.
| Entitate | Se citește cu | Cheie | Se leagă de |
|---|---|---|---|
| Film | GET / | id, imdb_id | genres[].id, production_, belongs_ |
| Persoană | GET / | id, imdb_id | Filme prin / |
| Companie | GET / | id | Filme prin with_companies în discover. parent_company este mereu null, iar description este gol |
| Colecție | GET / | id | parts[], o listă de filme |
| Cuvânt-cheie | GET / | id | Filme prin / sau with_keywords |
| Gen | GET / | id | genres[].id la filme |
Legăturile mulți la mulți cer tabele de joncțiune: movie_genre, movie_company, movie_keyword și movie_credit. Folosește credit_id din credite drept cheie pentru ultimul. Listele de câmpuri sunt la filme, persoane și colecții.
Începe în 3 pași
- Compară cele trei ritmuri de cereri la prețuri. Un ritm mai mare scurtează prima încărcare, nimic altceva nu se schimbă.
- Creează o cheie în panoul contului tău și păstrează-o într-o variabilă de mediu.
- Apelează
/3/discover/moviepentru o fereastră de o zi de date de lansare, apoi preia fiecare ID din listă.
Exemplu de cerere
Scriptul PHP listează filmele lansate ieri și azi prin discover și face upsert pentru fiecare înregistrare într-un tabel SQLite. Doarme 3 secunde între citirile de detalii ca să rămână sub 20 de cereri pe minut, reîncearcă un 429 după Retry-After și șterge rândurile al căror ID întoarce acum 404.
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 întoarce rânduri de listă, nu înregistrări complete. Acestea sunt 2 dintre cele 20 de rezultate de pe pagina 1. Citește /3/movie/{id} pentru restul câmpurilor.
{
"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
}Umple și reîmprospătează tabelele
| Sarcină | Apel | Limite |
|---|---|---|
| Prima încărcare | GET /3/discover/movie cu sort_by și o fereastră de date | page de la 1 la 500, până la 20 de rezultate pe pagină, deci 10,000 pe interogare. Împarte pe primary_ și .lte ca să mergi mai departe. |
| Lansări noi | GET /3/discover/movie cu primary_ setat la ultima ta rulare, și GET /3/movie/upcoming | Ținute în cache 30 de minute. |
| Reîmprospătare rânduri | GET / pentru rândurile mai vechi de 6 ore | Nu există flux de modificări. Reîmprospătează titlurile pe care le deschid utilizatorii, iar restul mai rar. |
| Legarea propriilor ID-uri | GET / | Un ID pe apel, sursa se alege cu external_source. |
| Umplere la cerere | GET / când un utilizator deschide un titlu | Stochează răspunsul cu un marcaj de timp. |
Fereastra de paginare este principala alegere de proiectare. Când total_pages dintr-un răspuns este peste 500, fereastra conține mai mult de 10,000 de rânduri, așa că împarte-o la jumătate până încape. Pentru un an recent îți trebuie luni, iar o singură zi de lansări înseamnă câteva sute de titluri. Stochează felia pe care ai terminat-o, ca o rulare întreruptă să poată continua. Regulile de potrivire pentru legare sunt la căutare după ID extern.
Dimensionarea sincronizării
Fiecare rând pe care îl reîmprospătezi costă o citire de detalii. Un plan de 20 de cereri pe minut dă 1,200 de apeluri pe oră, cel de 40 dă 2,400, iar cel de 120 dă 7,200. O pagină discover listează 20 de ID-uri pentru un singur apel. Rulează reîmprospătarea în afara orelor de vârf dacă aplicația ta folosește aceeași cheie, pentru că limita este pe cheie, nu pe script. Vezi limite de cereri pentru antetele care arată ce a mai rămas din fereastră.
Cache pe două straturi
| Strat | Conține | TTL |
|---|---|---|
| Cache la margine | Detalii și credite | 6 ore |
| Cache la margine | Liste discover, în tendințe și populare | 30 de minute |
| Cache la margine | Căutare | 10 minute |
| Cache la margine | Configurație | 24 de ore |
| Cache la margine | Imagini | 30 de zile |
| Tabelele tale | Rândurile pe care le păstrezi | Până când regula ta de vechime spune să le preiei din nou, niciodată mai devreme de 6 ore |
Cache-ul la margine este partajat între chei, dar limita de cereri rulează înaintea lui, așa că o lovitură de cache consumă tot o cerere din minutul tău. Recitirea unui rând mai des de 6 ore întoarce aceeași copie și irosește cota. Lasă o regulă de vechime să decidă când preiei din nou.
Cum se compară
| Capacitate | Extragi singur paginile | Fișiere de set de date statice | amodb |
|---|---|---|---|
| Prospețime | Cât de proaspăt e ultimul tău crawl | Cât de proaspăt e ultimul fișier publicat | Detalii în cache 6 ore, preluare după vechime |
| Actualizări incrementale | Reiei crawl-ul și compari | Descarci un fișier nou | Discover cu o fereastră de date de lansare, fără flux de modificări |
| ID-uri între entități | URL-uri de pagini | Definite de editor | ID-uri numerice, imdb_id și /3/find |
| Ce rulezi | Crawlere și stocare | Stocare și un job de import | Stocare și un script de sincronizare |
| Limită de cereri | Stabilită de site-urile pe care le parcurgi | Nu se aplică | 20, 40 sau 120 de cereri pe minut, numărate pe cheie |
| Limbi pe înregistrare | Ce oferă fiecare pagină | Depinde de fișiere | 14, alese cu language |
Prețuri
Prima încărcare este partea scumpă și este limitată de numărul tău de cereri pe minut. Alege 20, 40 sau 120 pe minut la prețuri. Plătești pentru 1, 3, 6, 12 sau 24 de luni, cu reduceri de 0, 10, 20, 30 sau 40 la sută. Nimic nu se reînnoiește automat, nu există nivel gratuit, iar fiecare plan citește aceleași date.