# Шкалы настроения

> GET /3/movie/{movie_id}/moods и GET /3/tv/{tv_id}/moods возвращают source и список results. Каждый результат содержит название настроения, например fear или humor, значение avg по шкале от 0 до 10 и данные, на которых оно основано. Добавьте append_to_response=moods к запросу деталей, чтобы получить шкалы без второго запроса.

## Эндпоинт

`GET /3/movie/{movie_id}/moods`

```bash
curl "https://api.amodb.dev/3/movie/155/moods" \
  -H "Authorization: Bearer $AMODB_KEY"
```

Ответ:

```json
{
  "id": 155,
  "source": "measured",
  "results": [
    {
      "name": "action",
      "avg": 3.9,
      "values": [
        3.6,
        3.4,
        4.2
      ]
    },
    {
      "name": "fear",
      "avg": 5.2,
      "values": [
        3.6,
        6,
        4.8
      ]
    },
    {
      "name": "humor",
      "avg": 0.9,
      "values": [
        2.4,
        1.8,
        0.4
      ]
    }
  ]
}
```

## Получение шкал настроения фильма

`GET /3/movie/{movie_id}/moods` возвращает шкалы настроения одного фильма. Шкала настроения состоит из названия и среднего значения по шкале от 0 до 10. В ответе есть `id` фильма, `source` и массив `results`. Каждый результат содержит `name`, `avg` и `values`.

Пример загружает шкалы для The Dark Knight (ID 155) и выводит их от самой сильной к самой слабой.

```bash
curl -sf "https://api.amodb.dev/3/movie/155/moods" \
  -H "Authorization: Bearer $AMODB_KEY"
```

```js
const res = await fetch('https://api.amodb.dev/3/movie/155/moods', {
  headers: { Authorization: `Bearer ${process.env.AMODB_KEY}` },
});
if (!res.ok) throw new Error(`amodb returned ${res.status}`);
const { source, results } = await res.json();
console.log('source:', source);
for (const m of [...results].sort((a, b) => b.avg - a.avg)) {
  console.log(m.name.padEnd(10), m.avg);
}
```

```python
import os
import requests

r = requests.get(
    "https://api.amodb.dev/3/movie/155/moods",
    headers={"Authorization": f"Bearer {os.environ['AMODB_KEY']}"},
    timeout=10,
)
r.raise_for_status()
data = r.json()
print("source:", data.get("source"))
for m in sorted(data["results"], key=lambda m: m["avg"], reverse=True):
    print(f"{m['name']:<10} {m['avg']}")
```

```php
<?php
$ch = curl_init('https://api.amodb.dev/3/movie/155/moods');
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");
}
$data = json_decode($body, true);
echo 'source: ', $data['source'] ?? 'none', PHP_EOL;
$results = $data['results'];
usort($results, fn ($a, $b) => $b['avg'] <=> $a['avg']);
foreach ($results as $m) {
    printf("%-10s %.1f\n", $m['name'], $m['avg']);
}
```

```go
package main

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

type Mood struct {
	Name   string    `json:"name"`
	Avg    float64   `json:"avg"`
	Values []float64 `json:"values"`
}

func main() {
	req, _ := http.NewRequest("GET", "https://api.amodb.dev/3/movie/155/moods", 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 body struct {
		Source  string `json:"source"`
		Results []Mood `json:"results"`
	}
	if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
		panic(err)
	}
	fmt.Println("source:", body.Source)
	sort.Slice(body.Results, func(i, j int) bool { return body.Results[i].Avg > body.Results[j].Avg })
	for _, m := range body.Results {
		fmt.Printf("%-10s %.1f\n", m.Name, m.Avg)
	}
}
```

### Параметры

| Параметр   | Где   | Обязателен   | Описание                                                         |
| ---------- | ----- | ------------ | ---------------------------------------------------------------- |
| `movie_id` | path  | да           | Числовой ID фильма, например `155`.                              |
| `api_key`  | query | один из двух | Ваш ключ. Используйте его или заголовок `Authorization: Bearer`. |

### Поля ответа

| Поле               | Тип             | Примечания                                                                                                         |
| ------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------ |
| `id`               | целое число     | ID фильма.                                                                                                         |
| `source`           | строка или null | `measured` для данных зрителей, `estimated`, когда значения выведены из жанров, `null`, когда у тайтла нет данных. |
| `results`          | массив          | Шкалы настроения, отсортированные по `name`. Пуст, если ничего не известно.                                        |
| `results[].name`   | строка          | Короткое слово в нижнем регистре, например `fear`.                                                                 |
| `results[].avg`    | число           | Среднее по шкале от 0 до 10. Один десятичный знак для измеренных тайтлов, целое число для оцененных.               |
| `results[].values` | массив чисел    | До 24 значений по той же шкале от 0 до 10. У встреченных измеренных тайтлов их 6, у оцененных 5.                   |

