Blog

Bangumi 和 TMDB 数据集成

apidatagallery

Bangumi 和 TMDB 数据集成

画廊模块的数据来自两个来源:Bangumi(动漫)和 TMDB(电影)。

Bangumi API v0

Bangumi 的 v0 API 现在需要 Bearer Token 鉴权。操作是去 bgm.tv 的开发者设置里生成一个 access token,然后在请求头里带上 Authorization: Bearer xxx

请求 GET /v0/users/{username}/collections 可以拿到用户的所有收藏,支持 subject_typetype 参数来筛选动画和收藏状态。分页参数是 limitoffset,实测 limit=100 是稳定可用的最大值。

同步脚本的逻辑是全量替换——每次运行都重新拉取所有数据,覆盖本地的 JSON 文件。这样避免了增量和全量之间的状态不一致。缺点是网络差的时候会比较慢。

TMDB 数据

TMDB 的 movie/top_rated 接口返回了按评分排序的电影列表。分页取 20 页,共 400 部电影,下载海报到本地,并映射 genre ID 到中文/英文标签。

需要注意的是 TMDB 的图片 CDN(image.tmdb.org)在国内访问不太稳定。海报先下载到本地存储,图片路径存为相对路径,前端从 /images/movies/ 加载。

本地存储方案

没有用数据库,直接把元数据存为 JSON 文件:

public/data/
  anime-collect.json
  anime-wish.json
  anime-doing.json
  movies.json

每次同步脚本运行时,读取新数据,和旧数据对比,只在有变化时才覆写文件。图片文件会做重复检查——如果本地已存在相同 id 的图片就不重复下载。

选择 JSON 而非 SQLite 是因为数据量不大,JSON 文件可以直接被 Next.js 的静态文件服务托管,前端用 fetch 直接读取,不需要额外的 API 路由。实际上 gallery 页面的数据加载就是直接从 public/data/ 读的,走 CDN 缓存,速度快。

标签筛选

前端拿到数据后,用 useMemo 缓存排序和筛选结果。标签来自 Bangumi 返回的 subject.tags 数组,前端聚合后提取出现频率 >=2 的标签作为筛选条件。

NSFW 过滤的实现比较朴素——维护了一个关键词列表(r18肉番等),对标签做大小写不敏感的匹配。准确率够用,但不完美。