API תמונות ושרת תמונות

נקודות הקצה של תמונות מציגות את הפוסטרים, תמונות הרקע, הלוגואים, תמונות הפרופיל ותמונות הסצנה של כותר כנתיבי קבצים. אחר כך טוענים כל קובץ מ-img.amodb.dev בגודל קבוע, בלי מפתח ועם מטמון קצה של 30 יום. הנתיבים וה-JSON זהים ל-TMDB v3, ולכן קוד תמונות קיים עובד אחרי החלפת כתובת בסיס.

עודכן צפייה כ-Markdown

קבלת תמונות של סרט

GET /3/movie/{movie_id}/images מציג כל תמונה ידועה של סרט אחד בשלושה מערכים: backdrops, logos ו-posters. בכל פריט יש file_path, גודלו בפיקסלים ותגית שפה. הקובץ עצמו אינו ב-JSON. טוענים אותו ממארח התמונות בשלב שני.

הדוגמה מבקשת תמונות באנגלית ובלי טקסט, בוחרת את הפוסטר האנגלי הראשון ואז שולפת את הקובץ ברוחב 500 פיקסלים.

bashjspyphpgo
curl -s "https://api.amodb.dev/3/movie/550/images?include_image_language=en,null" \
  -H "Authorization: Bearer $AMODB_KEY"

curl -s -o poster.jpg -w "%{http_code}\n" \
  "https://img.amodb.dev/t/p/w500/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg"
const url = 'https://api.amodb.dev/3/movie/550/images?include_image_language=en,null';
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` } });
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { posters } = await res.json();
if (posters.length === 0) throw new Error('no posters');
const poster = posters.find((p) => p.iso_639_1 === 'en') ?? posters[0];
const posterUrl = `https://img.amodb.dev/t/p/w500${poster.file_path}`;
const img = await fetch(posterUrl);
console.log(img.status, img.headers.get('content-type'), posterUrl);
import os
import requests

r = requests.get(
    "https://api.amodb.dev/3/movie/550/images",
    params={"include_image_language": "en,null"},
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
r.raise_for_status()
posters = r.json()["posters"]
if not posters:
    raise SystemExit("no posters")
poster = next((p for p in posters if p["iso_639_1"] == "en"), posters[0])
poster_url = "https://img.amodb.dev/t/p/w500" + poster["file_path"]
img = requests.get(poster_url, timeout=10)
print(img.status_code, img.headers.get("content-type"), poster_url)
<?php
$url = 'https://api.amodb.dev/3/movie/550/images?include_image_language=en,null';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('AMODB_KEY')],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
    throw new RuntimeException("amodb returned $status");
}
$posters = json_decode($body, true)['posters'];
if (!$posters) {
    throw new RuntimeException('no posters');
}
$poster = $posters[0];
foreach ($posters as $p) {
    if ($p['iso_639_1'] === 'en') {
        $poster = $p;
        break;
    }
}
$posterUrl = 'https://img.amodb.dev/t/p/w500' . $poster['file_path'];
$img = curl_init($posterUrl);
curl_setopt_array($img, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10]);
curl_exec($img);
echo curl_getinfo($img, CURLINFO_HTTP_CODE), ' ', curl_getinfo($img, CURLINFO_CONTENT_TYPE), ' ', $posterUrl, PHP_EOL;
curl_close($img);
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

type Images struct {
	Posters []struct {
		FilePath string `json:"file_path"`
		Language string `json:"iso_639_1"`
	} `json:"posters"`
}

func main() {
	url := "https://api.amodb.dev/3/movie/550/images?include_image_language=en,null"
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+os.Getenv("AMODB_KEY"))
	client := &http.Client{Timeout: 10 * time.Second}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		panic(res.Status)
	}
	var images Images
	if err := json.NewDecoder(res.Body).Decode(&images); err != nil || len(images.Posters) == 0 {
		panic("no posters")
	}
	poster := images.Posters[0]
	for _, p := range images.Posters {
		if p.Language == "en" {
			poster = p
			break
		}
	}
	posterURL := "https://img.amodb.dev/t/p/w500" + poster.FilePath
	img, err := client.Get(posterURL)
	if err != nil {
		panic(err)
	}
	defer img.Body.Close()
	fmt.Println(img.StatusCode, img.Header.Get("Content-Type"), posterURL)
}

