From ab4895ff854d769eb295e5e22d4851d078bb7ce3 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 15 Jun 2023 07:12:59 +0800 Subject: [PATCH] fix dboper --- app/chain/cookiecloud.py | 28 +++++++------- app/chain/site_manage.py | 16 ++++---- app/chain/subscribe.py | 30 +++++++-------- app/core/plugin.py | 6 +-- app/db/__init__.py | 14 ++++++- app/db/models/__init__.py | 1 - app/db/plugindata_oper.py | 35 ++++++++++++++++++ app/db/{sites.py => site_oper.py} | 10 +---- app/db/{siteicons.py => siteicon_oper.py} | 10 +---- app/db/{subscribes.py => subscribe_oper.py} | 10 +---- ...{systemconfigs.py => systemconfig_oper.py} | 11 ++---- app/helper/sites.cp310-win_amd64.pyd | Bin 161792 -> 161792 bytes app/plugins/__init__.py | 24 ++++-------- 13 files changed, 106 insertions(+), 89 deletions(-) create mode 100644 app/db/plugindata_oper.py rename app/db/{sites.py => site_oper.py} (91%) rename app/db/{siteicons.py => siteicon_oper.py} (83%) rename app/db/{subscribes.py => subscribe_oper.py} (90%) rename app/db/{systemconfigs.py => systemconfig_oper.py} (88%) diff --git a/app/chain/cookiecloud.py b/app/chain/cookiecloud.py index 298efc92..f4d65086 100644 --- a/app/chain/cookiecloud.py +++ b/app/chain/cookiecloud.py @@ -6,8 +6,8 @@ from lxml import etree from app.chain import ChainBase from app.core.config import settings -from app.db.siteicons import SiteIcons -from app.db.sites import Sites +from app.db.siteicon_oper import SiteIconOper +from app.db.site_oper import SiteOper from app.helper.cookiecloud import CookieCloudHelper from app.helper.sites import SitesHelper from app.log import logger @@ -21,8 +21,8 @@ class CookieCloudChain(ChainBase): def __init__(self): super().__init__() - self.sites = Sites() - self.siteicons = SiteIcons() + self.siteoper = SiteOper() + self.siteiconoper = SiteIconOper() self.siteshelper = SitesHelper() self.cookiecloud = CookieCloudHelper( server=settings.COOKIECLOUD_HOST, @@ -45,16 +45,16 @@ class CookieCloudChain(ChainBase): for domain, cookie in cookies.items(): # 获取站点信息 indexer = self.siteshelper.get_indexer(domain) - if self.sites.exists(domain): + if self.siteoper.exists(domain): # 更新站点Cookie - self.sites.update_cookie(domain=domain, cookies=cookie) + self.siteoper.update_cookie(domain=domain, cookies=cookie) _update_count += 1 elif indexer: # 新增站点 - self.sites.add(name=indexer.get("name"), - url=indexer.get("domain"), - domain=domain, - cookie=cookie) + self.siteoper.add(name=indexer.get("name"), + url=indexer.get("domain"), + domain=domain, + cookie=cookie) _add_count += 1 # 保存站点图标 if indexer: @@ -62,10 +62,10 @@ class CookieCloudChain(ChainBase): cookie=cookie, ua=settings.USER_AGENT) if icon_url: - self.siteicons.update_icon(name=indexer.get("name"), - domain=domain, - icon_url=icon_url, - icon_base64=icon_base64) + self.siteiconoper.update_icon(name=indexer.get("name"), + domain=domain, + icon_url=icon_url, + icon_base64=icon_base64) # 处理完成 ret_msg = f"更新了{_update_count}个站点,新增了{_add_count}个站点" logger.info(f"CookieCloud同步成功:{ret_msg}") diff --git a/app/chain/site_manage.py b/app/chain/site_manage.py index 3ca6e607..19c95128 100644 --- a/app/chain/site_manage.py +++ b/app/chain/site_manage.py @@ -1,5 +1,5 @@ from app.chain import ChainBase -from app.db.sites import Sites +from app.db.site_oper import SiteOper class SiteManageChain(ChainBase): @@ -7,17 +7,17 @@ class SiteManageChain(ChainBase): 站点远程管理处理链 """ - _sites: Sites = None + _sites: SiteOper = None def __init__(self): super().__init__() - self._sites = Sites() + self._siteoper = SiteOper() def process(self): """ 查询所有站点,发送消息 """ - site_list = self._sites.list() + site_list = self._siteoper.list() if not site_list: self.post_message(title="没有维护任何站点信息!") title = f"共有 {len(site_list)} 个站点,回复 `/site_disable` `[id]` 禁用站点,回复 `/site_enable` `[id]` 启用站点:" @@ -44,12 +44,12 @@ class SiteManageChain(ChainBase): if not arg_str.isdigit(): return site_id = int(arg_str) - site = self._sites.get(site_id) + site = self._siteoper.get(site_id) if not site: self.post_message(title=f"站点编号 {site_id} 不存在!") return # 禁用站点 - self._sites.update(site_id, { + self._siteoper.update(site_id, { "is_active": False }) # 重新发送消息 @@ -65,12 +65,12 @@ class SiteManageChain(ChainBase): if not arg_str.isdigit(): return site_id = int(arg_str) - site = self._sites.get(site_id) + site = self._siteoper.get(site_id) if not site: self.post_message(title=f"站点编号 {site_id} 不存在!") return # 禁用站点 - self._sites.update(site_id, { + self._siteoper.update(site_id, { "is_active": True }) # 重新发送消息 diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 8bf92c94..bc7dd4d6 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -6,7 +6,7 @@ from app.chain.search import SearchChain from app.core.metainfo import MetaInfo from app.core.context import TorrentInfo, Context, MediaInfo from app.core.config import settings -from app.db.subscribes import Subscribes +from app.db.subscribe_oper import SubscribeOper from app.helper.sites import SitesHelper from app.log import logger from app.schemas.context import NotExistMediaInfo @@ -26,7 +26,7 @@ class SubscribeChain(ChainBase): super().__init__() self.downloadchain = DownloadChain() self.searchchain = SearchChain() - self.subscribes = Subscribes() + self.subscribehelper = SubscribeOper() self.siteshelper = SitesHelper() def process(self, title: str, year: str, @@ -89,7 +89,7 @@ class SubscribeChain(ChainBase): 'lack_episode': kwargs.get('total_episode') }) # 添加订阅 - sid, err_msg = self.subscribes.add(mediainfo, season=season, **kwargs) + sid, err_msg = self.subscribehelper.add(mediainfo, season=season, **kwargs) if not sid: logger.error(f'{mediainfo.title_year} {err_msg}') # 发回原用户 @@ -115,15 +115,15 @@ class SubscribeChain(ChainBase): :return: 更新订阅状态为R或删除订阅 """ if sid: - subscribes = [self.subscribes.get(sid)] + subscribes = [self.subscribehelper.get(sid)] else: - subscribes = self.subscribes.list(state) + subscribes = self.subscribehelper.list(state) # 遍历订阅 for subscribe in subscribes: logger.info(f'开始搜索订阅,标题:{subscribe.name} ...') # 如果状态为N则更新为R if subscribe.state == 'N': - self.subscribes.update(subscribe.id, {'state': 'R'}) + self.subscribehelper.update(subscribe.id, {'state': 'R'}) # 生成元数据 meta = MetaInfo(subscribe.name) meta.year = subscribe.year @@ -138,7 +138,7 @@ class SubscribeChain(ChainBase): exist_flag, no_exists = self.downloadchain.get_no_exists_info(meta=meta, mediainfo=mediainfo) if exist_flag: logger.info(f'{mediainfo.title_year} 媒体库中已存在,完成订阅') - self.subscribes.delete(subscribe.id) + self.subscribehelper.delete(subscribe.id) # 发送通知 self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', image=mediainfo.get_message_image()) @@ -165,7 +165,7 @@ class SubscribeChain(ChainBase): if downloads and not lefts: # 全部下载完成 logger.info(f'{mediainfo.title_year} 下载完成,完成订阅') - self.subscribes.delete(subscribe.id) + self.subscribehelper.delete(subscribe.id) # 发送通知 self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', image=mediainfo.get_message_image()) @@ -224,7 +224,7 @@ class SubscribeChain(ChainBase): 从缓存中匹配订阅,并自动下载 """ # 所有订阅 - subscribes = self.subscribes.list('R') + subscribes = self.subscribehelper.list('R') # 遍历订阅 for subscribe in subscribes: logger.info(f'开始匹配订阅,标题:{subscribe.name} ...') @@ -242,7 +242,7 @@ class SubscribeChain(ChainBase): exist_flag, no_exists = self.downloadchain.get_no_exists_info(meta=meta, mediainfo=mediainfo) if exist_flag: logger.info(f'{mediainfo.title_year} 媒体库中已存在,完成订阅') - self.subscribes.delete(subscribe.id) + self.subscribehelper.delete(subscribe.id) # 发送通知 self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', image=mediainfo.get_message_image()) @@ -278,7 +278,7 @@ class SubscribeChain(ChainBase): if downloads and not lefts: # 全部下载完成 logger.info(f'{mediainfo.title_year} 下载完成,完成订阅') - self.subscribes.delete(subscribe.id) + self.subscribehelper.delete(subscribe.id) # 发送通知 self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', image=mediainfo.get_message_image()) @@ -291,7 +291,7 @@ class SubscribeChain(ChainBase): left_episodes = season_info.get('episodes') logger.info(f'{mediainfo.title_year} 季 {season} 未下载完整,' f'更新缺失集数为{len(left_episodes)} ...') - self.subscribes.update(subscribe.id, { + self.subscribehelper.update(subscribe.id, { "lack_episode": len(left_episodes) }) @@ -299,7 +299,7 @@ class SubscribeChain(ChainBase): """ 查询订阅并发送消息 """ - subscribes = self.subscribes.list() + subscribes = self.subscribehelper.list() if not subscribes: self.post_message(title='没有任何订阅!') return @@ -328,12 +328,12 @@ class SubscribeChain(ChainBase): if not arg_str.isdigit(): return subscribe_id = int(arg_str) - subscribe = self.subscribes.get(subscribe_id) + subscribe = self.subscribehelper.get(subscribe_id) if not subscribe: self.post_message(title=f"订阅编号 {subscribe_id} 不存在!") return # 删除订阅 - self.subscribes.delete(subscribe_id) + self.subscribehelper.delete(subscribe_id) # 重新发送消息 self.list() diff --git a/app/core/plugin.py b/app/core/plugin.py index 04f7c4b1..8fea972d 100644 --- a/app/core/plugin.py +++ b/app/core/plugin.py @@ -1,7 +1,7 @@ import traceback from typing import List, Any -from app.db.systemconfigs import SystemConfigs +from app.db.systemconfig_oper import SystemConfigOper from app.helper.module import ModuleHelper from app.log import logger from app.utils.singleton import Singleton @@ -11,7 +11,7 @@ class PluginManager(metaclass=Singleton): """ 插件管理器 """ - systemconfigs: SystemConfigs = None + systemconfigs: SystemConfigOper = None # 插件列表 _plugins: dict = {} @@ -24,7 +24,7 @@ class PluginManager(metaclass=Singleton): self.init_config() def init_config(self): - self.systemconfigs = SystemConfigs() + self.systemconfigs = SystemConfigOper() # 停止已有插件 self.stop() # 启动插件 diff --git a/app/db/__init__.py b/app/db/__init__.py index 54a690cf..44b0c613 100644 --- a/app/db/__init__.py +++ b/app/db/__init__.py @@ -1,5 +1,5 @@ from sqlalchemy import create_engine, QueuePool -from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import sessionmaker, Session from app.core.config import settings @@ -27,3 +27,15 @@ def get_db(): finally: if db: db.close() + + +class DbOper: + + _db: Session = None + + def __init__(self, _db=SessionLocal()): + self._db = _db + + def __del__(self): + if self._db: + self._db.close() diff --git a/app/db/models/__init__.py b/app/db/models/__init__.py index 953b90f9..4a6b5842 100644 --- a/app/db/models/__init__.py +++ b/app/db/models/__init__.py @@ -11,7 +11,6 @@ class Base: def create(self, db): db.add(self) db.commit() - db.refresh(self) return self @classmethod diff --git a/app/db/plugindata_oper.py b/app/db/plugindata_oper.py new file mode 100644 index 00000000..1f2da2dd --- /dev/null +++ b/app/db/plugindata_oper.py @@ -0,0 +1,35 @@ +import json +from typing import Any + +from app.db import DbOper +from app.db.models import Base +from app.db.models.plugin import PluginData +from app.utils.object import ObjectUtils + + +class PluginDataOper(DbOper): + """ + 插件数据管理 + """ + + def save(self, plugin_id: str, key: str, value: Any) -> Base: + """ + 保存插件数据 + :param plugin_id: 插件id + :param key: 数据key + :param value: 数据值 + """ + if ObjectUtils.is_obj(value): + value = json.dumps(value) + plugin = PluginData(plugin_id=plugin_id, key=key, value=value) + return plugin.create(self._db) + + def get_data(self, key: str) -> Any: + """ + 获取插件数据 + :param key: 数据key + """ + data = PluginData.get_plugin_data_by_key(self._db, self.__class__.__name__, key) + if ObjectUtils.is_obj(data): + return json.load(data) + return data diff --git a/app/db/sites.py b/app/db/site_oper.py similarity index 91% rename from app/db/sites.py rename to app/db/site_oper.py index 1621c260..67d6a122 100644 --- a/app/db/sites.py +++ b/app/db/site_oper.py @@ -1,19 +1,13 @@ from typing import Tuple, List -from sqlalchemy.orm import Session - -from app.db import SessionLocal +from app.db import DbOper from app.db.models.site import Site -class Sites: +class SiteOper(DbOper): """ 站点管理 """ - _db: Session = None - - def __init__(self, _db=SessionLocal()): - self._db = _db def add(self, **kwargs) -> Tuple[bool, str]: """ diff --git a/app/db/siteicons.py b/app/db/siteicon_oper.py similarity index 83% rename from app/db/siteicons.py rename to app/db/siteicon_oper.py index ca0ba5e3..b0569f01 100644 --- a/app/db/siteicons.py +++ b/app/db/siteicon_oper.py @@ -1,19 +1,13 @@ from typing import List -from sqlalchemy.orm import Session - -from app.db import SessionLocal +from app.db import DbOper from app.db.models.siteicon import SiteIcon -class SiteIcons: +class SiteIconOper(DbOper): """ 站点管理 """ - _db: Session = None - - def __init__(self, _db=SessionLocal()): - self._db = _db def list(self) -> List[SiteIcon]: """ diff --git a/app/db/subscribes.py b/app/db/subscribe_oper.py similarity index 90% rename from app/db/subscribes.py rename to app/db/subscribe_oper.py index 5229830c..32fb873a 100644 --- a/app/db/subscribes.py +++ b/app/db/subscribe_oper.py @@ -1,20 +1,14 @@ from typing import Tuple, List -from sqlalchemy.orm import Session - from app.core.context import MediaInfo -from app.db import SessionLocal +from app.db import DbOper from app.db.models.subscribe import Subscribe -class Subscribes: +class SubscribeOper(DbOper): """ 订阅管理 """ - _db: Session = None - - def __init__(self, _db=SessionLocal()): - self._db = _db def add(self, mediainfo: MediaInfo, **kwargs) -> Tuple[int, str]: """ diff --git a/app/db/systemconfigs.py b/app/db/systemconfig_oper.py similarity index 88% rename from app/db/systemconfigs.py rename to app/db/systemconfig_oper.py index 119c3719..8daa6ac7 100644 --- a/app/db/systemconfigs.py +++ b/app/db/systemconfig_oper.py @@ -1,25 +1,22 @@ import json from typing import Any, Union -from sqlalchemy.orm import Session - -from app.db import SessionLocal +from app.db import DbOper from app.db.models.systemconfig import SystemConfig from app.utils.object import ObjectUtils from app.utils.singleton import Singleton from app.utils.types import SystemConfigKey -class SystemConfigs(metaclass=Singleton): +class SystemConfigOper(DbOper, metaclass=Singleton): # 配置对象 __SYSTEMCONF: dict = {} - _db: Session = None - def __init__(self, _db=SessionLocal()): + def __init__(self): """ 加载配置到内存 """ - self._db = _db + super().__init__() for item in SystemConfig.list(self._db): if ObjectUtils.is_obj(item.value): self.__SYSTEMCONF[item.key] = json.loads(item.value) diff --git a/app/helper/sites.cp310-win_amd64.pyd b/app/helper/sites.cp310-win_amd64.pyd index b723777529b0e7985f6183baaa41627390502fe2..ceafd24649d530c9a178513a842b51d074af2203 100644 GIT binary patch delta 19526 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 RE!}%y?~yY6PciS;|9`ZvF@yjB delta 19507 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 diff --git a/app/plugins/__init__.py b/app/plugins/__init__.py index d74aafa3..8e220b85 100644 --- a/app/plugins/__init__.py +++ b/app/plugins/__init__.py @@ -1,15 +1,12 @@ -import json from abc import ABCMeta, abstractmethod from pathlib import Path from typing import Any from app.chain import ChainBase from app.core.config import settings -from app.db import SessionLocal from app.db.models import Base -from app.db.models.plugin import PluginData -from app.db.systemconfigs import SystemConfigs -from app.utils.object import ObjectUtils +from app.db.plugindata_oper import PluginDataOper +from app.db.systemconfig_oper import SystemConfigOper class PluginChian(ChainBase): @@ -39,8 +36,9 @@ class _PluginBase(metaclass=ABCMeta): plugin_desc: str = "" def __init__(self): - self.db = SessionLocal() + self.plugindata = PluginDataOper() self.chain = PluginChian() + self.systemconfig = SystemConfigOper() @abstractmethod def init_plugin(self, config: dict = None): @@ -65,7 +63,7 @@ class _PluginBase(metaclass=ABCMeta): """ if not plugin_id: plugin_id = self.__class__.__name__ - return SystemConfigs().set(f"plugin.{plugin_id}", config) + return self.systemconfig.set(f"plugin.{plugin_id}", config) def get_config(self, plugin_id: str = None) -> Any: """ @@ -74,7 +72,7 @@ class _PluginBase(metaclass=ABCMeta): """ if not plugin_id: plugin_id = self.__class__.__name__ - return SystemConfigs().get(f"plugin.{plugin_id}") + return self.systemconfig.get(f"plugin.{plugin_id}") def get_data_path(self, plugin_id: str = None) -> Path: """ @@ -93,17 +91,11 @@ class _PluginBase(metaclass=ABCMeta): :param key: 数据key :param value: 数据值 """ - if ObjectUtils.is_obj(value): - value = json.dumps(value) - plugin = PluginData(plugin_id=self.__class__.__name__, key=key, value=value) - return plugin.create(self.db) + return self.plugindata.save(self.__class__.__name__, key, value) def get_data(self, key: str) -> Any: """ 获取插件数据 :param key: 数据key """ - data = PluginData.get_plugin_data_by_key(self.db, self.__class__.__name__, key) - if ObjectUtils.is_obj(data): - return json.load(data) - return data + return self.plugindata.get_data(key)