From 574ac8d32fbb013288b5bb64c852a7b1f36630ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 09:52:31 +0000 Subject: [PATCH 1/8] Initial plan From d8399f7e85dc0121284d171f36c1d82b12d16ccf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 09:56:11 +0000 Subject: [PATCH 2/8] Consolidate CategoryHelper classes and add reload trigger Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com> --- app/api/endpoints/category.py | 2 +- app/helper/category.py | 52 ------------------------------ app/modules/themoviedb/category.py | 49 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 53 deletions(-) delete mode 100644 app/helper/category.py diff --git a/app/api/endpoints/category.py b/app/api/endpoints/category.py index 8859a3c7..ca8df808 100644 --- a/app/api/endpoints/category.py +++ b/app/api/endpoints/category.py @@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends from app import schemas from app.db.models import User from app.db.user_oper import get_current_active_superuser, get_current_active_user -from app.helper.category import CategoryHelper +from app.modules.themoviedb.category import CategoryHelper from app.schemas.category import CategoryConfig router = APIRouter() diff --git a/app/helper/category.py b/app/helper/category.py deleted file mode 100644 index 9bf23e18..00000000 --- a/app/helper/category.py +++ /dev/null @@ -1,52 +0,0 @@ -import yaml -from app.core.config import settings -from app.log import logger -from app.schemas.category import CategoryConfig - -HEADER_COMMENTS = """####### 配置说明 ####### -# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格附合语法规则 -# 2. 配置文件中的一级分类名称:`movie`、`tv` 为固定名称不可修改,二级名称同时也是目录名称,会按先后顺序匹配,匹配后程序会按这个名称建立二级目录 -# 3. 支持的分类条件: -# `original_language` 语种,具体含义参考下方字典 -# `production_countries` 国家或地区(电影)、`origin_country` 国家或地区(电视剧),具体含义参考下方字典 -# `genre_ids` 内容类型,具体含义参考下方字典 -# `release_year` 发行年份,格式:YYYY,电影实际对应`release_date`字段,电视剧实际对应`first_air_date`字段,支持范围设定,如:`YYYY-YYYY` -# themoviedb 详情API返回的其它一级字段 -# 4. 配置多项条件时需要同时满足,一个条件需要匹配多个值是使用`,`分隔 -# 5. !条件值表示排除该值 - -""" - -class CategoryHelper: - def __init__(self): - self._config_path = settings.CONFIG_PATH / 'category.yaml' - - def load(self) -> CategoryConfig: - """ - 加载配置 - """ - config = CategoryConfig() - if not self._config_path.exists(): - return config - try: - with open(self._config_path, 'r', encoding='utf-8') as f: - data = yaml.safe_load(f) - if data: - config = CategoryConfig(**data) - except Exception as e: - logger.error(f"Load category config failed: {e}") - return config - - def save(self, config: CategoryConfig) -> bool: - """ - 保存配置 - """ - data = config.model_dump(exclude_none=True) - try: - with open(self._config_path, 'w', encoding='utf-8') as f: - f.write(HEADER_COMMENTS) - yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) - return True - except Exception as e: - logger.error(f"Save category config failed: {e}") - return False diff --git a/app/modules/themoviedb/category.py b/app/modules/themoviedb/category.py index 387c0d9c..0308f8f6 100644 --- a/app/modules/themoviedb/category.py +++ b/app/modules/themoviedb/category.py @@ -1,4 +1,5 @@ import shutil +import yaml from pathlib import Path from typing import Union @@ -7,6 +8,7 @@ from ruamel.yaml import CommentedMap from app.core.config import settings from app.log import logger +from app.schemas.category import CategoryConfig from app.utils.singleton import WeakSingleton @@ -15,6 +17,21 @@ class CategoryHelper(metaclass=WeakSingleton): 二级分类 """ + # 配置文件头部注释 + HEADER_COMMENTS = """####### 配置说明 ####### +# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格附合语法规则 +# 2. 配置文件中的一级分类名称:`movie`、`tv` 为固定名称不可修改,二级名称同时也是目录名称,会按先后顺序匹配,匹配后程序会按这个名称建立二级目录 +# 3. 支持的分类条件: +# `original_language` 语种,具体含义参考下方字典 +# `production_countries` 国家或地区(电影)、`origin_country` 国家或地区(电视剧),具体含义参考下方字典 +# `genre_ids` 内容类型,具体含义参考下方字典 +# `release_year` 发行年份,格式:YYYY,电影实际对应`release_date`字段,电视剧实际对应`first_air_date`字段,支持范围设定,如:`YYYY-YYYY` +# themoviedb 详情API返回的其它一级字段 +# 4. 配置多项条件时需要同时满足,一个条件需要匹配多个值是使用`,`分隔 +# 5. !条件值表示排除该值 + +""" + def __init__(self): self._category_path: Path = settings.CONFIG_PATH / "category.yaml" self._categorys = {} @@ -44,6 +61,38 @@ class CategoryHelper(metaclass=WeakSingleton): self._tv_categorys = self._categorys.get('tv') logger.info(f"已加载二级分类策略 category.yaml") + def load(self) -> CategoryConfig: + """ + 加载配置 + """ + config = CategoryConfig() + if not self._category_path.exists(): + return config + try: + with open(self._category_path, 'r', encoding='utf-8') as f: + data = yaml.safe_load(f) + if data: + config = CategoryConfig(**data) + except Exception as e: + logger.error(f"Load category config failed: {e}") + return config + + def save(self, config: CategoryConfig) -> bool: + """ + 保存配置 + """ + data = config.model_dump(exclude_none=True) + try: + with open(self._category_path, 'w', encoding='utf-8') as f: + f.write(self.HEADER_COMMENTS) + yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) + # 保存后重新加载配置 + self.init() + return True + except Exception as e: + logger.error(f"Save category config failed: {e}") + return False + @property def is_movie_category(self) -> bool: """ From 4ee21ffae484ebb9df6ce06b67977f2697322f21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 09:58:28 +0000 Subject: [PATCH 3/8] Address code review feedback - use ruamel.yaml consistently and fix typo Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com> --- app/modules/themoviedb/category.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/modules/themoviedb/category.py b/app/modules/themoviedb/category.py index 0308f8f6..ce46108b 100644 --- a/app/modules/themoviedb/category.py +++ b/app/modules/themoviedb/category.py @@ -1,5 +1,4 @@ import shutil -import yaml from pathlib import Path from typing import Union @@ -19,7 +18,7 @@ class CategoryHelper(metaclass=WeakSingleton): # 配置文件头部注释 HEADER_COMMENTS = """####### 配置说明 ####### -# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格附合语法规则 +# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格符合语法规则 # 2. 配置文件中的一级分类名称:`movie`、`tv` 为固定名称不可修改,二级名称同时也是目录名称,会按先后顺序匹配,匹配后程序会按这个名称建立二级目录 # 3. 支持的分类条件: # `original_language` 语种,具体含义参考下方字典 @@ -70,7 +69,8 @@ class CategoryHelper(metaclass=WeakSingleton): return config try: with open(self._category_path, 'r', encoding='utf-8') as f: - data = yaml.safe_load(f) + yaml_loader = ruamel.yaml.YAML() + data = yaml_loader.load(f) if data: config = CategoryConfig(**data) except Exception as e: @@ -85,7 +85,8 @@ class CategoryHelper(metaclass=WeakSingleton): try: with open(self._category_path, 'w', encoding='utf-8') as f: f.write(self.HEADER_COMMENTS) - yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) + yaml_dumper = ruamel.yaml.YAML() + yaml_dumper.dump(data, f) # 保存后重新加载配置 self.init() return True From a857337b314c5caa747714fca86c9e42076de6ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:06:01 +0000 Subject: [PATCH 4/8] Fix architecture - restore helper layer and use ModuleManager for reload trigger Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com> --- app/api/endpoints/category.py | 2 +- app/helper/category.py | 58 ++++++++++++++++++++++++++++++ app/modules/themoviedb/category.py | 50 -------------------------- 3 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 app/helper/category.py diff --git a/app/api/endpoints/category.py b/app/api/endpoints/category.py index ca8df808..8859a3c7 100644 --- a/app/api/endpoints/category.py +++ b/app/api/endpoints/category.py @@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends from app import schemas from app.db.models import User from app.db.user_oper import get_current_active_superuser, get_current_active_user -from app.modules.themoviedb.category import CategoryHelper +from app.helper.category import CategoryHelper from app.schemas.category import CategoryConfig router = APIRouter() diff --git a/app/helper/category.py b/app/helper/category.py new file mode 100644 index 00000000..c1e3dc5a --- /dev/null +++ b/app/helper/category.py @@ -0,0 +1,58 @@ +import yaml +from app.core.config import settings +from app.core.module import ModuleManager +from app.log import logger +from app.schemas.category import CategoryConfig + +HEADER_COMMENTS = """####### 配置说明 ####### +# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格符合语法规则 +# 2. 配置文件中的一级分类名称:`movie`、`tv` 为固定名称不可修改,二级名称同时也是目录名称,会按先后顺序匹配,匹配后程序会按这个名称建立二级目录 +# 3. 支持的分类条件: +# `original_language` 语种,具体含义参考下方字典 +# `production_countries` 国家或地区(电影)、`origin_country` 国家或地区(电视剧),具体含义参考下方字典 +# `genre_ids` 内容类型,具体含义参考下方字典 +# `release_year` 发行年份,格式:YYYY,电影实际对应`release_date`字段,电视剧实际对应`first_air_date`字段,支持范围设定,如:`YYYY-YYYY` +# themoviedb 详情API返回的其它一级字段 +# 4. 配置多项条件时需要同时满足,一个条件需要匹配多个值是使用`,`分隔 +# 5. !条件值表示排除该值 + +""" + +class CategoryHelper: + def __init__(self): + self._config_path = settings.CONFIG_PATH / 'category.yaml' + + def load(self) -> CategoryConfig: + """ + 加载配置 + """ + config = CategoryConfig() + if not self._config_path.exists(): + return config + try: + with open(self._config_path, 'r', encoding='utf-8') as f: + data = yaml.safe_load(f) + if data: + config = CategoryConfig(**data) + except Exception as e: + logger.error(f"Load category config failed: {e}") + return config + + def save(self, config: CategoryConfig) -> bool: + """ + 保存配置 + """ + data = config.model_dump(exclude_none=True) + try: + with open(self._config_path, 'w', encoding='utf-8') as f: + f.write(HEADER_COMMENTS) + yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) + # 触发 themoviedb CategoryHelper 重新加载配置 + themoviedb_module = ModuleManager().get_running_module("TheMovieDbModule") + if themoviedb_module and hasattr(themoviedb_module, 'category'): + themoviedb_module.category.init() + logger.info("已触发 CategoryHelper 重新加载配置") + return True + except Exception as e: + logger.error(f"Save category config failed: {e}") + return False diff --git a/app/modules/themoviedb/category.py b/app/modules/themoviedb/category.py index ce46108b..387c0d9c 100644 --- a/app/modules/themoviedb/category.py +++ b/app/modules/themoviedb/category.py @@ -7,7 +7,6 @@ from ruamel.yaml import CommentedMap from app.core.config import settings from app.log import logger -from app.schemas.category import CategoryConfig from app.utils.singleton import WeakSingleton @@ -16,21 +15,6 @@ class CategoryHelper(metaclass=WeakSingleton): 二级分类 """ - # 配置文件头部注释 - HEADER_COMMENTS = """####### 配置说明 ####### -# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格符合语法规则 -# 2. 配置文件中的一级分类名称:`movie`、`tv` 为固定名称不可修改,二级名称同时也是目录名称,会按先后顺序匹配,匹配后程序会按这个名称建立二级目录 -# 3. 支持的分类条件: -# `original_language` 语种,具体含义参考下方字典 -# `production_countries` 国家或地区(电影)、`origin_country` 国家或地区(电视剧),具体含义参考下方字典 -# `genre_ids` 内容类型,具体含义参考下方字典 -# `release_year` 发行年份,格式:YYYY,电影实际对应`release_date`字段,电视剧实际对应`first_air_date`字段,支持范围设定,如:`YYYY-YYYY` -# themoviedb 详情API返回的其它一级字段 -# 4. 配置多项条件时需要同时满足,一个条件需要匹配多个值是使用`,`分隔 -# 5. !条件值表示排除该值 - -""" - def __init__(self): self._category_path: Path = settings.CONFIG_PATH / "category.yaml" self._categorys = {} @@ -60,40 +44,6 @@ class CategoryHelper(metaclass=WeakSingleton): self._tv_categorys = self._categorys.get('tv') logger.info(f"已加载二级分类策略 category.yaml") - def load(self) -> CategoryConfig: - """ - 加载配置 - """ - config = CategoryConfig() - if not self._category_path.exists(): - return config - try: - with open(self._category_path, 'r', encoding='utf-8') as f: - yaml_loader = ruamel.yaml.YAML() - data = yaml_loader.load(f) - if data: - config = CategoryConfig(**data) - except Exception as e: - logger.error(f"Load category config failed: {e}") - return config - - def save(self, config: CategoryConfig) -> bool: - """ - 保存配置 - """ - data = config.model_dump(exclude_none=True) - try: - with open(self._category_path, 'w', encoding='utf-8') as f: - f.write(self.HEADER_COMMENTS) - yaml_dumper = ruamel.yaml.YAML() - yaml_dumper.dump(data, f) - # 保存后重新加载配置 - self.init() - return True - except Exception as e: - logger.error(f"Save category config failed: {e}") - return False - @property def is_movie_category(self) -> bool: """ From 71cde6661dcee29ea162bddb71463ea10601cc13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 10:08:13 +0000 Subject: [PATCH 5/8] Improve comments for clarity Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com> --- app/helper/category.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helper/category.py b/app/helper/category.py index c1e3dc5a..bcdd80d4 100644 --- a/app/helper/category.py +++ b/app/helper/category.py @@ -47,11 +47,11 @@ class CategoryHelper: with open(self._config_path, 'w', encoding='utf-8') as f: f.write(HEADER_COMMENTS) yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) - # 触发 themoviedb CategoryHelper 重新加载配置 + # 触发模块重新加载配置 themoviedb_module = ModuleManager().get_running_module("TheMovieDbModule") if themoviedb_module and hasattr(themoviedb_module, 'category'): themoviedb_module.category.init() - logger.info("已触发 CategoryHelper 重新加载配置") + logger.info("已触发分类配置重新加载") return True except Exception as e: logger.error(f"Save category config failed: {e}") From 27756a53dbf3fc7b670f194189aebb191bb5fd9e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:07:56 +0000 Subject: [PATCH 6/8] Implement proper architecture: module->chain->API with single CategoryHelper Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com> --- app/api/endpoints/category.py | 6 ++-- app/chain/tmdb.py | 13 +++++++ app/helper/category.py | 58 ------------------------------ app/modules/themoviedb/__init__.py | 14 ++++++++ app/modules/themoviedb/category.py | 52 +++++++++++++++++++++++++-- 5 files changed, 80 insertions(+), 63 deletions(-) delete mode 100644 app/helper/category.py diff --git a/app/api/endpoints/category.py b/app/api/endpoints/category.py index 8859a3c7..771522c1 100644 --- a/app/api/endpoints/category.py +++ b/app/api/endpoints/category.py @@ -1,8 +1,8 @@ from fastapi import APIRouter, Depends from app import schemas +from app.chain.tmdb import TmdbChain from app.db.models import User from app.db.user_oper import get_current_active_superuser, get_current_active_user -from app.helper.category import CategoryHelper from app.schemas.category import CategoryConfig router = APIRouter() @@ -13,7 +13,7 @@ def get_category_config(_: User = Depends(get_current_active_user)): """ 获取分类策略配置 """ - config = CategoryHelper().load() + config = TmdbChain().load_category_config() return schemas.Response(success=True, data=config.model_dump()) @@ -22,7 +22,7 @@ def save_category_config(config: CategoryConfig, _: User = Depends(get_current_a """ 保存分类策略配置 """ - if CategoryHelper().save(config): + if TmdbChain().save_category_config(config): return schemas.Response(success=True, message="保存成功") else: return schemas.Response(success=False, message="保存失败") diff --git a/app/chain/tmdb.py b/app/chain/tmdb.py index 0ed91550..c3c201f2 100644 --- a/app/chain/tmdb.py +++ b/app/chain/tmdb.py @@ -5,6 +5,7 @@ from app import schemas from app.chain import ChainBase from app.core.context import MediaInfo from app.schemas import MediaType +from app.schemas.category import CategoryConfig class TmdbChain(ChainBase): @@ -320,3 +321,15 @@ class TmdbChain(ChainBase): if infos: return [info.backdrop_path for info in infos if info and info.backdrop_path][:num] return [] + + def load_category_config(self) -> CategoryConfig: + """ + 加载分类策略配置 + """ + return self.run_module("load_category_config") + + def save_category_config(self, config: CategoryConfig) -> bool: + """ + 保存分类策略配置 + """ + return self.run_module("save_category_config", config=config) diff --git a/app/helper/category.py b/app/helper/category.py deleted file mode 100644 index bcdd80d4..00000000 --- a/app/helper/category.py +++ /dev/null @@ -1,58 +0,0 @@ -import yaml -from app.core.config import settings -from app.core.module import ModuleManager -from app.log import logger -from app.schemas.category import CategoryConfig - -HEADER_COMMENTS = """####### 配置说明 ####### -# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格符合语法规则 -# 2. 配置文件中的一级分类名称:`movie`、`tv` 为固定名称不可修改,二级名称同时也是目录名称,会按先后顺序匹配,匹配后程序会按这个名称建立二级目录 -# 3. 支持的分类条件: -# `original_language` 语种,具体含义参考下方字典 -# `production_countries` 国家或地区(电影)、`origin_country` 国家或地区(电视剧),具体含义参考下方字典 -# `genre_ids` 内容类型,具体含义参考下方字典 -# `release_year` 发行年份,格式:YYYY,电影实际对应`release_date`字段,电视剧实际对应`first_air_date`字段,支持范围设定,如:`YYYY-YYYY` -# themoviedb 详情API返回的其它一级字段 -# 4. 配置多项条件时需要同时满足,一个条件需要匹配多个值是使用`,`分隔 -# 5. !条件值表示排除该值 - -""" - -class CategoryHelper: - def __init__(self): - self._config_path = settings.CONFIG_PATH / 'category.yaml' - - def load(self) -> CategoryConfig: - """ - 加载配置 - """ - config = CategoryConfig() - if not self._config_path.exists(): - return config - try: - with open(self._config_path, 'r', encoding='utf-8') as f: - data = yaml.safe_load(f) - if data: - config = CategoryConfig(**data) - except Exception as e: - logger.error(f"Load category config failed: {e}") - return config - - def save(self, config: CategoryConfig) -> bool: - """ - 保存配置 - """ - data = config.model_dump(exclude_none=True) - try: - with open(self._config_path, 'w', encoding='utf-8') as f: - f.write(HEADER_COMMENTS) - yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) - # 触发模块重新加载配置 - themoviedb_module = ModuleManager().get_running_module("TheMovieDbModule") - if themoviedb_module and hasattr(themoviedb_module, 'category'): - themoviedb_module.category.init() - logger.info("已触发分类配置重新加载") - return True - except Exception as e: - logger.error(f"Save category config failed: {e}") - return False diff --git a/app/modules/themoviedb/__init__.py b/app/modules/themoviedb/__init__.py index 5e5832d2..4cfd494d 100644 --- a/app/modules/themoviedb/__init__.py +++ b/app/modules/themoviedb/__init__.py @@ -14,10 +14,12 @@ from app.modules.themoviedb.category import CategoryHelper from app.modules.themoviedb.scraper import TmdbScraper from app.modules.themoviedb.tmdb_cache import TmdbCache from app.modules.themoviedb.tmdbapi import TmdbApi +from app.schemas.category import CategoryConfig from app.schemas.types import MediaType, MediaImageType, ModuleType, MediaRecognizeType from app.utils.http import RequestUtils + class TheMovieDbModule(_ModuleBase): """ TMDB媒体信息匹配 @@ -1290,3 +1292,15 @@ class TheMovieDbModule(_ModuleBase): self.tmdb.clear_cache() self.cache.clear() logger.info("TMDB缓存清除完成") + + def load_category_config(self) -> CategoryConfig: + """ + 加载分类配置 + """ + return self.category.load() + + def save_category_config(self, config: CategoryConfig) -> bool: + """ + 保存分类配置 + """ + return self.category.save(config) diff --git a/app/modules/themoviedb/category.py b/app/modules/themoviedb/category.py index 387c0d9c..60e50244 100644 --- a/app/modules/themoviedb/category.py +++ b/app/modules/themoviedb/category.py @@ -1,4 +1,5 @@ import shutil +import yaml from pathlib import Path from typing import Union @@ -7,8 +8,23 @@ from ruamel.yaml import CommentedMap from app.core.config import settings from app.log import logger +from app.schemas.category import CategoryConfig from app.utils.singleton import WeakSingleton +HEADER_COMMENTS = """####### 配置说明 ####### +# 1. 该配置文件用于配置电影和电视剧的分类策略,配置后程序会按照配置的分类策略名称进行分类,配置文件采用yaml格式,需要严格符合语法规则 +# 2. 配置文件中的一级分类名称:`movie`、`tv` 为固定名称不可修改,二级名称同时也是目录名称,会按先后顺序匹配,匹配后程序会按这个名称建立二级目录 +# 3. 支持的分类条件: +# `original_language` 语种,具体含义参考下方字典 +# `production_countries` 国家或地区(电影)、`origin_country` 国家或地区(电视剧),具体含义参考下方字典 +# `genre_ids` 内容类型,具体含义参考下方字典 +# `release_year` 发行年份,格式:YYYY,电影实际对应`release_date`字段,电视剧实际对应`first_air_date`字段,支持范围设定,如:`YYYY-YYYY` +# themoviedb 详情API返回的其它一级字段 +# 4. 配置多项条件时需要同时满足,一个条件需要匹配多个值是使用`,`分隔 +# 5. !条件值表示排除该值 + +""" + class CategoryHelper(metaclass=WeakSingleton): """ @@ -31,8 +47,8 @@ class CategoryHelper(metaclass=WeakSingleton): shutil.copy(settings.INNER_CONFIG_PATH / "category.yaml", self._category_path) with open(self._category_path, mode='r', encoding='utf-8') as f: try: - yaml = ruamel.yaml.YAML() - self._categorys = yaml.load(f) + yaml_loader = ruamel.yaml.YAML() + self._categorys = yaml_loader.load(f) except Exception as e: logger.warn(f"二级分类策略配置文件格式出现严重错误!请检查:{str(e)}") self._categorys = {} @@ -44,6 +60,38 @@ class CategoryHelper(metaclass=WeakSingleton): self._tv_categorys = self._categorys.get('tv') logger.info(f"已加载二级分类策略 category.yaml") + def load(self) -> CategoryConfig: + """ + 加载配置 + """ + config = CategoryConfig() + if not self._category_path.exists(): + return config + try: + with open(self._category_path, 'r', encoding='utf-8') as f: + data = yaml.safe_load(f) + if data: + config = CategoryConfig(**data) + except Exception as e: + logger.error(f"Load category config failed: {e}") + return config + + def save(self, config: CategoryConfig) -> bool: + """ + 保存配置 + """ + data = config.model_dump(exclude_none=True) + try: + with open(self._category_path, 'w', encoding='utf-8') as f: + f.write(HEADER_COMMENTS) + yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) + # 保存后重新加载配置 + self.init() + return True + except Exception as e: + logger.error(f"Save category config failed: {e}") + return False + @property def is_movie_category(self) -> bool: """ From 5d3443fee4b3e9270d054d4e5cef4356c0ecce78 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:10:15 +0000 Subject: [PATCH 7/8] Use ruamel.yaml consistently in CategoryHelper Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com> --- app/modules/themoviedb/category.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/modules/themoviedb/category.py b/app/modules/themoviedb/category.py index 60e50244..99e7104a 100644 --- a/app/modules/themoviedb/category.py +++ b/app/modules/themoviedb/category.py @@ -1,5 +1,4 @@ import shutil -import yaml from pathlib import Path from typing import Union @@ -69,7 +68,8 @@ class CategoryHelper(metaclass=WeakSingleton): return config try: with open(self._category_path, 'r', encoding='utf-8') as f: - data = yaml.safe_load(f) + yaml_loader = ruamel.yaml.YAML() + data = yaml_loader.load(f) if data: config = CategoryConfig(**data) except Exception as e: @@ -84,7 +84,8 @@ class CategoryHelper(metaclass=WeakSingleton): try: with open(self._category_path, 'w', encoding='utf-8') as f: f.write(HEADER_COMMENTS) - yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False) + yaml_dumper = ruamel.yaml.YAML() + yaml_dumper.dump(data, f) # 保存后重新加载配置 self.init() return True From 2883ccbe87f2784dd33f7843d2fa51fb38321383 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:32:11 +0000 Subject: [PATCH 8/8] Move category methods to ChainBase and use consistent naming Co-authored-by: jxxghp <51039935+jxxghp@users.noreply.github.com> --- app/api/endpoints/category.py | 2 +- app/chain/__init__.py | 13 +++++++++++++ app/chain/tmdb.py | 13 ------------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/api/endpoints/category.py b/app/api/endpoints/category.py index 771522c1..5f6d6977 100644 --- a/app/api/endpoints/category.py +++ b/app/api/endpoints/category.py @@ -13,7 +13,7 @@ def get_category_config(_: User = Depends(get_current_active_user)): """ 获取分类策略配置 """ - config = TmdbChain().load_category_config() + config = TmdbChain().category_config() return schemas.Response(success=True, data=config.model_dump()) diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 098fc951..f8a2aebd 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -26,6 +26,7 @@ from app.helper.service import ServiceConfigHelper from app.log import logger from app.schemas import TransferInfo, TransferTorrent, ExistMediaInfo, DownloadingTorrent, CommingMessage, Notification, \ WebhookEventInfo, TmdbEpisode, MediaPerson, FileItem, TransferDirectoryConf +from app.schemas.category import CategoryConfig from app.schemas.types import TorrentStatus, MediaType, MediaImageType, EventType, MessageChannel from app.utils.object import ObjectUtils @@ -1062,6 +1063,18 @@ class ChainBase(metaclass=ABCMeta): """ return self.run_module("media_category") + def category_config(self) -> CategoryConfig: + """ + 获取分类策略配置 + """ + return self.run_module("load_category_config") + + def save_category_config(self, config: CategoryConfig) -> bool: + """ + 保存分类策略配置 + """ + return self.run_module("save_category_config", config=config) + def register_commands(self, commands: Dict[str, dict]) -> None: """ 注册菜单命令 diff --git a/app/chain/tmdb.py b/app/chain/tmdb.py index c3c201f2..0ed91550 100644 --- a/app/chain/tmdb.py +++ b/app/chain/tmdb.py @@ -5,7 +5,6 @@ from app import schemas from app.chain import ChainBase from app.core.context import MediaInfo from app.schemas import MediaType -from app.schemas.category import CategoryConfig class TmdbChain(ChainBase): @@ -321,15 +320,3 @@ class TmdbChain(ChainBase): if infos: return [info.backdrop_path for info in infos if info and info.backdrop_path][:num] return [] - - def load_category_config(self) -> CategoryConfig: - """ - 加载分类策略配置 - """ - return self.run_module("load_category_config") - - def save_category_config(self, config: CategoryConfig) -> bool: - """ - 保存分类策略配置 - """ - return self.run_module("save_category_config", config=config)