mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-08 00:46:57 +08:00
feat:弱引用单例
This commit is contained in:
@@ -18,14 +18,14 @@ from app.db.systemconfig_oper import SystemConfigOper
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
from app.utils.url import UrlUtils
|
from app.utils.url import UrlUtils
|
||||||
|
|
||||||
PLUGIN_DIR = Path(settings.ROOT_PATH) / "app" / "plugins"
|
PLUGIN_DIR = Path(settings.ROOT_PATH) / "app" / "plugins"
|
||||||
|
|
||||||
|
|
||||||
class PluginHelper(metaclass=Singleton):
|
class PluginHelper(metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
插件市场管理,下载安装插件到本地
|
插件市场管理,下载安装插件到本地
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Union, Dict, Optional
|
from typing import Union, Optional
|
||||||
|
|
||||||
from app.schemas.types import ProgressKey
|
from app.schemas.types import ProgressKey
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
|
|
||||||
|
|
||||||
class ProgressHelper(metaclass=Singleton):
|
class ProgressHelper(metaclass=WeakSingleton):
|
||||||
_process_detail: Dict[str, dict] = {}
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._process_detail = {}
|
self._process_detail = {}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ from app.db.systemconfig_oper import SystemConfigOper
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.utils.system import SystemUtils
|
from app.utils.system import SystemUtils
|
||||||
|
|
||||||
|
|
||||||
class SubscribeHelper(metaclass=Singleton):
|
class SubscribeHelper(metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
订阅数据统计/订阅分享等
|
订阅数据统计/订阅分享等
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Tuple, Optional, List, Union, Dict
|
from typing import Tuple, Optional, List, Union, Dict, Any
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
from requests import Response
|
|
||||||
from torrentool.api import Torrent
|
from torrentool.api import Torrent
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
@@ -16,17 +15,17 @@ from app.db.systemconfig_oper import SystemConfigOper
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas.types import MediaType, SystemConfigKey
|
from app.schemas.types import MediaType, SystemConfigKey
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
class TorrentHelper(metaclass=Singleton):
|
class TorrentHelper(metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
种子帮助类
|
种子帮助类
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 失败的种子:站点链接
|
def __init__(self):
|
||||||
_invalid_torrents = []
|
self._invalid_torrents = []
|
||||||
|
|
||||||
def download_torrent(self, url: str,
|
def download_torrent(self, url: str,
|
||||||
cookie: Optional[str] = None,
|
cookie: Optional[str] = None,
|
||||||
@@ -170,7 +169,7 @@ class TorrentHelper(metaclass=Singleton):
|
|||||||
return "", []
|
return "", []
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_url_filename(req: Response, url: str) -> str:
|
def get_url_filename(req: Any, url: str) -> str:
|
||||||
"""
|
"""
|
||||||
从下载请求中获取种子文件名
|
从下载请求中获取种子文件名
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import requests
|
|||||||
from app.core.cache import cached
|
from app.core.cache import cached
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
|
|
||||||
|
|
||||||
class DoubanApi(metaclass=Singleton):
|
class DoubanApi(metaclass=WeakSingleton):
|
||||||
_urls = {
|
_urls = {
|
||||||
# 搜索类
|
# 搜索类
|
||||||
# sort=U:近期热门 T:标记最多 S:评分最高 R:最新上映
|
# sort=U:近期热门 T:标记最多 S:评分最高 R:最新上映
|
||||||
@@ -151,7 +151,6 @@ class DoubanApi(metaclass=Singleton):
|
|||||||
_api_key2 = "0ab215a8b1977939201640fa14c66bab"
|
_api_key2 = "0ab215a8b1977939201640fa14c66bab"
|
||||||
_base_url = "https://frodo.douban.com/api/v2"
|
_base_url = "https://frodo.douban.com/api/v2"
|
||||||
_api_url = "https://api.douban.com/v2"
|
_api_url = "https://api.douban.com/v2"
|
||||||
_session = None
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._session = requests.Session()
|
self._session = requests.Session()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from app.core.config import settings
|
|||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.core.metainfo import MetaInfo
|
from app.core.metainfo import MetaInfo
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
lock = RLock()
|
lock = RLock()
|
||||||
@@ -19,7 +19,7 @@ CACHE_EXPIRE_TIMESTAMP_STR = "cache_expire_timestamp"
|
|||||||
EXPIRE_TIMESTAMP = settings.CONF.meta
|
EXPIRE_TIMESTAMP = settings.CONF.meta
|
||||||
|
|
||||||
|
|
||||||
class DoubanCache(metaclass=Singleton):
|
class DoubanCache(metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
豆瓣缓存数据
|
豆瓣缓存数据
|
||||||
{
|
{
|
||||||
@@ -29,9 +29,6 @@ class DoubanCache(metaclass=Singleton):
|
|||||||
"type": MediaType
|
"type": MediaType
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
_meta_data: dict = {}
|
|
||||||
# 缓存文件路径
|
|
||||||
_meta_path: Path = None
|
|
||||||
# TMDB缓存过期
|
# TMDB缓存过期
|
||||||
_tmdb_cache_expire: bool = True
|
_tmdb_cache_expire: bool = True
|
||||||
|
|
||||||
@@ -233,3 +230,6 @@ class DoubanCache(metaclass=Singleton):
|
|||||||
if not cache_media_info:
|
if not cache_media_info:
|
||||||
return
|
return
|
||||||
self._meta_data[key]['title'] = cn_title
|
self._meta_data[key]['title'] = cn_title
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.save()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from app.core.config import settings
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules.filemanager import StorageBase
|
from app.modules.filemanager import StorageBase
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
@@ -29,7 +29,7 @@ class SessionInvalidException(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AliPan(StorageBase, metaclass=Singleton):
|
class AliPan(StorageBase, metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
阿里云盘相关操作
|
阿里云盘相关操作
|
||||||
"""
|
"""
|
||||||
@@ -43,17 +43,12 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
"copy": "复制"
|
"copy": "复制"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 验证参数
|
|
||||||
_auth_state = {}
|
|
||||||
|
|
||||||
# 上传进度值
|
|
||||||
_last_progress = 0
|
|
||||||
|
|
||||||
# 基础url
|
# 基础url
|
||||||
base_url = "https://openapi.alipan.com"
|
base_url = "https://openapi.alipan.com"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self._auth_state = {}
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self._init_session()
|
self._init_session()
|
||||||
|
|
||||||
@@ -244,6 +239,7 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
conf = self.get_conf()
|
conf = self.get_conf()
|
||||||
conf.update(result)
|
conf.update(result)
|
||||||
self.set_config(conf)
|
self.set_config(conf)
|
||||||
|
return None
|
||||||
|
|
||||||
def _request_api(self, method: str, endpoint: str,
|
def _request_api(self, method: str, endpoint: str,
|
||||||
result_key: Optional[str] = None, **kwargs) -> Optional[Union[dict, list]]:
|
result_key: Optional[str] = None, **kwargs) -> Optional[Union[dict, list]]:
|
||||||
@@ -369,7 +365,7 @@ class AliPan(StorageBase, metaclass=Singleton):
|
|||||||
break
|
break
|
||||||
next_marker = resp.get("next_marker")
|
next_marker = resp.get("next_marker")
|
||||||
for item in resp.get("items", []):
|
for item in resp.get("items", []):
|
||||||
items.append(self.__get_fileitem(item, parent=fileitem.path))
|
items.append(self.__get_fileitem(item, parent=str(fileitem.path)))
|
||||||
if len(resp.get("items")) < 100:
|
if len(resp.get("items")) < 100:
|
||||||
break
|
break
|
||||||
return items
|
return items
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ from app.log import logger
|
|||||||
from app.modules.filemanager.storages import StorageBase
|
from app.modules.filemanager.storages import StorageBase
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.utils.url import UrlUtils
|
from app.utils.url import UrlUtils
|
||||||
|
|
||||||
|
|
||||||
class Alist(StorageBase, metaclass=Singleton):
|
class Alist(StorageBase, metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
Alist相关操作
|
Alist相关操作
|
||||||
api文档:https://oplist.org/zh/
|
api文档:https://oplist.org/zh/
|
||||||
@@ -38,7 +38,7 @@ class Alist(StorageBase, metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
初始化
|
初始化
|
||||||
"""
|
"""
|
||||||
self.__generate_token.clear_cache()
|
self.__generate_token.clear_cache() # noqa
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def __get_base_url(self) -> str:
|
def __get_base_url(self) -> str:
|
||||||
|
|||||||
@@ -12,17 +12,19 @@ from app.core.config import settings
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules.filemanager import StorageBase
|
from app.modules.filemanager import StorageBase
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
class SMBConnectionError(Exception):
|
class SMBConnectionError(Exception):
|
||||||
"""SMB 连接错误"""
|
"""
|
||||||
|
SMB 连接错误
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SMB(StorageBase, metaclass=Singleton):
|
class SMB(StorageBase, metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
SMB网络挂载存储相关操作 - 使用 smbclient 高级接口
|
SMB网络挂载存储相关操作 - 使用 smbclient 高级接口
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from app.core.config import settings
|
|||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.modules.filemanager import StorageBase
|
from app.modules.filemanager import StorageBase
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
@@ -28,7 +28,7 @@ class NoCheckInException(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class U115Pan(StorageBase, metaclass=Singleton):
|
class U115Pan(StorageBase, metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
115相关操作
|
115相关操作
|
||||||
"""
|
"""
|
||||||
@@ -41,18 +41,12 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
"move": "移动",
|
"move": "移动",
|
||||||
"copy": "复制"
|
"copy": "复制"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 验证参数
|
|
||||||
_auth_state = {}
|
|
||||||
|
|
||||||
# 上传进度值
|
|
||||||
_last_progress = 0
|
|
||||||
|
|
||||||
# 基础url
|
# 基础url
|
||||||
base_url = "https://proapi.115.com"
|
base_url = "https://proapi.115.com"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self._auth_state = {}
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self._init_session()
|
self._init_session()
|
||||||
|
|
||||||
@@ -492,7 +486,8 @@ class U115Pan(StorageBase, metaclass=Singleton):
|
|||||||
type="file" if info_resp["file_category"] == "1" else "dir",
|
type="file" if info_resp["file_category"] == "1" else "dir",
|
||||||
name=info_resp["file_name"],
|
name=info_resp["file_name"],
|
||||||
basename=Path(info_resp["file_name"]).stem,
|
basename=Path(info_resp["file_name"]).stem,
|
||||||
extension=Path(info_resp["file_name"]).suffix[1:] if info_resp["file_category"] == "1" else None,
|
extension=Path(info_resp["file_name"]).suffix[1:] if info_resp[
|
||||||
|
"file_category"] == "1" else None,
|
||||||
pickcode=info_resp["pick_code"],
|
pickcode=info_resp["pick_code"],
|
||||||
size=StringUtils.num_filesize(info_resp['size']) if info_resp["file_category"] == "1" else None,
|
size=StringUtils.num_filesize(info_resp['size']) if info_resp["file_category"] == "1" else None,
|
||||||
modify_time=info_resp["utime"]
|
modify_time=info_resp["utime"]
|
||||||
|
|||||||
@@ -7,19 +7,19 @@ from ruamel.yaml import CommentedMap
|
|||||||
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
|
|
||||||
|
|
||||||
class CategoryHelper(metaclass=Singleton):
|
class CategoryHelper(metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
二级分类
|
二级分类
|
||||||
"""
|
"""
|
||||||
_categorys = {}
|
|
||||||
_movie_categorys = {}
|
|
||||||
_tv_categorys = {}
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._category_path: Path = settings.CONFIG_PATH / "category.yaml"
|
self._category_path: Path = settings.CONFIG_PATH / "category.yaml"
|
||||||
|
self._categorys = {}
|
||||||
|
self._movie_categorys = {}
|
||||||
|
self._tv_categorys = {}
|
||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
@@ -69,7 +69,7 @@ class CategoryHelper(metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
if not self._movie_categorys:
|
if not self._movie_categorys:
|
||||||
return []
|
return []
|
||||||
return self._movie_categorys.keys()
|
return list(self._movie_categorys.keys())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tv_categorys(self) -> list:
|
def tv_categorys(self) -> list:
|
||||||
@@ -78,7 +78,7 @@ class CategoryHelper(metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
if not self._tv_categorys:
|
if not self._tv_categorys:
|
||||||
return []
|
return []
|
||||||
return self._tv_categorys.keys()
|
return list(self._tv_categorys.keys())
|
||||||
|
|
||||||
def get_movie_category(self, tmdb_info) -> str:
|
def get_movie_category(self, tmdb_info) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -127,7 +127,7 @@ class CategoryHelper(metaclass=Singleton):
|
|||||||
continue
|
continue
|
||||||
elif attr == "production_countries":
|
elif attr == "production_countries":
|
||||||
# 制片国家
|
# 制片国家
|
||||||
info_values = [str(val.get("iso_3166_1")).upper() for val in info_value]
|
info_values = [str(val.get("iso_3166_1")).upper() for val in info_value] # type: ignore
|
||||||
else:
|
else:
|
||||||
if isinstance(info_value, list):
|
if isinstance(info_value, list):
|
||||||
info_values = [str(val).upper() for val in info_value]
|
info_values = [str(val).upper() for val in info_value]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from typing import Optional
|
|||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import WeakSingleton
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
lock = RLock()
|
lock = RLock()
|
||||||
@@ -18,7 +18,7 @@ CACHE_EXPIRE_TIMESTAMP_STR = "cache_expire_timestamp"
|
|||||||
EXPIRE_TIMESTAMP = settings.CONF.meta
|
EXPIRE_TIMESTAMP = settings.CONF.meta
|
||||||
|
|
||||||
|
|
||||||
class TmdbCache(metaclass=Singleton):
|
class TmdbCache(metaclass=WeakSingleton):
|
||||||
"""
|
"""
|
||||||
TMDB缓存数据
|
TMDB缓存数据
|
||||||
{
|
{
|
||||||
@@ -28,9 +28,6 @@ class TmdbCache(metaclass=Singleton):
|
|||||||
"type": MediaType
|
"type": MediaType
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
_meta_data: dict = {}
|
|
||||||
# 缓存文件路径
|
|
||||||
_meta_path: Path = None
|
|
||||||
# TMDB缓存过期
|
# TMDB缓存过期
|
||||||
_tmdb_cache_expire: bool = True
|
_tmdb_cache_expire: bool = True
|
||||||
|
|
||||||
@@ -218,3 +215,6 @@ class TmdbCache(metaclass=Singleton):
|
|||||||
if not cache_media_info:
|
if not cache_media_info:
|
||||||
return
|
return
|
||||||
self._meta_data[key]['title'] = cn_title
|
self._meta_data[key]['title'] = cn_title
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.save()
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import threading
|
||||||
|
import weakref
|
||||||
|
|
||||||
|
|
||||||
class Singleton(abc.ABCMeta, type):
|
class Singleton(abc.ABCMeta, type):
|
||||||
@@ -40,3 +42,17 @@ class AbstractSingletonClass(abc.ABC, metaclass=SingletonClass):
|
|||||||
抽像类单例模式(按类)
|
抽像类单例模式(按类)
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WeakSingleton(abc.ABCMeta, type):
|
||||||
|
"""
|
||||||
|
弱引用单例模式 - 当没有强引用时自动清理
|
||||||
|
"""
|
||||||
|
_instances: weakref.WeakKeyDictionary = weakref.WeakKeyDictionary()
|
||||||
|
_lock = threading.RLock()
|
||||||
|
|
||||||
|
def __call__(cls, *args, **kwargs):
|
||||||
|
with cls._lock:
|
||||||
|
if cls not in cls._instances:
|
||||||
|
cls._instances[cls] = super().__call__(*args, **kwargs)
|
||||||
|
return cls._instances[cls]
|
||||||
|
|||||||
Reference in New Issue
Block a user