Соглашения¶
Одна грамматика на все списки: выучив её на /titles, вы умеете фильтровать
связи, фильмографию и подборки.
Конверт списка¶
Любой список приходит в одной форме:
total — размер всей выборки до пагинации. Он приходит числом там, где выборку
задаёт адрес: состав тайтла, его связи, фильмография человека, подборка целиком.
Он приходит null там, где выборку задал запрос: точный счёт по фильтру — это
второй полный агрегат по 1.38 млн строк, и платить за него на каждой странице
незачем. Листать это не мешает: has_more отвечает на вопрос «есть ли ещё».
У чартов и витрин total не приходит вовсе — они бесконечны по построению.
Свои поля ресурса едут рядом с этими пятью, а не вместо них: у связей это
counts, у каста — summary, у подборки — collection.
Пагинация¶
Значения за границей клампятся, и ответ отдаёт эхом настоящее: ?limit=5000 на
каталоге вернёт "limit": 50. Нечисловое значение — 400.
| список | по умолчанию | максимум |
|---|---|---|
/titles, чарты, витрины, подборки, поиск людей |
50 | 50 |
| связи, фильмография, каст, медиа, тексты, серии | 1000 | 1000 |
секции при ?group= |
10 на секцию | 50 на секцию |
Разница в том, кто задал набор. Если набор задан адресом — «состав этого
тайтла», «фильмография этого человека» — набор и есть ответ, он приходит
целиком. Если набор задан запросом, ответ ранжирован, и приходит его верх;
глубже листайте offset или курсором.
Курсор¶
Для глубокой прокрутки каталога вместо offset берите курсор — он не
«съезжает», когда данные меняются под вами:
curl -H "X-API-Key: $KEY" \
"https://api.kinodata.space/v1.2/titles?sort=-rating_imdb&votes_imdb.gte=100000&cursor="
В ответе появится next_cursor; передайте его в следующий запрос. null
означает, что выборка кончилась.
Сортировка¶
?sort=-rating_imdb по убыванию
?sort=+year по возрастанию
?sort=-rating_imdb,+year ключ и тай-брейкер одним параметром
Знак — это направление; без знака подразумевается убывание. Допустимые ключи:
release_date, year, runtime, popularity, relevance, rating,
rating_kp, rating_tmdb, rating_imdb, rating_rt, rating_metacritic,
rating_critics, votes_kp, votes_tmdb, votes_imdb, budget, revenue,
box_office, awards_won, oscars_won, random.
sort=rating берёт первую известную оценку тайтла — IMDb, затем Кинопоиск,
затем TMDB — и ранжирует тайтлы с 50+ голосами выше остальных, чтобы десятка от
одного человека не обошла девятку от миллиона. Агрегата «общий балл» нет
намеренно: у источников разные шкалы и разная аудитория, и усреднение это
скрывает. Нужен свой порог доверия — ставьте его явно:
?sort=-rating_imdb&votes_imdb.gte=100000.
sort=random перемешивает выборку. У случайного порядка нет якоря, поэтому
cursor и offset с ним отвечают 400: каждый запрос — новая выборка.
Фильтры¶
Пять операторов, один список полей:
| оператор | смысл | пример |
|---|---|---|
| (нет) | равно; CSV = любое из | kind=movie,series |
.any |
явное «любое из» | genre.any=drama,comedy |
.all |
все сразу | genre.all=drama,war |
.not |
исключить | country.not=us |
.gte .lte |
диапазон | year.gte=1990&year.lte=1999 |
Диапазон, заданный без оператора, означает точное значение: year=1999 — это
year.gte=1999&year.lte=1999.
Что можно фильтровать:
| группа | поля |
|---|---|
| идентичность | imdb_id, kp_id, tmdb_id, tmdb_tv_id |
| тип | kind, is_series, adult |
| время | year, release_date, premiere_world, premiere_ru, digital_release, released, start_year, end_year |
| оценки | rating, rating_imdb, rating_kp, rating_tmdb, rating_rt, rating_metacritic, rating_critics, votes_kp, votes_tmdb, votes_imdb, popularity |
| содержание | genre, tag, country, original_language, companies, platform, runtime |
| люди | person, person.role |
| сериалы | seasons_total, episodes_total |
| деньги | budget, revenue, box_office |
| награды | awards_won, oscars_won |
| возраст | age_limit, age_limit_max |
| принадлежность | collection |
| полнота | required, hydrated |
person= ищет по участию человека и принимает внешний ключ:
?person=tmdb:525&person.role=director.
required=poster,rating_imdb оставляет только тайтлы, у которых эти поля
заполнены. Отдельный has_poster=true — то же самое для одного постера.
Пресеты¶
Пресет — это набор фильтров под именем. Он раскрывается после явных
параметров, поэтому ?preset=top&votes_imdb.gte=500000 ужесточает чарт, а не
конфликтует с ним. Раскрытие каждого пресета публикует
/filters.
Секции¶
Списки, элементы которых различаются по одному полю, приходят разделами:
?group=relation на /titles/{type}/{id}/relations
?group=role на /titles/{type}/{id}/cast, /persons/{type}/{id}/filmography
?group=kind на /titles/{type}/{id}/texts
?group=type|class|language на /titles/{type}/{id}/media
Секция сама устроена как список:
{
"items": [
{ "relation": "similar", "label": "Похожие", "total": 93, "has_more": true, "items": [ … ] },
{ "relation": "related", "label": "Связанные", "total": 8, "has_more": true, "items": [ … ] }
],
"total": 4, "limit": 10, "offset": 0, "has_more": false
}
Те же четыре слова на обоих уровнях, поэтому один разбор читает и то и другое.
total секции — её настоящий размер, даже когда элементы обрезаны по limit.
?group=none даёт плоский список. Сужение до одного значения (?relation=,
?role=, ?kind=) — тоже плоский список, с полной пагинацией: это переход
внутрь раздела.
Пагинация по сгруппированному ответу отвечает 400 — у секций нет единой
последовательности, в которую можно сместиться.
У серий ?group=season устроен иначе: это не секции с вложенными сериями, а
сводка по сезонам — строка на сезон с числом серий, датами и рейтингом.
Язык¶
Влияет на производные поля (title, overview, character) и на подписи
перечислений (kind_label, genres[].name, названия секций). Исходные
title_ru / title_en / title_original приходят всегда и не зависят от lang.
Выбор полей¶
На карточке тайтла именами служат её блоки, в списках — ключи карточки списка.
Смешивать два режима в одном параметре нельзя: title,-akas — 400, потому что
две трактовки неперечисленного противоречат друг другу. Неизвестное имя — тоже
400, со списком допустимых.
Выбор полей понимают /titles, подборка, связи и фильмография — списки, которые
строит один и тот же движок выборки.
Даты и числа¶
Даты — YYYY-MM-DD, строкой. Год — целое. Рейтинги — числа в своей шкале,
она указана рядом в scale (10 или 100). Пустое скалярное значение
приходит как null, а не как 0 или "". Массивы пустые — [], не null.
Кэширование¶
Ответы отдают ETag и Cache-Control. Повторный запрос с If-None-Match
получает 304 и не тратит тело. Сначала возьмите тег:
curl -sI -H "X-API-Key: $KINODATA_KEY" "https://api.kinodata.space/v1.2/titles/tmdb/27205" | grep -i etag
Затем пришлите его обратно — значение вместе с кавычками:
curl -s -o /dev/null -w '%{http_code}\n' -H "X-API-Key: $KINODATA_KEY" -H 'If-None-Match: "23930-981179597"' "https://api.kinodata.space/v1.2/titles/tmdb/27205"
Ответы сжимаются: с Accept-Encoding: gzip карточка «Начала» весит 7 КБ
вместо 24 КБ.