Сейчас встречаются названия `action`, `fear`, `humor`, `pace`, `profanity`, `romance`, `sadness`, `sex` и `violence`. Не у каждого тайтла есть все девять, поэтому выводите названия из данных, а не из фиксированного списка.

### Пример ответа

```json
{
  "id": 155,
  "source": "measured",
  "results": [
    { "name": "action", "avg": 3.9, "values": [3.6, 3.4, 4.2, 4.2, 4.8, 3] },
    { "name": "fear", "avg": 5.2, "values": [3.6, 6, 4.8, 6.4, 6.4, 3.8] },
    { "name": "humor", "avg": 0.9, "values": [2.4, 1.8, 0.4, 0.4, 0, 0.5] }
  ]
}
```

Список сокращен до 3 из 9 результатов.

### Измеренные и оцененные тайтлы

У Fight Club (ID 550) пока нет данных зрителей, поэтому его шкалы выведены из жанров. Каждое значение `avg` целое, а в `values` 5 элементов.

```json
{
  "id": 550,
  "source": "estimated",
  "results": [
    { "name": "action", "avg": 4, "values": [3, 4, 5, 4, 3] },
    { "name": "fear", "avg": 7, "values": [6, 7, 8, 7, 6] }
  ]
}
```

Проверяйте `source`, если хотите обрабатывать оцененные шкалы иначе, чем измеренные.

## Шкалы настроения сериала

`GET /3/tv/{tv_id}/moods` возвращает ответ такой же структуры, а ID сериала лежит в `id`. Для Breaking Bad используйте `1396`.

| Метод и путь                  | Возвращает                           | Параметры                                |
| ----------------------------- | ------------------------------------ | ---------------------------------------- |
| `GET /movie/{movie_id}/moods` | `id`, `source` и `results`           | `movie_id`                               |
| `GET /tv/{tv_id}/moods`       | `id`, `source` и `results`           | `tv_id`                                  |
| `append_to_response=moods`    | `source` и `results` в ключе `moods` | Добавляется к деталям фильма или сериала |

```bash
curl -sf "https://api.amodb.dev/3/tv/1396?append_to_response=moods" -H "Authorization: Bearer $AMODB_KEY"
```

Когда вы добавляете `moods` к запросу деталей, ключ `moods` находится на верхнем уровне записи рядом с `title` или `name`. Он содержит `source` и `results` и не имеет собственного `id`. Это стоит один запрос, тогда как два отдельных вызова стоят два.

## Как читать шкалу

Поле `avg` показывает интенсивность от 0 до 10. Чем выше число, тем сильнее настроение в тайтле.

| Диапазон    | Как читать                         |
| ----------- | ---------------------------------- |
| От 0 до 2.9 | Настроение слабое или отсутствует. |
| От 3 до 5.9 | Настроение присутствует.           |
| От 6 до 7.9 | Настроение выражено явно.          |
| От 8 до 10  | Настроение определяет тайтл.       |

Диапазоны предлагаются для вашего интерфейса и не являются полем ответа. Массив `values` содержит значения, из которых получено среднее, по той же шкале.

## Бейджи и фильтры

Настроения подходят для трех типичных интерфейсных паттернов. Выберите пороги, подходящие вашему каталогу, и подстройте их.

| Паттерн      | Правило                                                     | Примечания                                                    |
| ------------ | ----------------------------------------------------------- | ------------------------------------------------------------- |
| Бейдж        | Показывать до 3 настроений с `avg` 6 и выше                 | Сначала сортируйте по `avg`.                                  |
| Полоса шкалы | Ширина полосы в процентах равна `avg`, умноженному на 10    | Шкала идет от 0 до 10.                                        |
| Чип фильтра  | Подбирать тайтлы, у которых выбранное настроение 7 или выше | Получите шкалы для каждого тайтла, затем фильтруйте локально. |

```js
function topMoods(results, min = 6, limit = 3) {
  return results
    .filter((m) => m.avg >= min)
    .sort((a, b) => b.avg - a.avg)
    .slice(0, limit)
    .map((m) => m.name);
}

function matchesMood(results, name, min = 7) {
  return results.some((m) => m.name === name && m.avg >= min);
}
```

Пустой массив `results` должен отображаться как отсутствие бейджей, а не как ошибка.

