Zbuduj własną bazę filmów z API
amodb to niezależne API, które mówi protokołem TMDB v3. Filmy, osoby, firmy, kolekcje, słowa kluczowe i gatunki mają numeryczne ID i wskazują na siebie nawzajem, więc możesz zasilać z nich tabele SQL i utrzymywać je w aktualnym stanie. Zasil tabele paginacją discover, odświeżaj je według wieku i dopasowuj własne identyfikatory przez find. amodb nie ma kanału zmian, więc odświeżenie to zwykłe ponowne pobranie wierszy, które zachowujesz.
Co dostajesz:
- Sześć powiązanych typów encji ze stałymi numerycznymi ID.
- Paginację discover do pierwszego ładowania: do 20 wyników na stronę, 500 stron na zapytanie.
- Okna dat premier w discover, którymi podzielisz pierwsze ładowanie i wypiszesz nowe premiery.
- Wyszukiwanie po zewnętrznych ID
imdb_id,tvdb_id,wikidata_idikinopoisk_id.
Jakie dane dostajesz
To model danych. Każdy wiersz to jedna tabela w kopii relacyjnej.
| Encja | Odczyt | Klucz | Wskazuje na |
|---|---|---|---|
| Film | GET / | id, imdb_id | genres[].id, production_, belongs_ |
| Osoba | GET / | id, imdb_id | Filmy przez / |
| Firma | GET / | id | Filmy przez with_companies w discover. parent_company jest zawsze null, a description jest puste |
| Kolekcja | GET / | id | parts[], lista filmów |
| Słowo kluczowe | GET / | id | Filmy przez / lub with_keywords |
| Gatunek | GET / | id | genres[].id w filmach |
Powiązania wiele do wielu wymagają tabel łączących: movie_genre, movie_company, movie_keyword i movie_credit. Jako klucza ostatniej użyj credit_id ze strony obsada i ekipa. Listy pól są na stronach filmy, osoby i kolekcje.
Start w 3 krokach
- Porównaj trzy tempa żądań na stronie cennik. Wyższe tempo skraca pierwsze ładowanie, nic więcej się nie zmienia.
- Utwórz klucz w panelu konta i trzymaj go w zmiennej środowiskowej.
- Wywołaj
/3/discover/moviedla jednodniowego okna dat premier, a potem pobierz każde ID z listy.
Przykładowe żądanie
Skrypt w PHP wypisuje filmy wydane wczoraj i dziś przez discover i zapisuje (upsert) każdy rekord do tabeli SQLite. Między odczytami szczegółów czeka 3 sekundy, by zmieścić się w 20 żądaniach na minutę, ponawia 429 po Retry-After i usuwa wiersze, których ID zwraca teraz 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 zwraca wiersze listy, nie pełne rekordy. To 2 z 20 wyników na stronie 1. Pozostałe pola pobierz z /3/movie/{id}.
{
"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
}Wypełnianie i odświeżanie tabel
| Zadanie | Wywołanie | Limity |
|---|---|---|
| Pierwsze ładowanie | GET /3/discover/movie z sort_by i oknem dat | page od 1 do 500, do 20 wyników na stronę, czyli 10 000 na zapytanie. Aby pójść dalej, dziel według primary_ i .lte. |
| Nowe premiery | GET /3/discover/movie z primary_ ustawionym na ostatni przebieg oraz GET /3/movie/upcoming | W cache przez 30 minut. |
| Odświeżanie wierszy | GET / dla wierszy starszych niż 6 godzin | Nie ma kanału zmian. Odświeżaj tytuły, które otwierają użytkownicy, a resztę rzadziej. |
| Łączenie własnych ID | GET / | Jedno ID na wywołanie, źródło wybierane przez external_source. |
| Leniwe wypełnianie | GET /, gdy użytkownik otwiera tytuł | Zapisz odpowiedź ze znacznikiem czasu. |
Okno paginacji to główna decyzja projektowa. Gdy total_pages w odpowiedzi przekracza 500, okno zawiera ponad 10 000 wierszy, więc tnij je na pół, aż się zmieści. Ostatni rok wymaga miesięcy, a jeden dzień premier to kilkaset tytułów. Zapisuj ukończony wycinek, aby przerwany przebieg mógł wznowić pracę. Zasady dopasowania do łączenia opisuje strona wyszukiwanie po zewnętrznym ID.
Wymiarowanie synchronizacji
Każdy odświeżany wiersz kosztuje jeden odczyt szczegółów. Plan z 20 żądaniami na minutę daje 1 200 wywołań na godzinę, 40 daje 2 400, a 120 daje 7 200. Strona discover wypisuje 20 ID za jedno wywołanie. Uruchamiaj odświeżanie poza godzinami szczytu, jeśli aplikacja dzieli klucz, bo limit dotyczy klucza, a nie skryptu. Nagłówki podające, ile zostało w oknie, opisują limity żądań.
Cache w dwóch warstwach
| Warstwa | Zawiera | TTL |
|---|---|---|
| Cache na edge | Szczegóły i obsadę | 6 godzin |
| Cache na edge | Listy discover, trending i popular | 30 minut |
| Cache na edge | Wyszukiwanie | 10 minut |
| Cache na edge | Konfigurację | 24 godziny |
| Cache na edge | Obrazy | 30 dni |
| Twoje tabele | Wiersze, które zachowujesz | Dopóki reguła wieku nie każe pobrać ponownie, nigdy wcześniej niż po 6 godzinach |
Cache na edge jest współdzielony między kluczami, ale limit żądań działa przed nim, więc trafienie w cache nadal zużywa jedno żądanie z twojej minuty. Ponowny odczyt wiersza częściej niż co 6 godzin zwraca tę samą kopię i marnuje limit. Decyzję o ponownym pobraniu zostaw regule wieku.
Jak wypada w porównaniu
| Możliwość | Własny scraping stron | Statyczne pliki zbioru danych | amodb |
|---|---|---|---|
| Świeżość | Tak świeże jak ostatni crawl | Tak świeże jak ostatni opublikowany plik | Szczegóły w cache 6 godzin, odświeżanie według wieku |
| Aktualizacje przyrostowe | Ponowny crawl i porównanie | Pobranie nowego pliku | Discover z oknem dat premier, bez kanału zmian |
| ID między encjami | Adresy URL stron | Ustala wydawca | Numeryczne ID, imdb_id i /3/find |
| Co uruchamiasz | Crawlery i magazyn danych | Magazyn danych i zadanie importu | Magazyn danych i skrypt synchronizacji |
| Limit żądań | Ustalają strony, które przeszukujesz | Nie dotyczy | 20, 40 lub 120 żądań na minutę, liczone na klucz |
| Języki na rekord | Co oferuje dana strona | Zależy od plików | 14, wybierane przez language |
Cennik
Kosztowne jest pierwsze ładowanie i ogranicza je liczba żądań na minutę. Wybierz 20, 40 lub 120 na minutę na stronie cennik. Płacisz za 1, 3, 6, 12 lub 24 miesiące ze zniżką 0, 10, 20, 30 lub 40 procent. Nic nie odnawia się automatycznie, nie ma darmowego planu, a każdy plan czyta te same dane.