# Návod na filmové API v Pythonu

> Vytvořte malý nástroj příkazového řádku v Pythonu, který převezme název filmu, vyhledá ho a pak jedním dalším voláním načte detaily, herce a hodnocení. Potřebujete Python 3.10 nebo novější, knihovnu requests a klíč amodb. Zabere asi 15 minut a skončí kompletním skriptem.

Vytvoříte `movie.py`, nástroj příkazového řádku. Předáte mu název, on vyhledá, vybere první shodu a vypíše rok, délku, žánry, hlavní herce, hodnocení IMDb a URL plakátu. Potřebuje Python 3.10 nebo novější, knihovnu `requests` a klíč amodb. Počítejte s 15 minutami.

## Krok 1: Získejte API klíč

amodb je předplacené bez bezplatného tarifu, proto nejdřív zvolte tarif na stránce [ceník](https://amodb.dev/cs/pricing/). Tarify s 20, 40 a 120 požadavky za minutu vracejí všechna stejná data. Poté vytvořte klíč v panelu účtu. Klíč má 32 malých hexadecimálních znaků.

Exportujte ho jako proměnnou prostředí, aby zůstal mimo váš zdrojový kód:

```bash
export AMODB_KEY="YOUR_API_KEY"
```

V PowerShellu ve Windows je stejný řádek `$env:AMODB_KEY = "YOUR_API_KEY"`.

## Krok 2: Připravte projekt

Vytvořte virtuální prostředí a nainstalujte `requests`:

```bash
python -m venv .venv
source .venv/bin/activate
pip install requests
```

Ve Windows spusťte místo druhého řádku `.venv\Scripts\activate`.

## Krok 3: Odešlete první požadavek

Endpoint hledání je `GET /3/search/movie` s parametrem `query`. Tento úryvek vypíše stav HTTP a ID nejlepší shody. Vždy předávejte timeout, protože `requests` ve výchozím stavu čeká donekonečna.

```python
import os
import requests

res = requests.get(
    "https://api.amodb.dev/3/search/movie",
    params={"query": "fight club", "language": "en-US"},
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
print(res.status_code, res.json()["results"][0]["id"])
```

Odpověď má tvar TMDB: pole `results` seřazené podle relevance plus `page` a `total_results`. Všechny filtry najdete na stránce [vyhledávání](https://amodb.dev/cs/search/).

## Krok 4: Načtěte detaily, herce a hodnocení

Výsledky hledání jsou stručné. Použijte ID s `GET /3/movie/{movie_id}` a přidejte `append_to_response=credits,ratings`, abyste získali herce a skóre z IMDb, Kinopoisk, Metacritic, Rotten Tomatoes a TMDB ve stejném požadavku. Každé skóre je objekt s `value` a `votes`, nebo `null`, když zdroj není znám, proto před výpisem testujte. Podrobnosti jsou na stránkách [filmy](https://amodb.dev/cs/movies/) a [hodnocení](https://amodb.dev/cs/ratings/). V závěrečném skriptu je `session` objekt `requests.Session`, který už nese hlavičku Authorization.

```python
params = {"language": "en-US", "append_to_response": "credits,ratings"}
movie = session.get(f"{BASE}/movie/{movie_id}", params=params, timeout=10).json()
imdb = (movie.get("ratings") or {}).get("imdb")
```

## Krok 5: Ošetřete chyby a limity požadavků

Před parsováním těla zkontrolujte stav. Chyby používají tělo TMDB s `status_code` a `status_message`.

| Stav         | Význam                              | Co dělá skript                                                        |
| ------------ | ----------------------------------- | --------------------------------------------------------------------- |
| 401          | Neplatný nebo pozastavený klíč      | Zastaví se a doporučí zkontrolovat `AMODB_KEY`. Opakování nepomůže.   |
| 404          | Takové ID neexistuje                | Vypíše `status_message`.                                              |
| 429          | Překročili jste požadavky za minutu | Počká `Retry-After` sekund (maximum 30), pak opakuje nejvýše dvakrát. |
| 504          | Timeout backendu                    | Vypíše se jako chyba. Spusťte příkaz znovu.                           |
| Selhání sítě | Žádné připojení nebo timeout        | Zachyceno jako `requests.RequestException` a nahlášeno.               |

Smyčka opakování je krátká:

```python
for attempt in range(3):
    res = session.get(url, params=params, timeout=10)
    if res.status_code != 429 or attempt == 2:
        break
    time.sleep(min(int(res.headers.get("Retry-After", "1")), 30))
```

Další kódy uvádí stránka [chyby](https://amodb.dev/cs/errors/) a pravidla okna stránka [limity požadavků](https://amodb.dev/cs/rate-limits/).

## Krok 6: Ukládejte odpovědi na disk

Edge drží detaily 6 hodin a hledání 10 minut. Vaše vlastní cache ušetří cestu tam a zpět a neutratí žádný požadavek z vašeho limitu. Závěrečný skript ukládá každou odpověď do souboru pojmenovaného podle hashe cesty a parametrů a soubor používá, dokud je mladší než TTL.

## Krok 7: Kompletní program

Uložte to jako `movie.py` a spusťte `python movie.py "fight club"`.

```python
import hashlib
import json
import os
import sys
import time
from pathlib import Path

import requests

BASE = "https://api.amodb.dev/3"
POSTER = "https://img.amodb.dev/t/p/w342"
CACHE_DIR = Path.home() / ".cache" / "amodb-movie-cli"
SEARCH_TTL = 10 * 60
DETAILS_TTL = 6 * 60 * 60


class ApiError(Exception):
    pass


def error_message(res):
    try:
        return res.json()["status_message"]
    except (ValueError, KeyError):
        return res.reason


def get(session, path, params, ttl):
    CACHE_DIR.mkdir(parents=True, exist_ok=True)
    stamp = f"{path}?{json.dumps(params, sort_keys=True)}"
    cache_file = CACHE_DIR / (hashlib.sha256(stamp.encode()).hexdigest() + ".json")
    if cache_file.exists() and time.time() - cache_file.stat().st_mtime < ttl:
        return json.loads(cache_file.read_text(encoding="utf-8"))

    for attempt in range(3):
        try:
            res = session.get(BASE + path, params=params, timeout=10)
        except requests.RequestException as exc:
            raise ApiError(f"network error: {exc}") from exc
        if res.status_code != 429 or attempt == 2:
            break
        time.sleep(min(int(res.headers.get("Retry-After", "1")), 30))

    if res.status_code == 401:
        raise ApiError("401: the key is invalid or suspended. Check AMODB_KEY.")
    if res.status_code != 200:
        raise ApiError(f"{res.status_code}: {error_message(res)}")
    cache_file.write_text(res.text, encoding="utf-8")
    return res.json()


def search_movie(session, title):
    data = get(session, "/search/movie", {"query": title, "language": "en-US"}, SEARCH_TTL)
    if not data["results"]:
        raise ApiError(f"no movie found for {title!r}")
    return data["results"][0]["id"]


def movie_details(session, movie_id):
    params = {"language": "en-US", "append_to_response": "credits,ratings"}
    return get(session, f"/movie/{movie_id}", params, DETAILS_TTL)


def print_card(movie):
    year = (movie.get("release_date") or "")[:4] or "unknown year"
    genres = ", ".join(g["name"] for g in movie["genres"])
    cast = ", ".join(p["name"] for p in movie["credits"]["cast"][:3])
    imdb = (movie.get("ratings") or {}).get("imdb")
    print(f"{movie['title']} ({year}), {movie.get('runtime') or '?'} min")
    print(f"Genres: {genres or 'none listed'}")
    print(f"Cast: {cast or 'none listed'}")
    print(f"IMDb: {imdb['value']} ({imdb['votes']} votes)" if imdb else "IMDb: not known")
    if movie.get("poster_path"):
        print(f"Poster: {POSTER}{movie['poster_path']}")
    print(movie.get("overview") or "No overview in this language.")


def main():
    if len(sys.argv) < 2:
        sys.exit('Usage: python movie.py "fight club"')
    key = os.environ.get("AMODB_KEY")
    if not key:
        sys.exit("Set AMODB_KEY first.")
    session = requests.Session()
    session.headers["Authorization"] = f"Bearer {key}"
    try:
        movie_id = search_movie(session, " ".join(sys.argv[1:]))
        print_card(movie_details(session, movie_id))
    except ApiError as exc:
        sys.exit(f"error: {exc}")


if __name__ == "__main__":
    main()
```

## Další kroky

- Vypište plakát v jiné velikosti. Hostitel obrázků přijímá kteroukoli z velikostí uvedených na stránce [obrázky](https://amodb.dev/cs/images-api/) a nepotřebuje klíč.
- Přidejte do volání hledání filtr `year`, nebo přejděte na [discover](https://amodb.dev/cs/discover/) pro filtrované seznamy.
- Ukažte měřič nálady vedle hodnocení přidáním `moods` do `append_to_response`. Každá položka v `moods.results` má `name` a `avg` na škále 0 až 10.
- Přečtěte si [rychlý start](https://amodb.dev/cs/quickstart/) se stejným postupem v curlu, pak zkuste [návod pro JavaScript](https://amodb.dev/cs/javascript-movie-api/) s dávkovou verzí.

## Otázky a odpovědi

### Kterou verzi Pythonu a které knihovny potřebuji?

Python 3.10 nebo novější a knihovnu requests. Vše ostatní pochází ze standardní knihovny. Klíč putuje v hlavičce Authorization Bearer, takže se nikdy neobjeví v URL, historii shellu ani v logách serveru.

### Jak zůstat pod limitem požadavků?

Cachujte odpovědi a respektujte Retry-After. Každý klíč má 20, 40 nebo 120 požadavků za minutu v klouzavém 60sekundovém okně. Skript při 429 opakuje pokus dvakrát, počká počet sekund z hlavičky a čekání omezí na 30.

### Mohu znovu použít kód psaný pro klienta TMDB v3?

Ano pro čtecí volání. Cesty, parametry a JSON odpovídají TMDB v3, takže změníte základní URL na https://api.amodb.dev/3 a pošlete klíč amodb. Metody pro zápis, například hodnocení filmu, vracejí 405 se status_code 37.

### Má klíč jít do api_key, nebo do hlavičky?

Funguje obojí. Hlavička drží klíč mimo URL, logy proxy a řádky požadavků, proto tento návod používá Authorization: Bearer. Parametr dotazu api_key zůstává k dispozici pro nástroje, které hlavičky nastavit neumějí.

### Jak dlouho cachovat hledání a detaily?

Použijte 10 minut pro hledání a 6 hodin pro detaily, tedy stejné doby jako cache na edge. Kratší lokální cache jen přidává požadavky, protože kopie na edge je sdílená mezi klíči a tyto doby už drží.

## Související stránky

- [API vyhledávání](https://amodb.dev/cs/search/): API vyhledávání se stejnými cestami a JSON jako TMDB v3. Najděte filmy, seriály, osoby, kolekce, společnosti a klíčová slova podle textu s filtrem roku.
- [API detailu filmu](https://amodb.dev/cs/movies/): API detailu filmu se stejnými cestami a JSON jako TMDB v3. Načtěte film podle ID, přidejte obsazení, hodnocení a nálady přes append_to_response, 14 jazyků.
- [Hodnocení filmů a seriálů](https://amodb.dev/cs/ratings/): API hodnocení filmů, které vrací skóre z IMDb, Kinopoisk, Metacritic, Rotten Tomatoes a TMDB pro titul jedním voláním, s null u každého neznámého zdroje.
- [Rychlý start](https://amodb.dev/cs/quickstart/): První volání amodb: získejte klíč, vyžádejte detail filmu v curl, JavaScriptu, Pythonu, PHP nebo Go a načtěte plakát z img.amodb.dev. Za méně než 2 minuty.
- [Limity požadavků](https://amodb.dev/cs/rate-limits/): amodb omezuje klíč počtem požadavků za minutu v klouzavém okně 60 sekund: 20, 40 nebo 120 podle tarifu. Čtěte hlavičky limitu a řešte 429 pomocí Retry-After.
- [Chyby](https://amodb.dev/cs/errors/): Každá chyba amodb se stavem HTTP, status_code a zprávou TMDB a také tvar chyby OMDb. Co která znamená a co má váš klient udělat v dalším kroku.

Aktualizováno: 2026-10-18. Source: https://amodb.dev/cs/python-movie-api/
