mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-14 10:14:36 +08:00
feat(search): 搜索流事件携带过滤前候选资源数 candidate_items
标题搜索流逐批过滤导致候选数丢失,媒体搜索流最终事件补充候选数,供前端在候选全部被过滤规则淘汰时给出友好提示。Closes jxxghp/MoviePilot-Frontend#646
This commit is contained in:
@@ -976,11 +976,14 @@ class SearchChain(ChainBase):
|
||||
logger.info(f'开始渐进式浏览资源,站点:{sites} ...')
|
||||
|
||||
contexts: List[Context] = []
|
||||
# 记录过滤前的候选资源数,供前端在全部被过滤时给出友好提示
|
||||
candidate_count = 0
|
||||
if rule_groups is None:
|
||||
rule_groups = SystemConfigOper().get(SystemConfigKey.SearchFilterRuleGroups) or []
|
||||
async for event in self.__async_search_all_sites_stream(
|
||||
keyword=title, sites=sites, page=page, mtype=mtype):
|
||||
result = event.pop("items", []) or []
|
||||
candidate_count += len(result)
|
||||
result = await run_in_threadpool(
|
||||
self.__filter_title_search_torrents,
|
||||
torrents=result,
|
||||
@@ -1012,7 +1015,8 @@ class SearchChain(ChainBase):
|
||||
"type": "done",
|
||||
"text": f"搜索完成,共 {len(contexts)} 个资源",
|
||||
"items": [context.to_dict() for context in contexts],
|
||||
"total_items": len(contexts)
|
||||
"total_items": len(contexts),
|
||||
"candidate_items": candidate_count
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -1792,7 +1796,8 @@ class SearchChain(ChainBase):
|
||||
"value": 100,
|
||||
"text": f"过滤匹配完成,共 {len(contexts)} 个资源",
|
||||
"items": final_items,
|
||||
"total_items": len(contexts)
|
||||
"total_items": len(contexts),
|
||||
"candidate_items": len(candidate_contexts)
|
||||
}
|
||||
yield {
|
||||
"type": "done",
|
||||
@@ -1800,6 +1805,7 @@ class SearchChain(ChainBase):
|
||||
"text": f"搜索完成,共 {len(contexts)} 个资源",
|
||||
"items": final_items,
|
||||
"total_items": len(contexts),
|
||||
"candidate_items": len(candidate_contexts),
|
||||
"contexts": contexts
|
||||
}
|
||||
|
||||
|
||||
@@ -123,8 +123,49 @@ def test_async_search_by_title_stream_filters_batches_before_yield(monkeypatch):
|
||||
assert [item["torrent_info"]["title"] for item in append_event["items"]] == [keep.title]
|
||||
assert done_event["type"] == "done"
|
||||
assert done_event["total_items"] == 1
|
||||
assert done_event["candidate_items"] == 2
|
||||
assert [item["torrent_info"]["title"] for item in done_event["items"]] == [keep.title]
|
||||
assert len(filter_calls) == 1
|
||||
assert filter_calls[0]["rule_groups"] == ["exclude-remux"]
|
||||
assert filter_calls[0]["torrent_list"] == [keep, drop]
|
||||
assert filter_calls[0]["mediainfo"] is None
|
||||
|
||||
|
||||
def test_async_search_by_title_stream_reports_candidates_when_all_filtered(monkeypatch):
|
||||
"""
|
||||
标题搜索流在候选全部被过滤时,应在完成事件中报告过滤前的候选资源数。
|
||||
"""
|
||||
chain = _make_chain()
|
||||
drop = TorrentInfo(title="Movie 2026 2160p REMUX", description="")
|
||||
|
||||
async def search_stream(**_kwargs):
|
||||
"""
|
||||
模拟站点页完成后返回一批候选资源。
|
||||
"""
|
||||
yield {
|
||||
"type": "append",
|
||||
"stage": "searching",
|
||||
"items": [drop],
|
||||
"total_items": 1,
|
||||
}
|
||||
|
||||
chain._SearchChain__async_search_all_sites_stream = search_stream
|
||||
chain.filter_torrents = lambda **_kwargs: []
|
||||
_patch_search_filter_rule_groups(monkeypatch, ["exclude-remux"])
|
||||
|
||||
async def collect_events():
|
||||
"""
|
||||
收集标题搜索流全部事件。
|
||||
"""
|
||||
return [
|
||||
event
|
||||
async for event in chain.async_search_by_title_stream(title="Movie")
|
||||
]
|
||||
|
||||
events = asyncio.run(collect_events())
|
||||
|
||||
done_event = events[-1]
|
||||
assert done_event["type"] == "done"
|
||||
assert done_event["total_items"] == 0
|
||||
assert done_event["candidate_items"] == 1
|
||||
assert done_event["items"] == []
|
||||
|
||||
Reference in New Issue
Block a user