mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor(config): retire RuntimeSettingsCompat host usage
This commit is contained in:
Vendored
+5
-10
@@ -9,19 +9,14 @@ from anyio import Path as AsyncPath
|
||||
|
||||
from app.adapters.cache.redis import AsyncRedisHelper, RedisHelper
|
||||
from app.runtime.cache import (
|
||||
DEFAULT_CACHE_REGION,
|
||||
AsyncCacheBackend,
|
||||
CacheBackend,
|
||||
DEFAULT_CACHE_REGION,
|
||||
configure_cache_factories,
|
||||
)
|
||||
from app.runtime import config as _runtime_config
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
|
||||
# 兼容旧插件对模块级 Settings 的覆盖,工厂实际读取统一经过 runtime 端口。
|
||||
settings = _runtime_config.settings
|
||||
|
||||
|
||||
class RedisBackend(CacheBackend):
|
||||
"""通过同步 Redis 客户端实现缓存后端。"""
|
||||
|
||||
@@ -335,14 +330,14 @@ class AsyncFileBackend(AsyncCacheBackend):
|
||||
def configure_platform_cache() -> None:
|
||||
"""把配置感知的 Redis 与文件适配器注册到平台缓存工厂。"""
|
||||
configure_cache_factories(
|
||||
backend_type_provider=lambda: get_runtime_setting("CACHE_BACKEND_TYPE"),
|
||||
backend_type_provider=lambda: get_runtime_setting('CACHE_BACKEND_TYPE'),
|
||||
redis_factory=lambda ttl: RedisBackend(ttl=ttl),
|
||||
async_redis_factory=lambda ttl: AsyncRedisBackend(ttl=ttl),
|
||||
file_factory=lambda base: FileBackend(
|
||||
base=base or get_runtime_setting("TEMP_PATH")
|
||||
base=base or get_runtime_setting('TEMP_PATH')
|
||||
),
|
||||
async_file_factory=lambda base: AsyncFileBackend(
|
||||
base=base or get_runtime_setting("TEMP_PATH")
|
||||
base=base or get_runtime_setting('TEMP_PATH')
|
||||
),
|
||||
file_ttl_provider=lambda: get_runtime_setting("TEMP_FILE_DAYS") * 24 * 3600,
|
||||
file_ttl_provider=lambda: get_runtime_setting('TEMP_FILE_DAYS') * 24 * 3600,
|
||||
)
|
||||
|
||||
Vendored
+16
-21
@@ -2,22 +2,17 @@ import asyncio
|
||||
import json
|
||||
import pickle
|
||||
import threading
|
||||
from typing import Any, Optional, Generator, Tuple, AsyncGenerator, Union
|
||||
from typing import Any, AsyncGenerator, Generator, Optional, Tuple, Union
|
||||
from urllib.parse import quote, unquote
|
||||
|
||||
import redis
|
||||
from redis.asyncio import BlockingConnectionPool as AsyncBlockingConnectionPool
|
||||
from redis.asyncio import Redis
|
||||
|
||||
from app.runtime import config as _runtime_config
|
||||
from app.foundation.singleton import Singleton
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.reload import ConfigReloadMixin
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.foundation.singleton import Singleton
|
||||
|
||||
|
||||
# 兼容旧插件和测试对模块级 Settings 的覆盖,Redis 连接逻辑统一读取 runtime 端口。
|
||||
settings = _runtime_config.settings
|
||||
|
||||
# 类型缓存集合,针对非容器简单类型
|
||||
_complex_serializable_types = set()
|
||||
@@ -102,7 +97,7 @@ class RedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
"""
|
||||
初始化Redis助手实例
|
||||
"""
|
||||
self.redis_url = get_runtime_setting("CACHE_BACKEND_URL")
|
||||
self.redis_url = get_runtime_setting('CACHE_BACKEND_URL')
|
||||
self.client = None
|
||||
self._connect_lock = threading.RLock()
|
||||
|
||||
@@ -117,15 +112,15 @@ class RedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
with self._connect_lock:
|
||||
if self.client is not None:
|
||||
return
|
||||
self.redis_url = get_runtime_setting("CACHE_BACKEND_URL")
|
||||
self.redis_url = get_runtime_setting('CACHE_BACKEND_URL')
|
||||
connection_pool = redis.BlockingConnectionPool.from_url(
|
||||
self.redis_url,
|
||||
decode_responses=False,
|
||||
socket_timeout=_socket_timeout,
|
||||
socket_connect_timeout=_socket_connect_timeout,
|
||||
health_check_interval=_health_check_interval,
|
||||
max_connections=get_runtime_setting("CACHE_REDIS_MAX_CONNECTIONS"),
|
||||
timeout=get_runtime_setting("CACHE_REDIS_POOL_TIMEOUT"),
|
||||
max_connections=get_runtime_setting('CACHE_REDIS_MAX_CONNECTIONS'),
|
||||
timeout=get_runtime_setting('CACHE_REDIS_POOL_TIMEOUT'),
|
||||
)
|
||||
client = redis.Redis(connection_pool=connection_pool)
|
||||
# 测试连接,确保Redis可用
|
||||
@@ -143,7 +138,7 @@ class RedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
def on_config_changed(self):
|
||||
"""缓存配置变化后重建同步 Redis 连接。"""
|
||||
with self._connect_lock:
|
||||
self.redis_url = get_runtime_setting("CACHE_BACKEND_URL")
|
||||
self.redis_url = get_runtime_setting('CACHE_BACKEND_URL')
|
||||
self.close()
|
||||
self._connect()
|
||||
|
||||
@@ -159,8 +154,8 @@ class RedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
"""
|
||||
try:
|
||||
# 如果有显式值,则直接使用,为0时说明不限制,如果未配置,开启BIG_MEMORY_MODE时为"1024mb",未开启时为"256mb"
|
||||
maxmemory = get_runtime_setting("CACHE_REDIS_MAXMEMORY") or (
|
||||
"1024mb" if get_runtime_setting("BIG_MEMORY_MODE") else "256mb"
|
||||
maxmemory = get_runtime_setting('CACHE_REDIS_MAXMEMORY') or (
|
||||
"1024mb" if get_runtime_setting('BIG_MEMORY_MODE') else "256mb"
|
||||
)
|
||||
self.client.config_set("maxmemory", maxmemory)
|
||||
self.client.config_set("maxmemory-policy", policy)
|
||||
@@ -371,7 +366,7 @@ class AsyncRedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
"""
|
||||
初始化异步Redis助手实例
|
||||
"""
|
||||
self.redis_url = get_runtime_setting("CACHE_BACKEND_URL")
|
||||
self.redis_url = get_runtime_setting('CACHE_BACKEND_URL')
|
||||
self.client: Optional[Redis] = None
|
||||
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
||||
self._connect_lock: Optional[asyncio.Lock] = None
|
||||
@@ -401,15 +396,15 @@ class AsyncRedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
await self._close_client()
|
||||
if self.client is not None:
|
||||
return
|
||||
self.redis_url = get_runtime_setting("CACHE_BACKEND_URL")
|
||||
self.redis_url = get_runtime_setting('CACHE_BACKEND_URL')
|
||||
connection_pool = AsyncBlockingConnectionPool.from_url(
|
||||
self.redis_url,
|
||||
decode_responses=False,
|
||||
socket_timeout=_socket_timeout,
|
||||
socket_connect_timeout=_socket_connect_timeout,
|
||||
health_check_interval=_health_check_interval,
|
||||
max_connections=get_runtime_setting("CACHE_REDIS_MAX_CONNECTIONS"),
|
||||
timeout=get_runtime_setting("CACHE_REDIS_POOL_TIMEOUT"),
|
||||
max_connections=get_runtime_setting('CACHE_REDIS_MAX_CONNECTIONS'),
|
||||
timeout=get_runtime_setting('CACHE_REDIS_POOL_TIMEOUT'),
|
||||
)
|
||||
client = Redis(connection_pool=connection_pool)
|
||||
self._loop = current_loop
|
||||
@@ -440,7 +435,7 @@ class AsyncRedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
|
||||
async def on_config_changed(self):
|
||||
"""缓存配置变化后异步重建 Redis 连接。"""
|
||||
self.redis_url = get_runtime_setting("CACHE_BACKEND_URL")
|
||||
self.redis_url = get_runtime_setting('CACHE_BACKEND_URL')
|
||||
await self._close_client()
|
||||
await self._connect()
|
||||
|
||||
@@ -456,8 +451,8 @@ class AsyncRedisHelper(ConfigReloadMixin, metaclass=Singleton):
|
||||
"""
|
||||
try:
|
||||
# 如果有显式值,则直接使用,为0时说明不限制,如果未配置,开启BIG_MEMORY_MODE时为"1024mb",未开启时为"256mb"
|
||||
maxmemory = get_runtime_setting("CACHE_REDIS_MAXMEMORY") or (
|
||||
"1024mb" if get_runtime_setting("BIG_MEMORY_MODE") else "256mb"
|
||||
maxmemory = get_runtime_setting('CACHE_REDIS_MAXMEMORY') or (
|
||||
"1024mb" if get_runtime_setting('BIG_MEMORY_MODE') else "256mb"
|
||||
)
|
||||
await self.client.config_set("maxmemory", maxmemory)
|
||||
await self.client.config_set("maxmemory-policy", policy)
|
||||
|
||||
Vendored
+62
-63
@@ -37,7 +37,7 @@ from app.runtime.dependencies import (
|
||||
iter_runtime_requirement_strings,
|
||||
runtime_excluded_dependency_pairs,
|
||||
)
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.adapters.system.package import (
|
||||
PackageInstallRequest,
|
||||
build_package_install_strategies,
|
||||
@@ -64,9 +64,8 @@ from app.adapters.system.host import SystemUtils
|
||||
from app.foundation.url import UrlUtils
|
||||
from app.runtime.version import get_app_version
|
||||
|
||||
# 保留模块级可替换入口,代理默认读取组合根的最新 runtime 配置。
|
||||
settings = RuntimeSettingsCompat()
|
||||
PLUGIN_DIR = Path(settings.ROOT_PATH) / "app" / "plugins"
|
||||
# 插件市场只通过 runtime 读取端口消费组合根的最新配置。
|
||||
PLUGIN_DIR = Path(get_runtime_setting('ROOT_PATH')) / "app" / "plugins"
|
||||
LOCAL_REPO_PREFIX = "local://"
|
||||
PLUGIN_SYSTEM_VERSION_FIELD = "system_version"
|
||||
PLUGIN_MARKET_WIKI_START = "<!-- plugin-market-repos:start -->"
|
||||
@@ -317,7 +316,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
return None
|
||||
path = Path(values[0]).expanduser()
|
||||
if not path.is_absolute():
|
||||
path = settings.ROOT_PATH / path
|
||||
path = get_runtime_setting('ROOT_PATH') / path
|
||||
return path.resolve()
|
||||
except Exception:
|
||||
return None
|
||||
@@ -355,9 +354,9 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
未启用 VERSION_FLAG(v1)时返回空列表,表示仅使用 package.json 基础索引。
|
||||
"""
|
||||
flags: List[str] = []
|
||||
if settings.VERSION_FLAG:
|
||||
flags.append(settings.VERSION_FLAG)
|
||||
flags.extend(VERSION_BACKWARD_COMPATIBLE_FLAGS.get(settings.VERSION_FLAG, []))
|
||||
if get_runtime_setting('VERSION_FLAG'):
|
||||
flags.append(get_runtime_setting('VERSION_FLAG'))
|
||||
flags.extend(VERSION_BACKWARD_COMPATIBLE_FLAGS.get(get_runtime_setting('VERSION_FLAG'), []))
|
||||
return flags
|
||||
|
||||
@classmethod
|
||||
@@ -370,9 +369,9 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
"""
|
||||
if not isinstance(plugin_info, dict):
|
||||
return False
|
||||
if not settings.VERSION_FLAG:
|
||||
if not get_runtime_setting('VERSION_FLAG'):
|
||||
return True
|
||||
current_flag = settings.VERSION_FLAG
|
||||
current_flag = get_runtime_setting('VERSION_FLAG')
|
||||
if plugin_info.get(current_flag) is False:
|
||||
return False
|
||||
if plugin_info.get(current_flag) is True:
|
||||
@@ -398,7 +397,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
"""
|
||||
if not isinstance(plugin_info, dict):
|
||||
return False
|
||||
current_flag = settings.VERSION_FLAG
|
||||
current_flag = get_runtime_setting('VERSION_FLAG')
|
||||
if not current_flag:
|
||||
return not package_version
|
||||
if package_version == current_flag:
|
||||
@@ -416,7 +415,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
package_version: Optional[str],
|
||||
) -> Tuple[str, ...]:
|
||||
"""返回插件安装唯一的代际候选顺序,并去除重复的基础索引。"""
|
||||
preferred_version = package_version or settings.VERSION_FLAG
|
||||
preferred_version = package_version or get_runtime_setting('VERSION_FLAG')
|
||||
candidates = [preferred_version]
|
||||
candidates.extend(
|
||||
VERSION_BACKWARD_COMPATIBLE_FLAGS.get(preferred_version, [])
|
||||
@@ -491,16 +490,16 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
"""
|
||||
获取本地插件仓库目录列表
|
||||
"""
|
||||
if not settings.PLUGIN_LOCAL_REPO_PATHS:
|
||||
if not get_runtime_setting('PLUGIN_LOCAL_REPO_PATHS'):
|
||||
return []
|
||||
paths = []
|
||||
for item in settings.PLUGIN_LOCAL_REPO_PATHS.split(","):
|
||||
for item in get_runtime_setting('PLUGIN_LOCAL_REPO_PATHS').split(","):
|
||||
local_repo_path = item.strip()
|
||||
if not local_repo_path:
|
||||
continue
|
||||
path = Path(local_repo_path).expanduser()
|
||||
if not path.is_absolute():
|
||||
path = settings.ROOT_PATH / path
|
||||
path = get_runtime_setting('ROOT_PATH') / path
|
||||
paths.append(path.resolve())
|
||||
return paths
|
||||
|
||||
@@ -542,11 +541,11 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
continue
|
||||
|
||||
package_candidates = []
|
||||
if settings.VERSION_FLAG:
|
||||
package_candidates.append((settings.VERSION_FLAG, self.__get_local_package(repo_path,
|
||||
settings.VERSION_FLAG)))
|
||||
if get_runtime_setting('VERSION_FLAG'):
|
||||
package_candidates.append((get_runtime_setting('VERSION_FLAG'), self.__get_local_package(repo_path,
|
||||
get_runtime_setting('VERSION_FLAG'))))
|
||||
# 向后兼容:补充扫描更低版本的 package 文件,便于本地仓库复用历史版本插件。
|
||||
for backward_flag in VERSION_BACKWARD_COMPATIBLE_FLAGS.get(settings.VERSION_FLAG, []):
|
||||
for backward_flag in VERSION_BACKWARD_COMPATIBLE_FLAGS.get(get_runtime_setting('VERSION_FLAG'), []):
|
||||
package_candidates.append((backward_flag, self.__get_local_package(repo_path, backward_flag)))
|
||||
package_candidates.append(("", self.__get_local_package(repo_path)))
|
||||
|
||||
@@ -611,9 +610,9 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
repo_paths = [repo_path.resolve()] if repo_path else self.get_local_repo_paths()
|
||||
package_versions = [package_version] if package_version is not None else []
|
||||
if package_version is None:
|
||||
if settings.VERSION_FLAG:
|
||||
package_versions.append(settings.VERSION_FLAG)
|
||||
package_versions.extend(VERSION_BACKWARD_COMPATIBLE_FLAGS.get(settings.VERSION_FLAG, []))
|
||||
if get_runtime_setting('VERSION_FLAG'):
|
||||
package_versions.append(get_runtime_setting('VERSION_FLAG'))
|
||||
package_versions.extend(VERSION_BACKWARD_COMPATIBLE_FLAGS.get(get_runtime_setting('VERSION_FLAG'), []))
|
||||
package_versions.append("")
|
||||
selected_candidate = None
|
||||
for repo_order, local_repo_path in enumerate(self.get_local_repo_paths()):
|
||||
@@ -650,7 +649,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
if not is_compatible:
|
||||
candidate["compatible"] = False
|
||||
candidate["skip_reason"] = (
|
||||
f"插件索引条目不兼容 {settings.VERSION_FLAG}"
|
||||
f"插件索引条目不兼容 {get_runtime_setting('VERSION_FLAG')}"
|
||||
)
|
||||
self.annotate_plugin_system_version(candidate)
|
||||
if strict_system_version and candidate.get("system_version_compatible") is False:
|
||||
@@ -729,7 +728,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
else "package.json"
|
||||
)
|
||||
package_url = cls.__append_cache_buster(f"{raw_url}{package_file}")
|
||||
headers = settings.REPO_GITHUB_HEADERS(repo=f"{user}/{repo}")
|
||||
headers = get_runtime_setting('REPO_GITHUB_HEADERS')(repo=f"{user}/{repo}")
|
||||
return package_url, headers
|
||||
|
||||
@classmethod
|
||||
@@ -829,7 +828,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
return
|
||||
|
||||
user_repo = f"{user}/{repo}"
|
||||
headers = settings.REPO_GITHUB_HEADERS(repo=user_repo)
|
||||
headers = get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo)
|
||||
for page in range(1, 11):
|
||||
release_api = (
|
||||
f"https://api.github.com/repos/{user_repo}/releases"
|
||||
@@ -998,7 +997,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
package_version: Optional[str] = None) -> Optional[str]:
|
||||
"""
|
||||
检查并获取指定插件的可用版本,支持多版本优先级加载和版本兼容性检测
|
||||
1. 如果未指定版本,则使用系统配置的默认版本(通过 settings.VERSION_FLAG 设置)
|
||||
1. 如果未指定版本,则使用系统配置的默认版本(通过 get_runtime_setting('VERSION_FLAG') 设置)
|
||||
2. 优先检查指定版本的插件(如 `package.v2.json`)
|
||||
3. 检查更低版本的 package 文件,并应用版本兼容标志
|
||||
4. 检查 `package.json` 文件,并应用共享实现兼容标志
|
||||
@@ -1084,7 +1083,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
user_repo = f"{user}/{repo}"
|
||||
|
||||
if not package_version:
|
||||
package_version = settings.VERSION_FLAG
|
||||
package_version = get_runtime_setting('VERSION_FLAG')
|
||||
|
||||
# 1. 优先检查指定版本的插件
|
||||
package_version = self.get_plugin_package_version(pid, repo_url, package_version)
|
||||
@@ -1232,7 +1231,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
file_api += f"/{pid.lower()}"
|
||||
|
||||
res = self.__request_with_fallback(file_api,
|
||||
headers=settings.REPO_GITHUB_HEADERS(repo=user_repo),
|
||||
headers=get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo),
|
||||
is_api=True,
|
||||
timeout=30)
|
||||
if res is None:
|
||||
@@ -1273,7 +1272,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
if item.get("download_url"):
|
||||
logger.debug(f"正在下载文件:{item.get('path')}")
|
||||
res = self.__request_with_fallback(item.get('download_url'),
|
||||
headers=settings.REPO_GITHUB_HEADERS(repo=user_repo))
|
||||
headers=get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo))
|
||||
if not res:
|
||||
return False, f"文件 {item.get('path')} 下载失败!"
|
||||
elif res.status_code != 200:
|
||||
@@ -1285,7 +1284,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
relative_path = relative_path.replace(f"plugins.{package_version}", "plugins", 1)
|
||||
|
||||
# 创建插件文件夹并写入文件
|
||||
file_path = Path(settings.ROOT_PATH) / "app" / relative_path
|
||||
file_path = Path(get_runtime_setting('ROOT_PATH')) / "app" / relative_path
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.write(res.text)
|
||||
@@ -1327,7 +1326,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
:return: 备份目录路径
|
||||
"""
|
||||
plugin_dir = PLUGIN_DIR / pid.lower()
|
||||
backup_dir = Path(settings.TEMP_PATH) / "plugin_backup" / pid.lower()
|
||||
backup_dir = Path(get_runtime_setting('TEMP_PATH')) / "plugin_backup" / pid.lower()
|
||||
|
||||
if plugin_dir.exists():
|
||||
# 备份时清理已有的备份目录,防止残留文件影响
|
||||
@@ -1381,7 +1380,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
logger.warn(f"{pid} 插件目录不存在,跳过刷新插件备份")
|
||||
return False
|
||||
|
||||
backup_root = settings.CONFIG_PATH / "plugins_backup"
|
||||
backup_root = get_runtime_setting('CONFIG_PATH') / "plugins_backup"
|
||||
backup_dir = backup_root / pid.lower()
|
||||
staging_dir = backup_root / f".{pid.lower()}.tmp-{uuid.uuid4().hex}"
|
||||
previous_dir = backup_root / f".{pid.lower()}.old-{uuid.uuid4().hex}"
|
||||
@@ -1546,7 +1545,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
if package_name in cls._protected_runtime_packages
|
||||
}
|
||||
|
||||
project_file = settings.ROOT_PATH / "pyproject.toml"
|
||||
project_file = get_runtime_setting('ROOT_PATH') / "pyproject.toml"
|
||||
root_requirements = cls.__parse_project_requirement_roots(project_file)
|
||||
if not root_requirements:
|
||||
return protected_packages
|
||||
@@ -1595,7 +1594,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
def __get_strict_runtime_packages(cls) -> Set[str]:
|
||||
"""返回核心包及当前 ABI profile 中不得被插件改写的根包。"""
|
||||
packages = set(cls._protected_runtime_packages)
|
||||
project_file = settings.ROOT_PATH / "pyproject.toml"
|
||||
project_file = get_runtime_setting('ROOT_PATH') / "pyproject.toml"
|
||||
try:
|
||||
for raw_requirement in iter_runtime_profile_requirement_strings(project_file):
|
||||
requirement = Requirement(raw_requirement)
|
||||
@@ -1717,7 +1716,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
"""
|
||||
以主程序依赖的当前已安装版本生成临时约束文件,确保插件安装不会改写主程序依赖。
|
||||
"""
|
||||
temp_dir = Path(settings.TEMP_PATH) / "plugin_dependencies"
|
||||
temp_dir = Path(get_runtime_setting('TEMP_PATH')) / "plugin_dependencies"
|
||||
temp_dir.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w",
|
||||
@@ -1794,10 +1793,10 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
python_bin=Path(sys.executable),
|
||||
find_links_dirs=find_links_dirs or [],
|
||||
constraints_file=constraints_file,
|
||||
config_dir=settings.CONFIG_PATH,
|
||||
package_cache_root=settings.PACKAGE_CACHE_PATH,
|
||||
package_index_url=settings.PIP_PROXY or None,
|
||||
proxy_url=settings.PROXY_HOST or None,
|
||||
config_dir=get_runtime_setting('CONFIG_PATH'),
|
||||
package_cache_root=get_runtime_setting('PACKAGE_CACHE_PATH'),
|
||||
package_index_url=get_runtime_setting('PIP_PROXY') or None,
|
||||
proxy_url=get_runtime_setting('PROXY_HOST') or None,
|
||||
purpose=purpose,
|
||||
)
|
||||
|
||||
@@ -1868,7 +1867,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
return lines
|
||||
|
||||
excluded_pairs = runtime_excluded_dependency_pairs(
|
||||
Path(settings.ROOT_PATH) / "pyproject.toml"
|
||||
Path(get_runtime_setting('ROOT_PATH')) / "pyproject.toml"
|
||||
)
|
||||
package_errors = set()
|
||||
for match in matches:
|
||||
@@ -1930,12 +1929,12 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
if repair_target and not repair_target.exists():
|
||||
repair_target = None
|
||||
if repair_target is None:
|
||||
repair_target = settings.ROOT_PATH / "pyproject.toml"
|
||||
repair_target = get_runtime_setting('ROOT_PATH') / "pyproject.toml"
|
||||
repair_desc = "主程序 uv.lock"
|
||||
if not repair_target.exists():
|
||||
return False, f"恢复依赖文件不存在:{repair_target}"
|
||||
if snapshot_file is None and not (settings.ROOT_PATH / "uv.lock").exists():
|
||||
return False, f"恢复依赖文件不存在:{settings.ROOT_PATH / 'uv.lock'}"
|
||||
if snapshot_file is None and not (get_runtime_setting('ROOT_PATH') / "uv.lock").exists():
|
||||
return False, f"恢复依赖文件不存在:{get_runtime_setting('ROOT_PATH') / 'uv.lock'}"
|
||||
|
||||
last_error = ""
|
||||
request = cls.__build_package_install_request(repair_target, purpose="runtime-repair")
|
||||
@@ -2127,21 +2126,21 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
) -> List[Tuple[str, str, dict]]:
|
||||
"""构造同步与异步 GitHub 请求共用的镜像、代理和直连顺序。"""
|
||||
strategies: List[Tuple[str, str, dict]] = []
|
||||
if not is_api and settings.GITHUB_PROXY:
|
||||
if not is_api and get_runtime_setting('GITHUB_PROXY'):
|
||||
proxy_url = (
|
||||
f"{UrlUtils.standardize_base_url(settings.GITHUB_PROXY)}{url}"
|
||||
f"{UrlUtils.standardize_base_url(get_runtime_setting('GITHUB_PROXY'))}{url}"
|
||||
)
|
||||
strategies.append(
|
||||
("镜像站", proxy_url, {"headers": headers, "timeout": timeout})
|
||||
)
|
||||
if settings.PROXY_HOST:
|
||||
if get_runtime_setting('PROXY_HOST'):
|
||||
strategies.append(
|
||||
(
|
||||
"代理",
|
||||
url,
|
||||
{
|
||||
"headers": headers,
|
||||
"proxies": settings.PROXY,
|
||||
"proxies": get_runtime_setting('PROXY'),
|
||||
"timeout": timeout,
|
||||
},
|
||||
)
|
||||
@@ -2218,7 +2217,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
compatible, message = self.check_plugin_system_version(candidate)
|
||||
return None if compatible else message
|
||||
|
||||
package_version = self.get_plugin_package_version(pid, repo_url, settings.VERSION_FLAG)
|
||||
package_version = self.get_plugin_package_version(pid, repo_url, get_runtime_setting('VERSION_FLAG'))
|
||||
if package_version is None:
|
||||
return None
|
||||
meta = self.__get_plugin_meta(pid, repo_url, package_version)
|
||||
@@ -2235,7 +2234,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
if self.is_local_repo_url(repo_url):
|
||||
return await asyncio.to_thread(self.get_plugin_system_version_check_message, pid, repo_url)
|
||||
|
||||
package_version = await self.async_get_plugin_package_version(pid, repo_url, settings.VERSION_FLAG)
|
||||
package_version = await self.async_get_plugin_package_version(pid, repo_url, get_runtime_setting('VERSION_FLAG'))
|
||||
if package_version is None:
|
||||
return None
|
||||
meta = await self.__async_get_plugin_meta(pid, repo_url, package_version)
|
||||
@@ -2382,7 +2381,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
release_api = f"https://api.github.com/repos/{user_repo}/releases/tags/{release_tag}"
|
||||
rel_res = self.__request_with_fallback(
|
||||
release_api,
|
||||
headers=settings.REPO_GITHUB_HEADERS(repo=user_repo),
|
||||
headers=get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo),
|
||||
timeout=30,
|
||||
is_api=True,
|
||||
)
|
||||
@@ -2405,7 +2404,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
return False, f"解析 Release 信息失败:{e}"
|
||||
|
||||
# 使用资产的API端点下载,需要设置Accept头为application/octet-stream
|
||||
headers = settings.REPO_GITHUB_HEADERS(repo=user_repo).copy()
|
||||
headers = get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo).copy()
|
||||
headers["Accept"] = "application/octet-stream"
|
||||
res = self.__request_with_fallback(download_url, headers=headers, is_api=True)
|
||||
if res is None or res.status_code != 200:
|
||||
@@ -2416,7 +2415,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
infos = zf.infolist()
|
||||
if not infos:
|
||||
return False, "压缩包内容为空"
|
||||
dest_base = Path(settings.ROOT_PATH) / "app" / "plugins" / pid.lower()
|
||||
dest_base = Path(get_runtime_setting('ROOT_PATH')) / "app" / "plugins" / pid.lower()
|
||||
targets = self.__iter_release_zip_targets(zf, dest_base)
|
||||
wrote_any = False
|
||||
for info, dest_path, is_dir in targets:
|
||||
@@ -2721,7 +2720,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
file_api += f"/{pid.lower()}"
|
||||
|
||||
res = await self.__async_request_with_fallback(file_api,
|
||||
headers=settings.REPO_GITHUB_HEADERS(repo=user_repo),
|
||||
headers=get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo),
|
||||
is_api=True,
|
||||
timeout=30)
|
||||
if res is None:
|
||||
@@ -2762,7 +2761,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
if item.get("download_url"):
|
||||
logger.debug(f"正在下载文件:{item.get('path')}")
|
||||
res = await self.__async_request_with_fallback(item.get('download_url'),
|
||||
headers=settings.REPO_GITHUB_HEADERS(repo=user_repo))
|
||||
headers=get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo))
|
||||
if not res:
|
||||
return False, f"文件 {item.get('path')} 下载失败!"
|
||||
elif res.status_code != 200:
|
||||
@@ -2774,7 +2773,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
relative_path = relative_path.replace(f"plugins.{package_version}", "plugins", 1)
|
||||
|
||||
# 创建插件文件夹并写入文件
|
||||
file_path = AsyncPath(settings.ROOT_PATH) / "app" / relative_path
|
||||
file_path = AsyncPath(get_runtime_setting('ROOT_PATH')) / "app" / relative_path
|
||||
await file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
async with aiofiles.open(file_path, "w", encoding="utf-8") as f:
|
||||
await f.write(res.text)
|
||||
@@ -2825,13 +2824,13 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
if repair_target and not await _await_thread_operation(repair_target.exists):
|
||||
repair_target = None
|
||||
if repair_target is None:
|
||||
repair_target = settings.ROOT_PATH / "pyproject.toml"
|
||||
repair_target = get_runtime_setting('ROOT_PATH') / "pyproject.toml"
|
||||
repair_desc = "主程序 uv.lock"
|
||||
if not await _await_thread_operation(repair_target.exists):
|
||||
return False, f"恢复依赖文件不存在:{repair_target}"
|
||||
lock_file = settings.ROOT_PATH / "uv.lock"
|
||||
lock_file = get_runtime_setting('ROOT_PATH') / "uv.lock"
|
||||
if snapshot_file is None and not await _await_thread_operation(lock_file.exists):
|
||||
return False, f"恢复依赖文件不存在:{settings.ROOT_PATH / 'uv.lock'}"
|
||||
return False, f"恢复依赖文件不存在:{get_runtime_setting('ROOT_PATH') / 'uv.lock'}"
|
||||
|
||||
request = cls.__build_package_install_request(
|
||||
repair_target,
|
||||
@@ -3076,7 +3075,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
:return: 备份目录路径
|
||||
"""
|
||||
plugin_dir = AsyncPath(PLUGIN_DIR) / pid.lower()
|
||||
backup_dir = AsyncPath(settings.TEMP_PATH) / "plugin_backup" / pid.lower()
|
||||
backup_dir = AsyncPath(get_runtime_setting('TEMP_PATH')) / "plugin_backup" / pid.lower()
|
||||
|
||||
if await plugin_dir.exists():
|
||||
try:
|
||||
@@ -3234,7 +3233,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
user_repo = f"{user}/{repo}"
|
||||
|
||||
if not package_version:
|
||||
package_version = settings.VERSION_FLAG
|
||||
package_version = get_runtime_setting('VERSION_FLAG')
|
||||
|
||||
# 1. 优先检查指定版本的插件
|
||||
package_version = await self.async_get_plugin_package_version(pid, repo_url, package_version)
|
||||
@@ -3396,7 +3395,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
release_api = f"https://api.github.com/repos/{user_repo}/releases/tags/{release_tag}"
|
||||
rel_res = await self.__async_request_with_fallback(
|
||||
release_api,
|
||||
headers=settings.REPO_GITHUB_HEADERS(repo=user_repo),
|
||||
headers=get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo),
|
||||
timeout=30,
|
||||
is_api=True,
|
||||
)
|
||||
@@ -3419,7 +3418,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
return False, f"解析 Release 信息失败:{e}"
|
||||
|
||||
# 使用资产的API端点下载,需要设置Accept头为application/octet-stream
|
||||
headers = settings.REPO_GITHUB_HEADERS(repo=user_repo).copy()
|
||||
headers = get_runtime_setting('REPO_GITHUB_HEADERS')(repo=user_repo).copy()
|
||||
headers["Accept"] = "application/octet-stream"
|
||||
res = await self.__async_request_with_fallback(download_url,
|
||||
headers=headers,
|
||||
@@ -3432,7 +3431,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
||||
infos = zf.infolist()
|
||||
if not infos:
|
||||
return False, "压缩包内容为空"
|
||||
dest_base = Path(settings.ROOT_PATH) / "app" / "plugins" / pid.lower()
|
||||
dest_base = Path(get_runtime_setting('ROOT_PATH')) / "app" / "plugins" / pid.lower()
|
||||
targets = self.__iter_release_zip_targets(zf, dest_base)
|
||||
wrote_any = False
|
||||
for info, dest_path, is_dir in targets:
|
||||
|
||||
Vendored
+1
-1
@@ -13,7 +13,7 @@ class OcrHelper:
|
||||
def __init__(self, ocr_base_url: Optional[str] = None) -> None:
|
||||
"""初始化 OCR 服务地址,优先使用组合根设置快照。"""
|
||||
if ocr_base_url is None:
|
||||
ocr_base_url = get_runtime_setting("OCR_HOST")
|
||||
ocr_base_url = get_runtime_setting('OCR_HOST')
|
||||
self._ocr_b64_url = f"{str(ocr_base_url).rstrip('/')}/captcha/base64"
|
||||
|
||||
def get_captcha_text(
|
||||
|
||||
Vendored
+67
-68
@@ -7,7 +7,7 @@ from urllib.parse import parse_qs, quote, urlparse, urlsplit
|
||||
|
||||
from app.runtime.cache import cached
|
||||
from app.runtime.config import global_vars
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.tasks import get_task_registry
|
||||
from app.domain.context import MediaInfo, MusicInfo
|
||||
from app.domain.meta.metabase import MetaBase
|
||||
@@ -26,8 +26,7 @@ from app.adapters.system.host import SystemUtils
|
||||
from app.runtime.version import get_app_version, get_frontend_version
|
||||
|
||||
|
||||
# 保留旧插件可覆盖的模块级入口,默认通过 runtime 代理动态读取配置。
|
||||
settings = RuntimeSettingsCompat()
|
||||
# 中心服务适配器只通过 runtime 读取端口消费组合根的最新配置。
|
||||
|
||||
|
||||
_server_report_service: Any = None
|
||||
@@ -102,7 +101,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
判断请求地址是否指向配置中的 MoviePilot 服务端。
|
||||
"""
|
||||
server_host = (settings.MP_SERVER_HOST or "").strip().rstrip("/")
|
||||
server_host = (get_runtime_setting('MP_SERVER_HOST') or "").strip().rstrip("/")
|
||||
if not server_host or not url:
|
||||
return False
|
||||
|
||||
@@ -144,7 +143,7 @@ class MoviePilotServerHelper:
|
||||
user_uid = cls.get_user_uid()
|
||||
if user_uid:
|
||||
request_headers[cls.USER_UID_HEADER] = user_uid
|
||||
request_headers["User-Agent"] = settings.USER_AGENT
|
||||
request_headers["User-Agent"] = get_runtime_setting('USER_AGENT')
|
||||
return request_headers
|
||||
|
||||
@classmethod
|
||||
@@ -162,10 +161,10 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
获取当前 GitHub 用户名。
|
||||
"""
|
||||
if cls._github_user is None and settings.GITHUB_HEADERS:
|
||||
if cls._github_user is None and get_runtime_setting('GITHUB_HEADERS'):
|
||||
res = RequestUtils(
|
||||
headers=settings.GITHUB_HEADERS,
|
||||
proxies=settings.PROXY,
|
||||
headers=get_runtime_setting('GITHUB_HEADERS'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=15,
|
||||
).get_res("https://api.github.com/user")
|
||||
if res:
|
||||
@@ -178,10 +177,10 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步获取当前 GitHub 用户名。
|
||||
"""
|
||||
if cls._github_user is None and settings.GITHUB_HEADERS:
|
||||
if cls._github_user is None and get_runtime_setting('GITHUB_HEADERS'):
|
||||
res = await AsyncRequestUtils(
|
||||
headers=settings.GITHUB_HEADERS,
|
||||
proxies=settings.PROXY,
|
||||
headers=get_runtime_setting('GITHUB_HEADERS'),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=15,
|
||||
).get_res("https://api.github.com/user")
|
||||
if res:
|
||||
@@ -278,7 +277,7 @@ class MoviePilotServerHelper:
|
||||
"user_uid": cls.get_user_uid(),
|
||||
"backend_version": get_app_version(),
|
||||
"frontend_version": get_frontend_version(),
|
||||
"version_flag": settings.VERSION_FLAG,
|
||||
"version_flag": get_runtime_setting('VERSION_FLAG'),
|
||||
"platform": f"{platform.system()} {platform.release()}".strip(),
|
||||
"arch": SystemUtils.cpu_arch(),
|
||||
}
|
||||
@@ -288,7 +287,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
上报当前安装实例的版本统计。
|
||||
"""
|
||||
if not settings.USAGE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('USAGE_STATISTIC_SHARE'):
|
||||
return False
|
||||
payload = cls.build_usage_payload()
|
||||
if not payload.get("user_uid"):
|
||||
@@ -305,7 +304,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步上报当前安装实例的版本统计。
|
||||
"""
|
||||
if not settings.USAGE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('USAGE_STATISTIC_SHARE'):
|
||||
return False
|
||||
payload = cls.build_usage_payload()
|
||||
if not payload.get("user_uid"):
|
||||
@@ -322,7 +321,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步获取安装版本统计报表。
|
||||
"""
|
||||
if not settings.USAGE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('USAGE_STATISTIC_SHARE'):
|
||||
return {}
|
||||
try:
|
||||
res = await cls.async_usage_statistic()
|
||||
@@ -338,7 +337,7 @@ class MoviePilotServerHelper:
|
||||
初始化订阅统计上报状态。
|
||||
"""
|
||||
cls._report_service().init_report(
|
||||
enabled=settings.SUBSCRIBE_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'),
|
||||
state_key=SystemConfigKey.SubscribeReport,
|
||||
reporter=cls.sub_report,
|
||||
)
|
||||
@@ -347,7 +346,7 @@ class MoviePilotServerHelper:
|
||||
async def async_init_subscribe_report(cls) -> None:
|
||||
"""异步初始化订阅统计标记。"""
|
||||
await cls._report_service().async_init_report(
|
||||
enabled=settings.SUBSCRIBE_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'),
|
||||
state_key=SystemConfigKey.SubscribeReport,
|
||||
reporter=cls.async_sub_report,
|
||||
)
|
||||
@@ -358,7 +357,7 @@ class MoviePilotServerHelper:
|
||||
初始化插件安装统计上报状态。
|
||||
"""
|
||||
cls._report_service().init_report(
|
||||
enabled=settings.PLUGIN_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('PLUGIN_STATISTIC_SHARE'),
|
||||
state_key=SystemConfigKey.PluginInstallReport,
|
||||
reporter=cls.install_plugin_report,
|
||||
)
|
||||
@@ -367,7 +366,7 @@ class MoviePilotServerHelper:
|
||||
async def async_init_plugin_report(cls) -> None:
|
||||
"""异步初始化插件统计标记。"""
|
||||
await cls._report_service().async_init_report(
|
||||
enabled=settings.PLUGIN_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('PLUGIN_STATISTIC_SHARE'),
|
||||
state_key=SystemConfigKey.PluginInstallReport,
|
||||
reporter=cls.async_install_plugin_report,
|
||||
)
|
||||
@@ -514,7 +513,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
获取插件安装统计。
|
||||
"""
|
||||
if not settings.PLUGIN_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('PLUGIN_STATISTIC_SHARE'):
|
||||
return {}
|
||||
res = cls.plugin_statistic()
|
||||
if res is not None and res.status_code == 200:
|
||||
@@ -526,7 +525,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步获取插件安装统计。
|
||||
"""
|
||||
if not settings.PLUGIN_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('PLUGIN_STATISTIC_SHARE'):
|
||||
return {}
|
||||
res = await cls.async_plugin_statistic()
|
||||
if res is not None and res.status_code == 200:
|
||||
@@ -590,7 +589,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
上报单个插件安装统计。
|
||||
"""
|
||||
if not settings.PLUGIN_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('PLUGIN_STATISTIC_SHARE'):
|
||||
return False
|
||||
if not plugin_id:
|
||||
return False
|
||||
@@ -605,7 +604,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步上报单个插件安装统计。
|
||||
"""
|
||||
if not settings.PLUGIN_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('PLUGIN_STATISTIC_SHARE'):
|
||||
return False
|
||||
if not plugin_id:
|
||||
return False
|
||||
@@ -621,7 +620,7 @@ class MoviePilotServerHelper:
|
||||
批量上报存量插件安装统计。
|
||||
"""
|
||||
return cls._report_service().report_plugins(
|
||||
enabled=settings.PLUGIN_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('PLUGIN_STATISTIC_SHARE'),
|
||||
items=items,
|
||||
)
|
||||
|
||||
@@ -631,7 +630,7 @@ class MoviePilotServerHelper:
|
||||
异步批量上报存量插件安装统计。
|
||||
"""
|
||||
return await cls._report_service().async_report_plugins(
|
||||
enabled=settings.PLUGIN_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('PLUGIN_STATISTIC_SHARE'),
|
||||
items=items,
|
||||
)
|
||||
|
||||
@@ -820,7 +819,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
获取订阅统计数据。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return []
|
||||
params = cls._build_subscribe_query_params(
|
||||
page=page,
|
||||
@@ -848,7 +847,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步获取订阅统计数据。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return []
|
||||
params = cls._build_subscribe_query_params(
|
||||
page=page,
|
||||
@@ -866,7 +865,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
新增订阅统计。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False
|
||||
payload = cls._build_subscribe_statistic_payload(sub)
|
||||
if not payload:
|
||||
@@ -879,7 +878,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步新增订阅统计。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False
|
||||
payload = cls._build_subscribe_statistic_payload(sub)
|
||||
if not payload:
|
||||
@@ -890,14 +889,14 @@ class MoviePilotServerHelper:
|
||||
@classmethod
|
||||
def sub_reg_durable(cls, sub: dict) -> bool:
|
||||
"""同步上报新增统计;明确禁用时视为无需投递。"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return True
|
||||
return cls.sub_reg(sub)
|
||||
|
||||
@classmethod
|
||||
async def async_sub_reg_durable(cls, sub: dict) -> bool:
|
||||
"""异步上报新增统计;明确禁用时视为无需投递。"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return True
|
||||
return await cls.async_sub_reg(sub)
|
||||
|
||||
@@ -906,7 +905,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
完成订阅统计。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False
|
||||
payload = cls._build_subscribe_statistic_payload(sub)
|
||||
if not payload:
|
||||
@@ -917,7 +916,7 @@ class MoviePilotServerHelper:
|
||||
@classmethod
|
||||
async def async_sub_done(cls, sub: dict) -> bool:
|
||||
"""异步完成订阅统计,并仅在服务端确认成功时返回 True。"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False
|
||||
payload = cls._build_subscribe_statistic_payload(sub)
|
||||
if not payload:
|
||||
@@ -928,14 +927,14 @@ class MoviePilotServerHelper:
|
||||
@classmethod
|
||||
def sub_done_durable(cls, sub: dict) -> bool:
|
||||
"""同步上报完成统计;明确禁用时视为无需投递。"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return True
|
||||
return cls.sub_done(sub)
|
||||
|
||||
@classmethod
|
||||
async def async_sub_done_durable(cls, sub: dict) -> bool:
|
||||
"""异步上报完成统计;明确禁用时视为无需投递。"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return True
|
||||
return await cls.async_sub_done(sub)
|
||||
|
||||
@@ -989,14 +988,14 @@ class MoviePilotServerHelper:
|
||||
上报存量订阅统计。
|
||||
"""
|
||||
return cls._report_service().report_subscribes(
|
||||
enabled=settings.SUBSCRIBE_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def async_sub_report(cls) -> bool:
|
||||
"""异步上报存量订阅统计。"""
|
||||
return await cls._report_service().async_report_subscribes(
|
||||
enabled=settings.SUBSCRIBE_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -1011,7 +1010,7 @@ class MoviePilotServerHelper:
|
||||
分享订阅。
|
||||
"""
|
||||
return cls._sharing_service().share_subscribe(
|
||||
enabled=settings.SUBSCRIBE_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'),
|
||||
subscribe_id=subscribe_id,
|
||||
share_title=share_title,
|
||||
share_comment=share_comment,
|
||||
@@ -1030,7 +1029,7 @@ class MoviePilotServerHelper:
|
||||
异步分享订阅。
|
||||
"""
|
||||
return await cls._sharing_service().async_share_subscribe(
|
||||
enabled=settings.SUBSCRIBE_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'),
|
||||
subscribe_id=subscribe_id,
|
||||
share_title=share_title,
|
||||
share_comment=share_comment,
|
||||
@@ -1056,7 +1055,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
删除订阅分享。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启订阅数据共享功能"
|
||||
return cls._handle_response(
|
||||
cls.subscribe_share_delete(share_id, cls.get_user_uuid()),
|
||||
@@ -1068,7 +1067,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步删除订阅分享。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启订阅数据共享功能"
|
||||
return cls._handle_response(
|
||||
await cls.async_subscribe_share_delete(share_id, cls.get_user_uuid()),
|
||||
@@ -1080,7 +1079,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
复用订阅分享。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启订阅数据共享功能"
|
||||
return cls._handle_response(cls.subscribe_fork(share_id))
|
||||
|
||||
@@ -1089,7 +1088,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步复用订阅分享。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启订阅数据共享功能"
|
||||
return cls._handle_response(await cls.async_subscribe_fork(share_id))
|
||||
|
||||
@@ -1108,7 +1107,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
获取订阅分享数据。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return []
|
||||
params = cls._build_subscribe_query_params(
|
||||
page=page,
|
||||
@@ -1136,7 +1135,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步获取订阅分享数据。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return []
|
||||
params = cls._build_subscribe_query_params(
|
||||
page=page,
|
||||
@@ -1155,7 +1154,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
获取订阅分享统计数据。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return []
|
||||
return cls._handle_list_response(cls.subscribe_share_statistics())
|
||||
|
||||
@@ -1165,7 +1164,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步获取订阅分享统计数据。
|
||||
"""
|
||||
if not settings.SUBSCRIBE_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('SUBSCRIBE_STATISTIC_SHARE'):
|
||||
return []
|
||||
return cls._handle_list_response(await cls.async_subscribe_share_statistics())
|
||||
|
||||
@@ -1252,7 +1251,7 @@ class MoviePilotServerHelper:
|
||||
分享工作流。
|
||||
"""
|
||||
return cls._sharing_service().share_workflow(
|
||||
enabled=settings.WORKFLOW_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('WORKFLOW_STATISTIC_SHARE'),
|
||||
workflow_id=workflow_id,
|
||||
share_title=share_title,
|
||||
share_comment=share_comment,
|
||||
@@ -1271,7 +1270,7 @@ class MoviePilotServerHelper:
|
||||
异步分享工作流。
|
||||
"""
|
||||
return await cls._sharing_service().async_share_workflow(
|
||||
enabled=settings.WORKFLOW_STATISTIC_SHARE,
|
||||
enabled=get_runtime_setting('WORKFLOW_STATISTIC_SHARE'),
|
||||
workflow_id=workflow_id,
|
||||
share_title=share_title,
|
||||
share_comment=share_comment,
|
||||
@@ -1283,7 +1282,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
删除工作流分享。
|
||||
"""
|
||||
if not settings.WORKFLOW_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('WORKFLOW_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启工作流数据共享功能"
|
||||
return cls._handle_response(
|
||||
cls.workflow_share_delete(share_id, cls.get_user_uuid()),
|
||||
@@ -1295,7 +1294,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步删除工作流分享。
|
||||
"""
|
||||
if not settings.WORKFLOW_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('WORKFLOW_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启工作流数据共享功能"
|
||||
return cls._handle_response(
|
||||
await cls.async_workflow_share_delete(share_id, cls.get_user_uuid()),
|
||||
@@ -1307,7 +1306,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
复用工作流分享。
|
||||
"""
|
||||
if not settings.WORKFLOW_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('WORKFLOW_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启工作流数据共享功能"
|
||||
return cls._handle_response(cls.workflow_fork(share_id))
|
||||
|
||||
@@ -1316,7 +1315,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步复用工作流分享。
|
||||
"""
|
||||
if not settings.WORKFLOW_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('WORKFLOW_STATISTIC_SHARE'):
|
||||
return False, "当前没有开启工作流数据共享功能"
|
||||
return cls._handle_response(await cls.async_workflow_fork(share_id))
|
||||
|
||||
@@ -1331,7 +1330,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
获取工作流分享数据。
|
||||
"""
|
||||
if not settings.WORKFLOW_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('WORKFLOW_STATISTIC_SHARE'):
|
||||
return []
|
||||
return cls._handle_list_response(cls.workflow_shares({
|
||||
"name": name,
|
||||
@@ -1350,7 +1349,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步获取工作流分享数据。
|
||||
"""
|
||||
if not settings.WORKFLOW_STATISTIC_SHARE:
|
||||
if not get_runtime_setting('WORKFLOW_STATISTIC_SHARE'):
|
||||
return []
|
||||
return cls._handle_list_response(await cls.async_workflow_shares({
|
||||
"name": name,
|
||||
@@ -1370,10 +1369,10 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
获取共享识别服务端地址。
|
||||
"""
|
||||
custom_api = (settings.MEDIA_RECOGNIZE_SHARE_API or "").strip()
|
||||
custom_api = (get_runtime_setting('MEDIA_RECOGNIZE_SHARE_API') or "").strip()
|
||||
if custom_api:
|
||||
return custom_api.rstrip("/")
|
||||
server_host = (settings.MP_SERVER_HOST or "").strip().rstrip("/")
|
||||
server_host = (get_runtime_setting('MP_SERVER_HOST') or "").strip().rstrip("/")
|
||||
if not server_host:
|
||||
return None
|
||||
return f"{server_host}{cls._RECOGNIZE_SHARE_PATH}"
|
||||
@@ -1429,7 +1428,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
查询共享识别结果。
|
||||
"""
|
||||
if not settings.MEDIA_RECOGNIZE_SHARE:
|
||||
if not get_runtime_setting('MEDIA_RECOGNIZE_SHARE'):
|
||||
return None
|
||||
params = cls._build_recognize_query_params(
|
||||
meta=meta,
|
||||
@@ -1453,7 +1452,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步查询共享识别结果。
|
||||
"""
|
||||
if not settings.MEDIA_RECOGNIZE_SHARE:
|
||||
if not get_runtime_setting('MEDIA_RECOGNIZE_SHARE'):
|
||||
return None
|
||||
params = cls._build_recognize_query_params(
|
||||
meta=meta,
|
||||
@@ -1476,7 +1475,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
上报共享识别结果,电影、电视剧、音乐共用。
|
||||
"""
|
||||
if not settings.MEDIA_RECOGNIZE_SHARE:
|
||||
if not get_runtime_setting('MEDIA_RECOGNIZE_SHARE'):
|
||||
return False
|
||||
payload = cls._build_recognize_report_payload(
|
||||
meta=meta,
|
||||
@@ -1498,7 +1497,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
异步上报共享识别结果,电影、电视剧、音乐共用。
|
||||
"""
|
||||
if not settings.MEDIA_RECOGNIZE_SHARE:
|
||||
if not get_runtime_setting('MEDIA_RECOGNIZE_SHARE'):
|
||||
return False
|
||||
payload = cls._build_recognize_report_payload(
|
||||
meta=meta,
|
||||
@@ -1855,7 +1854,7 @@ class MoviePilotServerHelper:
|
||||
"""
|
||||
根据服务端基础地址和路径生成完整 URL。
|
||||
"""
|
||||
return f"{settings.MP_SERVER_HOST.rstrip('/')}{path}"
|
||||
return f"{get_runtime_setting('MP_SERVER_HOST').rstrip('/')}{path}"
|
||||
|
||||
@classmethod
|
||||
def _get(
|
||||
@@ -1869,7 +1868,7 @@ class MoviePilotServerHelper:
|
||||
发送服务端 GET 请求,默认携带安装用户 ID。
|
||||
"""
|
||||
return RequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=timeout,
|
||||
headers=cls.build_headers(url) if include_user_uid else {},
|
||||
).get_res(url, params=params)
|
||||
@@ -1886,7 +1885,7 @@ class MoviePilotServerHelper:
|
||||
异步发送服务端 GET 请求,默认携带安装用户 ID。
|
||||
"""
|
||||
return await AsyncRequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=timeout,
|
||||
headers=cls.build_headers(url) if include_user_uid else {},
|
||||
).get_res(url, params=params)
|
||||
@@ -1897,7 +1896,7 @@ class MoviePilotServerHelper:
|
||||
发送携带安装用户 ID 的服务端 JSON POST 请求。
|
||||
"""
|
||||
return RequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=timeout,
|
||||
headers=cls.build_headers(url, content_type="application/json"),
|
||||
).post(url, json=payload)
|
||||
@@ -1908,7 +1907,7 @@ class MoviePilotServerHelper:
|
||||
异步发送携带安装用户 ID 的服务端 JSON POST 请求。
|
||||
"""
|
||||
return await AsyncRequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=timeout,
|
||||
headers=cls.build_headers(url, content_type="application/json"),
|
||||
).post(url, json=payload)
|
||||
@@ -1919,7 +1918,7 @@ class MoviePilotServerHelper:
|
||||
发送携带安装用户 ID 的服务端 DELETE 请求。
|
||||
"""
|
||||
return RequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=timeout,
|
||||
headers=cls.build_headers(url),
|
||||
).delete_res(url, params=params)
|
||||
@@ -1930,7 +1929,7 @@ class MoviePilotServerHelper:
|
||||
异步发送携带安装用户 ID 的服务端 DELETE 请求。
|
||||
"""
|
||||
return await AsyncRequestUtils(
|
||||
proxies=settings.PROXY,
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
timeout=timeout,
|
||||
headers=cls.build_headers(url),
|
||||
).delete_res(url, params=params)
|
||||
|
||||
@@ -9,7 +9,7 @@ from typing import Any, Callable, Optional, Protocol
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
from app.runtime.managed_resources import (
|
||||
acquire_managed_resource,
|
||||
acquire_managed_resource_async,
|
||||
@@ -17,10 +17,6 @@ from app.runtime.managed_resources import (
|
||||
from app.adapters.network.http import RequestUtils, cookie_parse
|
||||
|
||||
|
||||
# 保留旧插件可覆盖的模块级入口,默认通过 runtime 代理动态读取浏览器配置。
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
|
||||
class BrowserElement(Protocol):
|
||||
"""
|
||||
页面元素的最小接口,避免为了类型标注直接导入 Playwright。
|
||||
@@ -721,8 +717,8 @@ class BrowserSessionHelper:
|
||||
) -> BrowserContext:
|
||||
"""按宿主反检测配置创建 CloakBrowser 上下文。"""
|
||||
context_kwargs = {
|
||||
"humanize": settings.CLOAKBROWSER_HUMANIZE,
|
||||
"human_preset": settings.CLOAKBROWSER_HUMAN_PRESET,
|
||||
"humanize": get_runtime_setting('CLOAKBROWSER_HUMANIZE'),
|
||||
"human_preset": get_runtime_setting('CLOAKBROWSER_HUMAN_PRESET'),
|
||||
}
|
||||
if user_agent:
|
||||
context_kwargs["user_agent"] = user_agent
|
||||
@@ -922,14 +918,18 @@ class PlaywrightHelper:
|
||||
"""
|
||||
兼容旧的 PlaywrightHelper(browser_type=...) 构造方式。
|
||||
"""
|
||||
self.browser_type = browser_type or settings.PLAYWRIGHT_BROWSER_TYPE
|
||||
self.browser_type = browser_type or get_runtime_setting(
|
||||
"PLAYWRIGHT_BROWSER_TYPE"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def __browser_emulation() -> str:
|
||||
"""
|
||||
当前浏览器仿真类型。
|
||||
"""
|
||||
return (settings.BROWSER_EMULATION or "cloakbrowser").lower()
|
||||
return (
|
||||
get_runtime_setting('BROWSER_EMULATION') or "cloakbrowser"
|
||||
).lower()
|
||||
|
||||
@staticmethod
|
||||
def __launch_cloakbrowser_context(headless: bool,
|
||||
@@ -941,8 +941,8 @@ class PlaywrightHelper:
|
||||
return launch_browser_context(headless=headless,
|
||||
proxy=proxies,
|
||||
user_agent=user_agent,
|
||||
humanize=settings.CLOAKBROWSER_HUMANIZE,
|
||||
human_preset=settings.CLOAKBROWSER_HUMAN_PRESET)
|
||||
humanize=get_runtime_setting('CLOAKBROWSER_HUMANIZE'),
|
||||
human_preset=get_runtime_setting('CLOAKBROWSER_HUMAN_PRESET'))
|
||||
|
||||
@staticmethod
|
||||
def __fs_cookie_str(cookies: list) -> str:
|
||||
@@ -960,11 +960,12 @@ class PlaywrightHelper:
|
||||
调用 FlareSolverr 解决 Cloudflare 并返回 solution 结果
|
||||
参考: https://github.com/FlareSolverr/FlareSolverr
|
||||
"""
|
||||
if not settings.FLARESOLVERR_URL:
|
||||
flaresolverr_url = get_runtime_setting('FLARESOLVERR_URL')
|
||||
if not flaresolverr_url:
|
||||
logger.warn("未配置 FLARESOLVERR_URL,无法使用 FlareSolverr")
|
||||
return None
|
||||
|
||||
fs_api = settings.FLARESOLVERR_URL.rstrip("/") + "/v1"
|
||||
fs_api = flaresolverr_url.rstrip("/") + "/v1"
|
||||
session_id = None
|
||||
|
||||
try:
|
||||
|
||||
@@ -27,10 +27,8 @@ import threading
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Optional, Union
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
# worker 脚本路径。用绝对路径直接执行,而不是 -m 或 import:
|
||||
# 直接执行文件不会触发 app/__init__.py 的导入链,代理启动才是毫秒级的
|
||||
@@ -258,7 +256,7 @@ class FileSystemProxy:
|
||||
"""
|
||||
if self._timeout_override is not None:
|
||||
return self._timeout_override
|
||||
return float(getattr(settings, "FS_PROXY_TIMEOUT", DEFAULT_TIMEOUT))
|
||||
return float(get_runtime_setting("FS_PROXY_TIMEOUT", DEFAULT_TIMEOUT))
|
||||
|
||||
@property
|
||||
def _stall_timeout(self) -> float:
|
||||
@@ -267,14 +265,16 @@ class FileSystemProxy:
|
||||
"""
|
||||
if self._stall_timeout_override is not None:
|
||||
return self._stall_timeout_override
|
||||
return float(getattr(settings, "FS_PROXY_STALL_TIMEOUT", DEFAULT_STALL_TIMEOUT))
|
||||
return float(
|
||||
get_runtime_setting("FS_PROXY_STALL_TIMEOUT", DEFAULT_STALL_TIMEOUT)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _enabled() -> bool:
|
||||
"""
|
||||
代理是否启用。关闭时退回直接调用,行为与引入代理之前完全一致。
|
||||
"""
|
||||
return bool(getattr(settings, "FS_PROXY_ENABLED", True))
|
||||
return bool(get_runtime_setting("FS_PROXY_ENABLED", True))
|
||||
|
||||
@staticmethod
|
||||
def _direct(op: str, payload: Dict[str, Any]) -> Any:
|
||||
|
||||
@@ -52,7 +52,7 @@ class PluginDependencyInstaller:
|
||||
self._helper = helper
|
||||
self._installed_plugins_provider = installed_plugins_provider or (lambda: [])
|
||||
self._plugin_dir = plugin_dir or (
|
||||
Path(get_runtime_setting("ROOT_PATH")) / "app" / "plugins"
|
||||
Path(get_runtime_setting('ROOT_PATH')) / "app" / "plugins"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -16,10 +16,7 @@ from app.runtime.execution import (
|
||||
run_in_threadpool_to_completion as _await_thread_operation,
|
||||
)
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
# 保留旧模块级入口,插件本地同步测试和旧扩展仍可能覆盖这些设置。
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class PluginPackageCheckpoint:
|
||||
@@ -58,7 +55,7 @@ class PluginPackageManager:
|
||||
def __plugin_dir(plugin_id: str) -> Path:
|
||||
"""解析插件运行目录并拒绝越出宿主插件根目录的标识。"""
|
||||
plugins_root = (
|
||||
Path(settings.ROOT_PATH) / "app" / "plugins"
|
||||
Path(get_runtime_setting('ROOT_PATH')) / "app" / "plugins"
|
||||
).resolve()
|
||||
plugin_dir = (plugins_root / plugin_id.lower()).resolve()
|
||||
if plugin_dir == plugins_root or not plugin_dir.is_relative_to(plugins_root):
|
||||
@@ -74,7 +71,9 @@ class PluginPackageManager:
|
||||
plugin_dir = self.__plugin_dir(plugin_id)
|
||||
durable = transaction_id is not None
|
||||
persistent_backup_dir = (
|
||||
Path(settings.CONFIG_PATH) / "plugins_backup" / plugin_id.lower()
|
||||
Path(get_runtime_setting('CONFIG_PATH'))
|
||||
/ "plugins_backup"
|
||||
/ plugin_id.lower()
|
||||
).resolve()
|
||||
backup_staging_dir = (
|
||||
persistent_backup_dir.parent
|
||||
@@ -89,9 +88,9 @@ class PluginPackageManager:
|
||||
else None
|
||||
)
|
||||
transaction_root = (
|
||||
Path(settings.CONFIG_PATH)
|
||||
Path(get_runtime_setting('CONFIG_PATH'))
|
||||
if durable
|
||||
else Path(settings.TEMP_PATH)
|
||||
else Path(get_runtime_setting('TEMP_PATH'))
|
||||
)
|
||||
transaction_dir = (
|
||||
transaction_root
|
||||
@@ -129,7 +128,9 @@ class PluginPackageManager:
|
||||
"""按受控根目录和事务 ID 重建崩溃回放所需的文件引用。"""
|
||||
plugin_dir = self.__plugin_dir(plugin_id)
|
||||
persistent_backup_dir = (
|
||||
Path(settings.CONFIG_PATH) / "plugins_backup" / plugin_id.lower()
|
||||
Path(get_runtime_setting('CONFIG_PATH'))
|
||||
/ "plugins_backup"
|
||||
/ plugin_id.lower()
|
||||
).resolve()
|
||||
durable_backup = SystemUtils.is_docker()
|
||||
return PluginPackageCheckpoint(
|
||||
@@ -149,7 +150,7 @@ class PluginPackageManager:
|
||||
else None
|
||||
),
|
||||
transaction_dir=(
|
||||
Path(settings.CONFIG_PATH)
|
||||
Path(get_runtime_setting('CONFIG_PATH'))
|
||||
/ "plugin_transactions"
|
||||
/ transaction_id
|
||||
),
|
||||
|
||||
@@ -5,18 +5,12 @@ import sysconfig
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
from app.runtime import config as _runtime_config
|
||||
from app.runtime.log import logger
|
||||
from app.adapters.network.http import RequestUtils
|
||||
from app.foundation.version import compare_version
|
||||
from app.adapters.system.host import SystemUtils
|
||||
from app.foundation.version import compare_version
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
|
||||
# 保留模块级旧 Settings 入口,旧插件和测试可能仍会对其做运行时覆盖;实现读取统一走 runtime 端口。
|
||||
settings = _runtime_config.settings
|
||||
|
||||
|
||||
ResourceVersionProvider = Callable[[], tuple[str, str]]
|
||||
|
||||
|
||||
@@ -39,9 +33,9 @@ class ResourceHelper:
|
||||
检测和更新资源包
|
||||
"""
|
||||
|
||||
_base_dir: Path = get_runtime_setting("ROOT_PATH")
|
||||
_base_dir: Path = get_runtime_setting('ROOT_PATH')
|
||||
_resource_target = Path("app/application/site")
|
||||
_version_flag = get_runtime_setting("RESOURCE_VERSION_FLAG")
|
||||
_version_flag = get_runtime_setting('RESOURCE_VERSION_FLAG')
|
||||
_repo = (
|
||||
f"{get_runtime_setting('GITHUB_PROXY')}https://raw.githubusercontent.com/"
|
||||
f"jxxghp/MoviePilot-Resources/main/package.{_version_flag}.json"
|
||||
@@ -56,8 +50,8 @@ class ResourceHelper:
|
||||
"""返回访问 GitHub 资源时应使用的代理配置。"""
|
||||
return (
|
||||
None
|
||||
if get_runtime_setting("GITHUB_PROXY")
|
||||
else get_runtime_setting("PROXY")
|
||||
if get_runtime_setting('GITHUB_PROXY')
|
||||
else get_runtime_setting('PROXY')
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -97,7 +91,7 @@ class ResourceHelper:
|
||||
"""读取 V3 资源清单。"""
|
||||
response = RequestUtils(
|
||||
proxies=self.proxies,
|
||||
headers=get_runtime_setting("GITHUB_HEADERS"),
|
||||
headers=get_runtime_setting('GITHUB_HEADERS'),
|
||||
timeout=10,
|
||||
).get_res(self._repo)
|
||||
return response if response and response.status_code == 200 else None
|
||||
@@ -115,7 +109,7 @@ class ResourceHelper:
|
||||
:param indexer_version: 当前已加载的站点索引资源版本;省略时使用组合根注入值
|
||||
:return: 是否成功安装了需要由上层处理重启的新资源
|
||||
"""
|
||||
if not get_runtime_setting("AUTO_UPDATE_RESOURCE"):
|
||||
if not get_runtime_setting('AUTO_UPDATE_RESOURCE'):
|
||||
return False
|
||||
if SystemUtils.is_frozen():
|
||||
return False
|
||||
@@ -170,8 +164,8 @@ class ResourceHelper:
|
||||
if need_updates:
|
||||
# 下载文件信息列表
|
||||
r = RequestUtils(
|
||||
proxies=get_runtime_setting("PROXY"),
|
||||
headers=get_runtime_setting("GITHUB_HEADERS"),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
headers=get_runtime_setting('GITHUB_HEADERS'),
|
||||
timeout=30,
|
||||
).get_res(self._files_api)
|
||||
if r and not r.ok:
|
||||
@@ -201,7 +195,7 @@ class ResourceHelper:
|
||||
)
|
||||
res = RequestUtils(
|
||||
proxies=self.proxies,
|
||||
headers=get_runtime_setting("GITHUB_HEADERS"),
|
||||
headers=get_runtime_setting('GITHUB_HEADERS'),
|
||||
timeout=180,
|
||||
).get_res(download_url)
|
||||
if not res:
|
||||
|
||||
@@ -17,7 +17,7 @@ else:
|
||||
|
||||
def _rust_accel_enabled() -> bool:
|
||||
"""读取 Rust 开关快照,组合根未装配时回退旧 Settings。"""
|
||||
return bool(get_runtime_setting("RUST_ACCEL"))
|
||||
return bool(get_runtime_setting('RUST_ACCEL'))
|
||||
|
||||
|
||||
def is_required() -> bool:
|
||||
|
||||
@@ -43,7 +43,7 @@ class SystemUpdateManager(metaclass=SingletonClass):
|
||||
|
||||
@property
|
||||
def _root(self) -> Path:
|
||||
return Path(get_runtime_setting("TEMP_PATH")) / "moviepilot-update"
|
||||
return Path(get_runtime_setting('TEMP_PATH')) / "moviepilot-update"
|
||||
|
||||
@property
|
||||
def _state_file(self) -> Path:
|
||||
@@ -268,8 +268,8 @@ class SystemUpdateManager(metaclass=SingletonClass):
|
||||
|
||||
def _request(self) -> RequestUtils:
|
||||
return RequestUtils(
|
||||
proxies=get_runtime_setting("PROXY"),
|
||||
headers=get_runtime_setting("GITHUB_HEADERS"),
|
||||
proxies=get_runtime_setting('PROXY'),
|
||||
headers=get_runtime_setting('GITHUB_HEADERS'),
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
@@ -474,7 +474,7 @@ class SystemUpdateManager(metaclass=SingletonClass):
|
||||
|
||||
@staticmethod
|
||||
def _proxied(url: str) -> str:
|
||||
proxy = str(get_runtime_setting("GITHUB_PROXY") or "").strip()
|
||||
proxy = str(get_runtime_setting('GITHUB_PROXY') or "").strip()
|
||||
return f"{proxy}{url}" if proxy else url
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from fastapi.security import (
|
||||
|
||||
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.token import TokenPayload
|
||||
|
||||
SuperuserTokenPayloadProvider = Callable[[], TokenPayload]
|
||||
@@ -28,15 +28,12 @@ _token_decoder: Optional[TokenDecoder] = None
|
||||
JWT_ALGORITHM = "HS256"
|
||||
|
||||
|
||||
# 兼容旧鉴权插件覆盖模块级设置;令牌实际策略仍由已注入 codec 和 runtime 配置提供。
|
||||
settings = RuntimeSettingsCompat()
|
||||
|
||||
oauth2_scheme_manual_error = OAuth2PasswordBearer(
|
||||
auto_error=False,
|
||||
tokenUrl=f"{settings.API_V1_STR}/login/access-token",
|
||||
tokenUrl=f"{get_runtime_setting('API_V1_STR')}/login/access-token",
|
||||
)
|
||||
resource_token_cookie = APIKeyCookie(
|
||||
name=settings.PROJECT_NAME,
|
||||
name=get_runtime_setting('PROJECT_NAME'),
|
||||
auto_error=False,
|
||||
scheme_name="resource_token_cookie",
|
||||
)
|
||||
@@ -133,12 +130,13 @@ def set_or_refresh_resource_token_cookie(
|
||||
payload: TokenPayload,
|
||||
) -> None:
|
||||
"""复用匹配的资源令牌,或为当前身份写入新的安全 Cookie。"""
|
||||
resource_token = request.cookies.get(settings.PROJECT_NAME)
|
||||
project_name = get_runtime_setting('PROJECT_NAME')
|
||||
resource_token = request.cookies.get(project_name)
|
||||
if resource_token:
|
||||
try:
|
||||
decoded = jwt.decode(
|
||||
resource_token,
|
||||
settings.RESOURCE_SECRET_KEY,
|
||||
get_runtime_setting('RESOURCE_SECRET_KEY'),
|
||||
algorithms=[JWT_ALGORITHM],
|
||||
)
|
||||
exp = decoded.get("exp")
|
||||
@@ -148,7 +146,12 @@ def set_or_refresh_resource_token_cookie(
|
||||
tz=datetime.UTC,
|
||||
) - datetime.datetime.now(datetime.UTC)
|
||||
if remaining_time < timedelta(
|
||||
seconds=settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS / 3
|
||||
seconds=(
|
||||
get_runtime_setting(
|
||||
"RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS"
|
||||
)
|
||||
/ 3
|
||||
)
|
||||
):
|
||||
raise jwt.ExpiredSignatureError
|
||||
expected_claims = {
|
||||
@@ -177,7 +180,7 @@ def set_or_refresh_resource_token_cookie(
|
||||
username=payload.username or "",
|
||||
super_user=payload.super_user,
|
||||
expires_delta=timedelta(
|
||||
seconds=settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS
|
||||
seconds=get_runtime_setting('RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS')
|
||||
),
|
||||
level=payload.level,
|
||||
purpose="resource",
|
||||
@@ -187,7 +190,7 @@ def set_or_refresh_resource_token_cookie(
|
||||
or request.headers.get("x-forwarded-proto", "").lower() == "https"
|
||||
)
|
||||
response.set_cookie(
|
||||
key=settings.PROJECT_NAME,
|
||||
key=project_name,
|
||||
value=resource_token,
|
||||
httponly=True,
|
||||
secure=is_https,
|
||||
@@ -261,11 +264,11 @@ def verify_apitoken(
|
||||
token: Annotated[str | None, Security(_get_api_token)],
|
||||
) -> str:
|
||||
"""校验 URL 查询参数中的兼容 API Token。"""
|
||||
return _verify_key(token, settings.API_TOKEN, "token")
|
||||
return _verify_key(token, get_runtime_setting('API_TOKEN'), "token")
|
||||
|
||||
|
||||
def verify_apikey(
|
||||
apikey: Annotated[str | None, Security(_get_api_key)],
|
||||
) -> str:
|
||||
"""校验请求头或查询参数中的兼容 API Key。"""
|
||||
return _verify_key(apikey, settings.API_TOKEN, "apikey")
|
||||
return _verify_key(apikey, get_runtime_setting('API_TOKEN'), "apikey")
|
||||
|
||||
Reference in New Issue
Block a user