diff --git a/app/api/apiv1.py b/app/api/apiv1.py index df2e7711..8285ec73 100644 --- a/app/api/apiv1.py +++ b/app/api/apiv1.py @@ -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"]) diff --git a/app/api/endpoints/dashboard.py b/app/api/endpoints/dashboard.py index 972177dd..76956108 100644 --- a/app/api/endpoints/dashboard.py +++ b/app/api/endpoints/dashboard.py @@ -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() diff --git a/app/api/endpoints/douban.py b/app/api/endpoints/douban.py index 1693ff86..8946dd47 100644 --- a/app/api/endpoints/douban.py +++ b/app/api/endpoints/douban.py @@ -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: 电影/电视剧 diff --git a/app/api/endpoints/history.py b/app/api/endpoints/history.py index 00b9f964..7102b1b0 100644 --- a/app/api/endpoints/history.py +++ b/app/api/endpoints/history.py @@ -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) # 发送事件 diff --git a/app/api/endpoints/media.py b/app/api/endpoints/media.py index 511e098e..087427e3 100644 --- a/app/api/endpoints/media.py +++ b/app/api/endpoints/media.py @@ -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} 刮削完成") diff --git a/app/api/endpoints/mediaserver.py b/app/api/endpoints/mediaserver.py index d6051bfd..b1ea2670 100644 --- a/app/api/endpoints/mediaserver.py +++ b/app/api/endpoints/mediaserver.py @@ -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 [] diff --git a/app/api/endpoints/plugin.py b/app/api/endpoints/plugin.py index 5af5195c..8ef66fbe 100644 --- a/app/api/endpoints/plugin.py +++ b/app/api/endpoints/plugin.py @@ -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="获取插件仪表板配置") diff --git a/app/api/endpoints/transfer.py b/app/api/endpoints/transfer.py index b20b169d..f11b1bf0 100644 --- a/app/api/endpoints/transfer.py +++ b/app/api/endpoints/transfer.py @@ -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 diff --git a/app/chain/download.py b/app/chain/download.py index 47e2b09b..a404c743 100644 --- a/app/chain/download.py +++ b/app/chain/download.py @@ -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: diff --git a/app/chain/tmdb.py b/app/chain/tmdb.py index 7b9aaddc..13c05b1e 100644 --- a/app/chain/tmdb.py +++ b/app/chain/tmdb.py @@ -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 diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 353ba3bc..1a2131b7 100644 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -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, diff --git a/app/core/plugin.py b/app/core/plugin.py index abcf5ec0..16b5a4be 100644 --- a/app/core/plugin.py +++ b/app/core/plugin.py @@ -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 diff --git a/app/helper/directory.py b/app/helper/directory.py index fae79237..d1ab40ab 100644 --- a/app/helper/directory.py +++ b/app/helper/directory.py @@ -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] diff --git a/app/modules/filemanager/__init__.py b/app/modules/filemanager/__init__.py index 51f36dc8..4a1cc2b9 100644 --- a/app/modules/filemanager/__init__.py +++ b/app/modules/filemanager/__init__.py @@ -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 diff --git a/app/schemas/filetransfer.py b/app/schemas/filetransfer.py deleted file mode 100644 index 415bc7de..00000000 --- a/app/schemas/filetransfer.py +++ /dev/null @@ -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 diff --git a/app/schemas/system.py b/app/schemas/system.py index b0a963fa..c8678188 100644 --- a/app/schemas/system.py +++ b/app/schemas/system.py @@ -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 diff --git a/app/schemas/types.py b/app/schemas/types.py index 19b67af4..0d727cfb 100644 --- a/app/schemas/types.py +++ b/app/schemas/types.py @@ -64,10 +64,8 @@ class SystemConfigKey(Enum): MediaServers = "MediaServers" # 消息通知配置 Notifications = "Notifications" - # 下载目录配置 - DownloadDirectories = "DownloadDirectories" - # 媒体库目录配置 - LibraryDirectories = "LibraryDirectories" + # 目录配置 + Directories = "Directories" # 阿里云盘认证参数 UserAliyunParams = "UserAliyunParams" # 115网盘认证参数