mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor: extract cache network and compatibility entrypoints
This commit is contained in:
@@ -72,6 +72,7 @@ from app.foundation.crypto import HashUtils
|
||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
||||
from app.adapters.system import rust as rust_accel
|
||||
from app.application.security.url import SecurityUtils
|
||||
from app.application.network import NetworkTestService
|
||||
from app.foundation.url import UrlUtils
|
||||
from version import APP_VERSION
|
||||
|
||||
@@ -1411,78 +1412,19 @@ async def nettest(
|
||||
target = _get_nettest_rule(url=url, target_id=target_id)
|
||||
if not target:
|
||||
return _SchemaResponse(success=False, message="测试目标不存在")
|
||||
# 记录开始的毫秒数
|
||||
start_time = datetime.now()
|
||||
url = target["url"]
|
||||
invalid_message = _validate_nettest_url(url)
|
||||
if invalid_message:
|
||||
logger.warning(f"拦截不安全的网络测试地址: {url}")
|
||||
return _SchemaResponse(success=False, message=invalid_message)
|
||||
if include:
|
||||
logger.debug("nettest include 参数已忽略,改为服务端固定校验")
|
||||
|
||||
request_utils = AsyncRequestUtils(
|
||||
proxies=get_runtime_settings().get("PROXY") if target.get("proxy") else None,
|
||||
headers=target.get("headers"),
|
||||
timeout=10,
|
||||
ua=get_runtime_settings().get("NORMAL_USER_AGENT"),
|
||||
verify=True,
|
||||
follow_redirects=False,
|
||||
)
|
||||
result = None
|
||||
current_url = url
|
||||
redirect_count = 0
|
||||
while redirect_count <= 3:
|
||||
result = await request_utils.get_res(current_url, allow_redirects=False)
|
||||
if result is None:
|
||||
break
|
||||
if result.status_code not in _NETTEST_REDIRECT_STATUS_CODES:
|
||||
break
|
||||
location = result.headers.get("location")
|
||||
if not location:
|
||||
break
|
||||
next_url = urljoin(current_url, location)
|
||||
if not _is_allowed_nettest_redirect(next_url, target):
|
||||
await _close_nettest_response(result)
|
||||
logger.warning(f"拦截网络测试重定向: {current_url} -> {next_url}")
|
||||
return _SchemaResponse(success=False, message="测试目标发生了未授权跳转")
|
||||
await _close_nettest_response(result)
|
||||
current_url = next_url
|
||||
redirect_count += 1
|
||||
if redirect_count > 3:
|
||||
return _SchemaResponse(success=False, message="测试目标重定向次数过多")
|
||||
# 计时结束的毫秒数
|
||||
end_time = datetime.now()
|
||||
time = round((end_time - start_time).total_seconds() * 1000)
|
||||
# 计算相关秒数
|
||||
if result is None:
|
||||
return _SchemaResponse(
|
||||
success=False,
|
||||
message=f"{target.get('proxy_name') or target.get('name')}无法连接",
|
||||
data={"time": time},
|
||||
)
|
||||
elif result.status_code == 200:
|
||||
expected_text = target.get("expected_text")
|
||||
if expected_text and expected_text.lower() not in (result.text or "").lower():
|
||||
return _SchemaResponse(
|
||||
success=False,
|
||||
message=target.get("invalid_message") or "无效响应",
|
||||
data={"time": time},
|
||||
)
|
||||
return _SchemaResponse(success=True, data={"time": time})
|
||||
else:
|
||||
if target.get("proxy_name"):
|
||||
# 加速代理失败
|
||||
message = f"{target['proxy_name']}已失效,错误码:{result.status_code}"
|
||||
else:
|
||||
message = f"错误码:{result.status_code}"
|
||||
if "github" in url:
|
||||
# 非加速代理访问github
|
||||
if result.status_code == 401:
|
||||
message = "Github Token已失效,请检查配置"
|
||||
elif result.status_code in {403, 429}:
|
||||
message = "触发限流,请配置Github Token"
|
||||
return _SchemaResponse(success=False, message=message, data={"time": time})
|
||||
success, message, data = await NetworkTestService(
|
||||
request_utils_cls=AsyncRequestUtils,
|
||||
settings_getter=get_runtime_settings,
|
||||
logger=logger,
|
||||
redirect_checker=_is_allowed_nettest_redirect,
|
||||
close_response=_close_nettest_response,
|
||||
).execute(target, include=include)
|
||||
return _SchemaResponse(success=success, message=message, data=data)
|
||||
|
||||
|
||||
@router.get(
|
||||
|
||||
+11
-129
@@ -9,22 +9,17 @@ from app.api.response import ResponseAPIRouter
|
||||
from app.chain.media import MediaChain
|
||||
from app.chain.torrents import TorrentsChain
|
||||
from app.application.configuration import get_api_runtime_config_snapshot
|
||||
from app.domain.context import MediaInfo, MusicInfo
|
||||
from app.domain.meta.metamusic import MetaMusic
|
||||
from app.domain.metainfo import MetaInfo
|
||||
from app.api.dependencies.auth import (
|
||||
get_current_active_superuser,
|
||||
get_current_active_superuser_async,
|
||||
)
|
||||
from app.schemas.types import (
|
||||
MUSIC_ENTITY_RECORDING,
|
||||
MediaSource,
|
||||
MediaType,
|
||||
MusicTargetEntityType,
|
||||
)
|
||||
from app.foundation.crypto import HashUtils
|
||||
from app.domain.media import is_music_media_source, normalize_music_type
|
||||
from app.schemas.media import resolve_media_identity
|
||||
from app.application.torrent_cache import TorrentCacheRecognitionService
|
||||
|
||||
router = ResponseAPIRouter()
|
||||
|
||||
@@ -209,132 +204,19 @@ async def reidentify_cache(
|
||||
:param _: 当前用户,必须是超级用户
|
||||
"""
|
||||
|
||||
torrents_chain = TorrentsChain()
|
||||
media_chain = MediaChain()
|
||||
|
||||
try:
|
||||
# 获取当前缓存
|
||||
cache_data = await torrents_chain.async_get_torrents()
|
||||
|
||||
if domain not in cache_data:
|
||||
return _SchemaResponse(success=False, message=f"站点 {domain} 缓存不存在")
|
||||
|
||||
# 查找指定种子
|
||||
target_context = None
|
||||
for context in cache_data[domain]:
|
||||
if (
|
||||
HashUtils.md5(
|
||||
f"{context.torrent_info.title}{context.torrent_info.description}"
|
||||
)
|
||||
== torrent_hash
|
||||
):
|
||||
target_context = context
|
||||
break
|
||||
|
||||
if not target_context:
|
||||
return _SchemaResponse(success=False, message="未找到指定的种子")
|
||||
|
||||
existing_music_type = normalize_music_type(
|
||||
getattr(target_context.media_info, "music_type", None),
|
||||
allow_artist=False,
|
||||
service = TorrentCacheRecognitionService(TorrentsChain(), MediaChain())
|
||||
success, message, data = await service.execute(
|
||||
domain=domain,
|
||||
torrent_hash=torrent_hash,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
music_type=music_type,
|
||||
)
|
||||
normalized_music_type = normalize_music_type(
|
||||
music_type,
|
||||
allow_artist=False,
|
||||
)
|
||||
if music_type is not None and not normalized_music_type:
|
||||
return _SchemaResponse(
|
||||
success=False,
|
||||
message="音乐实体类型无效,仅支持 recording 或 album",
|
||||
)
|
||||
is_music = (
|
||||
getattr(target_context.media_info, "type", None) == MediaType.MUSIC
|
||||
or isinstance(target_context.meta_info, MetaMusic)
|
||||
or target_context.torrent_info.category
|
||||
in (MediaType.MUSIC, MediaType.MUSIC.value, "music")
|
||||
or is_music_media_source(media_source)
|
||||
or normalized_music_type is not None
|
||||
)
|
||||
if is_music and media_source and not is_music_media_source(media_source):
|
||||
return _SchemaResponse(
|
||||
success=False,
|
||||
message="音乐重新识别只能使用音乐元数据源",
|
||||
)
|
||||
if is_music and not normalized_music_type:
|
||||
normalized_music_type = existing_music_type or MUSIC_ENTITY_RECORDING
|
||||
|
||||
# 重识别沿用原媒体域;音乐标题必须使用 MetaMusic,避免误入影视模块。
|
||||
if is_music:
|
||||
meta = (
|
||||
target_context.meta_info
|
||||
if isinstance(target_context.meta_info, MetaMusic)
|
||||
else MetaMusic.parse_query(target_context.torrent_info.title)
|
||||
)
|
||||
else:
|
||||
meta = MetaInfo(
|
||||
title=target_context.torrent_info.title,
|
||||
subtitle=target_context.torrent_info.description,
|
||||
)
|
||||
|
||||
has_explicit_id = media_source is not None or media_id is not None
|
||||
if has_explicit_id and (not media_source or not media_id):
|
||||
return _SchemaResponse(
|
||||
success=False,
|
||||
message="媒体来源和媒体 ID 必须同时提供",
|
||||
)
|
||||
if has_explicit_id:
|
||||
# 手动指定媒体身份时执行精确识别。
|
||||
mediainfo = await media_chain.async_recognize_media(
|
||||
meta=meta,
|
||||
media_source=media_source,
|
||||
media_id=media_id,
|
||||
mtype=MediaType.MUSIC if is_music else None,
|
||||
music_type=normalized_music_type,
|
||||
)
|
||||
else:
|
||||
# 未指定 ID 时按标题识别,请求级来源仍用于约束本次识别。
|
||||
mediainfo = await media_chain.async_recognize_by_meta(
|
||||
meta,
|
||||
media_source=media_source,
|
||||
mtype=MediaType.MUSIC if is_music else None,
|
||||
music_type=normalized_music_type,
|
||||
)
|
||||
|
||||
if not mediainfo:
|
||||
# 失败占位仍保留原媒体域,避免音乐缓存被误写进影视缓存文件。
|
||||
mediainfo = (
|
||||
MusicInfo(
|
||||
music_type=normalized_music_type or MUSIC_ENTITY_RECORDING
|
||||
)
|
||||
if is_music
|
||||
else MediaInfo()
|
||||
)
|
||||
else:
|
||||
# 清理多余数据
|
||||
mediainfo.clear()
|
||||
|
||||
# 更新上下文中的媒体信息
|
||||
target_context.media_info = mediainfo
|
||||
|
||||
# 保存更新后的缓存:影视与音乐分别回写各自存储文件
|
||||
video_cache, music_cache = torrents_chain.split_cache_contexts(cache_data)
|
||||
video_file, music_file = torrents_chain.cache_files()
|
||||
await torrents_chain.async_save_cache(video_cache, video_file)
|
||||
await torrents_chain.async_save_cache(music_cache, music_file)
|
||||
|
||||
return _SchemaResponse(
|
||||
success=True,
|
||||
message="重新识别完成",
|
||||
data={
|
||||
"media_name": mediainfo.title if mediainfo else "",
|
||||
"media_year": mediainfo.year if mediainfo else "",
|
||||
"media_type": mediainfo.type.value
|
||||
if mediainfo and mediainfo.type
|
||||
else "",
|
||||
"media_source": getattr(mediainfo, "media_source", None),
|
||||
"media_id": getattr(mediainfo, "media_id", None),
|
||||
"music_type": getattr(mediainfo, "music_type", None),
|
||||
},
|
||||
success=success,
|
||||
message=message,
|
||||
data=data,
|
||||
)
|
||||
except Exception as e:
|
||||
return _SchemaResponse(success=False, message=f"重新识别失败:{str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user