diff --git a/package.v2.json b/package.v2.json index a967d52b..22553f08 100644 --- a/package.v2.json +++ b/package.v2.json @@ -218,11 +218,12 @@ "name": "Plex演职人员刮削", "description": "实现刮削演职人员中文名称及角色。", "labels": "Plex,刮削", - "version": "2.2", + "version": "2.3", "icon": "https://raw.githubusercontent.com/InfinityPacer/MoviePilot-Plugins/main/icons/plexpersonmeta.png", "author": "InfinityPacer", "level": 1, "history": { + "v2.3": "修复开启清理缓存后插件加载失败的问题", "v2.2": "兼容 MoviePilot v2.13.x 的中文简繁转换接口,避免插件加载失败", "v2.1": "修复MoviePilot缓存接口变更导致无法启动的问题", "v2.0": "兼容MoviePilot的Redis缓存方案,需要MoviePilot v2.2.3+", diff --git a/plugins.v2/plexpersonmeta/README.md b/plugins.v2/plexpersonmeta/README.md index ab9fba00..25093ba1 100644 --- a/plugins.v2/plexpersonmeta/README.md +++ b/plugins.v2/plexpersonmeta/README.md @@ -2,7 +2,7 @@ 实现刮削演职人员中文名称及角色 -> 当前版本:v2.2,兼容 MoviePilot v2.13.x 的中文简繁转换接口,避免插件加载失败。 +> 当前版本:v2.3,修复开启清理缓存后插件加载失败的问题。 - **技术难点**:Plex 的 API 实现较为复杂,特别是在处理关联演职人员的 `tagKey`,我在尝试为 `actor.tag.tagKey` 赋值时遇到了问题。如果您对此有所了解,请不吝赐教,欢迎通过在项目的 GitHub 页面新增一个 issue 与我联系,我将非常感谢您的反馈和帮助。 diff --git a/plugins.v2/plexpersonmeta/__init__.py b/plugins.v2/plexpersonmeta/__init__.py index 90ca3018..8e7a3e27 100644 --- a/plugins.v2/plexpersonmeta/__init__.py +++ b/plugins.v2/plexpersonmeta/__init__.py @@ -30,7 +30,7 @@ class PlexPersonMeta(_PluginBase): # 插件图标 plugin_icon = "https://raw.githubusercontent.com/InfinityPacer/MoviePilot-Plugins/main/icons/plexpersonmeta.png" # 插件版本 - plugin_version = "2.2" + plugin_version = "2.3" # 插件作者 plugin_author = "InfinityPacer" # 作者主页 diff --git a/plugins.v2/plexpersonmeta/scrape.py b/plugins.v2/plexpersonmeta/scrape.py index c64f027c..dd81b0e4 100644 --- a/plugins.v2/plexpersonmeta/scrape.py +++ b/plugins.v2/plexpersonmeta/scrape.py @@ -11,11 +11,10 @@ from app.chain.mediaserver import MediaServerChain from app.chain.tmdb import TmdbChain -from app.core.cache import Cache from app.core.context import MediaInfo from app.log import logger from app.plugins import PluginChian -from app.plugins.plexpersonmeta.helper import RatingInfo, cache_with_logging +from app.plugins.plexpersonmeta.helper import RatingInfo, cache_backend, cache_with_logging from app.schemas import MediaPerson, ServiceInfo from app.schemas.types import MediaType from app.utils.zhconv import convert as zhconv_convert @@ -36,7 +35,6 @@ def __init__(self, config: dict, event: threading.Event, chain: PluginChian, self.service = service self.plex = service.instance if service else None self.libraries = libraries - self.cache_backend = Cache(maxsize=100000, ttl=60 * 60 * 24 * 3) if not config: return @@ -837,11 +835,7 @@ def extract_key_from_url(url: str) -> Optional[str]: return match.group(1) if match else None @staticmethod - def clear_cache(self): - """ - 清理缓存 - """ - self.cache_backend.clear(region="plex_tmdb_media") - self.cache_backend.clear(region="plex_tmdb_person") - self.cache_backend.clear(region="plex_douban_media") - self.cache_backend.close() + def clear_cache(): + """清理插件用于识别结果的缓存分区。""" + for region in ("plex_tmdb_media", "plex_tmdb_person", "plex_douban_media"): + cache_backend.clear(region=region) diff --git a/tests/v2/plexpersonmeta/test_cache_scheduler.py b/tests/v2/plexpersonmeta/test_cache_scheduler.py new file mode 100644 index 00000000..1a659f1e --- /dev/null +++ b/tests/v2/plexpersonmeta/test_cache_scheduler.py @@ -0,0 +1,53 @@ +"""PlexPersonMeta 缓存清理任务的调度与执行测试。""" + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType +from unittest.mock import MagicMock, call, patch + +from apscheduler.schedulers.background import BackgroundScheduler +from app.testing import stub_modules + + +def _load_source_module(): + """直接加载插件源码,避免测试误用后端运行时副本。""" + source_path = Path(__file__).parents[3] / "plugins.v2" / "plexpersonmeta" / "scrape.py" + module_name = "_plexpersonmeta_source_scrape" + spec = importlib.util.spec_from_file_location(module_name, source_path) + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + return module + + +_pypinyin = ModuleType("pypinyin") +_pypinyin.lazy_pinyin = lambda *_args, **_kwargs: [] + +with stub_modules({"pypinyin": _pypinyin}): + _scrape = _load_source_module() + + +def test_clear_cache_can_be_registered_as_a_no_argument_job(): + """清理缓存任务必须能被 APScheduler 按无参数函数注册。""" + scheduler = BackgroundScheduler() + try: + job = scheduler.add_job(func=_scrape.ScrapeHelper.clear_cache, trigger="date") + assert job.func is _scrape.ScrapeHelper.clear_cache + finally: + scheduler.remove_all_jobs() + + +def test_clear_cache_clears_the_regions_used_by_cache_decorator(): + """清理操作应作用于装饰器实际使用的三个缓存分区。""" + cache_backend = MagicMock() + + with patch.object(_scrape, "cache_backend", cache_backend): + _scrape.ScrapeHelper.clear_cache() + + assert cache_backend.clear.call_args_list == [ + call(region="plex_tmdb_media"), + call(region="plex_tmdb_person"), + call(region="plex_douban_media"), + ] + cache_backend.close.assert_not_called()