Files
MoviePilot/app/api/endpoints/torrent.py
T

223 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Optional
from fastapi import Depends
from app.schemas.cache import TorrentCacheData as _SchemaTorrentCacheData
from app.schemas.cache import TorrentReidentifyData as _SchemaTorrentReidentifyData
from app.schemas.response import Response as _SchemaResponse
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.api.dependencies.auth import (
get_current_active_superuser,
get_current_active_superuser_async,
)
from app.schemas.types import (
MediaSource,
MusicTargetEntityType,
)
from app.foundation.crypto import HashUtils
from app.schemas.media import resolve_media_identity
from app.application.torrent_cache import TorrentCacheRecognitionService
router = ResponseAPIRouter()
@router.get(
"/cache",
summary="获取种子缓存",
response_model=_SchemaResponse[_SchemaTorrentCacheData],
)
async def torrents_cache(_: object = Depends(get_current_active_superuser_async)):
"""
获取当前种子缓存数据
"""
torrents_chain = TorrentsChain()
# 获取spider和rss两种缓存
if get_api_runtime_config_snapshot().subscribe_mode == "rss":
cache_info = await torrents_chain.async_get_torrents("rss")
else:
cache_info = await torrents_chain.async_get_torrents("spider")
# 统计信息
torrent_count = sum(len(torrents) for torrents in cache_info.values())
# 转换为前端需要的格式
torrent_data = []
for domain, contexts in cache_info.items():
for context in contexts:
torrent_hash = HashUtils.md5(
f"{context.torrent_info.title}{context.torrent_info.description}"
)
media_source, media_id = resolve_media_identity(media=context.media_info)
torrent_data.append(
{
"hash": torrent_hash,
"domain": domain,
"title": context.torrent_info.title,
"description": context.torrent_info.description,
"size": context.torrent_info.size,
"pubdate": context.torrent_info.pubdate,
"site_name": context.torrent_info.site_name,
"media_name": context.media_info.title
if context.media_info
else "",
"media_year": context.media_info.year if context.media_info else "",
"media_type": context.media_info.type if context.media_info else "",
"media_source": media_source,
"media_id": media_id,
"music_type": getattr(context.media_info, "music_type", None),
"season_episode": context.meta_info.season_episode
if context.meta_info
else "",
"resource_term": context.meta_info.resource_term
if context.meta_info
else "",
"enclosure": context.torrent_info.enclosure,
"page_url": context.torrent_info.page_url,
"poster_path": context.media_info.get_poster_image()
if context.media_info
else "",
"backdrop_path": context.media_info.get_backdrop_image()
if context.media_info
else "",
}
)
return _SchemaResponse(
success=True,
data={"count": torrent_count, "sites": len(cache_info), "data": torrent_data},
)
@router.delete(
"/cache/{domain}/{torrent_hash}",
summary="删除指定种子缓存",
response_model=_SchemaResponse[None],
)
async def delete_cache(
domain: str,
torrent_hash: str,
_: object = Depends(get_current_active_superuser_async),
):
"""
删除指定的种子缓存
:param domain: 站点域名
:param torrent_hash: 种子hash(使用title+description的md5
:param _: 当前用户,必须是超级用户
"""
torrents_chain = TorrentsChain()
try:
# 获取当前缓存
cache_data = await torrents_chain.async_get_torrents()
if domain not in cache_data:
return _SchemaResponse(success=False, message=f"站点 {domain} 缓存不存在")
# 查找并删除指定种子
original_count = len(cache_data[domain])
cache_data[domain] = [
context
for context in cache_data[domain]
if HashUtils.md5(
f"{context.torrent_info.title}{context.torrent_info.description}"
)
!= torrent_hash
]
if len(cache_data[domain]) == original_count:
return _SchemaResponse(success=False, message="未找到指定的种子")
# 保存更新后的缓存:影视与音乐分别回写各自存储文件
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="种子删除成功")
except Exception as e:
return _SchemaResponse(success=False, message=f"删除失败:{str(e)}")
@router.delete("/cache", summary="清理种子缓存", response_model=_SchemaResponse[None])
async def clear_cache(_: object = Depends(get_current_active_superuser_async)):
"""
清理所有种子缓存
"""
torrents_chain = TorrentsChain()
try:
await torrents_chain.async_clear_torrents()
return _SchemaResponse(success=True, message="种子缓存清理完成")
except Exception as e:
return _SchemaResponse(success=False, message=f"清理失败:{str(e)}")
@router.post("/cache/refresh", summary="刷新种子缓存", response_model=_SchemaResponse[None])
def refresh_cache(_: object = Depends(get_current_active_superuser)):
"""
刷新种子缓存
"""
from app.chain.torrents import TorrentsChain
torrents_chain = TorrentsChain()
try:
result = torrents_chain.refresh()
# 统计刷新结果
total_count = sum(len(torrents) for torrents in result.values())
sites_count = len(result)
return _SchemaResponse(
success=True,
message=f"缓存刷新完成,共刷新 {sites_count} 个站点,{total_count} 个种子",
)
except Exception as e:
return _SchemaResponse(success=False, message=f"刷新失败:{str(e)}")
@router.post(
"/cache/reidentify/{domain}/{torrent_hash}",
summary="重新识别种子",
response_model=_SchemaResponse[_SchemaTorrentReidentifyData],
)
async def reidentify_cache(
domain: str,
torrent_hash: str,
media_source: Optional[MediaSource] = None,
media_id: Optional[str] = None,
music_type: Optional[MusicTargetEntityType] = None,
_: object = Depends(get_current_active_superuser_async),
):
"""
重新识别指定的种子
:param domain: 站点域名
:param torrent_hash: 种子hash(使用title+description的md5
:param media_source: 媒体数据源
:param media_id: 数据源原生 ID
:param music_type: 音乐实体类型,仅支持单曲或专辑
:param _: 当前用户,必须是超级用户
"""
try:
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,
)
return _SchemaResponse(
success=success,
message=message,
data=data,
)
except Exception as e:
return _SchemaResponse(success=False, message=f"重新识别失败:{str(e)}")