mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 15:38:19 +08:00
refactor: unify media capability contracts
This commit is contained in:
+28
-16
@@ -2972,15 +2972,17 @@
|
||||
"version": 1
|
||||
},
|
||||
"async_obtain_images": {
|
||||
"aggregation": "legacy",
|
||||
"aggregation": "pipeline_relay",
|
||||
"error_policy": "isolate_provider",
|
||||
"execution": "sync_or_async",
|
||||
"family": "media-recognition",
|
||||
"input_contract": "MediaRecognitionKeywordArguments",
|
||||
"input_contract": "MediaInfo",
|
||||
"plugin_short_circuit": true,
|
||||
"public_to_plugins": true,
|
||||
"required_parameters": [],
|
||||
"result_contract": "MediaRecognitionProviderResult",
|
||||
"required_parameters": [
|
||||
"mediainfo"
|
||||
],
|
||||
"result_contract": "MediaInfo | None",
|
||||
"result_shape": "any",
|
||||
"supports_async": true,
|
||||
"supports_sync": true,
|
||||
@@ -2988,15 +2990,22 @@
|
||||
"version": 1
|
||||
},
|
||||
"async_recognize_media": {
|
||||
"aggregation": "legacy",
|
||||
"aggregation": "first_non_empty",
|
||||
"error_policy": "isolate_provider",
|
||||
"execution": "sync_or_async",
|
||||
"family": "media-recognition",
|
||||
"input_contract": "MediaRecognitionKeywordArguments",
|
||||
"input_contract": "MediaRecognitionRequest",
|
||||
"plugin_short_circuit": true,
|
||||
"public_to_plugins": true,
|
||||
"required_parameters": [],
|
||||
"result_contract": "MediaRecognitionProviderResult",
|
||||
"required_parameters": [
|
||||
"cache",
|
||||
"episode_group",
|
||||
"media_id",
|
||||
"media_source",
|
||||
"meta",
|
||||
"mtype"
|
||||
],
|
||||
"result_contract": "MediaInfo | None",
|
||||
"result_shape": "any",
|
||||
"supports_async": true,
|
||||
"supports_sync": true,
|
||||
@@ -3036,16 +3045,19 @@
|
||||
"version": 1
|
||||
},
|
||||
"async_search_medias": {
|
||||
"aggregation": "legacy",
|
||||
"aggregation": "ordered_list_merge",
|
||||
"error_policy": "isolate_provider",
|
||||
"execution": "sync_or_async",
|
||||
"family": "media-discovery",
|
||||
"input_contract": "MediaDiscoveryKeywordArguments",
|
||||
"family": "media-recognition",
|
||||
"input_contract": "MediaSearchRequest",
|
||||
"plugin_short_circuit": true,
|
||||
"public_to_plugins": true,
|
||||
"required_parameters": [],
|
||||
"result_contract": "MediaDiscoveryProviderResult",
|
||||
"result_shape": "any",
|
||||
"required_parameters": [
|
||||
"media_source",
|
||||
"meta"
|
||||
],
|
||||
"result_contract": "list[MediaInfo]",
|
||||
"result_shape": "list",
|
||||
"supports_async": true,
|
||||
"supports_sync": true,
|
||||
"timeout_policy": "caller_budget",
|
||||
@@ -4899,7 +4911,7 @@
|
||||
"version": 1
|
||||
},
|
||||
"obtain_images": {
|
||||
"aggregation": "legacy",
|
||||
"aggregation": "pipeline_relay",
|
||||
"error_policy": "isolate_provider",
|
||||
"execution": "sync_or_async",
|
||||
"family": "media-recognition",
|
||||
@@ -5121,7 +5133,7 @@
|
||||
"meta"
|
||||
],
|
||||
"result_contract": "list[MediaInfo]",
|
||||
"result_shape": "any",
|
||||
"result_shape": "list",
|
||||
"supports_async": true,
|
||||
"supports_sync": true,
|
||||
"timeout_policy": "caller_budget",
|
||||
|
||||
@@ -167,6 +167,43 @@ def test_system_signature_relay_passes_previous_result() -> None:
|
||||
assert dispatcher.dispatch("execute") == {"value": 2}
|
||||
|
||||
|
||||
def test_explicit_pipeline_contract_relays_previous_result() -> None:
|
||||
"""图片补全契约应按优先级把上一 provider 结果交给下一 provider。"""
|
||||
class ImageModule:
|
||||
"""在统一媒体对象上记录当前图片 provider。"""
|
||||
|
||||
def __init__(self, name: str, priority: int) -> None:
|
||||
"""保存 provider 名称和优先级。"""
|
||||
self._name = name
|
||||
self._priority = priority
|
||||
|
||||
def get_name(self) -> str:
|
||||
"""返回测试模块名。"""
|
||||
return self._name
|
||||
|
||||
def get_priority(self) -> int:
|
||||
"""返回测试优先级。"""
|
||||
return self._priority
|
||||
|
||||
def obtain_images(self, mediainfo: dict) -> dict:
|
||||
"""追加当前 provider 名称并返回同一媒体结果。"""
|
||||
return {
|
||||
**mediainfo,
|
||||
"providers": [*mediainfo.get("providers", []), self._name],
|
||||
}
|
||||
|
||||
dispatcher, _, _, _ = _dispatcher(
|
||||
modules=[
|
||||
ImageModule("fanart", 20),
|
||||
ImageModule("tmdb", 10),
|
||||
]
|
||||
)
|
||||
|
||||
assert dispatcher.dispatch("obtain_images", mediainfo={}) == {
|
||||
"providers": ["tmdb", "fanart"]
|
||||
}
|
||||
|
||||
|
||||
def test_first_non_empty_contract_stops_legacy_signature_relay() -> None:
|
||||
"""显式首个非空契约不得再把结果交给后续宿主 provider 改写。"""
|
||||
class FirstModule:
|
||||
|
||||
@@ -214,6 +214,23 @@ def test_downloader_action_contracts_freeze_shared_provider_signatures() -> None
|
||||
assert contract.required_parameters == parameters
|
||||
|
||||
|
||||
def test_sync_and_async_media_capabilities_share_contracts() -> None:
|
||||
"""同一识别能力的同步与异步入口必须复用完全相同的契约。"""
|
||||
for sync_method, async_method in (
|
||||
("recognize_media", "async_recognize_media"),
|
||||
("search_medias", "async_search_medias"),
|
||||
("obtain_images", "async_obtain_images"),
|
||||
):
|
||||
assert get_module_method_contract(sync_method) is get_module_method_contract(
|
||||
async_method
|
||||
)
|
||||
|
||||
assert (
|
||||
get_module_method_contract("obtain_images").aggregation
|
||||
is ModuleResultAggregation.PIPELINE_RELAY
|
||||
)
|
||||
|
||||
|
||||
def test_attachment_result_diagnostics_distinguish_bytes_and_strings() -> None:
|
||||
"""附件契约应区分二进制内容和可展示字符串,偏差仍仅供诊断。"""
|
||||
assert diagnose_module_result("download_qq_file_bytes", b"content") == ()
|
||||
|
||||
Reference in New Issue
Block a user