docs(plan): on-demand site detail page parsing (HHClub details.php)

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-29 15:13:35 +08:00
1 parent 94fd48bff2
commit ac478673bc
39 files changed
+2446 -339

No files matched your search

+1
View File
@@ -164,3 +164,4 @@ NFO / 图片投影、身份巡检与挂载根因聚合、HTTP 失败自动切浏
- **短剧等 TMDB 缺失作品的自助补全(META-1,用户 2026-09-29)**:能力 `metadata.enrich`(propose):Brave 搜索 + 受限抓取(平台页/百科类)→ 模型抽字段(标题/别名/年份/平台/集数/简介/海报 URL)带来源与证据等级 → 写本地临时实体与字段级元数据(source=web/agent,可锁可改,不冒充 TMDB);触发:池条目"补全"、订阅/规则命中但 TMDB 无结果、Agent 委托;按归一化标题缓存、每日预算;后续 TMDB 收录由身份巡检补映射;不改路径不下载。DEC-1 之后实施。
- **Agent 快捷面板与会话排版(AGUI-2,用户 2026-09-29)**:抽屉内「当前/历史」Tab,历史列表可搜索筛选并在抽屉内打开(可续跑),"在页面打开"次要;会话页:阅读列宽、同回合分组、工具卡默认一行折叠、步骤清单改右侧栏/顶部折叠条不再 sticky 遮挡、预算在右栏底、长汇报折叠、输入区固定底部。
- **凭据统一注入浏览器(BR-2,用户 2026-09-29)**:打开站点浏览器会话时服务端把账号 Cookie(手填或自动登录得到的)注入 camofox profile,截图/桌面模式无需再登录;交还/关闭/定时从浏览器回写 Cookie 到凭据存储,HTTP 抓取/RSS/快照共用;账号显示登录态来源与时间;Cookie 不进日志/模型。
- **站点详情页抓取(用户 2026-09-29,憨憨 details.php)**:`SiteReleaseDetail` 扩展简介/MediaInfo/截图(经图片代理)/IMDb·豆瓣·TMDB ID/文件清单/标签/上传者;只按需抓(点开详情或评估缺字段),kv_cache 24 小时;详情抽屉展示;IMDb/豆瓣 ID 作为强证据进身份解析(TMDB find)。
+21 -8
View File
@@ -1,18 +1,27 @@
# 全局搜索运行手册(W9-G)
# 全局搜索运行手册(W9-G、SEARCH-2)
实现:`apps/server/src/modules/search/`(`routes.ts` 端点、`releases.ts` 跨站搜索/缓存/筛选/叠加、`media.ts` 本地作品与 TMDB、`actions.ts` 页面与操作清单、`dispatch.ts` 进程内转调);合同 `packages/contracts/src/search.ts`;测试 `apps/server/test/search/`。
实现:`apps/server/src/modules/search/`(`routes.ts` 端点、`releases.ts` 跨站搜索/缓存/筛选/叠加、`media.ts` 本地作品与 TMDB、`works.ts` 作品合并/库存对比/查询词、`actions.ts` 页面与操作清单、`dispatch.ts` 进程内转调);合同 `packages/contracts/src/search.ts`;测试 `apps/server/test/search/`(`search2.test.ts` 为两阶段)。
## 两阶段流程(SEARCH-2)
1. **作品**:`GET /search?scope=all|media` 只查本地账本 + TMDB,合并去重成 `media.works`(同一 TMDB 身份只出现一次,本地在前),每部带库存对比 `inventory`。**不请求任何站点**。
2. **资源**:用户在作品卡或作品详情页点「搜索资源」→ 选站点(默认 / 全部 / 指定,本机记住上次选择)→ `GET /search/releases?entityId=|tmdb=&sites=`,服务端生成查询词。
3. **订阅**只在作品详情页:本地作品 `/console/library/:id`,仅 TMDB 的 `/console/media/tmdb/:type/:id`。搜索结果与 ⌘K 面板没有订阅按钮;⌘K 选中作品打开详情页。
## 端点
| 端点 | scope | 说明 |
|---|---|---|
| `GET /search?q=&scope=all\|media\|releases\|actions&limit=` | read | 聚合搜索。`limit` 是每组条数(1–50,默认 10)。不查的组返回 `null` |
| `GET /search/releases?q=&sites=&resolution=&source=&minSize=&maxSize=&free=&hr=&sort=&order=&refresh=&cursor=&limit=` | read | 资源结果页。与 `/search` 共用缓存 |
| `GET /search?q=&scope=all\|media\|releases\|actions&limit=` | read | 聚合搜索。`limit` 是每组条数(1–50,默认 10)。不查的组返回 `null`。`all` = 作品 + 操作,不查站点;资源只在 `scope=releases` 时查(站点按默认设置) |
| `GET /search/releases?q=\|entityId=\|tmdb=&sites=&siteFilter=&resolution=&source=&minSize=&maxSize=&free=&hr=&sort=&order=&refresh=&cursor=&limit=` | read | 资源结果页。`q` / `entityId` / `tmdb`(`movie:157336`)至少给一个,都没有 422。与 `/search` 共用缓存 |
| `GET /media/tmdb/{type}/{id}?include=mediaServers` | read | 仅 TMDB 作品详情:TMDB 信息与季列表、本地对比、是否已建档(`localEntityId`)、查询词;`include=mediaServers` 同时转调 Emby(最慢约 8s)。没配 TMDB 412,TMDB 没有该条目 404 |
| `GET /inventory/media-servers?mediaEntityId=\|tmdbType=&tmdbId=` | read | Emby 对比。按 TMDB 条目查时本地已建档就按作品查,否则只用这一个 TMDB ID,`mediaEntityId = null` |
| `POST /search/releases/{siteId}/{siteReleaseId}/plan {slotId?, entityId?, note?}` | organize.execute | 从搜索结果下载,走 `POST /acquisition-plans` 同一流程 |
### `/search` 响应
- `media.local`:本地作品。召回方式有三种:`title_alias.normalized` 精确命中(score 1)、前缀命中(0.9)、trgm 相似度 ≥ 0.3;另外按作品名/原名前缀补召回没有别名的手建作品。每条带 `slotCounts`(版本目标各状态计数)、`versionCount`、`downloadingCount`、`subscribed`。
- `media.works`:作品卡(SEARCH-2)。`key` 为 `local:<id>` 或 `tmdb:<type>:<id>`;海报 / 简介本地缺时用 TMDB 候选的。`inventory.state`:`not_collected` 未收录(本地没有)/ `tracked` 已建档但没有入库版本 / `owned` 已有版本 / `downloading` 正在下载;另有 `versionCount`、`slotTotal` / `slotSatisfied`、`missing`(未满足的版本目标)、`subscribed`。Emby 另查(未知 ≠ 缺失)。
- `media.tmdb`:TMDB 候选。`configured=false` 表示没配置 TMDB,不代表没有结果;查询失败时 `error` 有值、`items` 为空。已有本地作品接受了该 TMDB 身份时,`localEntityId` 给出本地作品 ID(合并过的作品跟到最终作品)。
- `releases`:跨站资源,只返回前 `limit` 条(按默认排序),`total` 是全部条数。覆盖信息见下文。
- `actions`:页面与操作跳转的静态清单,按 IA-3 定稿导航分组(定时任务在任务页"计划"Tab;运行状态页路由确定后补)。标题前缀命中排在前面,关键词或路径包含查询词也算命中。
@@ -20,12 +29,13 @@
## 跨站资源搜索
- **站点范围**:`enabled=true` 且未删除的站点,全部参与,`sites=` 筛选在拿到结果之后才做。不支持搜索或没有适配器定义的站点记为 `skipped`。
- **站点范围**:只查选中的站点(`enabled=true` 且未删除)。`sites=` 给了就只查这些;没给按 `settings.search.defaultSiteMode`:`default` = 站点「默认参与搜索」(`site.search_default`,迁移 0048,缺省 true)的站点,`all` = 全部启用站点,`custom` = `settings.search.siteIds` 里仍启用的站点。`siteFilter=` 只筛选结果、不改查询的站点(前端站点分面用)。不支持搜索或没有适配器定义的站点记为 `skipped`。
- **查询词**:给了 `q` 只用 `q`;按作品(`entityId` / `tmdb`)时由服务端生成,最多 2 个:原名(电影且原名是拉丁字母时带年份,如 `Interstellar 2014`)+ 译名(归一化后与原名不同才加);剧集不带年份。每个站点每个词查一次,同站结果按站点资源 ID 去重,响应 `queries` 给出实际查询词。
- **限流**:沿用 sources-identity 运行时里每个站点的共享限流器,和快照、工作台、详情、下载用的是同一个令牌桶。
- **超时**:单站 `AbortSignal.timeout(15s)`,限流等待也算在内;超时记为 `timeout`。
- **站点级并发**:同时最多搜索 4 个站点(`createSearchModule({ siteConcurrency })`)。
- **并发合并**:相同查询词(去首尾空白、小写、合并空格)同时只会发一轮站点请求,后到的请求等同一个结果,`coalesced=true`。
- **短缓存**:结果缓存 30s,命中时 `cached=true`。只要有一个站点成功就缓存;全部失败不缓存。`refresh=true` 跳过缓存,但仍然参与合并。进程内缓存,多个 api 实例之间不共享。
- **并发合并**:按(站点, 查询词)合并,查询词去首尾空白、小写、合并空格;同一对同时只会发一次请求,后到的等同一个结果,`coalesced=true`。
- **短缓存**:按(站点, 查询词)缓存 30s,换站点选择时已查过的站点直接复用;全部命中时 `cached=true`。失败 / 超时也缓存,但一轮里新查的站点全部失败时不缓存。`refresh=true` 跳过缓存,但仍然参与合并。进程内缓存,多个 api 实例之间不共享。
- **部分失败**:任一站点失败或超时,`partial=true`,`failedSites` 列出这些站点(status 为 `failed` 或 `timeout`,附已脱敏的错误)。其它站点的结果照常返回。`sites` 是每个站点的覆盖情况:status、count、tookMs、error。
- **落库**:搜索结果按 site + 站点资源 ID upsert 到资源表(`source_release`),新资源照常发 `release.observed`,进入识别流水线,和工作台实时补查是同一条路径。`sourceReleaseId` 给站点工作台、下载、委托 Agent 用。落库失败只记警告,仍然返回结果,此时 `sourceReleaseId=null`。搜索不写 `site_snapshot`,也不改站点健康状态。
@@ -57,7 +67,10 @@
## 前端要点
- 命令面板用 `scope=media` 和 `scope=actions` 快速出结果(毫秒级,TMDB 首次查询约 1 秒)。资源组单独请求 `scope=releases`,或者直接跳到结果页 `/console/search?q=`。最慢的站点可能要等到 15s 超时。
- 搜索页 `/console/search?q=` 第一阶段:作品卡(海报 / 年份 / 类型 / 简介 + 库内状态;有媒体服务器实例时卡上另查 Emby)与操作。「按关键词搜资源」用原关键词搜站点(资源标题类查询)。
- 第二阶段 `/console/search?q=&work=&in=&t=`:`work` = `local:<id>` / `tmdb:<type>:<id>` / `q:<关键词>`,`in` = 选中的站点 ID,`t` = 显示的作品名。站点选择弹层的初始值 = 本机上次选择(localStorage `umt.search.sitePick`)> `settings.search`;站点页「搜索站点」改全局默认,站点卡 / 编辑里改「默认参与搜索」。
- 命令面板用 `scope=media` 和 `scope=actions` 快速出结果(毫秒级,TMDB 首次查询约 1 秒);选中作品打开详情页,最后一项进搜索页。面板不查站点、不订阅。
- 作品详情页有「订阅」(订阅表单预填作品;TMDB 条目保存时隐式建档)、「搜索资源」、「建档不订阅」(仅 TMDB 的作品)。
- 资源行的操作和站点工作台一致。`sourceReleaseId` 可以直接给工作台详情和委托 Agent 用;下载用 `POST /search/releases/{siteId}/{siteReleaseId}/plan`。
- `partial=true` 时列出 `failedSites`,提示哪些站点没有结果。`cached` / `fetchedAt` 用来标注数据时间,"刷新"按钮带 `refresh=true`。
+5
View File
@@ -65,6 +65,11 @@
注:定时任务 key 只允许小写(注册表校验),所以规格里的 `sites.userData` / `sites.healthCheck` 落为下划线形式。
## 默认参与搜索(SEARCH-2)
`site.search_default`(迁移 0048,缺省 true;`POST/PATCH /sites` 的 `searchDefault`):搜资源时「默认站点」集合 = 启用且打开此项的站点。站点卡底部和编辑面板都有开关。
全局默认范围在 `settings.search`(站点页「搜索站点」):`defaultSiteMode = default | all | custom`,`custom` 时 `siteIds` 生效;`GET /search/releases` 不带 `sites` 时按它选站点。详见 `docs/runbook/search.md`。
## 站点默认(`settings.sites`)
| 字段 | 缺省 | 说明 |
+5
View File
@@ -6,6 +6,11 @@
前端:`apps/web/app/routes/console/subscriptions.tsx`(`?view=calendar`)、`components/subscriptions/{calendar.tsx, actions.tsx, subscription-form.tsx, model.ts}`。
设计:`docs/design/subscriptions.md` §1–§3、§5;规则组见 `docs/runbook/rules.md`;W5-C 账本/完成/搜索见 `docs/plan/05-subscription-gap.md` §4。
## 入口(SEARCH-2)
订阅只从作品详情页发起:本地作品 `/console/library/:id`、仅 TMDB 的 `/console/media/tmdb/:type/:id`,「订阅」打开订阅表单并预填作品(`SubscriptionFormDialog initialTarget`)。
TMDB 条目保存时先调 `POST /files/entity-from-candidate` 建档(同一 TMDB id 复用),再按作品建订阅。搜索结果与 ⌘K 面板不再有订阅按钮;订阅页「新建订阅」仍可搜作品。
## 数据
| 列 | 含义 |