פרמטרים

  • movie_idנתיבחובה
    מזהה סרט מספרי, למשל 550.
  • api_keyשאילתהאחד משניים
    המפתח שלכם. השתמשו בו או בכותרת Authorization: Bearer.
  • include_image_languageשאילתה
    קודי ISO 639-1 מופרדים בפסיקים. הוסיפו null כדי לכלול תמונות בלי טקסט, למשל en,null. רק השפות שברשימה חוזרות. בלעדיו מקבלים כל שפה.
  • languageשאילתה
    מתקבל אבל מתעלמים ממנו בנקודת הקצה הזו. השתמשו ב-include_image_language כדי לבחור שפות.

שדות התגובה

שדהסוגהערות
idintegerמזהה הסרט.
backdrops, logos, postersarrayמערך אחד לכל סוג תמונה, בסדר המפתחות הזה. כל אחד יכול להיות ריק.
file_pathstringמתחיל בלוכסן. מצרפים אותו לגודל במארח התמונות. פוסטרים ותמונות רקע הם .jpg, לוגואים הם .png או .svg.
width, heightintegerגודל הקובץ המקורי בפיקסלים. 0 כשהגודל לא ידוע.
aspect_rationumberרוחב חלקי גובה. פוסטרים קרובים ל-0.667, תמונות רקע קרובות ל-1.778. 0 כשהגודל לא ידוע.
iso_639_1string או nullשפת הטקסט שעל התמונה. null פירושו בלי טקסט.
vote_average, vote_countnumber, integerתמיד 0 בתמונות של סרטים וסדרות, ולכן אי אפשר לדרג בהן תמונות. שמרו על הסדר שה-API מחזיר.

תגובה לדוגמה

מוצגים שני פריטים בכל מערך.

JSON
{
  "id": 550,
  "backdrops": [
    {
      "aspect_ratio": 1.778,
      "height": 1440,
      "iso_639_1": null,
      "file_path": "/c6OLXfKAk5BKeR6broC8pYiCquX.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 2560
    },
    {
      "aspect_ratio": 1.778,
      "height": 1080,
      "iso_639_1": "en",
      "file_path": "/b9HyPoxwxjxkWEUL5ErZdhApQe2.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 1920
    }
  ],
  "logos": [
    {
      "aspect_ratio": 4.638,
      "height": 389,
      "iso_639_1": "en",
      "file_path": "/7Uqhv24pGJs4Ns31NoOPWFJGWNG.png",
      "vote_average": 0,
      "vote_count": 0,
      "width": 1804
    },
    {
      "aspect_ratio": 0.848,
      "height": 1295,
      "iso_639_1": "en",
      "file_path": "/4XkF0Rf7gvSfea8fYLFbU5tmuJw.png",
      "vote_average": 0,
      "vote_count": 0,
      "width": 1098
    }
  ],
  "posters": [
    {
      "aspect_ratio": 0.667,
      "height": 3000,
      "iso_639_1": "en",
      "file_path": "/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 2000
    },
    {
      "aspect_ratio": 0,
      "height": 0,
      "iso_639_1": null,
      "file_path": "/qkrH7rjYKU4KOwwIl6wF6H3ISvm.jpg",
      "vote_average": 0,
      "vote_count": 0,
      "width": 0
    }
  ]
}

הפוסטר השני מציג פריט שגודלו לא ידוע. הקובץ עדיין נטען.

בניית כתובת תמונה

מחברים שלושה חלקים: https://img.amodb.dev/t/p/, קטע גודל וה-file_path. הנתיב כבר מתחיל בלוכסן, ולכן https://img.amodb.dev/t/p/w500 ועוד /jSziioSwPVrOy9Yow3XhWIBDjq1.jpg היא כתובת שלמה.

גודל הוא w ועוד רוחב בפיקסלים, h ועוד גובה בפיקסלים, או original לקובץ השמור. המארח מגיש עשרה גדלים: w92, w154, w185, w300, w342, w500, w780, w1280, h632 ו-original. כל גודל אחר, כמו w400, עונה 404. הנתיב /3/configuration מציג את הגדלים שמתאימים לכל סוג תמונה.

סוגשדה בהגדרותגדלים
פוסטרposter_sizesw92, w154, w185, w342, w500, w780, original
תמונת רקעbackdrop_sizesw300, w780, w1280, original
לוגוlogo_sizesw92, w154, w185, w300, w500, original
פרופילprofile_sizesw185, h632, original
תמונת סצנהstill_sizesw92, w185, w300, original

המארח לא בודק את הסוג, ולכן פוסטר נטען גם ב-w1280. הישארו בגדלים שרשומים לסוג, כדי שהקוד שלכם יתאים להגדרות ולכל לקוח TMDB.

מארח התמונות לא דורש מפתח, והקצה שומר כל קובץ 30 יום. מכיוון שלא נשלח מפתח, טעינת תמונות לא צורכת את הבקשות שלכם לדקה. התגובות נושאות Access-Control-Allow-Origin: *, ולכן דפדפן יכול לטעון אותן מכל עמוד. גודל לא מוכר או קובץ לא מוכר עונים 404 עם גוף JSON.

