种子浏览与媒体发现模块(种子详情 / 推荐 / 探索)
第三批移植原项目最后三个最大的站点/媒体功能。两个架构决策(已与用户确认锁定):
- 按需联动(on-demand join):推荐/探索展示媒体元数据(海报/评分/简介),每项「在站点中查找」按钮复用既有
POST /api/v1/search(把标题当 keyword)实时返回各站命中种子——不预过滤、无 N×M 预算、零后端改动。 - 信息流 vs 搜索:推荐 = 热门/趋势信息流(豆瓣热映/TMDB trending/Bangumi 热门);探索 = 关键词/分类跨源搜索媒体。两者共享「在站点查找」联动基础设施。
功能覆盖
| 模块 | 能力 | 数据来源 |
|---|---|---|
| 种子浏览 | 跨站搜索 → 点击单种查看详情页(简介 HTML/截图墙/MediaInfo/外链 ID/技术规格) | details.php(NexusPHP HTML)/ MTeam /api/torrent/detail |
| 推荐 | 多源热门/趋势信息流(豆瓣热映·即将上映·剧集 / TMDB trending·popular / Bangumi 热门),按源+分类筛选,一键在站点查找 | 豆瓣 frodo /api/v2/{movie,tv}/hot / TMDB /trending / Bangumi /v0/search/subjects?sort=heat |
| 探索 | 关键词跨源搜索媒体(豆瓣/TMDB/Bangumi),按源筛选,一键在站点查找 | 复用既有 MediaChain.Search(跨源合并 + PosterCache) |
后端结构
种子详情(TorrentDetail)— capability 范式
internal/site/
├── adapter.go TorrentDetailCapable(FetchTorrentDetail,type-assert 判断)
├── types.go DetailDef(fields map,空则 EnsureDefaults 填 NexusPHP 默认)+ TorrentDetail DTO
├── defaults.go DefaultNexusPHPDetail(details.php 选择器:#kdescr 简介/screenshots/imdb/douban/download)
└── seedetail.go ParseTorrentDetail(复用 Extract/ApplyFilters/ResolveURL)+ extractMulti(screenshots/file_list 多值收集)
internal/site/schemas/seedetail.go
├── NexusPHP.FetchTorrentDetail 直接 GET 绝对 detailURL(不经 joinURL)+ loggedOut + ParseTorrentDetail
└── MTeam.FetchTorrentDetail POST /api/torrent/detail {sn}(sn 从 detailURL 末段解析)+ mtSNFromDetailURL
internal/chain/site.go SiteChain.TorrentDetail(sameHost SSRF 守卫 → 10min 缓存 → dispatchAdapter → 类型断言)
internal/api/v1/site.go SiteHandler.TorrentDetail(?url=)关键点:① detailURL 为绝对 URL(前端透传种子的 detail_url),chain 先做 sameHost 守卫(比对最后两段注册域,兼容 api.m-team.cc/kp.m-team.cc 同站不同子域;socket 层 ssrf=true 已拦私网 IP,此为授权层防 A 站 cookie 探测 B 站)。② 顺手修 MTeam bug:parseMTTorrent 原本不设 DetailURL,现补 kp.m-team.cc/detail/<id>(同时承载 id 供详情抓取解析 sn)。③ 选择器为代表默认,各站 DOM 差异需 /sites/inspect 实测校准。
Bangumi 客户端 — 新 MediaSource
internal/media/bangumi.go Bangumi(v0 API,可选 Bearer token)+ Search(/v0/search/subjects filter.type [2,6]) + Detail(/v0/subjects/{id}) + Trending(sort=heat)
internal/media/media.go TrendingCapable 接口(可选,不破坏 base MediaSource;MusicBrainz 跳过)
internal/media/{douban,tmdb,bangumi}.go 各源实现 Trending(豆瓣 /movie/hot·/coming·/tv/hot、TMDB /trending/{movie,tv}/week·/movie/popular、Bangumi heat)
internal/media/douban.go 豆瓣 frodo **双鉴权**:微信免签(默认主策略)+安卓 HMAC-SHA1 `_sig`/`_ts`(参考 MoviePilot `apiv2.py`,bestyize 真实抓包向量验证);`DOUBAN_SECRET` 非空→切签名为主,鉴权类失败(401/403/`sig`·`api_key` 的 400)自动互切兜底关键点:① Bangumi 无需扩 MediaInfo——既有 Source/Type 字段够用(Bangumi→Source="bangumi"、动漫/三次元→TypeTV,前端按 source 区分)。② TrendingCapable 是可选接口(仿 *Capable 范式),推荐链类型断言跳过不支持者(MusicBrainz)。③ 图片反代 mediaImageHosts 加 lain.bgm.tv/bgm.tv(不防盗链,复用既有 utls mediaClient)。④ 配置 BANGUMI_TOKEN(可选,提升速率;空走匿名配额)。
寸光集(短剧)源 — 新 MediaSource + CategoryCapable
internal/media/cunguangji.go 寸光集(dj.hxpt.org / 寸光集 D:\www.ptang.top)短剧源:
Search/Trending(/api/explore?sort=&q=&page=) + Detail(/api/explore/<id>),短剧→TypeTV
internal/media/media.go CategoryCapable 接口(可选:声明源支持的 category;未实现者=支持全部,向后兼容)
internal/media/{douban,tmdb,bangumi,cunguangji}.go 各源实现 Categories()(豆瓣 movie/coming/tv、TMDB +anime/popular、Bangumi +real、寸光集=duanju)
internal/chain/recommend.go 扇出循环按 CategoryCapable 跳过不支持当前分类的源
站点 D:\www.ptang.top 新增公开只读 API /api/explore(列表/搜索) + /api/explore/<id>(详情),复用 lib/queries.ts;封面 lib/cover.ts.toExploreCoverSrc 返回站内相对路径 /api/img?url=…(站点自带反代绕防盗链),PTPatronus 端用配置 base 拼绝对地址(nginx 反代下 req.nextUrl.origin 不可靠,故不依赖站点感知自身公网域名)关键点:① 寸光集是自有外部站点(短剧点评社区),无列表 JSON API,故在站点侧加只读 /api/explore(不污染既有 SSR/管理 API)。② CategoryCapable 让「短剧」分类只由寸光集响应——豆瓣/TMDB/Bangumi 不会各自回退默认热映污染;顺带修复「anime 让豆瓣回退热映」之类既有回退污染。③ 短剧归 TypeTV(影视/视频桶),Categories()=['duanju'],故默认分类(movie)下不参与。④ 配置 CUNGUANGJI_BASE(默认 https://dj.hxpt.org,可指本地实例联调)。⑤ 站点需重新发布到 dj.hxpt.org 才生效;前端选「寸光集」自动联动分类到「短剧」(互斥,避免空态)。
推荐链(RecommendChain)— 仅内存缓存
internal/chain/recommend.go RecommendChain(仿 AssessmentChain 但更简:仅内存缓存 30min,无 DB 持久化——趋势是临时数据)
RecommendFeeds(source,category,page,force):并发扇出(semaphore 4、每源 15s 超时、类型断言 TrendingCapable、容错合并)+ 缓存
internal/api/v1/media.go MediaHandler.Recommend(?source=&category=&page=&refresh=1)+ Discover(复用 MediaChain.Search)
internal/app/app.go mediaSources 加 Bangumi + 构造 RecommendChain 注入 api.Deps关键点:推荐链不落库(趋势日级变化,内存 30min 缓存足够),比考核的 3 级缓存简单很多。
探索 + find-on-sites — 零新 chain/端点
- 探索:
MediaHandler.Discover直接调既有MediaChain.Search(chain/media.go,已跨源合并 + PosterCache),source参数做返回后过滤。无新 chain。 - 在站点查找(find-on-sites):前端「在站点查找」按钮直接
POST /api/v1/search?keyword=<标题>(既有SiteChain.SearchSites,已并发扇出 + 容错 + 返回各站site.Torrent[])。零后端改动。
数据流(一条完整链路)
推荐页 → GET /media/recommend → 各源 Trending → []MediaInfo(海报走 /media/image 反代)→ 点项「在站点查找」→ POST /api/v1/search(标题当 keyword)→ 各站命中种子 → 点种子 → GET /sites/:id/torrent/detail?url= → TorrentDetail。全链路复用既有 infra(httpclient SSRF、ElementQuery 选择器引擎、dispatchAdapter、mediaImageHosts 反代)。
API
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /media/recommend | 推荐信息流(?source=douban|tmdb|bangumi|cunguangji&category=movie|tv|anime|coming|duanju&page=&refresh=1) |
| GET | /media/discover | 跨源媒体搜索(?keyword=&year=&source=,复用 MediaChain.Search) |
| GET | /sites/:id/torrent/detail | 种子详情页(?url=<详情页绝对 URL>,sameHost 守卫) |
| POST | /search | 跨站搜索(复用既有,find-on-sites 联动用) |
均挂 guarded 组。无新 cron(推荐是请求时实时拉取 + 内存缓存)。
前端
- Web:3 视图(
SeedBrowse.vue跨站搜索表+详情抽屉渲染简介HTML/截图墙/MediaInfo /Recommend.vue卡片网格+源/分类 chip+在站点查找弹窗 /Discover.vue搜索框+源 chip+媒体卡+在站点查找)+ 3 api(seedbrowse/recommend/discover)+ 路由 + 导航(GlassIcon 加 seedbrowse/recommend/discover 字形)。海报统一<MediaPoster>。 - Mobile:3 feature 页(seedbrowse/recommend/discover)+ 3 api + 账户页入口(不加底部 tab,7 槽已满)。新增共享 widget
widgets/media_poster.dart(首批需海报的 Flutter 页:豆瓣走反代、TMDB/Bangumi 直连、失败回退 emoji)+widgets/find_on_sites_sheet.dart(推荐/探索共用「在站点查找」抽屉)。
测试
go test ./internal/site/... ./internal/media/... ./internal/chain/...:
- 种子详情:NexusPHP details.php fixture(简介/截图[]/IMDBID/DoubanID/DownloadURL 绝对化)+ MTeam
/api/torrent/detailJSON 映射 +parseMTTorrent补 DetailURL 回归 +mtSNFromDetailURL末段/query 抽 id。 - Bangumi:search/detail fixture(Source/SourceID/PosterURL/年容差)+ Trending。
- 推荐:豆瓣/TMDB/Bangumi Trending 端点 fixture + TMDB 未配置返 ErrNotConfigured。
- RecommendChain:两假源 TrendingCapable 合并 + 二次命中缓存(源不重复调用)+ force 绕过缓存 + source 过滤 + 无 Trending 能力源跳过。
待校准(真实环境)
按 [[pt-site-data-references]] 选择器校准工作流,配真实站点后逐项钉值:
- 种子详情:NexusPHP
Detail.Fields各站选择器(可用POST /api/v1/sites/inspect对真实details.php校准)。 - MTeam
/api/torrent/detail端点路径/sn参数名/字段名(description/body/mediaInfo/status);detailURL→sn 解析。 - Bangumi
/v0/search/subjects的rating形态(search 扁平rating_scorevs detailrating.score)。 - 豆瓣 frodo 热门路径与响应结构(
/api/v2/movie/hot等)。 - 各源
Trending端点字段。