Metadata-Version: 2.5
Name: funflix-api
Version: 1.0.30
Summary: funflix 的 HTTP API 服务：依赖 funflix 提供的领域逻辑，供 funflix-web 反代消费
Project-URL: Organization, https://github.com/farfarfun
Project-URL: Repository, https://github.com/farfarfun/funflix-api
Project-URL: Releases, https://github.com/farfarfun/funflix-api/releases
Author-email: 牛哥 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>
Maintainer-email: 牛哥 <niuliangtao@qq.com>, farfarfun <farfarfun@qq.com>
License-Expression: MIT
License-File: LICENSE
Keywords: fastapi,media,netdisk
Classifier: Framework :: FastAPI
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.12
Requires-Dist: fastapi>=0.117.0
Requires-Dist: funauth[fastapi]>=0.2.0
Requires-Dist: funflix>=1.0.30
Requires-Dist: funsecret>=1.4.96
Requires-Dist: itsdangerous>=2.0
Requires-Dist: uvicorn[standard]>=0.30
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# funflix-api

`funflix` 的 HTTP API 服务：依赖 [funflix](https://github.com/farfarfun/funflix)
提供的数据模型与流水线，把它暴露成一个可常驻运行的服务进程，被
[funflix-web](https://github.com/farfarfun/funflix-web) 反代消费。

数据采集、解析、校验、数据库迁移等领域逻辑都在 `funflix` 里，通过 `funflix` 自己
的 CLI（`funflix collect` / `parse` / `verify` / `worker` / `db upgrade` 等）管理，
本仓库不重复实现，也不依赖它们。

## 快速开始

```bash
scripts/setup.sh install-dev   # 从当前源码构建并安装

# 前台启动（开发用；默认端口 18810）
funflix-api server run --reload
# 接口文档 http://127.0.0.1:18810/docs

# 后台常驻、状态查询、停止、重启（生产用，本地一样适用）
funflix-api server start
funflix-api server status
funflix-api server stop
funflix-api server restart
```

## 命令行

| 命令 | 说明 |
| --- | --- |
| `funflix-api server run` | 前台启动 API 服务，Ctrl-C 停止；`--reload` 开发用 |
| `funflix-api server start` | 后台启动 API 服务：拉一个子进程跑 `server run` |
| `funflix-api server stop` | 停止后台服务（`SIGTERM` 优雅退出） |
| `funflix-api server restart` | 先 `stop` 再 `start` |
| `funflix-api server status` | 查看后台服务是否在跑、PID、安装的版本号 |
| `funflix-api upgrade [版本]` | 升级到最新版或指定版本 |
| `funflix-api rollback <版本>` | 回退到指定版本 |
| `funflix-api uninstall` | 停止服务后卸载包 |

各命令默认监听 `127.0.0.1:18810`，`--host`/`--port`/`--config` 可覆盖；`--config`
缺省时读 `~/.farfarfun/funflix/api/config.toml`（不存在
就用默认值，不算错误）。`start` 写的 PID 文件（`server.pid`）和日志（`server.log`）
都放在同一个配置目录下，跟 `--config` 默认路径统一管理。

源码仓库的 `scripts/setup.sh` 是上述命令的薄封装。`install-dev` 从当前工作树构建并
安装，`install-prod [版本]` 从包索引首次安装（省略版本且已安装时不升级），`publish`
调用 `funbuild build`；服务动作不区分 dev/prod，只运行当前已安装的 CLI。

若配置了 `FUNFLIX_WORKER_ENABLED=true`，服务进程会在启动时额外拉起一个进程内
后台 worker（周期性采集 → 解析 → 校验），行为与 `funflix worker` 等价。

## 配置

配置项与 `funflix` 共用同一套 `FUNFLIX_` 前缀环境变量（见
[funflix README](https://github.com/farfarfun/funflix#配置)），其中与本服务直接
相关的有：

| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `FUNFLIX_DATABASE_URL` | `sqlite+aiosqlite:///~/.farfarfun/funflix/funflix.db` | 切 PG 改成 `postgresql+asyncpg://...`；默认是绝对路径，换目录执行读写的也是同一份库 |
| `FUNFLIX_SESSION_SECRET` | 随机（每次重启变化） | 会话 cookie 签名密钥；多进程/需跨重启保留会话的部署必须固定配置 |
| `FUNFLIX_SESSION_MAX_AGE` | `2592000`（30 天） | 会话 cookie 有效期（秒） |
| `FUNFLIX_SESSION_COOKIE_SECURE` | `false` | 会话 cookie 是否加 Secure 标记；确认部署链路全程 HTTPS 后再打开 |
| `FUNFLIX_REGISTRATION_ENABLED` | `false` | 是否开放自助注册；默认关闭，账号由 `funflix user create` 创建 |
| `FUNFLIX_LOG_LEVEL` | `INFO` | |
| `FUNFLIX_WORKER_ENABLED` | `false` | 是否在服务进程内额外起一个后台 worker |

## 接口

### 采集源

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `POST` | `/api/v1/sources` | 登记采集源，只给 `url` 即可自动识别类型与标识 |
| `GET` | `/api/v1/sources` | 列表 |
| `GET` | `/api/v1/sources/supported` | 当前支持的源类型 |
| `PATCH` | `/api/v1/sources/{id}` | 改配置；回拨 `cursor_message_id` 即可重采历史 |
| `POST` | `/api/v1/sources/{id}/collect` | 立即采集一次 |
| `DELETE` | `/api/v1/sources/{id}` | 删除（已采文本保留） |

### 原始文本

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `POST` | `/api/v1/raw` | 提交一条；命中 `content_hash` 返回 `duplicated=true` |
| `POST` | `/api/v1/raw/bulk` | 批量提交 |
| `GET` | `/api/v1/raw` | 按状态 / 来源翻页，不返回全文 |
| `GET` | `/api/v1/raw/{id}` | 详情，含全文 |

### 查询

搜索与浏览的主体是 **`Work`（一部剧）**，季挂在它下面 —— 搜「大主宰」返回
一条「大主宰（4 季 / 1496 资源）」，而不是几百条同名行。非影视类型
（`book` / `comic` / `other`）默认不进结果，要显式传 `media_type`。

| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `GET` | `/api/v1/works` | 搜索 / 浏览作品，支持 `keyword`、`media_type`、`year`、`valid_only`、`provider` 与翻页 |
| `GET` | `/api/v1/works/{id}` | 作品详情：季列表嵌套，每季带若干资源，标签跨季取并集 |
| `GET` | `/api/v1/media/{id}` | **季**级详情，含该季全部网盘资源与标签 |
| `GET` | `/api/v1/resources` | 按 `provider` / `check_status` 翻页看链接 |
| `GET` | `/api/v1/resources/{id}` | 单条资源 |
| `GET` | `/api/v1/stats` | 流水线各环节记录数与分布（`funflix status` 的 HTTP 版）|
| `GET` | `/healthz` | 健康检查：探一次数据库连通性 |

### 鉴权

`/sources`、`/raw`、`/resources`、`/stats` 整个「运维」区都要求登录（基于会话
cookie）；`/works`、`/works/{id}` 与 `/media/{id}` 保持开放，面向使用者。

先用 `funflix` 的 CLI 建一个账号（自助注册默认关闭，见上面的
`FUNFLIX_REGISTRATION_ENABLED`）：

```bash
funflix user create funflix --password funflix
```

再走登录接口拿会话 cookie：

```bash
curl -c cookies.txt -X POST localhost:18810/api/v1/auth/login \
     -H "Content-Type: application/json" \
     -d '{"username": "funflix", "password": "funflix"}'

curl -b cookies.txt -X POST localhost:18810/api/v1/sources \
     -d '{"url": "https://t.me/s/某频道"}'
```

## 开发

```bash
uv sync
uv run pytest
```

测试依赖本地未发布的 `funflix` 时，先把它装成可编辑依赖：

```bash
uv pip install -e ../funflix
```