תמונות רספונסיביות עם srcset

בחרו את הגודל הקטן ביותר שעדיין מכסה את הרוחב המוצג כפול צפיפות המסך. פוסטר שמוצג ברוחב 171 פיקסלי CSS צריך w342 במסך 2x. פונקציית העזר הזו בונה srcset מנתיב שמור.

JavaScript
const WIDTHS = [185, 342, 500, 780];

function posterSrcset(filePath) {
  return WIDTHS.map((w) => `https://img.amodb.dev/t/p/w${w}${filePath} ${w}w`).join(', ');
}

const path = '/jSziioSwPVrOy9Yow3XhWIBDjq1.jpg';
console.log(`<img src="https://img.amodb.dev/t/p/w342${path}" srcset="${posterSrcset(path)}" sizes="(max-width: 600px) 50vw, 342px" alt="Fight Club poster">`);

נקודות קצה נוספות לתמונות

כל הנתיבים שלמטה משתמשים בבסיס https://api.amodb.dev/3 ומחזירים את המערכים שבעמודה השנייה.

שיטה ונתיבמערכים שמוחזריםפרמטרים
GET /tv/{series_id}/imagesbackdrops, logos, postersinclude_image_language
GET /tv/{series_id}/season/{season_number}/imagesposters, ריק תמידאין
GET /tv/{series_id}/season/{season_number}/episode/{episode_number}/imagesstills, פריט אחד עם width ו-height שהוגדרו ל-0אין
GET /person/{person_id}/imagesprofilesאין

לסרטים ולסדרות אפשר גם להוסיף append_to_response=images לקריאת פרטים. גם שם include_image_language עובד. לבלוק המצורף אין id משלו.

פרופילים של אנשים הם המקום היחיד שבו vote_average נושא ציון אמיתי. ה-id בתגובה של עונה או פרק הוא מזהה העונה או הפרק. את הרשומות האב ראו בעמודים אנשים וסדרות טלוויזיה.

amodb לא מציע נתיבי תמונות לאוספים, לחברות ולרשתות. הרשומות שלהם כבר מחזיקות poster_path, backdrop_path או logo_path אחד, כמתואר בעמודים אוספים וחברות ורשתות שידור.

נקודת ההגדרות

GET /3/configuration מחזיר בלוק images עם secure_base_url שמוגדר ל-https://img.amodb.dev/t/p/, עם base_url (צורת http רגילה), ועם רשימות הגדלים backdrop_sizes, logo_sizes, poster_sizes, profile_sizes ו-still_sizes. קראו אותו פעם אחת בהפעלה, השתמשו בצורה המאובטחת ובנו ממנו כתובות במקום לקבוע גדלים בקוד. הקצה שומר אותו במטמון 24 שעות.

שגיאות

HTTPstatus_codeמתי
4017המפתח חסר או שגוי בקריאת JSON.
40236תקופת המסלול הסתיימה.
40434אין אדם, עונה או פרק עם המזהה הזה, או שגודל או קובץ לא מוכרים במארח התמונות.
40537השתמשתם בשיטת כתיבה בנתיב תמונות.
42925חרגתם מהבקשות שלכם לדקה. המתינו Retry-After שניות.

מזהה סרט או סדרה שלא קיים אינו שגיאה כאן: נתיב התמונות עונה 200 עם מערכים ריקים, בעוד נתיב הפרטים עונה 404. את הרשימה המלאה ראו בעמוד שגיאות. בקשות לקבצי תמונה הן טעינות HTTP רגילות, ולכן בדקו את קוד הסטטוס של התגובה במקום לפענח גוף JSON.

הערות מעשיות

  • שמרו את file_path במסד הנתונים ובנו את הכתובת בזמן ההצגה. גודל חדש או מארח חדש עולים אז שינוי קוד אחד.
  • העבירו את include_image_language עם שפת התצוגה שלכם ועוד null, למשל ru,null, כדי שפוסטרים עם טקסט זר לא יופיעו.
  • מערך ריק הוא 200 רגיל. חזרו אל poster_path ברשומת פרטי הסרט.
  • השתמשו ב-w1280 לתמונות רקע ברוחב מלא וב-w780 לכרטיסים. original הוא הקובץ השמור ברזולוציה מלאה והוא גדול בהרבה מ-w780.
  • עותקי קצה חיים 30 יום ומשותפים, ולכן קובץ שלקוח אחד ביקש מוגש מהמטמון ללקוח הבא.