Ctrl K
ComunidadGuías…

README.zh.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
[English](README.md) | **中文**

# AnimeGo

全栈动漫追番、发现与本地播放平台。浏览每季新番、管理追番列表、本地视频文件匹配弹幕播放、与其他番友互动。

**在线访问:** [animegoclub.com](https://animegoclub.com)

---

## 项目状态

> **Go + Next.js 栈已是正式生产分支。**
> `main` 是稳定/生产分支 — 只有经过测试和审查的代码才会合并进来;生产部署从 `main` 发出。`feat/go-backend` 是活跃开发分支,新工作在合并到 `main` 前先在此落地。
> 旧的 Express + MongoDB + Vite SPA 栈已于 2026-06-01 完全退役。本仓库不再有 `client/` 或 `server/` 目录。

**状态:** 已上线生产环境(2026-05-31 切换;旧栈 2026-06-01 退役)
**重构启动:** 2026-05-10(基于 v2.0.0)
**生产架构:** Next.js 16 + Bun + Go 1.26(chi · pgx · sqlc)+ PostgreSQL 16 + Node ws-server(Socket.IO)
**构建方式:** Claude Code 辅助开发(产品方向、决策、部署由作者负责)

### 分支模型

| 分支 | 用途 |
|------|------|
| `main` | 稳定 / 生产。从此处部署。仅在测试 + 审查后合并代码。 |
| `feat/go-backend` | 活跃开发。新功能和修复先在此落地。 |

### 已知限制(有意为之,非 bug)
- **弹幕匹配** — 不追求 100% 准确率,冷门续作用手动选集兜底。原因见 project memory `feedback_danmaku_matching`(明确不接 LLM/AI 匹配)。
- **Bangumi 富化** — 后台管道,新番可能需要一个周期才出现中文标题/评分。
- **单实例 WebSocket** — Socket.IO 弹幕未接 Redis adapter,横向扩容需补(见 TODO.md 待办五)。
- **无用户隐私开关** — `/u/:username` 追番页当前阶段默认公开(见 TODO.md 待办四)。

### 本地开发
1. 读本 README + [CHANGELOG.md](CHANGELOG.md)(最近 2-3 条即可还原当前心智模型)。
2. 读 [TODO.md](TODO.md) 了解有意推迟的事项。
3. `cp .env.example .env && bash scripts/dev.sh` — 验证本地能跑起来。
4. SSH 到 VPS,`docker compose ps` — 确认生产正常。
5. 动 UI 前先读 [DESIGN.md](DESIGN.md)。

---

## 功能特性

### 浏览与发现
- **季度新番** — 按类型、格式、状态筛选,按评分/标题/格式排序
- **热门排行** — 按订阅数排名的热门番剧
- **年度榜单** — 当年最高评分番剧
- **完结佳作** — 高分完结作品推荐,支持刷新
- **每周放送** — 当季番剧播出时间一览
- **搜索** — 全文搜索 + 类型筛选
- **番剧详情** — 横幅、双评分(AniList + Bangumi)、简介、角色、制作人员、关联作品、推荐
- **分集标题** — 来自 Bangumi,续集自动偏移归一化

### 追番与观看
- **追番系统** — 在看 / 已完结 / 想看 / 弃坑
- **观看进度** — 记录当前集数,已看集数高亮
- **继续观看** — 首页展示正在追的番剧及进度条
- **磁力搜索** — 聚合 ACG.rip RSS 磁力链接,支持字幕组筛选

### 本地播放器 + 弹幕
- **拖放即播** — 拖入番剧文件夹或选择视频文件(MKV、MP4、AVI、WebM)
- **自动弹幕匹配** — 通过弹弹Play API 进行文件 hash + 文件名匹配,多阶段降级(hash 匹配 -> 关键词匹配 -> 逐文件匹配)
- **MKV 内嵌字幕提取** — 浏览器端 Web Worker 提取 ASS/SSA/SRT 字幕,自动转换为 VTT 显示
- **逐集弹幕选择器** — 每一集可手动设置或更换弹幕源;支持在弹弹Play 上搜索任意番剧并选择对应集数
- **无阻塞播放** — 视频立即播放,字幕和弹幕在后台异步加载
- **集数导航** — 无需退出播放器即可切换集数

### 社区
- **弹幕** — 基于 Socket.IO 的实时弹幕系统
- **剧集评论** — 按集评论,支持回复和点赞
- **关注系统** — 关注其他用户,在动态中查看他们的追番更新
- **公开主页** — `/u/:username` 展示追番列表、观看统计和粉丝/关注数
- **好友动态** — 已关注用户的最新追番更新
- **分享** — Web Share API + 剪贴板回退

### SEO 与社交
- **服务端渲染元数据** — 完整爬虫 HTML,含结构化数据(JSON-LD)、OG 标签、面包屑
- **动态站点地图** — 从数据库自动生成,按评分排序,动态优先级
- **内容丰富** — 番剧详情页包含角色、制作人员、关联作品,利于搜索引擎收录
- **Google Search Console** 已验证

### 管理后台
- **富化监控** — 实时显示 Phase 1-3、Phase 4、V3 队列状态
- **内联编辑** — 直接编辑中文标题和 Bangumi ID
- **V3 自愈** — 批量修复缺失的中文标题,带进度条、暂停/继续
- **用户管理** — 创建、编辑、删除用户;查看订阅和粉丝

### 国际化
- 完整中英文 UI — 一键切换,偏好持久化
- `pickTitle()` 根据当前语言选择最佳标题

---

## 技术栈

| 层级 | 技术 |
|------|------|
| 前端 | React 19 · Next.js 16(App Router、RSC)· Bun 1.3.x · libass-wasm · artplayer · dandanplay-vi |
| 后端 | Go 1.26 · chi 路由 · pgx v5 · sqlc · river 队列 · ristretto 缓存 |
| 数据库 | PostgreSQL 16 · pg_cron · pg_trgm |
| 实时 | ws-server — 独立 Node + socket.io 进程 |
| 认证 | JWT(access 15m + refresh 14d)· bcrypt · httpOnly Cookie |
| 播放器 | Artplayer · libass-wasm(ASS 字幕)· Web Workers(MD5 hash、MKV 解析) |
| 外部 API | AniList GraphQL · Bangumi API · 弹弹Play API · ACG.rip RSS |
| 部署 | Docker Compose · Nginx 反向代理 · Cloudflare · SSL |
| SEO | `/anime/:id`、`/seasonal`、`/search` 服务端组件 · 动态站点地图 · JSON-LD · OG 标签 |

---

## 快速开始

全新 clone 在干净 macOS 上 5 分钟内应能跑到 `http://localhost:3000`。

### 前置条件

一次性安装:

```bash
# Docker Desktop(Postgres 容器)
brew install --cask docker

# Bun 1.3+(Next.js 运行时 + ws-server)
curl -fsSL https://bun.sh/install | bash

# Go 1.26+(go-api)与 Air(热重载)
brew install go
go install github.com/cosmtrek/air@latest
```

> `scripts/setup.sh` 用于 Debian 12 VPS bootstrap(Docker + UFW)自动化。macOS 本地开发跑上面三条即可。

### 安装与启动

```bash
# 1. 复制 env 模板并填密钥(JWT、弹弹Play app id/secret)
cp .env.example .env

# 2. 一键 dev loop:拉起 Postgres,启动 go-api(Air 热重载)、
#    ws-server(Bun + socket.io)、next-app(Bun + Next.js 16)。
bash scripts/dev.sh
```

访问 `http://localhost:3000`。

### 本地生产栈(`docker compose` HTTPS + nginx)

在部署到 VPS 前,本地复现生产布局(nginx 路由 next-app + go-api + ws-server,自签名 SSL,完整 docker 栈):

```bash
# 1. 生成本地自签名证书(每台机器一次性操作)。
bash scripts/gen-local-cert.sh

# 2. 复制并填写生产 env 模板。
cp .env.production.example .env.production
# ⚠️  必填:ALLOWED_HOSTS=animegoclub.com,localhost,app
# ⚠️  必填:DATABASE_URL=postgres://animego:password@postgres:5432/animego

# 3. 构建并拉起完整栈(next-app、go-api、ws-server、postgres、nginx)。
docker compose --env-file .env.production up -d --build

# 4. 访问 https://localhost(接受自签名证书警告)。
```

### 开发端口

| 端口 | 服务 | 说明 |
|------|------|------|
| 3000 | next-app | App Router 前端(RSC) |
| 3001 | ws-server | socket.io 弹幕 |
| 8080 | go-api | chi + pgx |
| 5432 | postgres | 主数据库 |

### 环境变量

`.env.example` 已为每个 key 加注释。必填部分:

```env
# 共享
JWT_SECRET=fill-with-32-byte-random-hex
JWT_REFRESH_SECRET=fill-with-another-32-byte-random-hex
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=14d
DANDANPLAY_APP_ID=fill-with-your-app-id
DANDANPLAY_APP_SECRET=fill-with-your-secret

# Postgres(Go API)— 与 docker-compose.dev.yml 默认对齐
POSTGRES_PASSWORD=devpassword
DATABASE_URL=postgres://animego:devpassword@localhost:5432/animego?sslmode=disable
PORT_GO=8080
```

> 弹弹Play API 凭据可在 [api.dandanplay.net](https://api.dandanplay.net/) 申请。

---

## Docker 部署

```bash
docker compose --env-file .env.production up -d --build
```

服务:`go-api` · `next-app` · `ws-server` · `postgres`(16)· `nginx`

---

## 架构

```
浏览器
  │  (本地视频文件留在浏览器端 — 视频字节永远不触碰服务器)
  │
Cloudflare
  │
nginx(反向代理 + SSL,源站锁定为 Cloudflare IP 段)
  ├── /              → next-app  (Next.js 16 SSR — 所有面向用户的路由)
  ├── /api/          → go-api    (Go 1.26,chi 路由)
  └── /socket.io/    → ws-server (Node + socket.io — 实时弹幕)

go-api
  ├── PostgreSQL 16  (pg_cron · pg_trgm — 缓存 + 用户数据 + 弹幕)
  ├── AniList GraphQL
  ├── Bangumi API
  ├── 弹弹Play API   (离线交叉校验 oracle,不在请求热路径上)
  └── ACG.rip RSS
```

next-app 通过 SSR 服务所有面向用户的路由:首页、`/anime/:id`、`/seasonal`、`/search`、`/login`、`/library`、`/player`、`/admin`、`/u/:username`。

### 数据管道

1. **AniList 请求** — 季度/搜索/详情查询,700 ms 节流
2. **PostgreSQL** — 唯一主数据库;pg_cron 处理弹幕 TTL 清理
3. **Bangumi 富化** — 通过 river 队列的后台管道,经过四个置信度关卡:
   - **v0** — 原始 AniList 数据,无中文标题
   - **v1** — 通过权威 AniList↔Bangumi id-map 加置信度门控模糊评分器绑定到正确的 Bangumi 条目;低置信度绑定挂起人工审核,从不自动猜测("宁可显示罗马音也不显示错误中文标题")
   - **v2** — Bangumi 评分、角色中文名、分集标题
   - **v3** — 通过 `bgmId` 直接查询的终态中文标题自愈
4. **弹弹Play** — 独立的离线交叉校验 oracle,用于验证绑定;不在请求热路径上
5. **优先队列** — 用户请求的番剧插队到富化队列队首

### 弹幕匹配流程

1. **文件解析** — 从文件名提取集数号(支持 `[字幕组] 标题 - 01.mkv`、`S01E01`、`EP01` 等格式)
2. **Hash 计算** — Web Worker 计算文件前 16 MB 的 MD5(符合弹弹Play 规范)
3. **三阶段匹配** — hash+文件名组合匹配 -> 关键词匹配 -> 逐文件 hash 回退
4. **集数映射** — 弹弹Play 集数 ID 映射到本地文件,支持 OVA/SP(`O1`、`S1` 格式)
5. **弹幕加载** — 通过弹弹Play 集数 ID 加载弹幕,由 Artplayer 弹幕插件渲染

### 认证流程

- `accessToken` — JWT,15 分钟有效期,仅存于内存(不写 localStorage)
- `refreshToken` — JWT,14 天有效期,存于 httpOnly Cookie
- SSR 感知静默刷新:Next.js middleware 服务端读取 cookie 并在渲染受保护页面前刷新 token
- 密码使用 bcrypt 哈希

---

## API 端点

统一响应格式:`{ data }` · `{ data, pagination }` · `{ error: { code, message } }`

| 路由 | 说明 |
|------|------|
| `POST /api/auth/register` | 注册 |
| `POST /api/auth/login` | 登录 |
| `POST /api/auth/refresh` | 刷新 Token |
| `POST /api/auth/logout` | 登出 |
| `GET /api/auth/me` | 当前用户 |
| `GET /api/anime/seasonal` | 季度番剧 |
| `GET /api/anime/search` | 搜索 |
| `GET /api/anime/schedule` | 每周放送 |
| `GET /api/anime/trending` | 热门排行 |
| `GET /api/anime/:id` | 番剧详情 |
| `GET /api/anime/:id/watchers` | 在看用户 |
| `GET /api/anime/torrents` | 磁力搜索 |
| `GET/POST/PATCH/DELETE /api/subscriptions` | 追番 CRUD |
| `GET/POST/DELETE /api/comments` | 剧集评论 |
| `GET/POST/DELETE /api/users/:username/follow` | 关注系统 |
| `GET /api/feed` | 好友动态 |
| `POST /api/dandanplay/match` | 文件 hash/文件名匹配番剧 |
| `GET /api/dandanplay/search` | 在弹弹Play 搜索番剧 |
| `GET /api/dandanplay/episodes/:animeId` | 获取集数列表 |
| `GET /api/dandanplay/comments/:episodeId` | 获取弹幕评论 |
| `WS /` | Socket.IO — 弹幕事件 |

速率限制:所有 `/api` 路由 100 请求 / 15 分钟 / IP。

---

## 测试

```bash
# Go API 测试
cd go-api && go test ./...

# Next.js(next-app)测试 — Bun 内置测试运行器
cd next-app && bun test
```

---

## 致谢

- [AniList](https://anilist.co/) — 番剧元数据与 GraphQL API
- [Bangumi 番组计划](https://bgm.tv/) — 中文标题、评分、角色和分集数据
- [弹弹Play](https://www.dandanplay.com/) — 弹幕匹配 API 与弹幕评论数据库。感谢弹弹Play 开发者的支持。
- [ACG.rip](https://acg.rip/) — 种子 RSS 订阅源
- [bangumi-data](https://github.com/bangumi-data/bangumi-data) — 磁力搜索按番读取蜜柑计划订阅所用的 AniList / Bangumi → 蜜柑计划编号对照(`go-api/internal/mikanidmap/mikan_map.json`,每周自动更新),依 [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) 使用

---

## 贡献

AnimeGo 以 **GNU AGPL-3.0** 许可开源,欢迎贡献。提交 Pull Request 即表示你同意将贡献以 AGPL-3.0 授权,并签署[开发者原创声明(DCO)](DCO)(提交时加 `git commit -s`)。开 PR 前请先阅读 [CONTRIBUTING.md](CONTRIBUTING.md)。注意:磁力/种子元数据搜索子系统**不在外部贡献范围内**——欢迎通过 issue 反馈 bug,但不接受该部分的代码 PR。另见[行为准则](CODE_OF_CONDUCT.md)与[安全政策](SECURITY.md)。

---

## 许可证

AnimeGo 采用 **GNU Affero 通用公共许可证 v3.0(AGPL-3.0)**。你可在该许可下使用、研究、修改与再分发本项目;特别地,若以网络服务形式运行修改版,须向其用户提供修改后的源码。完整条款见 [LICENSE](LICENSE)。© 2026 lawrenceli0228(AnimeGo · animegoclub.com)。该许可仅覆盖本仓库的原创代码,不含其集成的第三方数据/接口(AniList、Bangumi、bangumi-data、MyAnimeList、dandanplay、ACG.rip),后者仍受各自所有者条款约束。