feat(identity): content unit data hygiene — specials, provisional promotion, duplicate merge (DH-1)
- metadata.includeSpecials (default true): full refresh and the language backfill fetch TMDB season 0 (specials) when present, like MP; season 0 stays out of the episode-total ledger - refreshMetadata promotes provisional season/episode units that match the TMDB season/episode layout to confirmed (source=tmdb), keeping ids and all references; non-matching units stay provisional - content_unit.source (migration 0053): release / file / tmdb / user - duplicate units (same entity/kind/season/episode/content type/segment) are merged as a refresh step and via `scripts/identity-cleanup.ts --merge-duplicates` (dry-run by default); keeper = most referenced, then confirmed, then earliest; references auto-discovered via FKs and repointed - ensureUnits takes a per-unit advisory xact lock to stop concurrent lazy creation from producing duplicates - runbooks updated; user DB read-only dry-run: 征途 S01E09/E10 4 units each -> 1 each (6 removed), not applied Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
2847512501
commit
044e718ec6
21 files changed
+734
-111
No files matched your search
@@ -14,13 +14,22 @@
|
||||
|
||||
| 触发 | 拉哪些季的每集信息 | 新建集单元 |
|
||||
|---|---|---|
|
||||
| `POST /media-entities/{id}/refresh`(定时追踪刷新同) | 追踪中订阅的季 ∪ 库里已有集单元的季(最近 8 季) | 只在追踪中的季 |
|
||||
| `POST /media-entities/{id}/refresh?full=1`(详情页「刷新元数据」「更新每集信息」) | 全部正片季(最多 60) | 是 |
|
||||
| `POST /media-entities/{id}/refresh`(定时追踪刷新同) | 追踪中订阅的季 ∪ 库里已有集单元的季(含季 0,最近 8 季) | 只在追踪中的季 |
|
||||
| `POST /media-entities/{id}/refresh?full=1`(详情页「刷新元数据」「更新每集信息」) | 全部季(最多 60);TMDB 有季 0 且 `metadata.includeSpecials` 开时含季 0 | 是 |
|
||||
| 新建档:按 TMDB 候选建作品(订阅建档、搜索 / TMDB 预览页建档、整理向导)、手动指定未登记的 TMDB 身份 | 同 `full=1`(幂等键 `refreshMetadata:created:{id}`) | 是 |
|
||||
|
||||
- 新建档时标题 / 简介当场按语言链取(富详情失败沿用基础详情),每集信息由随后入队的 full 刷新补齐,不用手动刷新。自动识别(RSS)新建的作品不入队 full,仍按追踪刷新。
|
||||
- 刷新是后台任务 `refreshMetadata`(输入 `{mediaEntityId, full?}`),结果里 `titles` 是本次取到的标题 / 语言 / 中文名,`episodes[]` 是各季写入统计(`created` 新建单元数、`changedUnits` 有变化的单元数)。
|
||||
- 增量:来源值和语言都没变的字段不写;只投影有变化的单元。不改编号、状态、路径绑定和资产覆盖。
|
||||
- 增量:来源值和语言都没变的字段不写;只投影有变化的单元。不改编号、路径绑定和资产覆盖。
|
||||
- 季 0(特别篇,DH-1):设置 → 元数据 → TMDB「包含特别篇(季 0)」(`metadata.includeSpecials`,缺省开)。开时 full 刷新和存量回刷
|
||||
在 TMDB 有季 0 时一并拉取每集信息并建季 0 的季 / 集单元(对照 MP);季 0 不进正片季总集数账本,不影响订阅完成判定。关掉后
|
||||
已建的季 0 单元保留,只是不再主动拉取。
|
||||
- 临时转正式(DH-1):刷新时与 TMDB 季集结构对得上的临时(provisional)季 / 集单元转正式(`status = confirmed`、`source = tmdb`),
|
||||
id 与所有引用不变;对不上的(季号不在 TMDB、集号超出该季集数)保持临时。只看本次拉了季详情的季(full 刷新即全部季)。
|
||||
`content_unit.source`(迁移 0053):`release` 资源解析懒建、`file` 整理 / 文件懒建、`tmdb` 按 TMDB 建或转正、`user` 人工;旧行为空。
|
||||
- 重复单元合并(DH-1):每次刷新先把同作品、同类型、同季同集号、同内容类型与分段的多条未退役单元合并为一条(规则同
|
||||
`scripts/identity-cleanup.ts --merge-duplicates`,见 `identity.md`),结果里 `mergedUnits` / `promotedUnits` 是合并 / 转正数。
|
||||
懒建单元(`ensureUnits`)按(作品, 类型, 季, 集)取事务级咨询锁,并发识别不再建出重复。
|
||||
- 存量回刷(FIX-5):维护任务 `metadata.backfill_languages`「元数据语言回刷」在部署后首次启动即运行,对所有已确认 TMDB 身份
|
||||
(active / provisional)的作品逐个跑 `full` 刷新。用独立的 TMDB 客户端串行请求,网络请求间隔 ≥ 500 ms(每秒最多 2 次),
|
||||
请求照常走 `tmdb.request` PG 缓存(对照 MP `@cached`),中断后重跑时已取过的不再请求。进度每 5 秒写进本次运行记录的摘要,
|
||||
@@ -58,3 +67,5 @@ curl -s -H "Authorization: Bearer $KEY" "$BASE/api/v1/media-entities?limit=200"
|
||||
| 标题仍是英文原名 | 作品详情「字段来源与锁」里 `标题` 的来源与语言;是否被锁;设置里的语言链;对作品跑 `full=1` |
|
||||
| 某集没有中文标题 | 该集在 TMDB 各语言都没有译名时保留占位名 / 英文名,属正常;可在每集行手动改并锁定 |
|
||||
| 季里没有集 | 默认刷新不为未追踪的季建集单元,点「更新每集信息」(full) |
|
||||
| 没有特别篇 | TMDB 该剧有没有季 0;设置 `metadata.includeSpecials` 是否关了;对作品跑 `full=1` |
|
||||
| 集还是「临时」 | 该季是否拉过季详情(跑 `full=1`);集号是否超出 TMDB 该季集数(对不上的保持临时,属正常) |
|
||||
Reference in new issue
Block a user