Costruire il tuo database di film da un'API
amodb è un'API indipendente che parla il protocollo TMDB v3. Film, persone, società, collezioni, parole chiave e generi hanno tutti ID numerici e si richiamano a vicenda, quindi puoi riempire delle tabelle SQL con essa e tenerle aggiornate. Inizializza le tabelle con la paginazione di discover, aggiornale in base all'età e collega i tuoi identificativi con find. amodb non ha un feed delle modifiche, quindi un aggiornamento è semplicemente una nuova lettura delle righe che tieni.
Cosa ottieni:
- Sei tipi di entità collegate con ID numerici stabili.
- La paginazione di discover per il primo caricamento: fino a 20 risultati per pagina, 500 pagine per query.
- Finestre di date di uscita su discover, per suddividere il primo caricamento e per elencare le nuove uscite.
- Ricerche per ID esterno per
imdb_id,tvdb_id,wikidata_idekinopoisk_id.
Quali dati ottieni
Questo è il modello dei dati. Ogni riga è una tabella in una copia relazionale.
| Entità | Si legge con | Chiave | Collegamenti a |
|---|---|---|---|
| Film | GET / | id, imdb_id | genres[].id, production_, belongs_ |
| Persona | GET / | id, imdb_id | I film tramite / |
| Società | GET / | id | I film tramite with_companies in discover. parent_company è sempre null e description è vuoto |
| Collezione | GET / | id | parts[], una lista di film |
| Parola chiave | GET / | id | I film tramite / o with_keywords |
| Genere | GET / | id | genres[].id sui film |
I collegamenti molti a molti richiedono tabelle di join: movie_genre, movie_company, movie_keyword e movie_credit. Usa credit_id da crediti come chiave dell'ultima. Gli elenchi dei campi sono nelle pagine film, persone e collezioni.
Parti in 3 passaggi
- Confronta le tre frequenze di richieste nella pagina dei prezzi. Una frequenza maggiore accorcia il primo caricamento, nient'altro cambia.
- Crea una chiave nella dashboard del tuo account e tienila in una variabile d'ambiente.
- Chiama
/3/discover/movieper una finestra di un giorno di date di uscita, poi recupera ogni ID della lista.
Esempio di richiesta
Lo script PHP elenca i film usciti ieri e oggi tramite discover e inserisce o aggiorna ogni scheda in una tabella SQLite. Attende 3 secondi tra le letture dei dettagli per restare sotto le 20 richieste al minuto, ritenta un 429 dopo Retry-After, ed elimina le righe il cui ID ora restituisce 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 restituisce righe di lista, non schede complete. Questi sono 2 dei 20 risultati della pagina 1. Leggi /3/movie/{id} per gli altri campi.
{
"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
}Riempire e aggiornare le tabelle
| Compito | Chiamata | Limiti |
|---|---|---|
| Primo caricamento | GET /3/discover/movie con sort_by e una finestra di date | page da 1 a 500, fino a 20 risultati per pagina, cioè 10.000 per query. Dividi con primary_ e .lte per andare oltre. |
| Nuove uscite | GET /3/discover/movie con primary_ impostato sull'ultima esecuzione, e GET /3/movie/upcoming | In cache per 30 minuti. |
| Aggiornare le righe | GET / per le righe più vecchie di 6 ore | Non c'è un feed delle modifiche. Aggiorna i titoli che gli utenti aprono, e gli altri meno spesso. |
| Collegare i tuoi ID | GET / | Un ID per chiamata, fonte scelta con external_source. |
| Riempimento su richiesta | GET / quando un utente apre un titolo | Salva la risposta con un timestamp. |
La finestra di paginazione è la scelta progettuale principale. Quando total_pages in una risposta supera 500, la finestra contiene più di 10.000 righe, quindi dividila a metà finché non rientra. Un anno recente richiede dei mesi, e un giorno di uscite sono poche centinaia di titoli. Salva la fetta completata così un'esecuzione interrotta può riprendere. Le regole di corrispondenza per il join sono nella pagina ricerca per ID esterno.
Dimensionare la sincronizzazione
Ogni riga che aggiorni costa una lettura di dettagli. Un piano da 20 richieste al minuto dà 1.200 chiamate all'ora, 40 ne dà 2.400 e 120 ne dà 7.200. Una pagina di discover elenca 20 ID con una chiamata. Esegui l'aggiornamento nelle ore di minore traffico se la tua app condivide la chiave, perché il limite è per chiave e non per script. Vedi limiti di richieste per gli header che indicano quanto resta nella finestra.
Cache su due livelli
| Livello | Contiene | TTL |
|---|---|---|
| Cache edge | Dettagli e crediti | 6 ore |
| Cache edge | Liste discover, tendenze e popolari | 30 minuti |
| Cache edge | Ricerca | 10 minuti |
| Cache edge | Configurazione | 24 ore |
| Cache edge | Immagini | 30 giorni |
| Le tue tabelle | Le righe che tieni | Finché la tua regola sull'età non impone di rileggere, mai prima di 6 ore |
La cache edge è condivisa tra le chiavi, ma il limite di richieste viene prima, quindi una risposta dalla cache usa comunque una richiesta del tuo minuto. Rileggere una riga più spesso che ogni 6 ore restituisce la stessa copia e spreca quota. Lascia che sia una regola sull'età a decidere quando rileggere.
Confronto
| Funzionalità | Fare scraping delle pagine da solo | File di dataset statici | amodb |
|---|---|---|---|
| Freschezza | Fresco quanto la tua ultima scansione | Fresco quanto l'ultimo file pubblicato | Dettagli in cache 6 ore, rilettura in base all'età |
| Aggiornamenti incrementali | Rifare la scansione e confrontare | Scaricare un nuovo file | Discover con finestra di date di uscita, nessun feed delle modifiche |
| ID tra entità | URL delle pagine | Definiti dall'editore | ID numerici, imdb_id e /3/find |
| Cosa gestisci | Crawler e storage | Storage e un job di importazione | Storage e uno script di sincronizzazione |
| Limite di richieste | Stabilito dai siti che scansioni | Non applicabile | 20, 40 o 120 richieste al minuto, contate per chiave |
| Lingue per scheda | Quelle offerte da ogni pagina | Dipende dai file | 14, scelte con language |
Prezzi
Il primo caricamento è la parte costosa, ed è limitato dalle tue richieste al minuto. Scegli 20, 40 o 120 al minuto nella pagina dei prezzi. Paga 1, 3, 6, 12 o 24 mesi con uno sconto di 0, 10, 20, 30 o 40 per cento. Nulla si rinnova automaticamente, non esiste un piano gratuito, e ogni piano legge gli stessi dati.