# 情绪指数

> 电影和电视剧的 moods 接口返回 source 和 results 列表。每条结果有情绪名（如 fear）、0 到 10 的 avg 以及 values。在详情调用中加 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`。

示例加载《蝙蝠侠：黑暗骑士》（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` | 路径 | 是   | 数字形式的电影 ID，例如 `155`。                      |
| `api_key`  | 查询 | 二选一 | 你的密钥。使用它，或使用 `Authorization: Bearer` 请求头。 |

### 响应字段

| 字段                 | 类型               | 说明                                                       |
| ------------------ | ---------------- | -------------------------------------------------------- |
| `id`               | integer          | 电影 ID。                                                   |
| `source`           | string 或 null    | 来自观众数据时为 `measured`，由类型推算时为 `estimated`，作品没有数据时为 `null`。 |
| `results`          | array            | 按 `name` 排序的情绪指数。什么都不知道时为空。                              |
| `results[].name`   | string           | 简短的小写单词，例如 `fear`。                                       |
| `results[].avg`    | number           | 0 到 10 刻度的平均值。实测作品保留一位小数，推算作品为整数。                        |
| `results[].values` | array of numbers | 同一 0 到 10 刻度上的最多 24 个读数。目前所见实测作品有 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] }
  ]
}
```

列表缩减为 9 条结果中的 3 条。

### 实测与推算的作品

《搏击俱乐部》（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。《绝命毒师》请用 `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`    | `moods` 下的 `source` 和 `results` | 加在电影或电视剧详情上 |

```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` 秒。 |

情绪接口对未知 ID 不返回 404。它返回 200 和空的 `results` 数组，`source` 为 `null` 或缺失。追加到详情时，未知 ID 由详情调用返回 404。完整列表见[错误](https://amodb.dev/zh/errors/)，限制见[速率限制](https://amodb.dev/zh/rate-limits/)。

## 实用说明

- 和其他详情数据一样，情绪指数在边缘缓存 6 小时。更频繁地获取只会得到相同的响应体。
- 20 部作品的列表需要 20 次情绪请求。如果你本来就要为每部作品调用详情，请追加 `moods`，省下第二次调用。
- 比较作品之前先检查 `source`。实测和推算的指数刻度相同，但精度不同。
- 如果经常筛选，请把 `name` 和 `avg` 连同作品 ID 保存到你自己的数据库中。本地筛选胜过每部作品一次请求。
- 把指数与[评分](https://amodb.dev/zh/ratings/)（得到分数）和[原声配乐](https://amodb.dev/zh/soundtracks/)（得到同一作品的音乐）结合起来。

## 常见问题

### 情绪值是什么意思？

avg 字段是 0 到 10 的强度，实测作品保留一位小数。恐惧的 avg 为 7 表示作品很吓人，0 表示没有这种情绪。它不是百分比，也不是评论分数。

### 情绪名称的列表是固定的吗？

不要假定固定。目前见到的名称有 action、fear、humor、pace、profanity、romance、sadness、sex 和 violence，一部作品可能少于九个。请渲染你拿到的名称字符串，这样新增的名称也能显示。

### source 中的 measured 和 estimated 是什么意思？

measured 表示指数来自观众数据，带小数。estimated 表示由作品的类型推算，所以 avg 是整数。没有数据的作品 results 为空数组，source 为 null。

### 如何按情绪筛选作品列表？

获取列表中每部作品的指数，可以用 moods 接口，也可以在详情上用 append_to_response，然后保留所选情绪通过你阈值的作品。筛选在你的代码中进行。

### 电视剧也有情绪吗？

有。使用 GET /3/tv/{tv_id}/moods，例如剧集 ID 1396。响应结构与电影接口相同。

## 相关页面

- [电影详情 API](https://amodb.dev/zh/movies/): 路径和 JSON 与 TMDB v3 相同的电影详情 API：按 ID 取电影，用 append_to_response 加入演职员和评分，支持 14 种语言。
- [电视剧 API](https://amodb.dev/zh/tv-series/): 路径和 JSON 与 TMDB v3 相同的电视剧 API。按 ID 获取剧集的季、电视网和下一集，以及内容分级和观看平台。
- [电影与电视剧评分](https://amodb.dev/zh/ratings/): 一次调用返回作品的 IMDb、Kinopoisk、Metacritic、Rotten Tomatoes 和 TMDB 评分，来源未知时为 null。
- [原声配乐](https://amodb.dev/zh/soundtracks/): 返回电影或电视剧原声专辑及曲目列表的 API。可直接调用该接口，或添加 append_to_response=soundtracks。
- [发现 API](https://amodb.dev/zh/discover/): 与 TMDB v3 筛选条件相同的 discover API。按类型、演员、公司、关键词、年份、评分、时长和平台列出电影和剧集，并可排序。
- [快速开始](https://amodb.dev/zh/quickstart/): 发出第一次 amodb 调用：获取密钥，用 curl、JavaScript、Python、PHP 或 Go 请求电影详情，再加载海报。

更新于: 2026-10-18. Source: https://amodb.dev/zh/moods/