## Ошибки

Тело ошибки содержит `success`, `status_code` и `status_message`, те же поля, что использует TMDB.

| HTTP | status_code | Когда                                                             |
| ---- | ----------- | ----------------------------------------------------------------- |
| 401  | 7           | Ключ отсутствует или неверен.                                     |
| 402  | 36          | Период тарифа закончился.                                         |
| 429  | 25          | Превышен лимит запросов в минуту. Подождите `Retry-After` секунд. |

Эндпоинт moods не отвечает 404 на неизвестный ID. Он отвечает 200 с пустым массивом `results`, а `source` равен `null` или отсутствует. При добавлении к деталям неизвестный ID дает 404 от запроса деталей. Полный список ошибок на странице [ошибки](https://amodb.dev/ru/errors/), а лимиты на странице [лимиты запросов](https://amodb.dev/ru/rate-limits/).

## Практические советы

- Как и остальные данные деталей, шкалы настроения кэшируются на edge на 6 часов. Более частые запросы вернут то же тело ответа.
- Для списка из 20 тайтлов нужно 20 запросов настроений. Если вы уже запрашиваете детали каждого тайтла, добавьте `moods` и сэкономьте второй вызов.
- Проверяйте `source` перед сравнением тайтлов. Измеренные и оцененные значения лежат на одной шкале, но имеют разную точность.
- Если вы часто фильтруете, храните `name` и `avg` вместе с ID тайтла в своей базе. Локальный фильтр лучше запроса на каждый тайтл.
- Сочетайте шкалы с [рейтингами](https://amodb.dev/ru/ratings/) для оценки и с [саундтреками](https://amodb.dev/ru/soundtracks/) для музыки того же тайтла.

## Вопросы и ответы

### Что означает значение настроения?

Поле avg показывает интенсивность по шкале от 0 до 10, с одним десятичным знаком для измеренных тайтлов. Значение avg 7 для fear означает, что тайтл страшный, а 0 означает, что настроения нет. Это не процент и не оценка по отзывам.

### Фиксирован ли список названий настроений?

Не считайте его фиксированным. Сейчас встречаются action, fear, humor, pace, profanity, romance, sadness, sex и violence, а у тайтла может быть меньше девяти. Выводите полученные строки name, чтобы новое настроение тоже отобразилось.

### Что означают measured и estimated в source?

Measured значит, что шкалы построены по данным зрителей и содержат десятичные знаки. Estimated значит, что они выведены из жанров тайтла, поэтому avg целое число. У тайтла без данных results пуст, а source равен null.

### Как отфильтровать список тайтлов по настроению?

Получите шкалы для каждого тайтла из списка, через эндпоинт moods или через append_to_response в деталях, и оставьте те тайтлы, у которых выбранное настроение проходит ваш порог. Фильтр работает в вашем коде.

### Есть ли настроения у сериалов?

Да. Используйте GET /3/tv/{tv_id}/moods, например с ID сериала 1396. Ответ имеет такую же структуру, как у эндпоинта фильмов.

## Связанные страницы

- [API деталей фильма](https://amodb.dev/ru/movies/): API деталей фильма с теми же путями и JSON, что в TMDB v3. Получите фильм по ID, добавьте актеров, рейтинги и настроения через append_to_response, на 14 языках.
- [API сериалов](https://amodb.dev/ru/tv-series/): API сериалов с теми же путями и JSON, что в TMDB v3. Сериал по id с сезонами, телесетями и следующей серией, а также возрастные рейтинги и сервисы просмотра.
- [Рейтинги фильмов и сериалов](https://amodb.dev/ru/ratings/): API рейтингов фильмов: оценки IMDb, Kinopoisk, Metacritic, Rotten Tomatoes и TMDB для тайтла одним запросом, с null для источника, о котором нет данных.
- [Саундтреки](https://amodb.dev/ru/soundtracks/): API саундтреков возвращает альбом саундтрека фильма или сериала со списком треков. Вызовите эндпоинт или добавьте append_to_response=soundtracks к деталям.
- [Discover API](https://amodb.dev/ru/discover/): Discover API с фильтрами TMDB v3. Фильмы и сериалы по жанру, актеру, студии, ключевому слову, году, рейтингу, длительности и провайдеру, с сортировкой.
- [Быстрый старт](https://amodb.dev/ru/quickstart/): Первый вызов amodb: получите ключ, запросите детали фильма через curl, JavaScript, Python, PHP или Go и загрузите постер с img.amodb.dev. Меньше 2 минут.

Обновлено: 2026-10-18. Source: https://amodb.dev/ru/moods/
