mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
refactor(config): retire RuntimeSettingsCompat host usage
This commit is contained in:
@@ -10,9 +10,8 @@ from typing import Any, Optional, Tuple, Union
|
||||
from uuid import UUID
|
||||
|
||||
from app.runtime.execution import run_in_threadpool
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.modules import _ModuleBase
|
||||
from app.schemas.types import ModuleType, OtherModulesType
|
||||
@@ -71,19 +70,19 @@ class AcoustIdModule(_ModuleBase):
|
||||
模块初始化早于 fpcalc 安装,或运行期依赖被移除,测试也能如实反映本地
|
||||
依赖状态,而不是只校验网络连通性。
|
||||
"""
|
||||
if not str(settings.ACOUSTID_API_KEY or "").strip():
|
||||
if not str(get_runtime_setting('ACOUSTID_API_KEY') or "").strip():
|
||||
return False, "AcoustID API Key 未配置"
|
||||
fpcalc_path = self._resolve_fpcalc()
|
||||
if not fpcalc_path:
|
||||
return False, "未找到 fpcalc,请先安装 Chromaprint"
|
||||
self._fpcalc_path = fpcalc_path
|
||||
response = RequestUtils(
|
||||
ua=settings.USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=15,
|
||||
).get_res(
|
||||
url=self._base_url,
|
||||
params={"client": settings.ACOUSTID_API_KEY, "format": "json"},
|
||||
params={"client": get_runtime_setting('ACOUSTID_API_KEY'), "format": "json"},
|
||||
)
|
||||
if response is None:
|
||||
return False, "AcoustID 网络连接失败"
|
||||
@@ -298,13 +297,13 @@ class AcoustIdModule(_ModuleBase):
|
||||
fingerprint: str,
|
||||
) -> Optional[str]:
|
||||
"""查询 AcoustID 指纹库并提取 MusicBrainz Recording ID。"""
|
||||
api_key = str(settings.ACOUSTID_API_KEY or "").strip()
|
||||
api_key = str(get_runtime_setting('ACOUSTID_API_KEY') or "").strip()
|
||||
if not api_key:
|
||||
return None
|
||||
self._wait_for_rate_limit()
|
||||
response = RequestUtils(
|
||||
ua=settings.USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=30,
|
||||
).post_res(
|
||||
url=self._base_url,
|
||||
@@ -337,13 +336,13 @@ class AcoustIdModule(_ModuleBase):
|
||||
fingerprint: str,
|
||||
) -> Optional[str]:
|
||||
"""异步查询 AcoustID 指纹库并提取 MusicBrainz Recording ID。"""
|
||||
api_key = str(settings.ACOUSTID_API_KEY or "").strip()
|
||||
api_key = str(get_runtime_setting('ACOUSTID_API_KEY') or "").strip()
|
||||
if not api_key:
|
||||
return None
|
||||
await self._async_wait_for_rate_limit()
|
||||
response = await AsyncRequestUtils(
|
||||
ua=settings.USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=30,
|
||||
).post_res(
|
||||
url=self._base_url,
|
||||
|
||||
@@ -77,7 +77,7 @@ class AniListModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param media_source: 请求级识别数据源
|
||||
:return: 是否启用 AniList 识别
|
||||
"""
|
||||
return (media_source or get_runtime_setting("RECOGNIZE_SOURCE")) == MediaSource.AniList
|
||||
return (media_source or get_runtime_setting('RECOGNIZE_SOURCE')) == MediaSource.AniList
|
||||
|
||||
@staticmethod
|
||||
def _media_type(info: dict) -> MediaType:
|
||||
@@ -570,7 +570,7 @@ class AniListModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param episode: 集号
|
||||
:return: NFO XML 文本
|
||||
"""
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')
|
||||
if scrape_source != "anilist":
|
||||
return None
|
||||
return self.scraper.get_metadata_nfo(mediainfo, season=season, episode=episode)
|
||||
@@ -589,7 +589,7 @@ class AniListModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param episode: 集号
|
||||
:return: 图片文件名与下载地址映射
|
||||
"""
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')
|
||||
if scrape_source != "anilist":
|
||||
return None
|
||||
return self.scraper.get_metadata_img(mediainfo, season=season, episode=episode)
|
||||
|
||||
@@ -2,9 +2,8 @@ from datetime import date
|
||||
from typing import Optional
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.adapters.network.http import AsyncRequestUtils, RequestUtils
|
||||
|
||||
@@ -103,16 +102,16 @@ class AniListApi:
|
||||
def __init__(self) -> None:
|
||||
"""初始化同步与异步请求客户端"""
|
||||
headers = {
|
||||
"User-Agent": settings.NORMAL_USER_AGENT,
|
||||
"User-Agent": get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
self._request = RequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
headers=headers,
|
||||
)
|
||||
self._async_request = AsyncRequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
headers=headers,
|
||||
)
|
||||
self._proxy_available = True
|
||||
@@ -363,8 +362,8 @@ class AniListApi:
|
||||
return seasons[(current.month - 1) // 3], current.year
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="detail",
|
||||
)
|
||||
@@ -380,8 +379,8 @@ class AniListApi:
|
||||
return result.get("Media") if result else None
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="detail",
|
||||
)
|
||||
@@ -397,8 +396,8 @@ class AniListApi:
|
||||
return result.get("Media") if result else None
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="search",
|
||||
)
|
||||
@@ -421,8 +420,8 @@ class AniListApi:
|
||||
return self._page_medias(result)
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="search",
|
||||
)
|
||||
@@ -445,8 +444,8 @@ class AniListApi:
|
||||
return self._page_medias(result)
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="discover",
|
||||
)
|
||||
@@ -483,8 +482,8 @@ class AniListApi:
|
||||
return self._page_medias(self._invoke(self._page_query, variables))
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="discover",
|
||||
)
|
||||
@@ -576,8 +575,8 @@ class AniListApi:
|
||||
)
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="credits",
|
||||
)
|
||||
@@ -606,8 +605,8 @@ class AniListApi:
|
||||
return result.get("Media", {}).get("characters", {}).get("edges") or [] if result else []
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="credits",
|
||||
)
|
||||
@@ -636,8 +635,8 @@ class AniListApi:
|
||||
return result.get("Media", {}).get("characters", {}).get("edges") or [] if result else []
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="recommendations",
|
||||
)
|
||||
@@ -662,8 +661,8 @@ class AniListApi:
|
||||
return self._medias_by_ids(media_ids)
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="recommendations",
|
||||
)
|
||||
@@ -688,8 +687,8 @@ class AniListApi:
|
||||
return await self._async_medias_by_ids(media_ids)
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="person_detail",
|
||||
)
|
||||
@@ -713,8 +712,8 @@ class AniListApi:
|
||||
return result.get("Staff") if result else None
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="person_detail",
|
||||
)
|
||||
@@ -738,8 +737,8 @@ class AniListApi:
|
||||
return result.get("Staff") if result else None
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="person_credits",
|
||||
)
|
||||
@@ -763,8 +762,8 @@ class AniListApi:
|
||||
return self._medias_by_ids([node.get("id") for node in nodes])
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.anilist,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').anilist,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_empty=True,
|
||||
shared_key="person_credits",
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
"""
|
||||
初始化Bangumi客户端
|
||||
"""
|
||||
self._config = BangumiConfigSnapshot(proxy=get_runtime_setting("PROXY"))
|
||||
self._config = BangumiConfigSnapshot(proxy=get_runtime_setting('PROXY'))
|
||||
self.bangumiapi = BangumiApi()
|
||||
self.scraper = MediaScraperHelper()
|
||||
|
||||
@@ -126,7 +126,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
return None
|
||||
bangumiid = int(media_id) if media_id is not None else None
|
||||
if not bangumiid and (
|
||||
not meta or (media_source or get_runtime_setting("RECOGNIZE_SOURCE")) != MediaSource.Bangumi
|
||||
not meta or (media_source or get_runtime_setting('RECOGNIZE_SOURCE')) != MediaSource.Bangumi
|
||||
):
|
||||
return None
|
||||
|
||||
@@ -175,7 +175,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
return None
|
||||
bangumiid = int(media_id) if media_id is not None else None
|
||||
if not bangumiid and (
|
||||
not meta or (media_source or get_runtime_setting("RECOGNIZE_SOURCE")) != MediaSource.Bangumi
|
||||
not meta or (media_source or get_runtime_setting('RECOGNIZE_SOURCE')) != MediaSource.Bangumi
|
||||
):
|
||||
return None
|
||||
|
||||
@@ -316,7 +316,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param episode: 集号
|
||||
:return: NFO XML文本
|
||||
"""
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')
|
||||
if scrape_source != "bangumi":
|
||||
return None
|
||||
return self.scraper.get_metadata_nfo(mediainfo, season=season, episode=episode)
|
||||
@@ -335,7 +335,7 @@ class BangumiModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param episode: 集号
|
||||
:return: 图片文件名与下载地址映射
|
||||
"""
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")
|
||||
scrape_source = mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')
|
||||
if scrape_source != "bangumi":
|
||||
return None
|
||||
return self.scraper.get_metadata_img(mediainfo, season=season, episode=episode)
|
||||
|
||||
@@ -4,9 +4,8 @@ from typing import Optional
|
||||
import requests
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
||||
|
||||
|
||||
@@ -33,16 +32,16 @@ class BangumiApi(object):
|
||||
def __init__(self):
|
||||
self._session = requests.Session()
|
||||
self._req = RequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
session=self._session,
|
||||
)
|
||||
self._async_req = AsyncRequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
)
|
||||
|
||||
@cached(maxsize=settings.CONF.bangumi, ttl=settings.CONF.meta, shared_key="get")
|
||||
@cached(maxsize=get_runtime_setting('CONF').bangumi, ttl=get_runtime_setting('CONF').meta, shared_key="get")
|
||||
def __invoke(self, url, key: Optional[str] = None, **kwargs):
|
||||
req_url = self._base_url + url
|
||||
params = {}
|
||||
@@ -58,7 +57,7 @@ class BangumiApi(object):
|
||||
print(e)
|
||||
return None
|
||||
|
||||
@cached(maxsize=settings.CONF.bangumi, ttl=settings.CONF.meta, shared_key="get")
|
||||
@cached(maxsize=get_runtime_setting('CONF').bangumi, ttl=get_runtime_setting('CONF').meta, shared_key="get")
|
||||
async def __async_invoke(self, url, key: Optional[str] = None, **kwargs):
|
||||
req_url = self._base_url + url
|
||||
params = {}
|
||||
|
||||
@@ -8,9 +8,8 @@ import discord
|
||||
from discord import app_commands
|
||||
|
||||
from app.runtime.execution import run_in_threadpool
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.messaging.ingress import async_forward_message_to_host
|
||||
from app.domain.context import MediaInfo, Context
|
||||
from app.domain.metainfo import MetaInfo
|
||||
@@ -72,7 +71,7 @@ class Discord:
|
||||
intents.guilds = True
|
||||
|
||||
self._client: Optional[discord.Client] = discord.Client(
|
||||
intents=intents, proxy=settings.PROXY_HOST
|
||||
intents=intents, proxy=get_runtime_setting('PROXY_HOST')
|
||||
)
|
||||
self._tree: Optional[app_commands.CommandTree] = app_commands.CommandTree(self._client)
|
||||
self._loop: asyncio.AbstractEventLoop = asyncio.new_event_loop()
|
||||
|
||||
@@ -706,7 +706,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
if (
|
||||
meta
|
||||
and not doubanid
|
||||
and (kwargs.get("media_source") or get_runtime_setting("RECOGNIZE_SOURCE")) != "douban"
|
||||
and (kwargs.get("media_source") or get_runtime_setting('RECOGNIZE_SOURCE')) != "douban"
|
||||
):
|
||||
return None
|
||||
|
||||
@@ -777,7 +777,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
if (
|
||||
meta
|
||||
and not doubanid
|
||||
and (kwargs.get("media_source") or get_runtime_setting("RECOGNIZE_SOURCE")) != "douban"
|
||||
and (kwargs.get("media_source") or get_runtime_setting('RECOGNIZE_SOURCE')) != "douban"
|
||||
):
|
||||
return None
|
||||
|
||||
@@ -1684,7 +1684,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param mediainfo: 媒体信息
|
||||
:param season: 季号
|
||||
"""
|
||||
if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "douban":
|
||||
if (mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')) != "douban":
|
||||
return None
|
||||
return self.scraper.get_metadata_nfo(mediainfo=mediainfo, season=season)
|
||||
|
||||
@@ -1695,7 +1695,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param season: 季号
|
||||
:param episode: 集号
|
||||
"""
|
||||
if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "douban":
|
||||
if (mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')) != "douban":
|
||||
return None
|
||||
return self.scraper.get_metadata_img(mediainfo=mediainfo, season=season, episode=episode)
|
||||
|
||||
@@ -1706,7 +1706,7 @@ class DoubanModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param mediainfo: 媒体信息
|
||||
:return: None 表示不处理,MediaInfo 表示继续处理
|
||||
"""
|
||||
if mediainfo.media_source != MediaSource.Douban and get_runtime_setting("RECOGNIZE_SOURCE") != "douban":
|
||||
if mediainfo.media_source != MediaSource.Douban and get_runtime_setting('RECOGNIZE_SOURCE') != "douban":
|
||||
return None
|
||||
if not mediainfo.douban_id:
|
||||
return None
|
||||
|
||||
+13
-14
@@ -13,9 +13,8 @@ import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
||||
from app.foundation.singleton import WeakSingleton
|
||||
|
||||
@@ -233,7 +232,7 @@ class DoubanApi(metaclass=WeakSingleton):
|
||||
"""
|
||||
return resp.json() if resp is not None else None
|
||||
|
||||
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta, skip_none=True, shared_key="get")
|
||||
@cached(maxsize=get_runtime_setting('CONF').douban, ttl=get_runtime_setting('CONF').meta, skip_none=True, shared_key="get")
|
||||
def __invoke(self, url: str, **kwargs) -> dict:
|
||||
"""
|
||||
GET请求
|
||||
@@ -245,7 +244,7 @@ class DoubanApi(metaclass=WeakSingleton):
|
||||
).get_res(url=req_url, params=params)
|
||||
return self._handle_response(resp)
|
||||
|
||||
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta, skip_none=True, shared_key="get")
|
||||
@cached(maxsize=get_runtime_setting('CONF').douban, ttl=get_runtime_setting('CONF').meta, skip_none=True, shared_key="get")
|
||||
async def __async_invoke(self, url: str, **kwargs) -> dict:
|
||||
"""
|
||||
GET请求(异步版本)
|
||||
@@ -268,7 +267,7 @@ class DoubanApi(metaclass=WeakSingleton):
|
||||
params.pop('_ts')
|
||||
return req_url, params
|
||||
|
||||
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta, skip_none=True, shared_key="post")
|
||||
@cached(maxsize=get_runtime_setting('CONF').douban, ttl=get_runtime_setting('CONF').meta, skip_none=True, shared_key="post")
|
||||
def __post(self, url: str, **kwargs) -> dict:
|
||||
"""
|
||||
POST请求
|
||||
@@ -285,19 +284,19 @@ class DoubanApi(metaclass=WeakSingleton):
|
||||
"""
|
||||
req_url, params = self._prepare_post_request(url, **kwargs)
|
||||
resp = RequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT,
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
session=self._session,
|
||||
).post_res(url=req_url, data=params)
|
||||
return self._handle_response(resp)
|
||||
|
||||
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta, skip_none=True, shared_key="post")
|
||||
@cached(maxsize=get_runtime_setting('CONF').douban, ttl=get_runtime_setting('CONF').meta, skip_none=True, shared_key="post")
|
||||
async def __async_post(self, url: str, **kwargs) -> dict:
|
||||
"""
|
||||
POST请求(异步版本)
|
||||
"""
|
||||
req_url, params = self._prepare_post_request(url, **kwargs)
|
||||
resp = await AsyncRequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT')
|
||||
).post_res(url=req_url, data=params)
|
||||
return self._handle_response(resp)
|
||||
|
||||
@@ -644,7 +643,7 @@ class DoubanApi(metaclass=WeakSingleton):
|
||||
self._urls["music_single"], start=start, count=count
|
||||
)
|
||||
|
||||
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta, skip_none=True)
|
||||
@cached(maxsize=get_runtime_setting('CONF').douban, ttl=get_runtime_setting('CONF').meta, skip_none=True)
|
||||
def music_tag(
|
||||
self,
|
||||
tag: str,
|
||||
@@ -665,8 +664,8 @@ class DoubanApi(metaclass=WeakSingleton):
|
||||
while len(items) < required:
|
||||
url = f"{self._music_web_url}/tag/{parse.quote(normalized_tag, safe='')}"
|
||||
response = RequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=20,
|
||||
accept_type="text/html,application/xhtml+xml",
|
||||
).get_res(url=url, params={"start": page * page_size, "type": sort})
|
||||
@@ -679,12 +678,12 @@ class DoubanApi(metaclass=WeakSingleton):
|
||||
page += 1
|
||||
return {"items": items[first_offset:first_offset + max(count, 1)]}
|
||||
|
||||
@cached(maxsize=settings.CONF.douban, ttl=settings.CONF.meta, skip_none=True)
|
||||
@cached(maxsize=get_runtime_setting('CONF').douban, ttl=get_runtime_setting('CONF').meta, skip_none=True)
|
||||
def music_chart(self) -> dict:
|
||||
"""从豆瓣音乐官方榜单页读取新碟榜,并补充专辑详情供卡片展示。"""
|
||||
response = RequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=20,
|
||||
accept_type="text/html,application/xhtml+xml",
|
||||
).get_res(url=f"{self._music_web_url}/chart")
|
||||
|
||||
@@ -12,9 +12,8 @@ from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibr
|
||||
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
|
||||
from app.schemas.mediaserver import RefreshMediaItem as _SchemaRefreshMediaItem
|
||||
from app.schemas.mediaserver import WebhookEventInfo as _SchemaWebhookEventInfo
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.mediaserver import MediaServerIdentityHelper, format_emby_family_item
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.mediaserver import MediaServerItem
|
||||
@@ -44,7 +43,7 @@ class Emby:
|
||||
self._playhost = UrlUtils.standardize_base_url(self._playhost)
|
||||
self._apikey = apikey
|
||||
self._username = username
|
||||
self.user = self.get_user(username or settings.SUPERUSER)
|
||||
self.user = self.get_user(username or get_runtime_setting('SUPERUSER'))
|
||||
self.folders = self.get_emby_folders()
|
||||
self.serverid = self.get_server_id()
|
||||
self._sync_libraries = sync_libraries or []
|
||||
|
||||
@@ -4,9 +4,8 @@ from typing import Optional, Tuple, Union
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.domain.context import MediaInfo
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.tasks import get_task_registry
|
||||
from app.modules import _ModuleBase
|
||||
@@ -309,14 +308,14 @@ class FanartModule(_ModuleBase):
|
||||
"""
|
||||
|
||||
# 代理
|
||||
_proxies: dict = settings.PROXY
|
||||
_proxies: dict = get_runtime_setting('PROXY')
|
||||
|
||||
# Fanart Api
|
||||
_movie_url: str = (
|
||||
f"https://webservice.fanart.tv/v3/movies/%s?api_key={settings.FANART_API_KEY}"
|
||||
f"https://webservice.fanart.tv/v3/movies/%s?api_key={get_runtime_setting('FANART_API_KEY')}"
|
||||
)
|
||||
_tv_url: str = (
|
||||
f"https://webservice.fanart.tv/v3/tv/%s?api_key={settings.FANART_API_KEY}"
|
||||
f"https://webservice.fanart.tv/v3/tv/%s?api_key={get_runtime_setting('FANART_API_KEY')}"
|
||||
)
|
||||
|
||||
def init_module(self) -> None:
|
||||
@@ -451,7 +450,7 @@ class FanartModule(_ModuleBase):
|
||||
"""
|
||||
获取 Fanart 查询参数
|
||||
"""
|
||||
if not settings.FANART_ENABLE:
|
||||
if not get_runtime_setting('FANART_ENABLE'):
|
||||
return None
|
||||
if not mediainfo.tmdb_id and not mediainfo.tvdb_id:
|
||||
return None
|
||||
@@ -532,7 +531,7 @@ class FanartModule(_ModuleBase):
|
||||
"""
|
||||
其他图片,优先环境变量指定语言,再like最多
|
||||
"""
|
||||
lang_env = settings.FANART_LANG
|
||||
lang_env = get_runtime_setting('FANART_LANG')
|
||||
if lang_env:
|
||||
langs = [lang.strip() for lang in lang_env.split(",") if lang.strip()]
|
||||
for lang in langs:
|
||||
@@ -582,7 +581,7 @@ class FanartModule(_ModuleBase):
|
||||
return cls._FANART_NAME_MAP.get(fanart_name.lower(), fanart_name)
|
||||
|
||||
@classmethod
|
||||
@cached(maxsize=settings.CONF.fanart, ttl=settings.CONF.meta, shared_key="get")
|
||||
@cached(maxsize=get_runtime_setting('CONF').fanart, ttl=get_runtime_setting('CONF').meta, shared_key="get")
|
||||
def __request_fanart(
|
||||
cls, media_type: MediaType, queryid: Union[str, int]
|
||||
) -> Optional[dict]:
|
||||
@@ -601,7 +600,7 @@ class FanartModule(_ModuleBase):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
@cached(maxsize=settings.CONF.fanart, ttl=settings.CONF.meta, shared_key="get")
|
||||
@cached(maxsize=get_runtime_setting('CONF').fanart, ttl=get_runtime_setting('CONF').meta, shared_key="get")
|
||||
async def __async_request_fanart(
|
||||
cls, media_type: MediaType, queryid: Union[str, int]
|
||||
) -> Optional[dict]:
|
||||
|
||||
@@ -50,9 +50,8 @@ from lark_oapi.event.callback.model.p2_card_action_trigger import (
|
||||
P2CardActionTriggerResponse,
|
||||
)
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.messaging.ingress import submit_message_to_host
|
||||
from app.domain.context import Context, MediaInfo
|
||||
from app.application.security.user import get_configured_user_channel_lookup
|
||||
@@ -1065,7 +1064,7 @@ class Feishu:
|
||||
response = None
|
||||
temp_path = None
|
||||
try:
|
||||
response = RequestUtils(timeout=30, ua=settings.USER_AGENT).get_res(image_url)
|
||||
response = RequestUtils(timeout=30, ua=get_runtime_setting('USER_AGENT')).get_res(image_url)
|
||||
if not response or not getattr(response, "content", None):
|
||||
logger.warning(f"飞书图片下载失败:{image_url}")
|
||||
return None
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, List, Tuple, Union, Dict, Callable
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import MediaInfo, MusicInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
@@ -191,7 +190,7 @@ class FileManagerModule(_ModuleBase):
|
||||
"""
|
||||
handler = TransHandler()
|
||||
# 重命名格式
|
||||
rename_format = settings.RENAME_FORMAT(mediainfo.type)
|
||||
rename_format = get_runtime_setting('RENAME_FORMAT')(mediainfo.type)
|
||||
# 获取重命名后的名称
|
||||
path = handler.get_rename_path(
|
||||
template_string=rename_format,
|
||||
@@ -631,7 +630,7 @@ class FileManagerModule(_ModuleBase):
|
||||
# 媒体分类路径
|
||||
dir_path = handler.get_dest_dir(mediainfo=mediainfo, target_dir=dest_dir)
|
||||
# 重命名格式
|
||||
rename_format = settings.RENAME_FORMAT(mediainfo.type)
|
||||
rename_format = get_runtime_setting('RENAME_FORMAT')(mediainfo.type)
|
||||
# 元数据补上常用属性,尽可能确保重命名后的路径不出现空白
|
||||
meta = self._build_library_lookup_meta(mediainfo)
|
||||
# 获取路径(重命名路径)
|
||||
@@ -665,9 +664,9 @@ class FileManagerModule(_ModuleBase):
|
||||
continue
|
||||
if media_files:
|
||||
media_extensions = (
|
||||
settings.RMT_AUDIOEXT
|
||||
get_runtime_setting('RMT_AUDIOEXT')
|
||||
if mediainfo.type == MediaType.MUSIC
|
||||
else settings.RMT_MEDIAEXT
|
||||
else get_runtime_setting('RMT_MEDIAEXT')
|
||||
)
|
||||
for media_file in media_files:
|
||||
if (
|
||||
@@ -692,7 +691,7 @@ class FileManagerModule(_ModuleBase):
|
||||
if kwargs.get("server"):
|
||||
return None
|
||||
|
||||
if not settings.LOCAL_EXISTS_SEARCH:
|
||||
if not get_runtime_setting('LOCAL_EXISTS_SEARCH'):
|
||||
return None
|
||||
|
||||
logger.debug(f"正在本地媒体库中查找 {mediainfo.title_year}...")
|
||||
|
||||
@@ -8,19 +8,17 @@ from typing import List, Optional, Tuple, Union
|
||||
|
||||
import requests
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.foundation import temporal as time_tools
|
||||
from app.foundation.singleton import WeakSingleton
|
||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.exception import StorageQueryError
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.types import StorageSchema
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
lock = threading.Lock()
|
||||
|
||||
@@ -48,7 +46,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
||||
base_url = "https://openapi.alipan.com"
|
||||
|
||||
# 阿里云盘目录时间不随子文件变更而更新,默认关闭目录修改时间检查
|
||||
snapshot_check_folder_modtime = settings.ALIPAN_SNAPSHOT_CHECK_FOLDER_MODTIME
|
||||
snapshot_check_folder_modtime = get_runtime_setting('ALIPAN_SNAPSHOT_CHECK_FOLDER_MODTIME')
|
||||
|
||||
# 文件块大小,默认10MB
|
||||
chunk_size = 10 * 1024 * 1024
|
||||
@@ -117,7 +115,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
||||
resp = self.session.post(
|
||||
f"{self.base_url}/oauth/authorize/qrcode",
|
||||
json={
|
||||
"client_id": settings.ALIPAN_APP_ID,
|
||||
"client_id": get_runtime_setting('ALIPAN_APP_ID'),
|
||||
"scopes": [
|
||||
"user:base",
|
||||
"file:all:read",
|
||||
@@ -181,7 +179,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
||||
resp = self.session.post(
|
||||
f"{self.base_url}/oauth/access_token",
|
||||
json={
|
||||
"client_id": settings.ALIPAN_APP_ID,
|
||||
"client_id": get_runtime_setting('ALIPAN_APP_ID'),
|
||||
"grant_type": "authorization_code",
|
||||
"code": self._auth_state["authCode"],
|
||||
"code_verifier": self._auth_state["code_verifier"],
|
||||
@@ -205,7 +203,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
||||
resp = self.session.post(
|
||||
f"{self.base_url}/oauth/access_token",
|
||||
json={
|
||||
"client_id": settings.ALIPAN_APP_ID,
|
||||
"client_id": get_runtime_setting('ALIPAN_APP_ID'),
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refresh_token,
|
||||
},
|
||||
@@ -745,7 +743,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
||||
logger.error(f"【阿里云盘】下载链接为空: {fileitem.name}")
|
||||
return None
|
||||
|
||||
local_path = self._build_download_path(fileitem, path or settings.TEMP_PATH)
|
||||
local_path = self._build_download_path(fileitem, path or get_runtime_setting('TEMP_PATH'))
|
||||
if not local_path:
|
||||
return None
|
||||
|
||||
@@ -759,7 +757,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
||||
try:
|
||||
# 构建请求头,包含必要的认证信息
|
||||
headers = {
|
||||
"User-Agent": settings.NORMAL_USER_AGENT,
|
||||
"User-Agent": get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
"Referer": "https://www.aliyundrive.com/",
|
||||
"Accept": "*/*",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
||||
|
||||
@@ -5,20 +5,18 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.foundation.singleton import WeakSingleton
|
||||
from app.foundation.url import UrlUtils
|
||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.exception import OperationInterrupted, StorageQueryError
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.types import StorageSchema
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
# OpenList/AList 在 per_page<=0 时会退回后端默认 200,显式指定最大页大小避免大目录被截断。
|
||||
OPENLIST_MAX_LIST_PAGE_SIZE = 500
|
||||
@@ -41,7 +39,7 @@ class Alist(StorageBase, metaclass=WeakSingleton):
|
||||
}
|
||||
|
||||
# 快照检查目录修改时间
|
||||
snapshot_check_folder_modtime = settings.OPENLIST_SNAPSHOT_CHECK_FOLDER_MODTIME
|
||||
snapshot_check_folder_modtime = get_runtime_setting('OPENLIST_SNAPSHOT_CHECK_FOLDER_MODTIME')
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -692,7 +690,7 @@ class Alist(StorageBase, metaclass=WeakSingleton):
|
||||
download_url = download_url + "?sign=" + result["data"]["sign"]
|
||||
|
||||
if not path:
|
||||
local_path = settings.TEMP_PATH / fileitem.name
|
||||
local_path = get_runtime_setting('TEMP_PATH') / fileitem.name
|
||||
else:
|
||||
local_path = path / fileitem.name
|
||||
|
||||
|
||||
@@ -4,19 +4,17 @@ import time
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.system.fsproxy import fsproxy
|
||||
from app.adapters.system.host import SystemUtils
|
||||
from app.application.directory import DirectoryHelper
|
||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.exception import StorageQueryError
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.types import StorageSchema
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
|
||||
class LocalStorage(StorageBase):
|
||||
@@ -477,7 +475,7 @@ class LocalStorage(StorageBase):
|
||||
total_storage, free_storage = SystemUtils.space_usage(
|
||||
[Path(d.download_path) for d in directory_helper.get_local_download_dirs() if d.download_path] +
|
||||
[Path(d.library_path) for d in directory_helper.get_local_library_dirs() if d.library_path],
|
||||
btrfs_fsid_dedup=settings.BTRFS_FSID_DEDUP,
|
||||
btrfs_fsid_dedup=get_runtime_setting('BTRFS_FSID_DEDUP'),
|
||||
)
|
||||
return _SchemaStorageUsage(
|
||||
total=total_storage,
|
||||
|
||||
@@ -6,17 +6,15 @@ from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.system.host import SystemUtils
|
||||
from app.foundation import temporal as time_tools
|
||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.schemas.exception import StorageQueryError
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.types import StorageSchema
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
_MAX_FOLDER_LOCKS = 4096
|
||||
_folder_locks: OrderedDict[str, threading.Lock] = OrderedDict()
|
||||
@@ -50,7 +48,7 @@ class Rclone(StorageBase):
|
||||
"copy": "复制"
|
||||
}
|
||||
|
||||
snapshot_check_folder_modtime = settings.RCLONE_SNAPSHOT_CHECK_FOLDER_MODTIME
|
||||
snapshot_check_folder_modtime = get_runtime_setting('RCLONE_SNAPSHOT_CHECK_FOLDER_MODTIME')
|
||||
|
||||
def init_storage(self):
|
||||
"""
|
||||
@@ -376,7 +374,7 @@ class Rclone(StorageBase):
|
||||
"""
|
||||
带实时进度显示的下载
|
||||
"""
|
||||
local_path = self._build_download_path(fileitem, path or settings.TEMP_PATH)
|
||||
local_path = self._build_download_path(fileitem, path or get_runtime_setting('TEMP_PATH'))
|
||||
if not local_path:
|
||||
return None
|
||||
|
||||
|
||||
@@ -12,17 +12,15 @@ from smbprotocol.exceptions import (
|
||||
SMBResponseException,
|
||||
)
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.foundation.singleton import WeakSingleton
|
||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.exception import StorageQueryError
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.types import StorageSchema
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
lock = threading.Lock()
|
||||
|
||||
@@ -550,7 +548,7 @@ class SMB(StorageBase, metaclass=WeakSingleton):
|
||||
"""
|
||||
带实时进度显示的下载
|
||||
"""
|
||||
local_path = self._build_download_path(fileitem, path or settings.TEMP_PATH)
|
||||
local_path = self._build_download_path(fileitem, path or get_runtime_setting('TEMP_PATH'))
|
||||
if not local_path:
|
||||
return None
|
||||
smb_path = self._normalize_path(fileitem.path)
|
||||
|
||||
@@ -12,19 +12,17 @@ from cryptography.hazmat.primitives import hashes
|
||||
from oss2 import SizedFileAdapter, determine_part_size
|
||||
from oss2.models import PartInfo
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.foundation import size as size_tools
|
||||
from app.foundation.singleton import WeakSingleton
|
||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.rate import QpsRateLimiter, RateStats
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.stop import runtime_stop_state
|
||||
from app.schemas.exception import StorageQueryError
|
||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||
from app.schemas.types import StorageSchema
|
||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||
|
||||
lock = Lock()
|
||||
|
||||
@@ -130,7 +128,7 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
|
||||
生成 OAuth2 授权 URL
|
||||
"""
|
||||
try:
|
||||
resp = self.session.get(f"{settings.U115_AUTH_SERVER}/u115/auth_url")
|
||||
resp = self.session.get(f"{get_runtime_setting('U115_AUTH_SERVER')}/u115/auth_url")
|
||||
if resp is None:
|
||||
return {}, "无法连接到授权服务器"
|
||||
|
||||
@@ -165,7 +163,7 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
|
||||
resp = self.session.post(
|
||||
"https://passportapi.115.com/open/authDeviceCode",
|
||||
data={
|
||||
"client_id": settings.U115_APP_ID,
|
||||
"client_id": get_runtime_setting('U115_APP_ID'),
|
||||
"code_challenge": code_challenge,
|
||||
"code_challenge_method": "sha256",
|
||||
},
|
||||
@@ -229,7 +227,7 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
|
||||
|
||||
try:
|
||||
resp = self.session.get(
|
||||
f"{settings.U115_AUTH_SERVER}/u115/token", params={"state": state}
|
||||
f"{get_runtime_setting('U115_AUTH_SERVER')}/u115/token", params={"state": state}
|
||||
)
|
||||
if resp is None:
|
||||
return {}, "无法连接到授权服务器"
|
||||
@@ -910,7 +908,7 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
|
||||
logger.error(f"【115】下载链接为空: {fileitem.name}")
|
||||
return None
|
||||
|
||||
local_path = self._build_download_path(fileitem, path or settings.TEMP_PATH)
|
||||
local_path = self._build_download_path(fileitem, path or get_runtime_setting('TEMP_PATH'))
|
||||
if not local_path:
|
||||
return None
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@ from typing import Optional, List, Tuple
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import MediaInfo, MusicInfo
|
||||
from app.runtime.events import eventmanager
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
@@ -204,7 +203,7 @@ class TransHandler:
|
||||
"""
|
||||
if not _fileitem.extension:
|
||||
return False
|
||||
if f".{_fileitem.extension.lower()}" in settings.RMT_SUBEXT:
|
||||
if f".{_fileitem.extension.lower()}" in get_runtime_setting('RMT_SUBEXT'):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -224,11 +223,11 @@ class TransHandler:
|
||||
if not _fileitem.extension:
|
||||
return False
|
||||
extension = f".{_fileitem.extension.lower()}"
|
||||
if extension in settings.RMT_SUBEXT:
|
||||
if extension in get_runtime_setting('RMT_SUBEXT'):
|
||||
return True
|
||||
if __is_music_lyrics_file(_fileitem):
|
||||
return True
|
||||
if mediainfo.type != MediaType.MUSIC and extension in settings.RMT_AUDIOEXT:
|
||||
if mediainfo.type != MediaType.MUSIC and extension in get_runtime_setting('RMT_AUDIOEXT'):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -252,7 +251,7 @@ class TransHandler:
|
||||
|
||||
try:
|
||||
# 重命名格式
|
||||
rename_format = settings.RENAME_FORMAT(mediainfo.type)
|
||||
rename_format = get_runtime_setting('RENAME_FORMAT')(mediainfo.type)
|
||||
|
||||
# 判断是否为文件夹
|
||||
if fileitem.type == "dir":
|
||||
@@ -953,10 +952,10 @@ class TransHandler:
|
||||
|
||||
# 添加默认字幕标识
|
||||
if (
|
||||
(settings.DEFAULT_SUB == "zh-cn" and new_file_type == ".chi.zh-cn")
|
||||
or (settings.DEFAULT_SUB == "zh-tw" and new_file_type == ".zh-tw")
|
||||
or (settings.DEFAULT_SUB == "ja" and new_file_type == ".ja")
|
||||
or (settings.DEFAULT_SUB == "eng" and new_file_type == ".eng")
|
||||
(get_runtime_setting('DEFAULT_SUB') == "zh-cn" and new_file_type == ".chi.zh-cn")
|
||||
or (get_runtime_setting('DEFAULT_SUB') == "zh-tw" and new_file_type == ".zh-tw")
|
||||
or (get_runtime_setting('DEFAULT_SUB') == "ja" and new_file_type == ".ja")
|
||||
or (get_runtime_setting('DEFAULT_SUB') == "eng" and new_file_type == ".eng")
|
||||
):
|
||||
new_sub_tag = ".default" + new_file_type
|
||||
else:
|
||||
@@ -1283,7 +1282,7 @@ class TransHandler:
|
||||
if media_file.type != "file":
|
||||
continue
|
||||
# 当前只有视频文件需要保留最新版本,其余格式无需处理,以避免误删 (issue 5449)
|
||||
if f".{media_file.extension.lower()}" not in settings.RMT_MEDIAEXT:
|
||||
if f".{media_file.extension.lower()}" not in get_runtime_setting('RMT_MEDIAEXT'):
|
||||
continue
|
||||
# 识别文件中的季集信息
|
||||
filemeta = MetaInfoPath(media_path)
|
||||
|
||||
@@ -12,10 +12,8 @@ from app.domain.scraper import MediaScraperHelper
|
||||
from app.foundation.text import convert as zhconv_convert
|
||||
from app.modules import _ModuleBase
|
||||
from app.modules._base.media_auxiliary import MediaAuxiliaryProviderMixin
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.schemas.context import MediaCredit, MediaImageSet
|
||||
from app.schemas.media import normalize_media_source
|
||||
from app.schemas.types import (
|
||||
@@ -59,7 +57,7 @@ class ImdbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
|
||||
def init_module(self) -> None:
|
||||
"""按当前代理配置初始化 IMDb 客户端和通用刮削器。"""
|
||||
self._config = ImdbConfigSnapshot(proxy=settings.PROXY)
|
||||
self._config = ImdbConfigSnapshot(proxy=get_runtime_setting('PROXY'))
|
||||
self.imdb_api = ImdbApi(proxies=self._config.proxy)
|
||||
self.scraper = MediaScraperHelper()
|
||||
|
||||
@@ -517,7 +515,7 @@ class ImdbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
if requested_source not in {None, MediaSource.IMDb}:
|
||||
return None
|
||||
selected_source = requested_source or normalize_media_source(
|
||||
settings.RECOGNIZE_SOURCE
|
||||
get_runtime_setting('RECOGNIZE_SOURCE')
|
||||
)
|
||||
if selected_source != MediaSource.IMDb or not meta or not meta.name:
|
||||
return None
|
||||
@@ -560,7 +558,7 @@ class ImdbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
if requested_source not in {None, MediaSource.IMDb}:
|
||||
return None
|
||||
selected_source = requested_source or normalize_media_source(
|
||||
settings.RECOGNIZE_SOURCE
|
||||
get_runtime_setting('RECOGNIZE_SOURCE')
|
||||
)
|
||||
if selected_source != MediaSource.IMDb or not meta or not meta.name:
|
||||
return None
|
||||
@@ -657,7 +655,7 @@ class ImdbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
) -> Optional[str]:
|
||||
"""生成 IMDb 来源的 NFO 元数据文本。"""
|
||||
del kwargs
|
||||
if (mediainfo.scrape_source or settings.SCRAP_SOURCE) != MediaSource.IMDb.value:
|
||||
if (mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')) != MediaSource.IMDb.value:
|
||||
return None
|
||||
if not self.scraper:
|
||||
return None
|
||||
@@ -672,7 +670,7 @@ class ImdbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
episode: Optional[int] = None,
|
||||
) -> Optional[dict]:
|
||||
"""生成 IMDb 来源的图片文件名与下载地址映射。"""
|
||||
if (mediainfo.scrape_source or settings.SCRAP_SOURCE) != MediaSource.IMDb.value:
|
||||
if (mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')) != MediaSource.IMDb.value:
|
||||
return None
|
||||
if not self.scraper:
|
||||
return None
|
||||
|
||||
+11
-13
@@ -9,11 +9,9 @@ from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
||||
|
||||
from app.adapters.network.http import AsyncRequestUtils, RequestUtils
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.tasks import get_task_registry
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.tasks import get_task_registry
|
||||
|
||||
TModel = TypeVar("TModel", bound=BaseModel)
|
||||
|
||||
@@ -227,7 +225,7 @@ class ImdbApi:
|
||||
def __init__(self, proxies: Optional[dict] = None) -> None:
|
||||
"""按一次模块配置快照创建网络请求适配器。"""
|
||||
headers = {
|
||||
"User-Agent": settings.NORMAL_USER_AGENT,
|
||||
"User-Agent": get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
"Accept": "application/graphql+json, application/json",
|
||||
"Content-Type": "application/json",
|
||||
"x-imdb-client-name": "imdb-web-next-localized",
|
||||
@@ -262,8 +260,8 @@ class ImdbApi:
|
||||
return cls._freeze_value(params or {})
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.imdb,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').imdb,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_none=True,
|
||||
shared_key="imdb_get",
|
||||
)
|
||||
@@ -274,8 +272,8 @@ class ImdbApi:
|
||||
return self._request.get_json(url, params=dict(params_key))
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.imdb,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').imdb,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_none=True,
|
||||
shared_key="imdb_get",
|
||||
)
|
||||
@@ -286,8 +284,8 @@ class ImdbApi:
|
||||
return await self._async_request.get_json(url, params=dict(params_key))
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.imdb,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').imdb,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_none=True,
|
||||
shared_key="imdb_graphql",
|
||||
skip_if=_is_graphql_error,
|
||||
@@ -302,8 +300,8 @@ class ImdbApi:
|
||||
)
|
||||
|
||||
@cached(
|
||||
maxsize=settings.CONF.imdb,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').imdb,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_none=True,
|
||||
shared_key="imdb_graphql",
|
||||
skip_if=_is_graphql_error,
|
||||
|
||||
@@ -8,9 +8,8 @@ from urllib.parse import urljoin, urlsplit
|
||||
|
||||
from requests import Session
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.cloudflare import under_challenge
|
||||
from app.runtime.log import logger
|
||||
from app.adapters.network.http import RequestUtils
|
||||
@@ -228,7 +227,7 @@ class SiteParserBase(metaclass=ABCMeta):
|
||||
)
|
||||
)
|
||||
# 解析用户未读消息
|
||||
if settings.SITE_MESSAGE:
|
||||
if get_runtime_setting('SITE_MESSAGE'):
|
||||
self._pase_unread_msgs()
|
||||
# 解析用户上传、下载、分享率等信息
|
||||
if self._user_traffic_page:
|
||||
@@ -346,7 +345,7 @@ class SiteParserBase(metaclass=ABCMeta):
|
||||
:return:
|
||||
"""
|
||||
req_headers = None
|
||||
proxies = settings.PROXY if self._proxy else None
|
||||
proxies = get_runtime_setting('PROXY') if self._proxy else None
|
||||
if self._ua or headers or self._addition_headers:
|
||||
|
||||
if self.request_mode == "apikey":
|
||||
@@ -408,8 +407,8 @@ class SiteParserBase(metaclass=ABCMeta):
|
||||
f"{self._site_name} 检测到Cloudflare,请更新Cookie和UA")
|
||||
return ""
|
||||
return RequestUtils.get_decoded_html_content(res,
|
||||
settings.ENCODING_DETECTION_PERFORMANCE_MODE,
|
||||
settings.ENCODING_DETECTION_MIN_CONFIDENCE)
|
||||
get_runtime_setting('ENCODING_DETECTION_PERFORMANCE_MODE'),
|
||||
get_runtime_setting('ENCODING_DETECTION_MIN_CONFIDENCE'))
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@ from urllib.parse import urljoin
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.domain import site as site_rules
|
||||
from app.foundation import temporal as time_tools
|
||||
@@ -195,7 +194,7 @@ class RousiSiteUserInfo(SiteParserBase):
|
||||
res = RequestUtils(
|
||||
headers=headers,
|
||||
timeout=60,
|
||||
proxies=settings.PROXY if self._proxy else None
|
||||
proxies=get_runtime_setting('PROXY') if self._proxy else None
|
||||
).get_res(
|
||||
url=urljoin(self._base_url, "api/messages"),
|
||||
params=params
|
||||
@@ -231,7 +230,7 @@ class RousiSiteUserInfo(SiteParserBase):
|
||||
RequestUtils(
|
||||
headers=headers,
|
||||
timeout=60,
|
||||
proxies=settings.PROXY if self._proxy else None
|
||||
proxies=get_runtime_setting('PROXY') if self._proxy else None
|
||||
).post_res(
|
||||
url=urljoin(self._base_url, "api/messages/read-all")
|
||||
)
|
||||
|
||||
@@ -9,9 +9,8 @@ from jinja2 import Template
|
||||
from pyquery import PyQuery
|
||||
|
||||
from app.runtime.execution import run_in_threadpool
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
from app.adapters.system import rust as rust_accel
|
||||
@@ -136,9 +135,9 @@ class SiteSpider:
|
||||
self.page = page
|
||||
if self.domain and not str(self.domain).endswith("/"):
|
||||
self.domain = self.domain + "/"
|
||||
self.ua = indexer.get('ua') or settings.USER_AGENT
|
||||
self.proxies = settings.PROXY if indexer.get('proxy') else None
|
||||
self.proxy_server = settings.PROXY_SERVER if indexer.get('proxy') else None
|
||||
self.ua = indexer.get('ua') or get_runtime_setting('USER_AGENT')
|
||||
self.proxies = get_runtime_setting('PROXY') if indexer.get('proxy') else None
|
||||
self.proxy_server = get_runtime_setting('PROXY_SERVER') if indexer.get('proxy') else None
|
||||
self.cookie = indexer.get('cookie')
|
||||
self.referer = referer
|
||||
# 初始化属性
|
||||
@@ -362,8 +361,8 @@ class SiteSpider:
|
||||
return self.parse(
|
||||
RequestUtils.get_decoded_html_content(
|
||||
ret,
|
||||
performance_mode=settings.ENCODING_DETECTION_PERFORMANCE_MODE,
|
||||
confidence_threshold=settings.ENCODING_DETECTION_MIN_CONFIDENCE
|
||||
performance_mode=get_runtime_setting('ENCODING_DETECTION_PERFORMANCE_MODE'),
|
||||
confidence_threshold=get_runtime_setting('ENCODING_DETECTION_MIN_CONFIDENCE')
|
||||
)
|
||||
)
|
||||
|
||||
@@ -394,8 +393,8 @@ class SiteSpider:
|
||||
self.parse,
|
||||
RequestUtils.get_decoded_html_content(
|
||||
ret,
|
||||
performance_mode=settings.ENCODING_DETECTION_PERFORMANCE_MODE,
|
||||
confidence_threshold=settings.ENCODING_DETECTION_MIN_CONFIDENCE
|
||||
performance_mode=get_runtime_setting('ENCODING_DETECTION_PERFORMANCE_MODE'),
|
||||
confidence_threshold=get_runtime_setting('ENCODING_DETECTION_MIN_CONFIDENCE')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import urllib.parse
|
||||
from typing import Tuple, List
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.configuration import get_configured_system_config
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
@@ -70,7 +69,7 @@ class HaiDanSpider:
|
||||
self._searchurl = self._searchurl % self._url
|
||||
self._name = indexer.get('name')
|
||||
if indexer.get('proxy'):
|
||||
self._proxy = settings.PROXY
|
||||
self._proxy = get_runtime_setting('PROXY')
|
||||
self._cookie = indexer.get('cookie')
|
||||
self._ua = indexer.get('ua')
|
||||
self._timeout = indexer.get('timeout') or 15
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import Tuple, List, Optional
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.configuration import get_configured_system_config
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
@@ -76,7 +75,7 @@ class HddolbySpider:
|
||||
self._domain_host = site_rules.extract_domain(self._domain)
|
||||
self._name = indexer.get('name')
|
||||
if indexer.get('proxy'):
|
||||
self._proxy = settings.PROXY
|
||||
self._proxy = get_runtime_setting('PROXY')
|
||||
self._cookie = indexer.get('cookie')
|
||||
self._ua = indexer.get('ua')
|
||||
self._apikey = indexer.get('apikey')
|
||||
|
||||
@@ -4,9 +4,8 @@ import re
|
||||
from typing import Tuple, List, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.configuration import get_configured_system_config
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
@@ -75,7 +74,7 @@ class MTorrentSpider:
|
||||
self._searchurl = self._searchurl % self._domain
|
||||
self._name = indexer.get('name')
|
||||
if indexer.get('proxy'):
|
||||
self._proxy = settings.PROXY
|
||||
self._proxy = get_runtime_setting('PROXY')
|
||||
self._cookie = indexer.get('cookie')
|
||||
self._ua = indexer.get('ua')
|
||||
self._apikey = indexer.get('apikey')
|
||||
|
||||
@@ -2,9 +2,8 @@ import base64
|
||||
import json
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.configuration import get_configured_system_config
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
@@ -60,7 +59,7 @@ class RousiSpider:
|
||||
self._downloadurl = self._downloadurl % (self._domain, "%s")
|
||||
self._name = indexer.get('name')
|
||||
if indexer.get('proxy'):
|
||||
self._proxy = settings.PROXY
|
||||
self._proxy = get_runtime_setting('PROXY')
|
||||
self._cookie = indexer.get('cookie')
|
||||
self._ua = indexer.get('ua')
|
||||
self._apikey = indexer.get('apikey')
|
||||
|
||||
@@ -3,9 +3,8 @@ import json
|
||||
import time
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
from app.adapters.network.http import AsyncRequestUtils, RequestUtils
|
||||
@@ -34,9 +33,9 @@ class SunnyPTSpider:
|
||||
self._api_url = str(
|
||||
indexer.get("api_url") or "https://api.sunnypt.top/api/v1/mp"
|
||||
).rstrip("/")
|
||||
self._proxy = settings.PROXY if indexer.get("proxy") else None
|
||||
self._proxy = get_runtime_setting('PROXY') if indexer.get("proxy") else None
|
||||
self._use_proxy = bool(indexer.get("proxy"))
|
||||
self._user_agent = indexer.get("ua") or settings.USER_AGENT
|
||||
self._user_agent = indexer.get("ua") or get_runtime_setting('USER_AGENT')
|
||||
self._api_key = indexer.get("apikey")
|
||||
self._timeout = indexer.get("timeout") or 15
|
||||
self._configured_categories = self._parse_configured_categories(
|
||||
|
||||
@@ -2,9 +2,8 @@ import re
|
||||
from typing import Tuple, List, Optional
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
||||
from app.foundation.singleton import SingletonClass
|
||||
@@ -33,7 +32,7 @@ class TNodeSpider(metaclass=SingletonClass):
|
||||
self._searchurl = self._baseurl % self._domain
|
||||
self._name = indexer.get('name')
|
||||
if indexer.get('proxy'):
|
||||
self._proxy = settings.PROXY
|
||||
self._proxy = get_runtime_setting('PROXY')
|
||||
self._cookie = indexer.get('cookie')
|
||||
self._ua = indexer.get('ua')
|
||||
self._timeout = indexer.get('timeout') or 15
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from typing import List, Tuple, Optional
|
||||
from urllib.parse import quote
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
||||
@@ -34,7 +33,7 @@ class TorrentLeech:
|
||||
"""初始化站点认证信息和媒体分类配置。"""
|
||||
self._indexer = indexer
|
||||
if indexer.get('proxy'):
|
||||
self._proxy = settings.PROXY
|
||||
self._proxy = get_runtime_setting('PROXY')
|
||||
self._timeout = indexer.get('timeout') or 15
|
||||
|
||||
def __category_ids(self, mtype: MediaType = None) -> List[str]:
|
||||
|
||||
@@ -2,9 +2,8 @@ import base64
|
||||
import json
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
from app.adapters.network.http import AsyncRequestUtils, RequestUtils
|
||||
@@ -46,9 +45,9 @@ class YemaSpider:
|
||||
indexer = indexer or {}
|
||||
self._name = indexer.get("name") or "YemaPT"
|
||||
self._site_url = str(indexer.get("domain") or "https://www.yemapt.org/").rstrip("/")
|
||||
self._proxy = settings.PROXY if indexer.get("proxy") else None
|
||||
self._proxy = get_runtime_setting('PROXY') if indexer.get("proxy") else None
|
||||
self._use_proxy = bool(indexer.get("proxy"))
|
||||
self._user_agent = indexer.get("ua") or settings.USER_AGENT
|
||||
self._user_agent = indexer.get("ua") or get_runtime_setting('USER_AGENT')
|
||||
self._api_key = indexer.get("apikey")
|
||||
self._timeout = indexer.get("timeout") or 15
|
||||
self._search_url = f"{self._site_url}/openApi/torrent/fetchOpenTorrentList.json"
|
||||
|
||||
@@ -9,9 +9,8 @@ from app.schemas.mediaserver import MediaServerItem as _SchemaMediaServerItem
|
||||
from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibrary
|
||||
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
|
||||
from app.schemas.mediaserver import WebhookEventInfo as _SchemaWebhookEventInfo
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.mediaserver import MediaServerIdentityHelper, format_emby_family_item
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
@@ -40,7 +39,7 @@ class Jellyfin:
|
||||
if self._playhost:
|
||||
self._playhost = UrlUtils.standardize_base_url(self._playhost)
|
||||
self._apikey = apikey
|
||||
self.user = self.get_user(settings.SUPERUSER)
|
||||
self.user = self.get_user(get_runtime_setting('SUPERUSER'))
|
||||
self.serverid = self.get_server_id()
|
||||
self._sync_libraries = sync_libraries or []
|
||||
|
||||
@@ -253,9 +252,9 @@ class Jellyfin:
|
||||
for user in users:
|
||||
if user.get("Name") == user_name:
|
||||
return user.get("Id")
|
||||
if user_name == settings.SUPERUSER:
|
||||
if user_name == get_runtime_setting('SUPERUSER'):
|
||||
logger.warning(
|
||||
"MoviePilot 当前配置的超级管理员用户名为 {},请确保Jellyfin中存在同名管理员账号,否则可能无法正常使用部分功能!".format(settings.SUPERUSER)
|
||||
"MoviePilot 当前配置的超级管理员用户名为 {},请确保Jellyfin中存在同名管理员账号,否则可能无法正常使用部分功能!".format(get_runtime_setting('SUPERUSER'))
|
||||
)
|
||||
# 查询管理员,优先选择同时具备全库访问能力的账号,再回退到普通管理员。
|
||||
# 获取总媒体库数量
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from typing import Any, Optional, Tuple, Union
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import MusicInfo
|
||||
from app.runtime.log import logger
|
||||
from app.modules import _ModuleBase
|
||||
@@ -155,7 +154,7 @@ class ListenBrainzModule(_ModuleBase):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@cached(maxsize=settings.CONF.listenbrainz, ttl=settings.CONF.meta, skip_none=True)
|
||||
@cached(maxsize=get_runtime_setting('CONF').listenbrainz, ttl=get_runtime_setting('CONF').meta, skip_none=True)
|
||||
def _request_json(
|
||||
cls,
|
||||
path: str,
|
||||
@@ -164,10 +163,10 @@ class ListenBrainzModule(_ModuleBase):
|
||||
"""请求 ListenBrainz JSON 接口并统一处理网络和响应错误。"""
|
||||
response = RequestUtils(
|
||||
headers={
|
||||
"User-Agent": f"{settings.USER_AGENT} (https://github.com/jxxghp/MoviePilot)",
|
||||
"User-Agent": f"{get_runtime_setting('USER_AGENT')} (https://github.com/jxxghp/MoviePilot)",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=20,
|
||||
).get_res(f"{cls._base_url}{path}", params=params)
|
||||
if response is None:
|
||||
@@ -281,7 +280,7 @@ class ListenBrainzModule(_ModuleBase):
|
||||
if not release_mbid:
|
||||
return None
|
||||
# 支持配置音乐封面代理地址,解决 coverartarchive.org 无法访问的问题
|
||||
base = (settings.MUSIC_COVER_PROXY or "https://coverartarchive.org").rstrip("/")
|
||||
base = (get_runtime_setting('MUSIC_COVER_PROXY') or "https://coverartarchive.org").rstrip("/")
|
||||
return f"{base}/release/{release_mbid}/front-500"
|
||||
|
||||
@classmethod
|
||||
@@ -290,7 +289,7 @@ class ListenBrainzModule(_ModuleBase):
|
||||
if not release_group_id:
|
||||
return None
|
||||
# 支持配置音乐封面代理地址,解决 coverartarchive.org 无法访问的问题
|
||||
base = (settings.MUSIC_COVER_PROXY or "https://coverartarchive.org").rstrip("/")
|
||||
base = (get_runtime_setting('MUSIC_COVER_PROXY') or "https://coverartarchive.org").rstrip("/")
|
||||
return f"{base}/release-group/{release_group_id}/front-500"
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -9,10 +9,9 @@ from app.domain.meta.metamusic import MetaMusic
|
||||
from app.modules import _ModuleBase
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.schemas.types import ModuleType, OtherModulesType
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
|
||||
class LrclibModule(_ModuleBase):
|
||||
@@ -201,13 +200,13 @@ class LrclibModule(_ModuleBase):
|
||||
time.sleep(delay)
|
||||
response = RequestUtils(
|
||||
headers={
|
||||
"User-Agent": f"{settings.USER_AGENT} (https://github.com/jxxghp/MoviePilot)",
|
||||
"User-Agent": f"{get_runtime_setting('USER_AGENT')} (https://github.com/jxxghp/MoviePilot)",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=20,
|
||||
).get_res(
|
||||
f"{(base_url or str(settings.LRCLIB_BASE_URL)).rstrip('/')}{path}",
|
||||
f"{(base_url or str(get_runtime_setting('LRCLIB_BASE_URL'))).rstrip('/')}{path}",
|
||||
params=params,
|
||||
)
|
||||
cls._last_request_at = time.monotonic()
|
||||
@@ -231,7 +230,7 @@ class LrclibModule(_ModuleBase):
|
||||
if response.status_code in (429, 503):
|
||||
retry_after = cls._retry_after_seconds(response.headers.get("Retry-After"))
|
||||
response.close()
|
||||
max_wait = max(int(settings.LYRICS_PROVIDER_RETRY_MAX_WAIT), 0)
|
||||
max_wait = max(int(get_runtime_setting('LYRICS_PROVIDER_RETRY_MAX_WAIT')), 0)
|
||||
if retry_after > max_wait:
|
||||
cls._cooldown_until = time.monotonic() + retry_after
|
||||
logger.warning(f"LRCLIB 进入冷却 {retry_after:g} 秒,跳过当前批次后续请求")
|
||||
|
||||
@@ -8,9 +8,8 @@ from typing import Any, Iterable, Optional, Tuple, Union
|
||||
from requests import Session
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import (
|
||||
MusicAlbumInfo,
|
||||
MusicArtistInfo,
|
||||
@@ -1761,7 +1760,7 @@ class MusicBrainzModule(_ModuleBase):
|
||||
if not release_group_id:
|
||||
return None
|
||||
# 支持配置音乐封面代理地址,解决 coverartarchive.org 无法访问的问题
|
||||
base = (settings.MUSIC_COVER_PROXY or "https://coverartarchive.org").rstrip("/")
|
||||
base = (get_runtime_setting('MUSIC_COVER_PROXY') or "https://coverartarchive.org").rstrip("/")
|
||||
return f"{base}/release-group/{release_group_id}/front-500"
|
||||
|
||||
@classmethod
|
||||
@@ -1795,7 +1794,7 @@ class MusicBrainzModule(_ModuleBase):
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
@classmethod
|
||||
@cached(maxsize=settings.CONF.musicbrainz, ttl=settings.CONF.meta, skip_none=True)
|
||||
@cached(maxsize=get_runtime_setting('CONF').musicbrainz, ttl=get_runtime_setting('CONF').meta, skip_none=True)
|
||||
def _request_json(
|
||||
cls,
|
||||
path: str,
|
||||
@@ -1811,10 +1810,10 @@ class MusicBrainzModule(_ModuleBase):
|
||||
cls._wait_for_rate_limit()
|
||||
response = RequestUtils(
|
||||
headers={
|
||||
"User-Agent": f"{settings.USER_AGENT} (https://github.com/jxxghp/MoviePilot)",
|
||||
"User-Agent": f"{get_runtime_setting('USER_AGENT')} (https://github.com/jxxghp/MoviePilot)",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
session=cls._get_session(),
|
||||
timeout=20,
|
||||
).get_res(f"{cls._base_url}{path}", params=params)
|
||||
@@ -1850,8 +1849,8 @@ class MusicBrainzModule(_ModuleBase):
|
||||
|
||||
@classmethod
|
||||
@cached(
|
||||
maxsize=settings.CONF.musicbrainz,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').musicbrainz,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_none=True,
|
||||
shared_key="_request_json",
|
||||
)
|
||||
@@ -1866,10 +1865,10 @@ class MusicBrainzModule(_ModuleBase):
|
||||
await cls._async_wait_for_rate_limit()
|
||||
response = await AsyncRequestUtils(
|
||||
headers={
|
||||
"User-Agent": f"{settings.USER_AGENT} (https://github.com/jxxghp/MoviePilot)",
|
||||
"User-Agent": f"{get_runtime_setting('USER_AGENT')} (https://github.com/jxxghp/MoviePilot)",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=20,
|
||||
).get_res(f"{cls._base_url}{path}", params=params)
|
||||
if response is None:
|
||||
|
||||
@@ -6,9 +6,8 @@ from time import time
|
||||
from typing import Optional
|
||||
|
||||
from app.runtime.cache import FileCache, TTLCache
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import MusicInfo
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
from app.runtime.log import logger
|
||||
@@ -37,15 +36,15 @@ class MusicBrainzCache(metaclass=WeakSingleton):
|
||||
|
||||
def __init__(self):
|
||||
"""初始化音乐识别缓存并恢复未过期的持久化数据。"""
|
||||
self.maxsize = settings.CONF.musicbrainz
|
||||
self.ttl = settings.CONF.meta
|
||||
self.maxsize = get_runtime_setting('CONF').musicbrainz
|
||||
self.ttl = get_runtime_setting('CONF').meta
|
||||
self.region = "__musicbrainz_cache__"
|
||||
self._cache = TTLCache(region=self.region, maxsize=self.maxsize, ttl=self.ttl)
|
||||
self._expires_at: dict[str, float] = {}
|
||||
self._dirty = False
|
||||
self._file_cache = None
|
||||
if not self._cache.is_redis():
|
||||
self._file_cache = FileCache(base=settings.CACHE_PATH, ttl=self.ttl)
|
||||
self._file_cache = FileCache(base=get_runtime_setting('CACHE_PATH'), ttl=self.ttl)
|
||||
self._restore()
|
||||
|
||||
def _restore(self) -> None:
|
||||
|
||||
@@ -6,11 +6,9 @@ from app.domain.context import MusicInfo, MusicLyrics
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
from app.modules import _ModuleBase
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.schemas.types import ModuleType, OtherModulesType
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
|
||||
class MusixmatchModule(_ModuleBase):
|
||||
"""使用用户授权的 Musixmatch 官方 API 获取同步或纯文本歌词。"""
|
||||
@@ -30,7 +28,7 @@ class MusixmatchModule(_ModuleBase):
|
||||
|
||||
def test(self) -> Tuple[bool, str]:
|
||||
"""验证 API Key 和官方接口连通性。"""
|
||||
if not str(settings.MUSIXMATCH_API_KEY or "").strip():
|
||||
if not str(get_runtime_setting('MUSIXMATCH_API_KEY') or "").strip():
|
||||
return False, "Musixmatch API Key 未配置"
|
||||
payload = self._request("matcher.lyrics.get", {"q_track": "test", "q_artist": "test"})
|
||||
return (True, "") if payload is not None else (False, "Musixmatch API 连接或授权失败")
|
||||
@@ -106,15 +104,15 @@ class MusixmatchModule(_ModuleBase):
|
||||
|
||||
def _request(self, method: str, params: dict[str, Any]) -> Optional[dict[str, Any]]:
|
||||
"""请求官方 API,并对限流或服务过载设置进程内冷却。"""
|
||||
api_key = str(settings.MUSIXMATCH_API_KEY or "").strip()
|
||||
api_key = str(get_runtime_setting('MUSIXMATCH_API_KEY') or "").strip()
|
||||
if not api_key or time.monotonic() < self._cooldown_until:
|
||||
return None
|
||||
response = RequestUtils(
|
||||
ua=settings.USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=20,
|
||||
).get_res(
|
||||
f"{str(settings.MUSIXMATCH_BASE_URL).rstrip('/')}/{method}",
|
||||
f"{str(get_runtime_setting('MUSIXMATCH_BASE_URL')).rstrip('/')}/{method}",
|
||||
params={**params, "apikey": api_key},
|
||||
)
|
||||
if response is None:
|
||||
|
||||
@@ -2,11 +2,9 @@ from typing import Tuple, Union
|
||||
|
||||
from app.application.database import get_database_governance
|
||||
from app.modules import _ModuleBase
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.schemas.types import ModuleType, OtherModulesType
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
|
||||
class PostgreSQLModule(_ModuleBase):
|
||||
"""
|
||||
@@ -55,7 +53,7 @@ class PostgreSQLModule(_ModuleBase):
|
||||
"""
|
||||
测试模块连接性
|
||||
"""
|
||||
if settings.DB_TYPE != "postgresql":
|
||||
if get_runtime_setting('DB_TYPE') != "postgresql":
|
||||
return None
|
||||
error = get_database_governance().test()
|
||||
if error:
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Set, Tuple, Optional, Union, List, Dict
|
||||
from app.schemas.dashboard import DownloaderInfo as _SchemaDownloaderInfo
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.modules._base import _DownloaderModuleBase
|
||||
from app.modules.qbittorrent.qbittorrent import Qbittorrent
|
||||
from app.schemas.transfer import DownloaderFile, DownloaderTorrent
|
||||
@@ -19,7 +19,6 @@ from app.foundation import size as size_tools
|
||||
from app.foundation import temporal as time_tools
|
||||
from app.foundation import text as text_tools
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
_QBITTORRENT_DOWNLOADING_STATES = {
|
||||
"allocating",
|
||||
@@ -128,8 +127,8 @@ class QbittorrentModule(_DownloaderModuleBase[Qbittorrent]):
|
||||
tag = text_tools.random_string(10)
|
||||
if label:
|
||||
tags = label.split(',') + [tag]
|
||||
elif settings.TORRENT_TAG:
|
||||
tags = [tag, settings.TORRENT_TAG]
|
||||
elif get_runtime_setting('TORRENT_TAG'):
|
||||
tags = [tag, get_runtime_setting('TORRENT_TAG')]
|
||||
else:
|
||||
tags = [tag]
|
||||
# 如果要选择文件则先暂停
|
||||
@@ -163,9 +162,9 @@ class QbittorrentModule(_DownloaderModuleBase[Qbittorrent]):
|
||||
# 给种子打上标签
|
||||
if "已整理" in torrent_tags:
|
||||
server.remove_torrents_tag(ids=torrent_hash, tag=['已整理'])
|
||||
if settings.TORRENT_TAG and settings.TORRENT_TAG not in torrent_tags:
|
||||
logger.info(f"给种子 {torrent_hash} 打上标签:{settings.TORRENT_TAG}")
|
||||
server.set_torrents_tag(ids=torrent_hash, tags=[settings.TORRENT_TAG])
|
||||
if get_runtime_setting('TORRENT_TAG') and get_runtime_setting('TORRENT_TAG') not in torrent_tags:
|
||||
logger.info(f"给种子 {torrent_hash} 打上标签:{get_runtime_setting('TORRENT_TAG')}")
|
||||
server.set_torrents_tag(ids=torrent_hash, tags=[get_runtime_setting('TORRENT_TAG')])
|
||||
# 获取种子内容布局: `Original: 原始, Subfolder: 创建子文件夹, NoSubfolder: 不创建子文件夹`
|
||||
torrent_layout = server.get_content_layout()
|
||||
return downloader or self.get_default_config_name(), torrent_hash, torrent_layout, f"下载任务已存在"
|
||||
@@ -250,7 +249,7 @@ class QbittorrentModule(_DownloaderModuleBase[Qbittorrent]):
|
||||
servers: Dict[str, Qbittorrent] = self.get_instances()
|
||||
ret_torrents = []
|
||||
query_status = self._normalize_query_status(status)
|
||||
query_tags = None if include_all_tags else settings.TORRENT_TAG
|
||||
query_tags = None if include_all_tags else get_runtime_setting('TORRENT_TAG')
|
||||
|
||||
def __get_torrent_path(torrent_data: dict) -> Path:
|
||||
"""
|
||||
|
||||
@@ -7,19 +7,15 @@ import hashlib
|
||||
import io
|
||||
import pickle
|
||||
import threading
|
||||
from typing import Optional, List, Tuple
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from app.runtime.cache import FileCache
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.application.messaging.ingress import submit_message_to_host
|
||||
from app.domain.context import MediaInfo, Context
|
||||
from app.domain.context import Context, MediaInfo
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.thread import ThreadHelper
|
||||
from app.foundation import size as size_tools
|
||||
from app.modules.qqbot.api import (
|
||||
get_access_token,
|
||||
get_gateway_url,
|
||||
@@ -27,8 +23,9 @@ from app.modules.qqbot.api import (
|
||||
send_proactive_group_message,
|
||||
)
|
||||
from app.modules.qqbot.gateway import run_gateway
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.foundation import size as size_tools
|
||||
from app.runtime.cache import FileCache
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.thread import ThreadHelper
|
||||
|
||||
# QQ Markdown 图片展示尺寸限制,避免竖版海报被客户端拉伸变形
|
||||
_DEFAULT_IMAGE_SIZE: Tuple[int, int] = (208, 320)
|
||||
|
||||
@@ -2,11 +2,9 @@ from typing import Tuple, Union
|
||||
|
||||
from app.adapters.cache.redis import RedisHelper
|
||||
from app.modules import _ModuleBase
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.schemas.types import ModuleType, OtherModulesType
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
|
||||
class RedisModule(_ModuleBase):
|
||||
"""
|
||||
@@ -55,7 +53,7 @@ class RedisModule(_ModuleBase):
|
||||
"""
|
||||
测试模块连接性
|
||||
"""
|
||||
if settings.CACHE_BACKEND_TYPE != "redis":
|
||||
if get_runtime_setting('CACHE_BACKEND_TYPE') != "redis":
|
||||
return None
|
||||
if RedisHelper().test():
|
||||
return True, ""
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Set, Tuple, Optional, Union, List, Dict
|
||||
from app.schemas.dashboard import DownloaderInfo as _SchemaDownloaderInfo
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.modules._base import _DownloaderModuleBase
|
||||
from app.modules.rtorrent.rtorrent import Rtorrent
|
||||
from app.schemas.transfer import DownloaderFile, DownloaderTorrent
|
||||
@@ -19,7 +19,6 @@ from app.foundation import size as size_tools
|
||||
from app.foundation import temporal as time_tools
|
||||
from app.foundation import text as text_tools
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
|
||||
class RtorrentModule(_DownloaderModuleBase[Rtorrent]):
|
||||
@@ -113,8 +112,8 @@ class RtorrentModule(_DownloaderModuleBase[Rtorrent]):
|
||||
tag = text_tools.random_string(10)
|
||||
if label:
|
||||
tags = label.split(",") + [tag]
|
||||
elif settings.TORRENT_TAG:
|
||||
tags = [tag, settings.TORRENT_TAG]
|
||||
elif get_runtime_setting('TORRENT_TAG'):
|
||||
tags = [tag, get_runtime_setting('TORRENT_TAG')]
|
||||
else:
|
||||
tags = [tag]
|
||||
# 如果要选择文件则先暂停
|
||||
@@ -160,14 +159,14 @@ class RtorrentModule(_DownloaderModuleBase[Rtorrent]):
|
||||
ids=torrent_hash, tag=["已整理"]
|
||||
)
|
||||
if (
|
||||
settings.TORRENT_TAG
|
||||
and settings.TORRENT_TAG not in torrent_tags
|
||||
get_runtime_setting('TORRENT_TAG')
|
||||
and get_runtime_setting('TORRENT_TAG') not in torrent_tags
|
||||
):
|
||||
logger.info(
|
||||
f"给种子 {torrent_hash} 打上标签:{settings.TORRENT_TAG}"
|
||||
f"给种子 {torrent_hash} 打上标签:{get_runtime_setting('TORRENT_TAG')}"
|
||||
)
|
||||
server.set_torrents_tag(
|
||||
ids=torrent_hash, tags=[settings.TORRENT_TAG]
|
||||
ids=torrent_hash, tags=[get_runtime_setting('TORRENT_TAG')]
|
||||
)
|
||||
return (
|
||||
downloader or self.get_default_config_name(),
|
||||
@@ -266,7 +265,7 @@ class RtorrentModule(_DownloaderModuleBase[Rtorrent]):
|
||||
servers: Dict[str, Rtorrent] = self.get_instances()
|
||||
ret_torrents = []
|
||||
query_status = self._normalize_query_status(status)
|
||||
query_tags = None if include_all_tags else settings.TORRENT_TAG
|
||||
query_tags = None if include_all_tags else get_runtime_setting('TORRENT_TAG')
|
||||
|
||||
def __get_torrent_path(torrent_data: dict) -> Path:
|
||||
"""
|
||||
|
||||
@@ -8,9 +8,8 @@ from slack_bolt import App
|
||||
from slack_bolt.adapter.socket_mode import SocketModeHandler
|
||||
from slack_sdk import WebClient
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.messaging.ingress import forward_message_to_host
|
||||
from app.domain.context import MediaInfo, Context
|
||||
from app.domain.metainfo import MetaInfo
|
||||
@@ -266,7 +265,7 @@ class Slack:
|
||||
try:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {self._oauth_token}",
|
||||
"User-Agent": settings.USER_AGENT,
|
||||
"User-Agent": get_runtime_setting('USER_AGENT'),
|
||||
"Accept": "*/*",
|
||||
}
|
||||
resp = RequestUtils(headers=headers, timeout=30).get_res(file_url)
|
||||
|
||||
@@ -4,9 +4,8 @@ from urllib.parse import urljoin, urlparse
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import Context
|
||||
from app.application.site.query import get_configured_site_query_service
|
||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||
@@ -146,7 +145,7 @@ class SubtitleModule(_ModuleBase):
|
||||
request = RequestUtils(
|
||||
cookies=torrent.site_cookie,
|
||||
ua=torrent.site_ua,
|
||||
proxies=settings.PROXY if torrent.site_proxy else None,
|
||||
proxies=get_runtime_setting('PROXY') if torrent.site_proxy else None,
|
||||
)
|
||||
res = request.get_res(torrent.page_url)
|
||||
if res and res.status_code == 200:
|
||||
|
||||
@@ -36,9 +36,8 @@ try:
|
||||
except ImportError:
|
||||
from telegramify_markdown.type import ContentTypes, File, Photo, Text # noqa: E402
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat # noqa: E402
|
||||
from app.runtime.settings import get_runtime_setting # noqa: E402
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import MediaInfo, Context # noqa: E402
|
||||
from app.domain.metainfo import MetaInfo # noqa: E402
|
||||
from app.application.image import ImageHelper # noqa: E402
|
||||
@@ -124,7 +123,7 @@ class Telegram:
|
||||
apihelper.API_URL = "https://api.telegram.org/bot{0}/{1}"
|
||||
apihelper.FILE_URL = "https://api.telegram.org/file/bot{0}/{1}"
|
||||
# 设置代理
|
||||
apihelper.proxy = settings.PROXY
|
||||
apihelper.proxy = get_runtime_setting('PROXY')
|
||||
# bot
|
||||
_bot = TeleBot(self._telegram_token, parse_mode=TELEGRAM_PARSE_MODE_MARKDOWN)
|
||||
# 记录句柄
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from typing import Any, Optional, Tuple, Union
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import (
|
||||
MusicAlbumInfo,
|
||||
MusicArtistInfo,
|
||||
@@ -553,20 +552,20 @@ class TheAudioDbModule(_ModuleBase):
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
@cached(maxsize=settings.CONF.theaudiodb, ttl=settings.CONF.meta, skip_none=True)
|
||||
@cached(maxsize=get_runtime_setting('CONF').theaudiodb, ttl=get_runtime_setting('CONF').meta, skip_none=True)
|
||||
def _request_json(
|
||||
cls,
|
||||
endpoint: str,
|
||||
params: Optional[dict[str, Any]] = None,
|
||||
) -> Optional[dict[str, Any]]:
|
||||
"""请求 TheAudioDB V1 JSON 接口并统一处理错误响应。"""
|
||||
api_key = str(settings.THEAUDIODB_API_KEY or "").strip()
|
||||
api_key = str(get_runtime_setting('THEAUDIODB_API_KEY') or "").strip()
|
||||
if not api_key:
|
||||
logger.warning("TheAudioDB API Key 未配置,跳过请求")
|
||||
return None
|
||||
response = RequestUtils(
|
||||
ua=settings.USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=30,
|
||||
).get_res(
|
||||
url=f"{cls._base_url}/{api_key}/{endpoint}",
|
||||
@@ -594,8 +593,8 @@ class TheAudioDbModule(_ModuleBase):
|
||||
|
||||
@classmethod
|
||||
@cached(
|
||||
maxsize=settings.CONF.theaudiodb,
|
||||
ttl=settings.CONF.meta,
|
||||
maxsize=get_runtime_setting('CONF').theaudiodb,
|
||||
ttl=get_runtime_setting('CONF').meta,
|
||||
skip_none=True,
|
||||
shared_key="_request_json",
|
||||
)
|
||||
@@ -605,13 +604,13 @@ class TheAudioDbModule(_ModuleBase):
|
||||
params: Optional[dict[str, Any]] = None,
|
||||
) -> Optional[dict[str, Any]]:
|
||||
"""异步请求 TheAudioDB V1 JSON 接口并统一处理错误响应。"""
|
||||
api_key = str(settings.THEAUDIODB_API_KEY or "").strip()
|
||||
api_key = str(get_runtime_setting('THEAUDIODB_API_KEY') or "").strip()
|
||||
if not api_key:
|
||||
logger.warning("TheAudioDB API Key 未配置,跳过请求")
|
||||
return None
|
||||
response = await AsyncRequestUtils(
|
||||
ua=settings.USER_AGENT,
|
||||
proxies=settings.PROXY,
|
||||
ua=get_runtime_setting('USER_AGENT'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=30,
|
||||
).get_res(
|
||||
url=f"{cls._base_url}/{api_key}/{endpoint}",
|
||||
|
||||
@@ -94,13 +94,13 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
"""
|
||||
测试模块连接性
|
||||
"""
|
||||
ret = RequestUtils(ua=get_runtime_setting("NORMAL_USER_AGENT"), proxies=get_runtime_setting("PROXY")).get_res(
|
||||
f"https://{get_runtime_setting("TMDB_API_DOMAIN")}/3/movie/550?api_key={get_runtime_setting("TMDB_API_KEY")}")
|
||||
ret = RequestUtils(ua=get_runtime_setting('NORMAL_USER_AGENT'), proxies=get_runtime_setting('PROXY')).get_res(
|
||||
f"https://{get_runtime_setting('TMDB_API_DOMAIN')}/3/movie/550?api_key={get_runtime_setting('TMDB_API_KEY')}")
|
||||
if ret and ret.status_code == 200:
|
||||
return True, ""
|
||||
elif ret:
|
||||
return False, f"无法连接 {get_runtime_setting("TMDB_API_DOMAIN")},错误码:{ret.status_code}"
|
||||
return False, f"{get_runtime_setting("TMDB_API_DOMAIN")} 网络连接失败"
|
||||
return False, f"无法连接 {get_runtime_setting('TMDB_API_DOMAIN')},错误码:{ret.status_code}"
|
||||
return False, f"{get_runtime_setting('TMDB_API_DOMAIN')} 网络连接失败"
|
||||
|
||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||
pass
|
||||
@@ -122,7 +122,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
if not tmdbid and not meta:
|
||||
return False
|
||||
|
||||
selected_source = normalize_media_source(media_source or get_runtime_setting("RECOGNIZE_SOURCE"))
|
||||
selected_source = normalize_media_source(media_source or get_runtime_setting('RECOGNIZE_SOURCE'))
|
||||
if meta and not tmdbid and selected_source != MediaSource.TMDB:
|
||||
return False
|
||||
|
||||
@@ -973,7 +973,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param season: 季号
|
||||
:param episode: 集号
|
||||
"""
|
||||
if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "themoviedb":
|
||||
if (mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')) != "themoviedb":
|
||||
return None
|
||||
return self.scraper.get_metadata_nfo(meta=meta, mediainfo=mediainfo, season=season, episode=episode)
|
||||
|
||||
@@ -985,7 +985,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param season: 季号
|
||||
:param episode: 集号
|
||||
"""
|
||||
if (mediainfo.scrape_source or get_runtime_setting("SCRAP_SOURCE")) != "themoviedb":
|
||||
if (mediainfo.scrape_source or get_runtime_setting('SCRAP_SOURCE')) != "themoviedb":
|
||||
return None
|
||||
return self.scraper.get_metadata_img(mediainfo=mediainfo, season=season, episode=episode)
|
||||
|
||||
@@ -1106,7 +1106,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
:param mediainfo: 媒体信息
|
||||
:return: None 表示不处理,MediaInfo 表示继续处理
|
||||
"""
|
||||
if mediainfo.media_source != "themoviedb" and get_runtime_setting("RECOGNIZE_SOURCE") != "themoviedb":
|
||||
if mediainfo.media_source != "themoviedb" and get_runtime_setting('RECOGNIZE_SOURCE') != "themoviedb":
|
||||
return None
|
||||
if not mediainfo.tmdb_id:
|
||||
return mediainfo
|
||||
@@ -1147,15 +1147,15 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
# 背景图
|
||||
if not mediainfo.backdrop_path:
|
||||
if image_path := cls._pick_best_tmdb_image(images.get("backdrops")):
|
||||
mediainfo.backdrop_path = get_runtime_setting("TMDB_IMAGE_URL")(image_path)
|
||||
mediainfo.backdrop_path = get_runtime_setting('TMDB_IMAGE_URL')(image_path)
|
||||
# 标志
|
||||
if not mediainfo.logo_path:
|
||||
if image_path := cls._pick_best_tmdb_image(images.get("logos")):
|
||||
mediainfo.logo_path = get_runtime_setting("TMDB_IMAGE_URL")(image_path)
|
||||
mediainfo.logo_path = get_runtime_setting('TMDB_IMAGE_URL')(image_path)
|
||||
# 海报
|
||||
if not mediainfo.poster_path:
|
||||
if image_path := cls._pick_best_tmdb_image(images.get("posters")):
|
||||
mediainfo.poster_path = get_runtime_setting("TMDB_IMAGE_URL")(image_path)
|
||||
mediainfo.poster_path = get_runtime_setting('TMDB_IMAGE_URL')(image_path)
|
||||
return mediainfo
|
||||
|
||||
def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]:
|
||||
@@ -1245,7 +1245,7 @@ class TheMovieDbModule(MediaAuxiliaryProviderMixin, _ModuleBase):
|
||||
image_path = seasoninfo.get(image_type.value)
|
||||
|
||||
if image_path:
|
||||
return get_runtime_setting("TMDB_IMAGE_URL")(image_path, image_prefix)
|
||||
return get_runtime_setting('TMDB_IMAGE_URL')(image_path, image_prefix)
|
||||
return None
|
||||
|
||||
def tmdb_movie_similar(self, tmdbid: int) -> List[MediaInfo]:
|
||||
|
||||
@@ -5,9 +5,8 @@ from typing import Union
|
||||
import ruamel.yaml
|
||||
from ruamel.yaml import CommentedMap
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.category import CategoryConfig
|
||||
from app.foundation.singleton import WeakSingleton
|
||||
@@ -33,7 +32,7 @@ class CategoryHelper(metaclass=WeakSingleton):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._category_path: Path = settings.CONFIG_PATH / "category.yaml"
|
||||
self._category_path: Path = get_runtime_setting('CONFIG_PATH') / "category.yaml"
|
||||
self._categorys = {}
|
||||
self._movie_categorys = {}
|
||||
self._tv_categorys = {}
|
||||
@@ -45,7 +44,7 @@ class CategoryHelper(metaclass=WeakSingleton):
|
||||
"""
|
||||
try:
|
||||
if not self._category_path.exists():
|
||||
shutil.copy(settings.INNER_CONFIG_PATH / "category.yaml", self._category_path)
|
||||
shutil.copy(get_runtime_setting('INNER_CONFIG_PATH') / "category.yaml", self._category_path)
|
||||
with open(self._category_path, mode='r', encoding='utf-8', errors='replace') as f:
|
||||
try:
|
||||
yaml_loader = ruamel.yaml.YAML()
|
||||
|
||||
@@ -2,9 +2,8 @@ from pathlib import Path
|
||||
from typing import Optional, Tuple
|
||||
from xml.dom import minidom
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.context import MediaInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.schemas.types import MediaType
|
||||
@@ -22,14 +21,14 @@ class TmdbScraper:
|
||||
获取元数据TMDB Api
|
||||
"""
|
||||
if not self._meta_tmdb:
|
||||
self._meta_tmdb = TmdbApi(language=settings.TMDB_LOCALE)
|
||||
self._meta_tmdb = TmdbApi(language=get_runtime_setting('TMDB_LOCALE'))
|
||||
return self._meta_tmdb
|
||||
|
||||
def original_tmdb(self, mediainfo: Optional[MediaInfo] = None):
|
||||
"""
|
||||
获取图片TMDB Api
|
||||
"""
|
||||
if settings.TMDB_SCRAP_ORIGINAL_IMAGE and mediainfo:
|
||||
if get_runtime_setting('TMDB_SCRAP_ORIGINAL_IMAGE') and mediainfo:
|
||||
return TmdbApi(language=mediainfo.original_language)
|
||||
return self.default_tmdb
|
||||
|
||||
@@ -116,7 +115,7 @@ class TmdbScraper:
|
||||
# TMDB集still图片
|
||||
ext = Path(still_path).suffix
|
||||
still_name = f"episode-thumb{ext}"
|
||||
still_url = settings.TMDB_IMAGE_URL(still_path)
|
||||
still_url = get_runtime_setting('TMDB_IMAGE_URL')(still_path)
|
||||
images[still_name] = still_url
|
||||
else:
|
||||
# 季的图片
|
||||
@@ -144,14 +143,14 @@ class TmdbScraper:
|
||||
images[image_name] = attr_value
|
||||
|
||||
# 替换原语言Poster
|
||||
if settings.TMDB_SCRAP_ORIGINAL_IMAGE:
|
||||
if get_runtime_setting('TMDB_SCRAP_ORIGINAL_IMAGE'):
|
||||
_mediainfo = self.original_tmdb(mediainfo).get_info(
|
||||
mediainfo.type, mediainfo.tmdb_id
|
||||
)
|
||||
if _mediainfo:
|
||||
for attr_name, attr_value in _mediainfo.items():
|
||||
if attr_name.endswith("_path") and attr_value is not None:
|
||||
image_url = settings.TMDB_IMAGE_URL(attr_value)
|
||||
image_url = get_runtime_setting('TMDB_IMAGE_URL')(attr_value)
|
||||
image_name = (
|
||||
attr_name.replace("_path", "") + Path(image_url).suffix
|
||||
)
|
||||
@@ -181,11 +180,11 @@ class TmdbScraper:
|
||||
if not mediainfo.poster_path:
|
||||
poster_path = self.__pick_best_image_path(image_info.get("posters"))
|
||||
if poster_path:
|
||||
mediainfo.poster_path = settings.TMDB_IMAGE_URL(poster_path)
|
||||
mediainfo.poster_path = get_runtime_setting('TMDB_IMAGE_URL')(poster_path)
|
||||
if not mediainfo.backdrop_path:
|
||||
backdrop_path = self.__pick_best_image_path(image_info.get("backdrops"))
|
||||
if backdrop_path:
|
||||
mediainfo.backdrop_path = settings.TMDB_IMAGE_URL(backdrop_path)
|
||||
mediainfo.backdrop_path = get_runtime_setting('TMDB_IMAGE_URL')(backdrop_path)
|
||||
|
||||
@staticmethod
|
||||
def __pick_best_image_path(images: list) -> Optional[str]:
|
||||
@@ -215,7 +214,7 @@ class TmdbScraper:
|
||||
# 后缀
|
||||
ext = Path(poster_path).suffix
|
||||
# URL
|
||||
url = settings.TMDB_IMAGE_URL(poster_path)
|
||||
url = get_runtime_setting('TMDB_IMAGE_URL')(poster_path)
|
||||
# S0海报格式不同
|
||||
if season == 0:
|
||||
image_name = f"season-specials-poster{ext}"
|
||||
@@ -286,7 +285,7 @@ class TmdbScraper:
|
||||
DomUtils.add_node(doc, xactor, "tmdbid", actor.get("id") or "")
|
||||
if profile_path := actor.get("profile_path"):
|
||||
DomUtils.add_node(
|
||||
doc, xactor, "thumb", settings.TMDB_IMAGE_URL(profile_path)
|
||||
doc, xactor, "thumb", get_runtime_setting('TMDB_IMAGE_URL')(profile_path)
|
||||
)
|
||||
DomUtils.add_node(
|
||||
doc,
|
||||
@@ -453,7 +452,7 @@ class TmdbScraper:
|
||||
DomUtils.add_node(doc, xactor, "tmdbid", actor.get("id") or "")
|
||||
if profile_path := actor.get("profile_path"):
|
||||
DomUtils.add_node(
|
||||
doc, xactor, "thumb", settings.TMDB_IMAGE_URL(profile_path)
|
||||
doc, xactor, "thumb", get_runtime_setting('TMDB_IMAGE_URL')(profile_path)
|
||||
)
|
||||
DomUtils.add_node(
|
||||
doc,
|
||||
|
||||
@@ -6,9 +6,8 @@ from time import time
|
||||
from typing import Any, Optional
|
||||
|
||||
from app.runtime.cache import FileCache, TTLCache
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaSource, MediaType
|
||||
@@ -32,8 +31,8 @@ class TmdbCache(metaclass=WeakSingleton):
|
||||
"""
|
||||
def __init__(self):
|
||||
"""初始化 TMDB 识别缓存并恢复未过期的持久化数据。"""
|
||||
self.maxsize = settings.CONF.tmdb
|
||||
self.ttl = settings.CONF.meta
|
||||
self.maxsize = get_runtime_setting('CONF').tmdb
|
||||
self.ttl = get_runtime_setting('CONF').meta
|
||||
self.region = "__tmdb_cache__"
|
||||
self._cache = TTLCache(region=self.region, maxsize=self.maxsize, ttl=self.ttl)
|
||||
self._expires_at: dict[str, float] = {}
|
||||
@@ -42,8 +41,8 @@ class TmdbCache(metaclass=WeakSingleton):
|
||||
self._legacy_file_cache = None
|
||||
self._legacy_cache_found = False
|
||||
if not self._cache.is_redis():
|
||||
self._file_cache = FileCache(base=settings.CACHE_PATH, ttl=self.ttl)
|
||||
self._legacy_file_cache = FileCache(base=settings.TEMP_PATH.parent, ttl=self.ttl)
|
||||
self._file_cache = FileCache(base=get_runtime_setting('CACHE_PATH'), ttl=self.ttl)
|
||||
self._legacy_file_cache = FileCache(base=get_runtime_setting('TEMP_PATH').parent, ttl=self.ttl)
|
||||
self._restore()
|
||||
|
||||
def _restore(self) -> None:
|
||||
@@ -53,7 +52,7 @@ class TmdbCache(metaclass=WeakSingleton):
|
||||
if not content:
|
||||
content = self._legacy_file_cache.get(
|
||||
self.region,
|
||||
region=settings.TEMP_PATH.name,
|
||||
region=get_runtime_setting('TEMP_PATH').name,
|
||||
)
|
||||
if content:
|
||||
self._legacy_cache_found = True
|
||||
@@ -146,7 +145,7 @@ class TmdbCache(metaclass=WeakSingleton):
|
||||
获取缓存KEY
|
||||
"""
|
||||
media_id = meta.media_id if meta.media_source == MediaSource.TMDB else None
|
||||
return f"[{meta.type.value if meta.type else '未知'}][{settings.TMDB_LOCALE}]{media_id or meta.name}-{meta.year}-{meta.begin_season}"
|
||||
return f"[{meta.type.value if meta.type else '未知'}][{get_runtime_setting('TMDB_LOCALE')}]{media_id or meta.name}-{meta.year}-{meta.begin_season}"
|
||||
|
||||
@staticmethod
|
||||
def __is_type_conflicted(meta: MetaBase, media_type: Any, tmdb_id: Any) -> bool:
|
||||
@@ -263,7 +262,7 @@ class TmdbCache(metaclass=WeakSingleton):
|
||||
# 负识别缓存使用独立的短 TTL:故障期间「合法 JSON 但结果为空」会被
|
||||
# 记为未识别,若按完整有效期固化,故障自愈后同名仍会被判无法识别;
|
||||
# 短过期让恢复后可重新识别,真不存在的条目过期后重新确认一次即可
|
||||
self._set(key, {"id": 0}, ttl=settings.EMPTY_RESULT_CACHE_TTL)
|
||||
self._set(key, {"id": 0}, ttl=get_runtime_setting('EMPTY_RESULT_CACHE_TTL'))
|
||||
|
||||
def save(self, force: bool = False) -> None:
|
||||
"""
|
||||
@@ -314,7 +313,7 @@ class TmdbCache(metaclass=WeakSingleton):
|
||||
if self._legacy_cache_found:
|
||||
self._legacy_file_cache.delete(
|
||||
self.region,
|
||||
region=settings.TEMP_PATH.name,
|
||||
region=get_runtime_setting('TEMP_PATH').name,
|
||||
)
|
||||
self._legacy_cache_found = False
|
||||
self._dirty = False
|
||||
|
||||
@@ -2,9 +2,8 @@ import re
|
||||
import traceback
|
||||
from typing import Optional, List
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.schemas.types import MediaType
|
||||
from app.foundation import text as text_tools
|
||||
@@ -1279,7 +1278,7 @@ class TmdbApi:
|
||||
"""
|
||||
languages = []
|
||||
for language in (
|
||||
settings.TMDB_LOCALE,
|
||||
get_runtime_setting('TMDB_LOCALE'),
|
||||
"en",
|
||||
None,
|
||||
original_language,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from ..tmdb import TMDb
|
||||
|
||||
try:
|
||||
@@ -16,7 +15,7 @@ class Discover(TMDb):
|
||||
"tv": "/discover/tv"
|
||||
}
|
||||
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=settings.EMPTY_RESULT_CACHE_TTL)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=get_runtime_setting('EMPTY_RESULT_CACHE_TTL'))
|
||||
def discover_movies(self, params_tuple):
|
||||
"""
|
||||
Discover movies by different types of data like average rating, number of votes, genres and certifications.
|
||||
@@ -26,7 +25,7 @@ class Discover(TMDb):
|
||||
params = dict(params_tuple)
|
||||
return self._request_obj(self._urls["movies"], urlencode(params), key="results", call_cached=False)
|
||||
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=settings.EMPTY_RESULT_CACHE_TTL)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=get_runtime_setting('EMPTY_RESULT_CACHE_TTL'))
|
||||
def discover_tv_shows(self, params_tuple):
|
||||
"""
|
||||
Discover TV shows by different types of data like average rating, number of votes, genres,
|
||||
@@ -36,7 +35,7 @@ class Discover(TMDb):
|
||||
"""
|
||||
return self._request_obj(self._urls["tv"], urlencode(params_tuple), key="results", call_cached=False)
|
||||
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=settings.EMPTY_RESULT_CACHE_TTL)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=get_runtime_setting('EMPTY_RESULT_CACHE_TTL'))
|
||||
async def async_discover_movies(self, params_tuple):
|
||||
"""
|
||||
Discover movies by different types of data like average rating, number of votes, genres and certifications.(异步版本)
|
||||
@@ -46,7 +45,7 @@ class Discover(TMDb):
|
||||
params = dict(params_tuple)
|
||||
return await self._async_request_obj(self._urls["movies"], urlencode(params), key="results", call_cached=False)
|
||||
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=settings.EMPTY_RESULT_CACHE_TTL)
|
||||
@cached(maxsize=1, ttl=43200, empty_ttl=get_runtime_setting('EMPTY_RESULT_CACHE_TTL'))
|
||||
async def async_discover_tv_shows(self, params_tuple):
|
||||
"""
|
||||
Discover TV shows by different types of data like average rating, number of votes, genres,
|
||||
|
||||
@@ -10,9 +10,8 @@ import requests
|
||||
import requests.exceptions
|
||||
|
||||
from app.runtime.cache import cached, fresh, async_fresh
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
||||
from .exceptions import TMDbException, TMDbConnectionError
|
||||
|
||||
@@ -44,7 +43,7 @@ def _is_empty_result_snapshot(snapshot) -> bool:
|
||||
判断响应快照是否为空结果(列表/搜索类接口的 results 为空列表)。
|
||||
|
||||
这类快照结构合法但无业务内容,常由代理瞬时故障产生;不能靠 skip_none/skip_empty
|
||||
识别(快照本身是非空字典),需单独谓词判定后按 settings.EMPTY_RESULT_CACHE_TTL
|
||||
识别(快照本身是非空字典),需单独谓词判定后按 get_runtime_setting('EMPTY_RESULT_CACHE_TTL')
|
||||
短 TTL 缓存。详情类接口无 results 字段,不属于空结果。
|
||||
"""
|
||||
if not isinstance(snapshot, dict):
|
||||
@@ -60,13 +59,13 @@ class TMDb(object):
|
||||
_RESPONSE_SNAPSHOT_MARKER = "__mp_tmdb_response_snapshot__"
|
||||
|
||||
def __init__(self, session=None, language=None):
|
||||
self._api_key = settings.TMDB_API_KEY
|
||||
self._language = language or settings.TMDB_LOCALE or "en-US"
|
||||
self._api_key = get_runtime_setting('TMDB_API_KEY')
|
||||
self._language = language or get_runtime_setting('TMDB_LOCALE') or "en-US"
|
||||
self._session_id = None
|
||||
self._session = session
|
||||
self._wait_on_rate_limit = True
|
||||
self._proxies = settings.PROXY
|
||||
self._domain = settings.TMDB_API_DOMAIN
|
||||
self._proxies = get_runtime_setting('PROXY')
|
||||
self._domain = get_runtime_setting('TMDB_API_DOMAIN')
|
||||
self._page = None
|
||||
self._total_results = None
|
||||
self._total_pages = None
|
||||
@@ -76,7 +75,7 @@ class TMDb(object):
|
||||
|
||||
# TMDB 在部分代理和运营商链路下的 HTTP/2 长连接偶发卡死,识别路径优先保证稳定性。
|
||||
self._async_req = AsyncRequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT,
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
proxies=self.proxies,
|
||||
http2=False,
|
||||
)
|
||||
@@ -91,7 +90,7 @@ class TMDb(object):
|
||||
"""
|
||||
self._session = session or requests.Session()
|
||||
self._req = RequestUtils(
|
||||
ua=settings.NORMAL_USER_AGENT,
|
||||
ua=get_runtime_setting('NORMAL_USER_AGENT'),
|
||||
session=self._session,
|
||||
proxies=self.proxies,
|
||||
)
|
||||
@@ -175,9 +174,9 @@ class TMDb(object):
|
||||
def wait_on_rate_limit(self, wait_on_rate_limit):
|
||||
self._wait_on_rate_limit = bool(wait_on_rate_limit)
|
||||
|
||||
@cached(maxsize=settings.CONF.tmdb, ttl=settings.CONF.meta, skip_none=True,
|
||||
@cached(maxsize=get_runtime_setting('CONF').tmdb, ttl=get_runtime_setting('CONF').meta, skip_none=True,
|
||||
skip_if=_is_business_failure_snapshot,
|
||||
empty_ttl=settings.EMPTY_RESULT_CACHE_TTL, empty_if=_is_empty_result_snapshot)
|
||||
empty_ttl=get_runtime_setting('EMPTY_RESULT_CACHE_TTL'), empty_if=_is_empty_result_snapshot)
|
||||
def request(self, method, url, data, json, **kwargs):
|
||||
req = self._request_once(method, url, data, json)
|
||||
if req is None and method == "GET" and self._owns_session:
|
||||
@@ -201,9 +200,9 @@ class TMDb(object):
|
||||
return self._req.get_res(url, params=data, json=json)
|
||||
return self._req.post_res(url, data=data, json=json)
|
||||
|
||||
@cached(maxsize=settings.CONF.tmdb, ttl=settings.CONF.meta, skip_none=True,
|
||||
@cached(maxsize=get_runtime_setting('CONF').tmdb, ttl=get_runtime_setting('CONF').meta, skip_none=True,
|
||||
skip_if=_is_business_failure_snapshot,
|
||||
empty_ttl=settings.EMPTY_RESULT_CACHE_TTL, empty_if=_is_empty_result_snapshot)
|
||||
empty_ttl=get_runtime_setting('EMPTY_RESULT_CACHE_TTL'), empty_if=_is_empty_result_snapshot)
|
||||
async def async_request(self, method, url, data, json, **kwargs):
|
||||
req = await self._async_request_once(method, url, data, json)
|
||||
if req is None:
|
||||
|
||||
@@ -38,11 +38,11 @@ class TheTvDbModule(_ModuleBase):
|
||||
action = "刷新" if is_retry else "创建"
|
||||
logger.info(f"开始{action}TVDB登录会话...")
|
||||
try:
|
||||
if not get_runtime_setting("TVDB_V4_API_KEY"):
|
||||
if not get_runtime_setting('TVDB_V4_API_KEY'):
|
||||
raise ConnectionError("TVDB API Key 未配置,无法初始化会话。")
|
||||
self.tvdb = tvdb_v4_official.TVDB(apikey=get_runtime_setting("TVDB_V4_API_KEY"),
|
||||
pin=get_runtime_setting("TVDB_V4_API_PIN"),
|
||||
proxy=get_runtime_setting("PROXY"),
|
||||
self.tvdb = tvdb_v4_official.TVDB(apikey=get_runtime_setting('TVDB_V4_API_KEY'),
|
||||
pin=get_runtime_setting('TVDB_V4_API_PIN'),
|
||||
proxy=get_runtime_setting('PROXY'),
|
||||
timeout=self.__timeout)
|
||||
if self.tvdb:
|
||||
logger.info(f"TVDB登录会话{action}成功。")
|
||||
|
||||
@@ -7,11 +7,9 @@ import json
|
||||
import urllib.parse
|
||||
from http import HTTPStatus
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
|
||||
class Auth:
|
||||
@@ -69,7 +67,7 @@ class Request:
|
||||
self.proxy = proxy
|
||||
self.timeout = timeout
|
||||
|
||||
@cached(maxsize=settings.CONF.tmdb, ttl=settings.CONF.meta, skip_none=True)
|
||||
@cached(maxsize=get_runtime_setting('CONF').tmdb, ttl=get_runtime_setting('CONF').meta, skip_none=True)
|
||||
def make_request(self, url: str, if_modified_since: bool = None):
|
||||
"""
|
||||
向指定的 URL 发起请求并返回数据
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Set, Tuple, Optional, Union, List, Dict
|
||||
from app.schemas.dashboard import DownloaderInfo as _SchemaDownloaderInfo
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.modules._base import _DownloaderModuleBase
|
||||
from app.modules.transmission.transmission import Transmission
|
||||
from app.schemas.transfer import DownloaderFile, DownloaderTorrent
|
||||
@@ -18,7 +18,6 @@ from app.schemas.types import (
|
||||
from app.foundation import size as size_tools
|
||||
from app.foundation import temporal as time_tools
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
_TRANSMISSION_DOWNLOADING_STATES = {
|
||||
"download_pending",
|
||||
@@ -111,8 +110,8 @@ class TransmissionModule(_DownloaderModuleBase[Transmission]):
|
||||
# 标签
|
||||
if label:
|
||||
labels = label.split(',')
|
||||
elif settings.TORRENT_TAG:
|
||||
labels = settings.TORRENT_TAG.split(',')
|
||||
elif get_runtime_setting('TORRENT_TAG'):
|
||||
labels = get_runtime_setting('TORRENT_TAG').split(',')
|
||||
else:
|
||||
labels = None
|
||||
# 添加任务
|
||||
@@ -139,16 +138,16 @@ class TransmissionModule(_DownloaderModuleBase[Transmission]):
|
||||
torrent_hash = torrent.hashString
|
||||
logger.warn(f"下载器中已存在该种子任务:{torrent_hash} - {torrent.name}")
|
||||
# 给种子打上标签
|
||||
if settings.TORRENT_TAG:
|
||||
logger.info(f"给种子 {torrent_hash} 打上标签:{settings.TORRENT_TAG}")
|
||||
if get_runtime_setting('TORRENT_TAG'):
|
||||
logger.info(f"给种子 {torrent_hash} 打上标签:{get_runtime_setting('TORRENT_TAG')}")
|
||||
# 种子标签
|
||||
labels = [str(tag).strip()
|
||||
for tag in torrent.labels] if hasattr(torrent, "labels") else []
|
||||
if "已整理" in labels:
|
||||
labels.remove("已整理")
|
||||
server.set_torrent_tag(ids=torrent_hash, tags=labels)
|
||||
if settings.TORRENT_TAG and settings.TORRENT_TAG not in labels:
|
||||
labels.append(settings.TORRENT_TAG)
|
||||
if get_runtime_setting('TORRENT_TAG') and get_runtime_setting('TORRENT_TAG') not in labels:
|
||||
labels.append(get_runtime_setting('TORRENT_TAG'))
|
||||
server.set_torrent_tag(ids=torrent_hash, tags=labels)
|
||||
return downloader or self.get_default_config_name(), torrent_hash, torrent_layout, f"下载任务已存在"
|
||||
finally:
|
||||
@@ -213,7 +212,7 @@ class TransmissionModule(_DownloaderModuleBase[Transmission]):
|
||||
servers: Dict[str, Transmission] = self.get_instances()
|
||||
ret_torrents = []
|
||||
query_status = self._normalize_query_status(status)
|
||||
query_tags = None if include_all_tags else settings.TORRENT_TAG
|
||||
query_tags = None if include_all_tags else get_runtime_setting('TORRENT_TAG')
|
||||
|
||||
def __get_torrent_attr(torrent_data, *attr_names):
|
||||
"""
|
||||
|
||||
@@ -7,9 +7,8 @@ from enum import Enum
|
||||
from typing import List, Optional, Union
|
||||
from urllib.parse import quote
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.adapters.network.http import RequestUtils, requests
|
||||
|
||||
@@ -583,7 +582,7 @@ class Api:
|
||||
else:
|
||||
queries_unquoted = None
|
||||
headers = {
|
||||
"User-Agent": settings.USER_AGENT,
|
||||
"User-Agent": get_runtime_setting('USER_AGENT'),
|
||||
"Accept": "application/json",
|
||||
"Referer": self._host,
|
||||
"Authorization": self._token,
|
||||
|
||||
@@ -4,9 +4,8 @@ from typing import Union, Tuple
|
||||
from pywebpush import webpush, WebPushException
|
||||
|
||||
from app.runtime.config import global_vars
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.modules import _ModuleBase, _MessageBase
|
||||
from app.schemas.message import Message
|
||||
@@ -103,9 +102,9 @@ class WebPushModule(_ModuleBase, _MessageBase):
|
||||
"body": content,
|
||||
"url": message.link or "/?shotcut=message"
|
||||
}),
|
||||
vapid_private_key=settings.VAPID.get("privateKey"),
|
||||
vapid_private_key=get_runtime_setting('VAPID').get("privateKey"),
|
||||
vapid_claims={
|
||||
"sub": settings.VAPID.get("subject")
|
||||
"sub": get_runtime_setting('VAPID').get("subject")
|
||||
},
|
||||
**webpush_options,
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import pickle
|
||||
@@ -5,26 +6,22 @@ import re
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
import base64
|
||||
from typing import Optional, List, Dict, Tuple, Set
|
||||
from typing import Dict, List, Optional, Set, Tuple
|
||||
|
||||
import websocket
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from app.runtime.cache import FileCache
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.messaging.ingress import submit_message_to_host
|
||||
from app.domain.context import MediaInfo, Context
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.application.messaging.agent import matches_channel_admin
|
||||
from app.application.messaging.ingress import submit_message_to_host
|
||||
from app.domain.context import Context, MediaInfo
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.foundation import size as size_tools
|
||||
from app.runtime.cache import FileCache
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.thread import ThreadHelper
|
||||
from app.schemas.message import IncomingMessage
|
||||
from app.schemas.types import NotificationChannel
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.foundation import size as size_tools
|
||||
|
||||
|
||||
class WeChatBot:
|
||||
|
||||
@@ -17,9 +17,8 @@ from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import pad
|
||||
|
||||
from app.runtime.cache import FileCache
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.application.messaging.ingress import forward_message_to_host
|
||||
from app.domain.context import Context, MediaInfo
|
||||
from app.domain.metainfo import MetaInfo
|
||||
@@ -1686,14 +1685,14 @@ class WechatClawBot:
|
||||
except Exception:
|
||||
return None
|
||||
if image_url.startswith("/"):
|
||||
image_url = settings.MP_DOMAIN(image_url)
|
||||
image_url = get_runtime_setting('MP_DOMAIN')(image_url)
|
||||
if not image_url.lower().startswith("http"):
|
||||
return None
|
||||
try:
|
||||
resp = RequestUtils(
|
||||
timeout=20,
|
||||
proxies=settings.PROXY,
|
||||
ua=settings.USER_AGENT,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
ua=get_runtime_setting('USER_AGENT'),
|
||||
).get_res(image_url)
|
||||
if resp and resp.status_code == 200 and resp.content:
|
||||
content_type = (resp.headers.get("Content-Type") or "").lower()
|
||||
|
||||
Reference in New Issue
Block a user