mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-04-14 02:00:31 +08:00
fix directories
This commit is contained in:
@@ -2,7 +2,7 @@ from fastapi import APIRouter
|
||||
|
||||
from app.api.endpoints import login, user, site, message, webhook, subscribe, \
|
||||
media, douban, search, plugin, tmdb, history, system, download, dashboard, \
|
||||
transfer, mediaserver, bangumi, aliyun, storage
|
||||
transfer, mediaserver, bangumi, storage
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(login.router, prefix="/login", tags=["login"])
|
||||
@@ -20,8 +20,7 @@ api_router.include_router(system.router, prefix="/system", tags=["system"])
|
||||
api_router.include_router(plugin.router, prefix="/plugin", tags=["plugin"])
|
||||
api_router.include_router(download.router, prefix="/download", tags=["download"])
|
||||
api_router.include_router(dashboard.router, prefix="/dashboard", tags=["dashboard"])
|
||||
api_router.include_router(storage.router, prefix="/local", tags=["storage"])
|
||||
api_router.include_router(storage.router, prefix="/storage", tags=["storage"])
|
||||
api_router.include_router(transfer.router, prefix="/transfer", tags=["transfer"])
|
||||
api_router.include_router(mediaserver.router, prefix="/mediaserver", tags=["mediaserver"])
|
||||
api_router.include_router(bangumi.router, prefix="/bangumi", tags=["bangumi"])
|
||||
api_router.include_router(aliyun.router, prefix="/aliyun", tags=["aliyun"])
|
||||
|
||||
@@ -43,23 +43,23 @@ def statistic2(_: str = Depends(verify_apitoken)) -> Any:
|
||||
return statistic()
|
||||
|
||||
|
||||
@router.get("/storage", summary="存储空间", response_model=schemas.Storage)
|
||||
@router.get("/storage", summary="本地存储空间", response_model=schemas.Storage)
|
||||
def storage(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
"""
|
||||
查询存储空间信息
|
||||
查询本地存储空间信息
|
||||
"""
|
||||
library_dirs = DirectoryHelper().get_library_dirs()
|
||||
total_storage, free_storage = SystemUtils.space_usage([Path(d.path) for d in library_dirs if d.path])
|
||||
library_dirs = DirectoryHelper().get_local_library_dirs()
|
||||
total_storage, free_storage = SystemUtils.space_usage([Path(d.library_path) for d in library_dirs])
|
||||
return schemas.Storage(
|
||||
total_storage=total_storage,
|
||||
used_storage=total_storage - free_storage
|
||||
)
|
||||
|
||||
|
||||
@router.get("/storage2", summary="存储空间(API_TOKEN)", response_model=schemas.Storage)
|
||||
@router.get("/storage2", summary="本地存储空间(API_TOKEN)", response_model=schemas.Storage)
|
||||
def storage2(_: str = Depends(verify_apitoken)) -> Any:
|
||||
"""
|
||||
查询存储空间信息 API_TOKEN认证(?token=xxx)
|
||||
查询本地存储空间信息 API_TOKEN认证(?token=xxx)
|
||||
"""
|
||||
return storage()
|
||||
|
||||
@@ -78,8 +78,8 @@ def downloader(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
查询下载器信息
|
||||
"""
|
||||
# 下载目录空间
|
||||
download_dirs = DirectoryHelper().get_download_dirs()
|
||||
_, free_space = SystemUtils.space_usage([Path(d.path) for d in download_dirs if d.path])
|
||||
download_dirs = DirectoryHelper().get_local_download_dirs()
|
||||
_, free_space = SystemUtils.space_usage([Path(d.download_path) for d in download_dirs])
|
||||
# 下载器信息
|
||||
downloader_info = schemas.DownloaderInfo()
|
||||
transfer_infos = DashboardChain().downloader_info()
|
||||
|
||||
@@ -176,7 +176,6 @@ def tv_hot(page: int = 1,
|
||||
@router.get("/credits/{doubanid}/{type_name}", summary="豆瓣演员阵容", response_model=List[schemas.MediaPerson])
|
||||
def douban_credits(doubanid: str,
|
||||
type_name: str,
|
||||
page: int = 1,
|
||||
_: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
"""
|
||||
根据豆瓣ID查询演员阵容,type_name: 电影/电视剧
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
from typing import List, Any
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
@@ -81,17 +81,19 @@ def delete_transfer_history(history_in: schemas.TransferHistory,
|
||||
"""
|
||||
删除转移历史记录
|
||||
"""
|
||||
history = TransferHistory.get(db, history_in.id)
|
||||
history: TransferHistory = TransferHistory.get(db, history_in.id)
|
||||
if not history:
|
||||
return schemas.Response(success=False, msg="记录不存在")
|
||||
# 册除媒体库文件
|
||||
if deletedest and history.dest:
|
||||
state, msg = TransferChain().delete_files(Path(history.dest))
|
||||
if deletedest and history.dest_fileitem:
|
||||
dest_fileitem = schemas.FileItem(**json.loads(history.dest_fileitem))
|
||||
state, msg = TransferChain().delete_files(dest_fileitem)
|
||||
if not state:
|
||||
return schemas.Response(success=False, msg=msg)
|
||||
# 删除源文件
|
||||
if deletesrc and history.src:
|
||||
state, msg = TransferChain().delete_files(Path(history.src))
|
||||
if deletesrc and history.dest_fileitem:
|
||||
dest_fileitem = schemas.FileItem(**json.loads(history.dest_fileitem))
|
||||
state, msg = TransferChain().delete_files(dest_fileitem)
|
||||
if not state:
|
||||
return schemas.Response(success=False, msg=msg)
|
||||
# 发送事件
|
||||
|
||||
@@ -120,7 +120,7 @@ def scrape(fileitem: schemas.FileItem,
|
||||
if not fileitem.fileid:
|
||||
return schemas.Response(success=False, message="刮削文件ID无效")
|
||||
# 手动刮削
|
||||
chain.scrape_metadata(storage=storage, fileitem=fileitem, meta=meta, mediainfo=mediainfo)
|
||||
chain.scrape_metadata(fileitem=fileitem, meta=meta, mediainfo=mediainfo)
|
||||
return schemas.Response(success=True, message=f"{fileitem.path} 刮削完成")
|
||||
|
||||
|
||||
|
||||
@@ -119,26 +119,26 @@ def not_exists(media_in: schemas.MediaInfo,
|
||||
|
||||
|
||||
@router.get("/latest", summary="最新入库条目", response_model=List[schemas.MediaServerPlayItem])
|
||||
def latest(count: int = 18,
|
||||
def latest(server: str, count: int = 18,
|
||||
userinfo: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
"""
|
||||
获取媒体服务器最新入库条目
|
||||
"""
|
||||
return MediaServerChain().latest(count=count, username=userinfo.username) or []
|
||||
return MediaServerChain().latest(server=server, count=count, username=userinfo.username) or []
|
||||
|
||||
|
||||
@router.get("/playing", summary="正在播放条目", response_model=List[schemas.MediaServerPlayItem])
|
||||
def playing(count: int = 12,
|
||||
def playing(server: str, count: int = 12,
|
||||
userinfo: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
"""
|
||||
获取媒体服务器正在播放条目
|
||||
"""
|
||||
return MediaServerChain().playing(count=count, username=userinfo.username) or []
|
||||
return MediaServerChain().playing(server=server, count=count, username=userinfo.username) or []
|
||||
|
||||
|
||||
@router.get("/library", summary="媒体库列表", response_model=List[schemas.MediaServerLibrary])
|
||||
def library(userinfo: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
def library(server: str, userinfo: schemas.TokenPayload = Depends(verify_token)) -> Any:
|
||||
"""
|
||||
获取媒体服务器媒体库列表
|
||||
"""
|
||||
return MediaServerChain().librarys(username=userinfo.username) or []
|
||||
return MediaServerChain().librarys(server=server, username=userinfo.username) or []
|
||||
|
||||
@@ -164,7 +164,7 @@ def plugin_dashboard(plugin_id: str, user_agent: Annotated[str | None, Header()]
|
||||
"""
|
||||
根据插件ID获取插件仪表板
|
||||
"""
|
||||
return PluginManager().get_plugin_dashboard(plugin_id, key=None, user_agent=user_agent)
|
||||
return PluginManager().get_plugin_dashboard(plugin_id, user_agent=user_agent)
|
||||
|
||||
|
||||
@router.get("/dashboard/{plugin_id}/{key}", summary="获取插件仪表板配置")
|
||||
|
||||
@@ -96,14 +96,14 @@ def manual_transfer(fileitem: FileItem = None,
|
||||
force = True
|
||||
if history.status and ("move" in history.mode):
|
||||
# 重新整理成功的转移,则使用成功的 dest 做 in_path
|
||||
src_fileitem = json.loads(history.dest_fileitem)
|
||||
src_fileitem = FileItem(**json.loads(history.dest_fileitem))
|
||||
else:
|
||||
# 源路径
|
||||
src_fileitem = json.loads(history.src_fileitem)
|
||||
src_fileitem = FileItem(**json.loads(history.src_fileitem))
|
||||
# 目的路径
|
||||
if history.dest_fileitem:
|
||||
# 删除旧的已整理文件
|
||||
dest_fileitem = json.loads(history.dest_fileitem)
|
||||
dest_fileitem = FileItem(**json.loads(history.dest_fileitem))
|
||||
transfer.delete_files(dest_fileitem)
|
||||
elif fileitem:
|
||||
src_fileitem = fileitem
|
||||
|
||||
@@ -245,22 +245,23 @@ class DownloadChain(ChainBase):
|
||||
# 下载目录
|
||||
if save_path:
|
||||
# 有自定义下载目录时,尝试匹配目录配置
|
||||
dir_info = self.directoryhelper.get_download_dir(_media, to_path=Path(save_path))
|
||||
dir_info = self.directoryhelper.get_dir(_media, src_path=Path(save_path), local=True)
|
||||
else:
|
||||
# 根据媒体信息查询下载目录配置
|
||||
dir_info = self.directoryhelper.get_download_dir(_media)
|
||||
dir_info = self.directoryhelper.get_dir(_media, local=True)
|
||||
|
||||
# 拼装子目录
|
||||
if dir_info:
|
||||
# 一级目录
|
||||
if not dir_info.media_type and dir_info.auto_category:
|
||||
if not dir_info.media_type and dir_info.download_type_folder:
|
||||
# 一级自动分类
|
||||
download_dir = Path(dir_info.path) / _media.type.value
|
||||
download_dir = Path(dir_info.download_path) / _media.type.value
|
||||
else:
|
||||
# 一级不分类
|
||||
download_dir = Path(dir_info.path)
|
||||
download_dir = Path(dir_info.download_path)
|
||||
|
||||
# 二级目录
|
||||
if not dir_info.category and dir_info.auto_category and _media and _media.category:
|
||||
if not dir_info.media_category and dir_info.download_category_folder and _media and _media.category:
|
||||
# 二级自动分类
|
||||
download_dir = download_dir / _media.category
|
||||
elif save_path:
|
||||
|
||||
@@ -5,7 +5,6 @@ from cachetools import cached, TTLCache
|
||||
|
||||
from app import schemas
|
||||
from app.chain import ChainBase
|
||||
from app.core.config import settings
|
||||
from app.core.context import MediaInfo
|
||||
from app.schemas import MediaType
|
||||
from app.utils.singleton import Singleton
|
||||
|
||||
@@ -63,11 +63,11 @@ class TransferChain(ChainBase):
|
||||
|
||||
# 全局锁,避免重复处理
|
||||
with lock:
|
||||
logger.info("开始执行下载器文件转移 ...")
|
||||
logger.info("开始整理下载器中已经完成下载的文件 ...")
|
||||
# 从下载器获取种子列表
|
||||
torrents: Optional[List[TransferTorrent]] = self.list_torrents(status=TorrentStatus.TRANSFER)
|
||||
if not torrents:
|
||||
logger.info("没有获取到已完成的下载任务")
|
||||
logger.info("没有已完成下载但未整理的任务")
|
||||
return False
|
||||
|
||||
logger.info(f"获取到 {len(torrents)} 个已完成的下载任务")
|
||||
@@ -109,7 +109,7 @@ class TransferChain(ChainBase):
|
||||
# 设置下载任务状态
|
||||
self.transfer_completed(hashs=torrent.hash, path=torrent.path)
|
||||
# 结束
|
||||
logger.info("下载器文件转移执行完成")
|
||||
logger.info("所有下载器中下载完成的文件已整理完成")
|
||||
return True
|
||||
|
||||
def __do_transfer(self, fileitem: FileItem,
|
||||
|
||||
@@ -374,7 +374,7 @@ class PluginManager(metaclass=Singleton):
|
||||
return plugin.get_page() or []
|
||||
return []
|
||||
|
||||
def get_plugin_dashboard(self, pid: str, key: str, **kwargs) -> Optional[schemas.PluginDashboard]:
|
||||
def get_plugin_dashboard(self, pid: str, key: str = None, **kwargs) -> Optional[schemas.PluginDashboard]:
|
||||
"""
|
||||
获取插件仪表盘
|
||||
:param pid: 插件ID
|
||||
|
||||
@@ -2,12 +2,9 @@ from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
from app import schemas
|
||||
from app.core.config import settings
|
||||
from app.core.context import MediaInfo
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.log import logger
|
||||
from app.schemas.types import SystemConfigKey, MediaType
|
||||
from app.utils.system import SystemUtils
|
||||
|
||||
|
||||
class DirectoryHelper:
|
||||
@@ -18,147 +15,72 @@ class DirectoryHelper:
|
||||
def __init__(self):
|
||||
self.systemconfig = SystemConfigOper()
|
||||
|
||||
def get_download_dirs(self) -> List[schemas.MediaDirectory]:
|
||||
def get_download_dirs(self) -> List[schemas.TransferDirectoryConf]:
|
||||
"""
|
||||
获取下载目录
|
||||
获取所有下载目录
|
||||
"""
|
||||
dir_conf: List[dict] = self.systemconfig.get(SystemConfigKey.DownloadDirectories)
|
||||
if not dir_conf:
|
||||
dir_confs: List[dict] = self.systemconfig.get(SystemConfigKey.Directories)
|
||||
if not dir_confs:
|
||||
return []
|
||||
return [schemas.MediaDirectory(**d) for d in dir_conf]
|
||||
dirs = [schemas.TransferDirectoryConf(**d) for d in dir_confs]
|
||||
return sorted([d for d in dirs if d.download_path], key=lambda x: x.priority)
|
||||
|
||||
def get_library_dirs(self) -> List[schemas.MediaDirectory]:
|
||||
def get_local_download_dirs(self) -> List[schemas.TransferDirectoryConf]:
|
||||
"""
|
||||
获取媒体库目录
|
||||
获取所有本地的可下载目录
|
||||
"""
|
||||
dir_conf: List[dict] = self.systemconfig.get(SystemConfigKey.LibraryDirectories)
|
||||
if not dir_conf:
|
||||
return [d for d in self.get_download_dirs() if d.storage == "local"]
|
||||
|
||||
def get_library_dirs(self) -> List[schemas.TransferDirectoryConf]:
|
||||
"""
|
||||
获取所有媒体库目录
|
||||
"""
|
||||
dir_confs: List[dict] = self.systemconfig.get(SystemConfigKey.Directories)
|
||||
if not dir_confs:
|
||||
return []
|
||||
return [schemas.MediaDirectory(**d) for d in dir_conf]
|
||||
dirs = [schemas.TransferDirectoryConf(**d) for d in dir_confs]
|
||||
return sorted([d for d in dirs if d.library_path], key=lambda x: x.priority)
|
||||
|
||||
def get_download_dir(self, media: MediaInfo = None, to_path: Path = None) -> Optional[schemas.MediaDirectory]:
|
||||
def get_local_library_dirs(self) -> List[schemas.TransferDirectoryConf]:
|
||||
"""
|
||||
根据媒体信息获取下载目录
|
||||
获取所有本地的媒体库目录
|
||||
"""
|
||||
return [d for d in self.get_library_dirs() if d.library_storage == "local"]
|
||||
|
||||
def get_dir(self, media: MediaInfo, src_path: Path = None, dest_path: Path = None,
|
||||
local: bool = False) -> Optional[schemas.TransferDirectoryConf]:
|
||||
"""
|
||||
根据媒体信息获取下载目录、媒体库目录配置
|
||||
:param media: 媒体信息
|
||||
:param to_path: 目标目录
|
||||
:param src_path: 源目录,有值时直接匹配
|
||||
:param dest_path: 目标目录,有值时直接匹配
|
||||
:param local: 是否本地目录
|
||||
"""
|
||||
# 处理类型
|
||||
if media:
|
||||
media_type = media.type.value
|
||||
else:
|
||||
media_type = MediaType.UNKNOWN.value
|
||||
download_dirs = self.get_download_dirs()
|
||||
# 按照配置顺序查找(保存后的数据已经排序)
|
||||
for download_dir in download_dirs:
|
||||
if not download_dir.path:
|
||||
dirs = self.get_download_dirs()
|
||||
# 按照配置顺序查找
|
||||
for d in dirs:
|
||||
download_path = Path(d.download_path)
|
||||
# 有目录时直接匹配
|
||||
if src_path and download_path != src_path:
|
||||
continue
|
||||
download_path = Path(download_dir.path)
|
||||
# 有目标目录,但目标目录与当前目录不相等时不要
|
||||
if to_path and download_path != to_path:
|
||||
if dest_path and download_path != dest_path:
|
||||
continue
|
||||
# 本地目录
|
||||
if local and d.storage != "local":
|
||||
continue
|
||||
# 目录类型为全部的,符合条件
|
||||
if not download_dir.media_type:
|
||||
return download_dir
|
||||
if not d.media_type:
|
||||
return d
|
||||
# 目录类型相等,目录类别为全部,符合条件
|
||||
if download_dir.media_type == media_type and not download_dir.category:
|
||||
return download_dir
|
||||
if d.media_type == media_type and not d.media_category:
|
||||
return d
|
||||
# 目录类型相等,目录类别相等,符合条件
|
||||
if download_dir.media_type == media_type and download_dir.category == media.category:
|
||||
return download_dir
|
||||
if d.media_type == media_type and d.media_category == media.category:
|
||||
return d
|
||||
|
||||
return None
|
||||
|
||||
def get_library_dir(self, media: MediaInfo = None, in_path: Path = None,
|
||||
to_path: Path = None) -> Optional[schemas.MediaDirectory]:
|
||||
"""
|
||||
根据媒体信息获取媒体库目录,需判断是否同盘优先
|
||||
:param media: 媒体信息
|
||||
:param in_path: 源目录
|
||||
:param to_path: 目标目录
|
||||
"""
|
||||
|
||||
def __comman_parts(path1: Path, path2: Path) -> int:
|
||||
"""
|
||||
计算两个路径的公共路径长度
|
||||
"""
|
||||
parts1 = path1.parts
|
||||
parts2 = path2.parts
|
||||
root_flag = parts1[0] == '/' and parts2[0] == '/'
|
||||
length = min(len(parts1), len(parts2))
|
||||
for i in range(length):
|
||||
if parts1[i] == '/' and parts2[i] == '/':
|
||||
continue
|
||||
if parts1[i] != parts2[i]:
|
||||
return i - 1 if root_flag else i
|
||||
return length - 1 if root_flag else length
|
||||
|
||||
# 处理类型
|
||||
if media:
|
||||
media_type = media.type.value
|
||||
else:
|
||||
media_type = MediaType.UNKNOWN.value
|
||||
|
||||
# 匹配的目录
|
||||
matched_dirs = []
|
||||
library_dirs = self.get_library_dirs()
|
||||
# 按照配置顺序查找(保存后的数据已经排序)
|
||||
for library_dir in library_dirs:
|
||||
if not library_dir.path:
|
||||
continue
|
||||
# 有目标目录,但目标目录与当前目录不相等时不要
|
||||
if to_path and Path(library_dir.path) != to_path:
|
||||
continue
|
||||
# 目录类型为全部的,符合条件
|
||||
if not library_dir.media_type:
|
||||
matched_dirs.append(library_dir)
|
||||
# 目录类型相等,目录类别为全部,符合条件
|
||||
if library_dir.media_type == media_type and not library_dir.category:
|
||||
matched_dirs.append(library_dir)
|
||||
# 目录类型相等,目录类别相等,符合条件
|
||||
if library_dir.media_type == media_type and library_dir.category == media.category:
|
||||
matched_dirs.append(library_dir)
|
||||
|
||||
# 未匹配到
|
||||
if not matched_dirs:
|
||||
return None
|
||||
|
||||
# 没有目录则创建
|
||||
for matched_dir in matched_dirs:
|
||||
matched_path = Path(matched_dir.path)
|
||||
if not matched_path.exists():
|
||||
matched_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 只匹配到一项
|
||||
if len(matched_dirs) == 1:
|
||||
return matched_dirs[0]
|
||||
|
||||
# 有源路径,且开启同盘/同目录优先时
|
||||
if in_path and settings.TRANSFER_SAME_DISK:
|
||||
# 优先同根路径
|
||||
max_length = 0
|
||||
target_dirs = []
|
||||
for matched_dir in matched_dirs:
|
||||
try:
|
||||
# 计算in_path和path的公共路径长度
|
||||
relative_len = __comman_parts(in_path, Path(matched_dir.path))
|
||||
if relative_len and relative_len >= max_length:
|
||||
max_length = relative_len
|
||||
target_dirs.append({
|
||||
'path': matched_dir,
|
||||
'relative_len': relative_len
|
||||
})
|
||||
except Exception as e:
|
||||
logger.debug(f"计算目标路径时出错:{str(e)}")
|
||||
continue
|
||||
if target_dirs:
|
||||
target_dirs.sort(key=lambda x: x['relative_len'], reverse=True)
|
||||
matched_dirs = [x['path'] for x in target_dirs]
|
||||
|
||||
# 优先同盘
|
||||
for matched_dir in matched_dirs:
|
||||
matched_path = Path(matched_dir.path)
|
||||
if SystemUtils.is_same_disk(matched_path, in_path):
|
||||
return matched_dir
|
||||
|
||||
# 返回最优先的匹配
|
||||
return matched_dirs[0]
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.helper.module import ModuleHelper
|
||||
from app.log import logger
|
||||
from app.modules import _ModuleBase
|
||||
from app.modules.filemanager.storage import StorageBase
|
||||
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, MediaDirectory, FileItem
|
||||
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, TransferDirectoryConf, FileItem
|
||||
from app.schemas.types import MediaType
|
||||
from app.utils.system import SystemUtils
|
||||
|
||||
@@ -51,39 +51,31 @@ class FileManagerModule(_ModuleBase):
|
||||
测试模块连接性
|
||||
"""
|
||||
directoryhelper = DirectoryHelper()
|
||||
# 检查下载目录
|
||||
download_paths = directoryhelper.get_download_dirs()
|
||||
# 检查本地下载目录是否存在
|
||||
download_paths = directoryhelper.get_local_download_dirs()
|
||||
if not download_paths:
|
||||
return False, "下载目录未设置"
|
||||
for d_path in download_paths:
|
||||
path = d_path.path
|
||||
path = d_path.download_path
|
||||
if not path:
|
||||
return False, f"下载目录 {d_path.name} 对应路径未设置"
|
||||
download_path = Path(path)
|
||||
if not download_path.exists():
|
||||
return False, f"下载目录 {d_path.name} 对应路径 {path} 不存在"
|
||||
# 检查媒体库目录
|
||||
libaray_paths = directoryhelper.get_library_dirs()
|
||||
# 检查本地媒体库目录是否存在
|
||||
libaray_paths = directoryhelper.get_local_library_dirs()
|
||||
if not libaray_paths:
|
||||
return False, "媒体库目录未设置"
|
||||
for l_path in libaray_paths:
|
||||
path = l_path.path
|
||||
path = l_path.library_path
|
||||
if not path:
|
||||
return False, f"媒体库目录 {l_path.name} 对应路径未设置"
|
||||
library_path = Path(path)
|
||||
if not library_path.exists():
|
||||
return False, f"媒体库目录{l_path.name} 对应的路径 {path} 不存在"
|
||||
# 检查硬链接条件
|
||||
if settings.DOWNLOADER_MONITOR and settings.TRANSFER_TYPE == "link":
|
||||
for d_path in download_paths:
|
||||
link_ok = False
|
||||
for l_path in libaray_paths:
|
||||
if SystemUtils.is_same_disk(Path(d_path.path), Path(l_path.path)):
|
||||
link_ok = True
|
||||
break
|
||||
if not link_ok:
|
||||
return False, f"媒体库目录中未找到" \
|
||||
f"与下载目录 {d_path.path} 在同一磁盘/存储空间/映射路径的目录,将无法硬链接"
|
||||
# TODO 检查硬链接条件
|
||||
|
||||
# TODO 检查网盘目录
|
||||
return True, ""
|
||||
|
||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||
@@ -198,20 +190,23 @@ class FileManagerModule(_ModuleBase):
|
||||
# 获取目标路径
|
||||
directoryhelper = DirectoryHelper()
|
||||
if target_path:
|
||||
dir_info = directoryhelper.get_library_dir(mediainfo, in_path=fileitem.path, to_path=target_path)
|
||||
dir_info = directoryhelper.get_dir(mediainfo, dest_path=target_path)
|
||||
else:
|
||||
dir_info = directoryhelper.get_library_dir(mediainfo, in_path=fileitem.path)
|
||||
dir_info = directoryhelper.get_dir(mediainfo)
|
||||
if dir_info:
|
||||
# 是否需要刮削
|
||||
if scrape is None:
|
||||
need_scrape = dir_info.scrape
|
||||
need_scrape = dir_info.scraping
|
||||
else:
|
||||
need_scrape = scrape
|
||||
# 是否需要重命名
|
||||
need_rename = dir_info.renaming
|
||||
# 拼装媒体库一、二级子目录
|
||||
target_path = self.__get_dest_dir(mediainfo=mediainfo, target_dir=dir_info)
|
||||
elif target_path:
|
||||
# 自定义目标路径
|
||||
need_scrape = scrape or False
|
||||
need_rename = True
|
||||
else:
|
||||
# 未找到有效的媒体库目录
|
||||
logger.error(
|
||||
@@ -229,7 +224,8 @@ class FileManagerModule(_ModuleBase):
|
||||
target_storage=target_storage,
|
||||
target_path=target_path,
|
||||
episodes_info=episodes_info,
|
||||
need_scrape=need_scrape)
|
||||
need_scrape=need_scrape,
|
||||
need_rename=need_rename)
|
||||
|
||||
def __get_storage_oper(self, _storage: str):
|
||||
"""
|
||||
@@ -595,7 +591,7 @@ class FileManagerModule(_ModuleBase):
|
||||
if target_storage == "local" and (target_file.exists() or target_file.is_symlink()):
|
||||
if not over_flag:
|
||||
logger.warn(f"文件已存在:{target_file}")
|
||||
return 0
|
||||
return None, f"{target_file} 已存在"
|
||||
else:
|
||||
logger.info(f"正在删除已存在的文件:{target_file}")
|
||||
target_file.unlink()
|
||||
@@ -617,24 +613,24 @@ class FileManagerModule(_ModuleBase):
|
||||
return None, errmsg
|
||||
|
||||
@staticmethod
|
||||
def __get_dest_dir(mediainfo: MediaInfo, target_dir: MediaDirectory) -> Path:
|
||||
def __get_dest_dir(mediainfo: MediaInfo, target_dir: TransferDirectoryConf) -> Path:
|
||||
"""
|
||||
根据设置并装媒体库目录
|
||||
:param mediainfo: 媒体信息
|
||||
:target_dir: 媒体库根目录
|
||||
:typename_dir: 是否加上类型目录
|
||||
"""
|
||||
if not target_dir.media_type and target_dir.auto_category:
|
||||
if not target_dir.media_type and target_dir.library_type_folder:
|
||||
# 一级自动分类
|
||||
download_dir = Path(target_dir.path) / mediainfo.type.value
|
||||
library_dir = Path(target_dir.library_path) / mediainfo.type.value
|
||||
else:
|
||||
download_dir = Path(target_dir.path)
|
||||
library_dir = Path(target_dir.library_path)
|
||||
|
||||
if not target_dir.category and target_dir.auto_category and mediainfo.category:
|
||||
if not target_dir.media_category and target_dir.library_category_folder and mediainfo.category:
|
||||
# 二级自动分类
|
||||
download_dir = download_dir / mediainfo.category
|
||||
library_dir = library_dir / mediainfo.category
|
||||
|
||||
return download_dir
|
||||
return library_dir
|
||||
|
||||
def transfer_media(self,
|
||||
fileitem: FileItem,
|
||||
@@ -644,7 +640,8 @@ class FileManagerModule(_ModuleBase):
|
||||
target_storage: str,
|
||||
target_path: Path,
|
||||
episodes_info: List[TmdbEpisode] = None,
|
||||
need_scrape: bool = False
|
||||
need_scrape: bool = False,
|
||||
need_rename: bool = True
|
||||
) -> TransferInfo:
|
||||
"""
|
||||
识别并整理一个文件或者一个目录下的所有文件
|
||||
@@ -656,6 +653,7 @@ class FileManagerModule(_ModuleBase):
|
||||
:param transfer_type: 文件整理方式
|
||||
:param episodes_info: 当前季的全部集信息
|
||||
:param need_scrape: 是否需要刮削
|
||||
:param need_rename: 是否需要重命名
|
||||
:return: TransferInfo、错误信息
|
||||
"""
|
||||
|
||||
@@ -693,12 +691,15 @@ class FileManagerModule(_ModuleBase):
|
||||
# 判断是否为文件夹
|
||||
if fileitem.type == "dir":
|
||||
# 整理整个目录,一般为蓝光原盘
|
||||
new_path = self.get_rename_path(
|
||||
path=target_path,
|
||||
template_string=rename_format,
|
||||
rename_dict=self.__get_naming_dict(meta=in_meta,
|
||||
mediainfo=mediainfo)
|
||||
).parent
|
||||
if need_rename:
|
||||
new_path = self.get_rename_path(
|
||||
path=target_path,
|
||||
template_string=rename_format,
|
||||
rename_dict=self.__get_naming_dict(meta=in_meta,
|
||||
mediainfo=mediainfo)
|
||||
).parent
|
||||
else:
|
||||
new_path = target_path / fileitem.name
|
||||
# 整理目录
|
||||
new_item, errmsg = self.__transfer_dir(fileitem=fileitem,
|
||||
target_storage=target_storage,
|
||||
@@ -740,16 +741,19 @@ class FileManagerModule(_ModuleBase):
|
||||
in_meta.end_episode = None
|
||||
|
||||
# 目的文件名
|
||||
new_file = self.get_rename_path(
|
||||
path=target_path,
|
||||
template_string=rename_format,
|
||||
rename_dict=self.__get_naming_dict(
|
||||
meta=in_meta,
|
||||
mediainfo=mediainfo,
|
||||
episodes_info=episodes_info,
|
||||
file_ext=f".{fileitem.extension}"
|
||||
if need_rename:
|
||||
new_file = self.get_rename_path(
|
||||
path=target_path,
|
||||
template_string=rename_format,
|
||||
rename_dict=self.__get_naming_dict(
|
||||
meta=in_meta,
|
||||
mediainfo=mediainfo,
|
||||
episodes_info=episodes_info,
|
||||
file_ext=f".{fileitem.extension}"
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
new_file = target_path / fileitem.name
|
||||
|
||||
# 判断是否要覆盖
|
||||
overflag = False
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MediaDirectory(BaseModel):
|
||||
"""
|
||||
下载目录/媒体库目录
|
||||
"""
|
||||
# 类型 download/library
|
||||
type: Optional[str] = None
|
||||
# 别名
|
||||
name: Optional[str] = None
|
||||
# 路径
|
||||
path: Optional[str] = None
|
||||
# 媒体类型 电影/电视剧
|
||||
media_type: Optional[str] = None
|
||||
# 媒体类别 动画电影/国产剧
|
||||
category: Optional[str] = None
|
||||
# 刮削媒体信息
|
||||
scrape: Optional[bool] = False
|
||||
# 自动二级分类,未指定类别时自动分类
|
||||
auto_category: Optional[bool] = False
|
||||
# 优先级
|
||||
priority: Optional[int] = 0
|
||||
@@ -47,3 +47,41 @@ class NotificationConf(BaseModel):
|
||||
switchs: Optional[list] = []
|
||||
# 是否启用
|
||||
enabled: Optional[bool] = False
|
||||
|
||||
|
||||
class TransferDirectoryConf(BaseModel):
|
||||
"""
|
||||
文件整理目录配置
|
||||
"""
|
||||
# 名称
|
||||
name: Optional[str] = None
|
||||
# 优先级
|
||||
priority: Optional[int] = 0
|
||||
# 存储
|
||||
storage: Optional[str] = None
|
||||
# 下载目录
|
||||
download_path: Optional[str] = None
|
||||
# 适用媒体类型
|
||||
media_type: Optional[str] = None
|
||||
# 适用媒体类别
|
||||
media_category: Optional[str] = None
|
||||
# 下载类型子目录
|
||||
download_type_folder: Optional[bool] = False
|
||||
# 下载类别子目录
|
||||
download_category_folder: Optional[bool] = False
|
||||
# 监控方式 downloader/monitor,None为不监控
|
||||
monitor_type: Optional[str] = None
|
||||
# 整理方式 move/copy/link/softlink
|
||||
transfer_type: Optional[str] = None
|
||||
# 整理到媒体库目录
|
||||
library_path: Optional[str] = None
|
||||
# 媒体库目录存储
|
||||
library_storage: Optional[str] = None
|
||||
# 智能重命名
|
||||
renaming: Optional[bool] = False
|
||||
# 刮削
|
||||
scraping: Optional[bool] = False
|
||||
# 媒体库类型子目录
|
||||
library_type_folder: Optional[bool] = False
|
||||
# 媒体库类别子目录
|
||||
library_category_folder: Optional[bool] = False
|
||||
|
||||
@@ -64,10 +64,8 @@ class SystemConfigKey(Enum):
|
||||
MediaServers = "MediaServers"
|
||||
# 消息通知配置
|
||||
Notifications = "Notifications"
|
||||
# 下载目录配置
|
||||
DownloadDirectories = "DownloadDirectories"
|
||||
# 媒体库目录配置
|
||||
LibraryDirectories = "LibraryDirectories"
|
||||
# 目录配置
|
||||
Directories = "Directories"
|
||||
# 阿里云盘认证参数
|
||||
UserAliyunParams = "UserAliyunParams"
|
||||
# 115网盘认证参数
|
||||
|
||||
Reference in New Issue
Block a user