Metadata-Version: 2.4
Name: drama-fingerprint
Version: 0.1.0
Summary: First-episode content IDs and sparse perceptual video matching
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: pdqhash==0.2.8
Requires-Dist: numpy
Requires-Dist: pydantic
Requires-Dist: typer

# Drama Fingerprint

输入第一集视频，提取最多 24 个帧指纹，先通过内容 ID 精确查找，再通过帧指纹模糊匹配已有剧。
命中后保存对应关系；没有候选时，用本次内容 ID 创建新剧。

## 安装与运行

需要 Python 3.12+，并在系统中安装 FFmpeg 和 ffprobe。当前持久化使用 POSIX 文件锁，支持 macOS / Linux。

```bash
python -m pip install drama-fingerprint
drama-fingerprint '/绝对路径/第一集.mp4' --database data/registry.json --json
```

普通查询会创建新剧或学习已命中版本的对应关系。只查询、不修改登记内容：

```bash
drama-fingerprint '/绝对路径/第一集.mp4' --database data/registry.json --lookup-only --json
```

也可使用 `python -m drama_fingerprint`。省略 `--json` 输出简洁文本。

| 返回字段 | 含义 |
| --- | --- |
| `content_id` | 本次视频保留帧指纹序列生成的确定性 ID；空内容为 null |
| `drama_id` | 最终归属的剧 ID；无法确定时为 null |
| `match_method` | `exact` 精确命中、`fuzzy` 模糊命中，或 null |
| `sampled_frames` | 本次保留的帧指纹数，最多 24 |
| `computed_hashes` | 实际计算的指纹数量，含临时时间搜索，可能大于 24 |

退出码：`0` 表示匹配或创建成功；`1` 表示只读查询无匹配；`2` 表示歧义、内容不足或 CLI 参数错误，检查 JSON 的 `status` 或错误输出区分。`--lookup-only` 不改变登记记录，但仍会创建目录和锁文件。

## 识别流程

1. 在开头最多 120 秒内均匀取样，跳过黑白屏等低信息画面，重复画面只保留一张；不足时按整秒补选，最多保留 24 帧。
2. 固定缩放、裁剪和中央遮罩，每帧计算 256 位 PDQ 感知指纹。
3. 按时间顺序序列化指纹与算法版本，用 SHA-256 生成 `content_v1_…`。路径、时间戳、时长和质量分不参与。
4. 查参考内容 ID 和已保存的对应关系；唯一精确命中就返回已有剧 ID。
5. 没有精确命中时，粗搜时间偏移，再局部细搜验证。唯一明确匹配后保存 `内容 ID → 剧 ID`。
6. 没有任何候选时创建新剧；部分匹配、竞争候选或空内容不写入。有效剧情即使只剩一帧，也允许匹配或登记。

每部剧保留最初的参考指纹，额外版本只增加 ID 对应关系。相同指纹序列可以无状态计算同一内容 ID；不同画质、片头偏移等版本归到同一剧，仍需要参考库和对应关系。

数据库是 JSON，通过文件锁和原子替换保存。固定遮罩仅覆盖特定区域，不保证识别任意水印；变速、大面积遮挡和跨剧共享画面也不在已验证保证范围内。这是小规模本地实验实现，模糊搜索会逐部比较参考库。

## Python 调用

```python
from pathlib import Path
from drama_fingerprint import identify

result = identify(
    Path('/绝对路径/第一集.mp4'),
    Path('data/registry.json'),
)
print(result.content_id, result.drama_id, result.match_method)
```

只计算内容 ID，无需数据库：

```python
from pathlib import Path
from drama_fingerprint.content import extract
from drama_fingerprint.registry import content_id

fingerprint = extract(Path('/绝对路径/第一集.mp4')).signature
print(content_id(fingerprint))
```

## 开发与验证

在源码目录中使用 uv 安装锁定的开发依赖：

```bash
uv sync --locked
uv run pytest -q
uv run mypy
uv build
```

- `src/drama_fingerprint/content.py`：当前选帧、去重、补帧和局部时间搜索。
- `src/drama_fingerprint/registry.py`：内容 ID、两级匹配与持久化。
- `src/drama_fingerprint/cli.py`：命令参数和结果输出。
- `tests/`：当前方案的行为测试，包括单帧、末尾空帧、内容 ID、已有登记库、命令行和并发写入。
- `docs/validation.md`：验证基线和迁移范围。

`fingerprint.py` 保存基础模型和指纹距离计算；`temporal.py` 保存帧序列模型和时间对齐。库中 `strategy: "content-search"` 是固定存储格式标记，不是调用参数；已有当前方案登记库可直接使用。

修改取样、裁剪、遮罩、去重或 ID 序列化策略时，应明确内容 ID 的版本变化。运行数据放在 `data/`，本地检查产物放在 `artifacts/`，均不纳入 Git。
