mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
fix #3178 手动整理时支持选择一二级分类
This commit is contained in:
@@ -35,6 +35,8 @@ class ManualTransferItem(BaseModel):
|
|||||||
episode_offset: Optional[str] = None,
|
episode_offset: Optional[str] = None,
|
||||||
min_filesize: Optional[int] = 0,
|
min_filesize: Optional[int] = 0,
|
||||||
scrape: bool = False,
|
scrape: bool = False,
|
||||||
|
library_type_folder: bool = False,
|
||||||
|
library_category_folder: bool = False,
|
||||||
from_history: bool = False
|
from_history: bool = False
|
||||||
|
|
||||||
|
|
||||||
@@ -148,6 +150,8 @@ def manual_transfer(transer_item: ManualTransferItem,
|
|||||||
epformat=epformat,
|
epformat=epformat,
|
||||||
min_filesize=transer_item.min_filesize,
|
min_filesize=transer_item.min_filesize,
|
||||||
scrape=transer_item.scrape,
|
scrape=transer_item.scrape,
|
||||||
|
library_type_folder=transer_item.library_type_folder,
|
||||||
|
library_category_folder=transer_item.library_category_folder,
|
||||||
force=force
|
force=force
|
||||||
)
|
)
|
||||||
# 失败
|
# 失败
|
||||||
|
|||||||
@@ -385,6 +385,7 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
target_directory: TransferDirectoryConf = None,
|
target_directory: TransferDirectoryConf = None,
|
||||||
target_storage: str = None, target_path: Path = None,
|
target_storage: str = None, target_path: Path = None,
|
||||||
transfer_type: str = None, scrape: bool = None,
|
transfer_type: str = None, scrape: bool = None,
|
||||||
|
library_type_folder: bool = False, library_category_folder: bool = False,
|
||||||
episodes_info: List[TmdbEpisode] = None) -> Optional[TransferInfo]:
|
episodes_info: List[TmdbEpisode] = None) -> Optional[TransferInfo]:
|
||||||
"""
|
"""
|
||||||
文件转移
|
文件转移
|
||||||
@@ -396,6 +397,8 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
:param target_path: 目标路径
|
:param target_path: 目标路径
|
||||||
:param transfer_type: 转移模式
|
:param transfer_type: 转移模式
|
||||||
:param scrape: 是否刮削元数据
|
:param scrape: 是否刮削元数据
|
||||||
|
:param library_type_folder: 是否按类型创建目录
|
||||||
|
:param library_category_folder: 是否按类别创建目录
|
||||||
:param episodes_info: 当前季的全部集信息
|
:param episodes_info: 当前季的全部集信息
|
||||||
:return: {path, target_path, message}
|
:return: {path, target_path, message}
|
||||||
"""
|
"""
|
||||||
@@ -404,6 +407,8 @@ class ChainBase(metaclass=ABCMeta):
|
|||||||
target_directory=target_directory,
|
target_directory=target_directory,
|
||||||
target_path=target_path, target_storage=target_storage,
|
target_path=target_path, target_storage=target_storage,
|
||||||
transfer_type=transfer_type, scrape=scrape,
|
transfer_type=transfer_type, scrape=scrape,
|
||||||
|
library_type_folder=library_type_folder,
|
||||||
|
library_category_folder=library_category_folder,
|
||||||
episodes_info=episodes_info)
|
episodes_info=episodes_info)
|
||||||
|
|
||||||
def transfer_completed(self, hashs: str, downloader: str = None) -> None:
|
def transfer_completed(self, hashs: str, downloader: str = None) -> None:
|
||||||
|
|||||||
+12
-1
@@ -148,6 +148,7 @@ class TransferChain(ChainBase):
|
|||||||
target_directory: TransferDirectoryConf = None,
|
target_directory: TransferDirectoryConf = None,
|
||||||
target_storage: str = None, target_path: Path = None,
|
target_storage: str = None, target_path: Path = None,
|
||||||
transfer_type: str = None, scrape: bool = None,
|
transfer_type: str = None, scrape: bool = None,
|
||||||
|
library_type_folder: bool = False, library_category_folder: bool = False,
|
||||||
season: int = None, epformat: EpisodeFormat = None,
|
season: int = None, epformat: EpisodeFormat = None,
|
||||||
min_filesize: int = 0, download_hash: str = None,
|
min_filesize: int = 0, download_hash: str = None,
|
||||||
force: bool = False, src_match: bool = False) -> Tuple[bool, str]:
|
force: bool = False, src_match: bool = False) -> Tuple[bool, str]:
|
||||||
@@ -161,6 +162,8 @@ class TransferChain(ChainBase):
|
|||||||
:param target_path: 目标路径
|
:param target_path: 目标路径
|
||||||
:param transfer_type: 整理类型
|
:param transfer_type: 整理类型
|
||||||
:param scrape: 是否刮削元数据
|
:param scrape: 是否刮削元数据
|
||||||
|
:param library_type_folder: 媒体库类型子目录
|
||||||
|
:param library_category_folder: 媒体库类别子目录
|
||||||
:param season: 季
|
:param season: 季
|
||||||
:param epformat: 剧集格式
|
:param epformat: 剧集格式
|
||||||
:param min_filesize: 最小文件大小(MB)
|
:param min_filesize: 最小文件大小(MB)
|
||||||
@@ -409,7 +412,9 @@ class TransferChain(ChainBase):
|
|||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
transfer_type=transfer_type,
|
transfer_type=transfer_type,
|
||||||
episodes_info=episodes_info,
|
episodes_info=episodes_info,
|
||||||
scrape=scrape)
|
scrape=scrape,
|
||||||
|
library_type_folder=library_type_folder,
|
||||||
|
library_category_folder=library_category_folder)
|
||||||
if not transferinfo:
|
if not transferinfo:
|
||||||
logger.error("文件整理模块运行失败")
|
logger.error("文件整理模块运行失败")
|
||||||
return False, "文件整理模块运行失败"
|
return False, "文件整理模块运行失败"
|
||||||
@@ -680,6 +685,8 @@ class TransferChain(ChainBase):
|
|||||||
epformat: EpisodeFormat = None,
|
epformat: EpisodeFormat = None,
|
||||||
min_filesize: int = 0,
|
min_filesize: int = 0,
|
||||||
scrape: bool = None,
|
scrape: bool = None,
|
||||||
|
library_type_folder: bool = False,
|
||||||
|
library_category_folder: bool = False,
|
||||||
force: bool = False) -> Tuple[bool, Union[str, list]]:
|
force: bool = False) -> Tuple[bool, Union[str, list]]:
|
||||||
"""
|
"""
|
||||||
手动整理,支持复杂条件,带进度显示
|
手动整理,支持复杂条件,带进度显示
|
||||||
@@ -694,6 +701,8 @@ class TransferChain(ChainBase):
|
|||||||
:param epformat: 剧集格式
|
:param epformat: 剧集格式
|
||||||
:param min_filesize: 最小文件大小(MB)
|
:param min_filesize: 最小文件大小(MB)
|
||||||
:param scrape: 是否刮削元数据
|
:param scrape: 是否刮削元数据
|
||||||
|
:param library_type_folder: 是否按类型建立目录
|
||||||
|
:param library_category_folder: 是否按类别建立目录
|
||||||
:param force: 是否强制整理
|
:param force: 是否强制整理
|
||||||
"""
|
"""
|
||||||
logger.info(f"手动整理:{fileitem.path} ...")
|
logger.info(f"手动整理:{fileitem.path} ...")
|
||||||
@@ -722,6 +731,8 @@ class TransferChain(ChainBase):
|
|||||||
epformat=epformat,
|
epformat=epformat,
|
||||||
min_filesize=min_filesize,
|
min_filesize=min_filesize,
|
||||||
scrape=scrape,
|
scrape=scrape,
|
||||||
|
library_type_folder=library_type_folder,
|
||||||
|
library_category_folder=library_category_folder,
|
||||||
force=force,
|
force=force,
|
||||||
)
|
)
|
||||||
if not state:
|
if not state:
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ class FileManagerModule(_ModuleBase):
|
|||||||
target_directory: TransferDirectoryConf = None,
|
target_directory: TransferDirectoryConf = None,
|
||||||
target_storage: str = None, target_path: Path = None,
|
target_storage: str = None, target_path: Path = None,
|
||||||
transfer_type: str = None, scrape: bool = None,
|
transfer_type: str = None, scrape: bool = None,
|
||||||
|
library_type_folder: bool = False, library_category_folder: bool = False,
|
||||||
episodes_info: List[TmdbEpisode] = None) -> TransferInfo:
|
episodes_info: List[TmdbEpisode] = None) -> TransferInfo:
|
||||||
"""
|
"""
|
||||||
文件整理
|
文件整理
|
||||||
@@ -333,6 +334,8 @@ class FileManagerModule(_ModuleBase):
|
|||||||
:param target_path: 目标路径
|
:param target_path: 目标路径
|
||||||
:param transfer_type: 转移模式
|
:param transfer_type: 转移模式
|
||||||
:param scrape: 是否刮削元数据
|
:param scrape: 是否刮削元数据
|
||||||
|
:param library_type_folder: 是否按媒体类型创建目录
|
||||||
|
:param library_category_folder: 是否按媒体类别创建目录
|
||||||
:param episodes_info: 当前季的全部集信息
|
:param episodes_info: 当前季的全部集信息
|
||||||
:return: {path, target_path, message}
|
:return: {path, target_path, message}
|
||||||
"""
|
"""
|
||||||
@@ -350,7 +353,9 @@ class FileManagerModule(_ModuleBase):
|
|||||||
# 获取目标路径
|
# 获取目标路径
|
||||||
if target_directory:
|
if target_directory:
|
||||||
# 拼装媒体库一、二级子目录
|
# 拼装媒体库一、二级子目录
|
||||||
target_path = self.__get_dest_dir(mediainfo=mediainfo, target_dir=target_directory)
|
target_path = self.__get_dest_dir(mediainfo=mediainfo, target_dir=target_directory,
|
||||||
|
need_type_folder=library_type_folder,
|
||||||
|
need_category_folder=library_category_folder)
|
||||||
# 目标存储类型
|
# 目标存储类型
|
||||||
if not target_storage:
|
if not target_storage:
|
||||||
target_storage = target_directory.library_storage
|
target_storage = target_directory.library_storage
|
||||||
@@ -362,24 +367,27 @@ class FileManagerModule(_ModuleBase):
|
|||||||
return TransferInfo(success=False,
|
return TransferInfo(success=False,
|
||||||
fileitem=fileitem,
|
fileitem=fileitem,
|
||||||
message=f"{target_directory.name} 未设置整理方式")
|
message=f"{target_directory.name} 未设置整理方式")
|
||||||
# 是否需要刮削
|
|
||||||
if scrape is None:
|
|
||||||
need_scrape = target_directory.scraping
|
|
||||||
else:
|
|
||||||
need_scrape = scrape
|
|
||||||
# 是否需要重命名
|
# 是否需要重命名
|
||||||
need_rename = target_directory.renaming
|
need_rename = target_directory.renaming
|
||||||
# 是否需要通知
|
# 是否需要通知
|
||||||
need_notify = target_directory.notify
|
need_notify = target_directory.notify
|
||||||
# 覆盖模式
|
# 覆盖模式
|
||||||
overwrite_mode = target_directory.overwrite_mode
|
overwrite_mode = target_directory.overwrite_mode
|
||||||
|
# 是否需要刮削
|
||||||
|
if scrape is None:
|
||||||
|
need_scrape = target_directory.scraping
|
||||||
|
else:
|
||||||
|
need_scrape = scrape
|
||||||
elif target_path:
|
elif target_path:
|
||||||
# 手动整理的场景,有自定义目标路径
|
# 手动整理的场景,有自定义目标路径
|
||||||
|
target_path = self.__get_dest_path(mediainfo=mediainfo, target_path=target_path,
|
||||||
|
need_type_folder=library_type_folder,
|
||||||
|
need_category_folder=library_category_folder)
|
||||||
need_scrape = scrape or False
|
need_scrape = scrape or False
|
||||||
need_rename = True
|
need_rename = True
|
||||||
need_notify = False
|
need_notify = False
|
||||||
overwrite_mode = "never"
|
overwrite_mode = "never"
|
||||||
logger.warn(f"{target_path} 为自定义路径, 通知将不会发送")
|
logger.info(f"{target_path} 为自定义路径, 通知将不会发送")
|
||||||
else:
|
else:
|
||||||
# 未找到有效的媒体库目录
|
# 未找到有效的媒体库目录
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -827,26 +835,43 @@ class FileManagerModule(_ModuleBase):
|
|||||||
return None, errmsg
|
return None, errmsg
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_dest_dir(mediainfo: MediaInfo, target_dir: TransferDirectoryConf) -> Path:
|
def __get_dest_path(mediainfo: MediaInfo, target_path: Path,
|
||||||
|
need_type_folder: bool = False, need_category_folder: bool = False):
|
||||||
|
"""
|
||||||
|
获取目标路径
|
||||||
|
"""
|
||||||
|
if need_type_folder:
|
||||||
|
target_path = target_path / mediainfo.type.value
|
||||||
|
if need_category_folder and mediainfo.category:
|
||||||
|
target_path = target_path / mediainfo.category
|
||||||
|
return target_path
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def __get_dest_dir(mediainfo: MediaInfo, target_dir: TransferDirectoryConf,
|
||||||
|
need_type_folder: bool = None, need_category_folder: bool = None) -> Path:
|
||||||
"""
|
"""
|
||||||
根据设置并装媒体库目录
|
根据设置并装媒体库目录
|
||||||
:param mediainfo: 媒体信息
|
:param mediainfo: 媒体信息
|
||||||
:target_dir: 媒体库根目录
|
:target_dir: 媒体库根目录
|
||||||
:typename_dir: 是否加上类型目录
|
:need_type_folder: 是否需要按媒体类型创建目录
|
||||||
|
:need_category_folder: 是否需要按媒体类别创建目录
|
||||||
"""
|
"""
|
||||||
if not target_dir.media_type and target_dir.library_type_folder:
|
if need_type_folder is None:
|
||||||
|
need_type_folder = target_dir.library_type_folder
|
||||||
|
if need_category_folder is None:
|
||||||
|
need_category_folder = target_dir.library_category_folder
|
||||||
|
if not target_dir.media_type and need_type_folder:
|
||||||
# 一级自动分类
|
# 一级自动分类
|
||||||
library_dir = Path(target_dir.library_path) / mediainfo.type.value
|
library_dir = Path(target_dir.library_path) / mediainfo.type.value
|
||||||
elif target_dir.media_type and target_dir.library_type_folder:
|
elif target_dir.media_type and need_type_folder:
|
||||||
# 一级手动分类
|
# 一级手动分类
|
||||||
library_dir = Path(target_dir.library_path) / target_dir.media_type
|
library_dir = Path(target_dir.library_path) / target_dir.media_type
|
||||||
else:
|
else:
|
||||||
library_dir = Path(target_dir.library_path)
|
library_dir = Path(target_dir.library_path)
|
||||||
|
if not target_dir.media_category and need_category_folder and mediainfo.category:
|
||||||
if not target_dir.media_category and target_dir.library_category_folder and mediainfo.category:
|
|
||||||
# 二级自动分类
|
# 二级自动分类
|
||||||
library_dir = library_dir / mediainfo.category
|
library_dir = library_dir / mediainfo.category
|
||||||
elif target_dir.media_category and target_dir.library_category_folder:
|
elif target_dir.media_category and need_category_folder:
|
||||||
# 二级手动分类
|
# 二级手动分类
|
||||||
library_dir = library_dir / target_dir.media_category
|
library_dir = library_dir / target_dir.media_category
|
||||||
|
|
||||||
@@ -1212,17 +1237,19 @@ class FileManagerModule(_ModuleBase):
|
|||||||
# 重命名格式
|
# 重命名格式
|
||||||
rename_format = settings.TV_RENAME_FORMAT \
|
rename_format = settings.TV_RENAME_FORMAT \
|
||||||
if mediainfo.type == MediaType.TV else settings.MOVIE_RENAME_FORMAT
|
if mediainfo.type == MediaType.TV else settings.MOVIE_RENAME_FORMAT
|
||||||
# 获取相对路径(重命名路径)
|
# 计算重命名中的文件夹层数
|
||||||
rel_path = self.get_rename_path(
|
rename_format_level = len(rename_format.split("/")) - 1
|
||||||
|
if rename_format_level < 1:
|
||||||
|
continue
|
||||||
|
# 获取路径(重命名路径)
|
||||||
|
target_path = self.get_rename_path(
|
||||||
|
path=dir_path,
|
||||||
template_string=rename_format,
|
template_string=rename_format,
|
||||||
rename_dict=self.__get_naming_dict(meta=MetaInfo(mediainfo.title),
|
rename_dict=self.__get_naming_dict(meta=MetaInfo(mediainfo.title),
|
||||||
mediainfo=mediainfo)
|
mediainfo=mediainfo)
|
||||||
)
|
)
|
||||||
# 取相对路径的第1层目录
|
# 取相对路径的第1层目录
|
||||||
if rel_path.parts:
|
media_path = target_path.parents[rename_format_level - 1]
|
||||||
media_path = dir_path / rel_path.parts[0]
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
# 检索媒体文件
|
# 检索媒体文件
|
||||||
fileitem = storage_oper.get_item(media_path)
|
fileitem = storage_oper.get_item(media_path)
|
||||||
if not fileitem:
|
if not fileitem:
|
||||||
|
|||||||
Reference in New Issue
Block a user