add remote transfer
This commit is contained in:
parent
e06e00204b
commit
1e1f80b6d9
@ -107,7 +107,7 @@ def delete_aliyun(fileitem: schemas.FileItem,
|
|||||||
"""
|
"""
|
||||||
if not fileitem.fileid:
|
if not fileitem.fileid:
|
||||||
return schemas.Response(success=False)
|
return schemas.Response(success=False)
|
||||||
result = AliyunHelper().delete(fileitem.fileid)
|
result = AliyunHelper().delete(drive_id=fileitem.drive_id, file_id=fileitem.fileid)
|
||||||
if result:
|
if result:
|
||||||
return schemas.Response(success=True)
|
return schemas.Response(success=True)
|
||||||
return schemas.Response(success=False)
|
return schemas.Response(success=False)
|
||||||
|
@ -48,6 +48,9 @@ def query_name(path: str, filetype: str,
|
|||||||
@router.post("/manual", summary="手动转移", response_model=schemas.Response)
|
@router.post("/manual", summary="手动转移", response_model=schemas.Response)
|
||||||
def manual_transfer(storage: str = "local",
|
def manual_transfer(storage: str = "local",
|
||||||
path: str = None,
|
path: str = None,
|
||||||
|
drive_id: str = None,
|
||||||
|
fileid: str = None,
|
||||||
|
filetype: str = None,
|
||||||
logid: int = None,
|
logid: int = None,
|
||||||
target: str = None,
|
target: str = None,
|
||||||
tmdbid: int = None,
|
tmdbid: int = None,
|
||||||
@ -67,6 +70,9 @@ def manual_transfer(storage: str = "local",
|
|||||||
手动转移,文件或历史记录,支持自定义剧集识别格式
|
手动转移,文件或历史记录,支持自定义剧集识别格式
|
||||||
:param storage: 存储类型:local/aliyun/u115
|
:param storage: 存储类型:local/aliyun/u115
|
||||||
:param path: 转移路径或文件
|
:param path: 转移路径或文件
|
||||||
|
:param drive_id: 云盘ID(网盘等)
|
||||||
|
:param fileid: 文件ID(网盘等)
|
||||||
|
:param filetype: 文件类型,dir/file
|
||||||
:param logid: 转移历史记录ID
|
:param logid: 转移历史记录ID
|
||||||
:param target: 目标路径
|
:param target: 目标路径
|
||||||
:param type_name: 媒体类型、电影/电视剧
|
:param type_name: 媒体类型、电影/电视剧
|
||||||
@ -123,6 +129,9 @@ def manual_transfer(storage: str = "local",
|
|||||||
state, errormsg = transfer.manual_transfer(
|
state, errormsg = transfer.manual_transfer(
|
||||||
storage=storage,
|
storage=storage,
|
||||||
in_path=in_path,
|
in_path=in_path,
|
||||||
|
drive_id=drive_id,
|
||||||
|
fileid=fileid,
|
||||||
|
filetype=filetype,
|
||||||
target=target,
|
target=target,
|
||||||
tmdbid=tmdbid,
|
tmdbid=tmdbid,
|
||||||
doubanid=doubanid,
|
doubanid=doubanid,
|
||||||
|
@ -399,6 +399,8 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
if fileitem.type == "file" \
|
if fileitem.type == "file" \
|
||||||
and (not filepath.suffix or filepath.suffix.lower() not in settings.RMT_MEDIAEXT):
|
and (not filepath.suffix or filepath.suffix.lower() not in settings.RMT_MEDIAEXT):
|
||||||
return
|
return
|
||||||
|
if not mediainfo:
|
||||||
|
return
|
||||||
logger.info(f"开始刮削:{filepath} ...")
|
logger.info(f"开始刮削:{filepath} ...")
|
||||||
if mediainfo.type == MediaType.MOVIE:
|
if mediainfo.type == MediaType.MOVIE:
|
||||||
# 电影
|
# 电影
|
||||||
|
@ -4,21 +4,24 @@ import threading
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional, Tuple, Union, Dict
|
from typing import List, Optional, Tuple, Union, Dict
|
||||||
|
|
||||||
|
from app import schemas
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.media import MediaChain
|
from app.chain.media import MediaChain
|
||||||
from app.chain.tmdb import TmdbChain
|
from app.chain.tmdb import TmdbChain
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.core.context import MediaInfo
|
from app.core.context import MediaInfo
|
||||||
from app.core.meta import MetaBase
|
from app.core.meta import MetaBase
|
||||||
from app.core.metainfo import MetaInfoPath
|
from app.core.metainfo import MetaInfoPath, MetaInfo
|
||||||
from app.db.downloadhistory_oper import DownloadHistoryOper
|
from app.db.downloadhistory_oper import DownloadHistoryOper
|
||||||
from app.db.models.downloadhistory import DownloadHistory
|
from app.db.models.downloadhistory import DownloadHistory
|
||||||
from app.db.models.transferhistory import TransferHistory
|
from app.db.models.transferhistory import TransferHistory
|
||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.db.transferhistory_oper import TransferHistoryOper
|
from app.db.transferhistory_oper import TransferHistoryOper
|
||||||
|
from app.helper.aliyun import AliyunHelper
|
||||||
from app.helper.directory import DirectoryHelper
|
from app.helper.directory import DirectoryHelper
|
||||||
from app.helper.format import FormatParser
|
from app.helper.format import FormatParser
|
||||||
from app.helper.progress import ProgressHelper
|
from app.helper.progress import ProgressHelper
|
||||||
|
from app.helper.u115 import U115Helper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.schemas import TransferInfo, TransferTorrent, Notification, EpisodeFormat
|
from app.schemas import TransferInfo, TransferTorrent, Notification, EpisodeFormat
|
||||||
from app.schemas.types import TorrentStatus, EventType, MediaType, ProgressKey, NotificationType, MessageChannel, \
|
from app.schemas.types import TorrentStatus, EventType, MediaType, ProgressKey, NotificationType, MessageChannel, \
|
||||||
@ -43,6 +46,7 @@ class TransferChain(ChainBase):
|
|||||||
self.tmdbchain = TmdbChain()
|
self.tmdbchain = TmdbChain()
|
||||||
self.systemconfig = SystemConfigOper()
|
self.systemconfig = SystemConfigOper()
|
||||||
self.directoryhelper = DirectoryHelper()
|
self.directoryhelper = DirectoryHelper()
|
||||||
|
self.all_exts = settings.RMT_MEDIAEXT + settings.RMT_SUBEXT + settings.RMT_AUDIO_TRACK_EXT
|
||||||
|
|
||||||
def recommend_name(self, meta: MetaBase, mediainfo: MediaInfo) -> Optional[str]:
|
def recommend_name(self, meta: MetaBase, mediainfo: MediaInfo) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
@ -96,7 +100,7 @@ class TransferChain(ChainBase):
|
|||||||
logger.info("下载器文件转移执行完成")
|
logger.info("下载器文件转移执行完成")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def do_transfer(self, storage: str, path: Path,
|
def do_transfer(self, storage: str, path: Path, drive_id: str = None, fileid: str = None, filetype: str = None,
|
||||||
meta: MetaBase = None, mediainfo: MediaInfo = None,
|
meta: MetaBase = None, mediainfo: MediaInfo = None,
|
||||||
download_hash: str = None,
|
download_hash: str = None,
|
||||||
target: Path = None, transfer_type: str = None,
|
target: Path = None, transfer_type: str = None,
|
||||||
@ -107,6 +111,9 @@ class TransferChain(ChainBase):
|
|||||||
执行一个复杂目录的转移操作
|
执行一个复杂目录的转移操作
|
||||||
:param storage: 存储器
|
:param storage: 存储器
|
||||||
:param path: 待转移目录或文件
|
:param path: 待转移目录或文件
|
||||||
|
:param drive_id: 网盘ID
|
||||||
|
:param fileid: 文件ID
|
||||||
|
:param filetype: 文件类型
|
||||||
:param meta: 元数据
|
:param meta: 元数据
|
||||||
:param mediainfo: 媒体信息
|
:param mediainfo: 媒体信息
|
||||||
:param download_hash: 下载记录hash
|
:param download_hash: 下载记录hash
|
||||||
@ -134,10 +141,43 @@ class TransferChain(ChainBase):
|
|||||||
# 开始进度
|
# 开始进度
|
||||||
self.progress.start(ProgressKey.FileTransfer)
|
self.progress.start(ProgressKey.FileTransfer)
|
||||||
|
|
||||||
|
# 本地存储
|
||||||
|
if storage == "local":
|
||||||
|
# 本地整理
|
||||||
|
result = self.__transfer_local(path=path, meta=meta, mediainfo=mediainfo,
|
||||||
|
formaterHandler=formaterHandler,
|
||||||
|
transfer_exclude_words=transfer_exclude_words,
|
||||||
|
min_filesize=min_filesize, transfer_type=transfer_type,
|
||||||
|
target=target, season=season, scrape=scrape,
|
||||||
|
download_hash=download_hash, force=force)
|
||||||
|
else:
|
||||||
|
# 网盘整理
|
||||||
|
result = self.__transfer_remote(storage=storage,
|
||||||
|
fileitem=schemas.FileItem(
|
||||||
|
path=str(path) + ("/" if filetype == "dir" else ""),
|
||||||
|
type=filetype,
|
||||||
|
drive_id=drive_id,
|
||||||
|
fileid=fileid,
|
||||||
|
name=path.name
|
||||||
|
),
|
||||||
|
meta=meta,
|
||||||
|
mediainfo=mediainfo)
|
||||||
|
|
||||||
|
# 结速进度
|
||||||
|
self.progress.end(ProgressKey.FileTransfer)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __transfer_local(self, path: Path, meta: MetaBase = None, mediainfo: MediaInfo = None,
|
||||||
|
formaterHandler: FormatParser = None, transfer_exclude_words: List[str] = None,
|
||||||
|
min_filesize: int = 0, transfer_type: str = None, target: Path = None,
|
||||||
|
season: int = None, scrape: bool = None, download_hash: str = None,
|
||||||
|
force: bool = False) -> Tuple[bool, str]:
|
||||||
|
"""
|
||||||
|
整理一个本地目录
|
||||||
|
"""
|
||||||
|
|
||||||
# 汇总错误信息
|
# 汇总错误信息
|
||||||
err_msgs: List[str] = []
|
err_msgs: List[str] = []
|
||||||
# 总文件数
|
|
||||||
total_num = 0
|
|
||||||
# 已处理数量
|
# 已处理数量
|
||||||
processed_num = 0
|
processed_num = 0
|
||||||
# 失败数量
|
# 失败数量
|
||||||
@ -145,236 +185,194 @@ class TransferChain(ChainBase):
|
|||||||
# 跳过数量
|
# 跳过数量
|
||||||
skip_num = 0
|
skip_num = 0
|
||||||
|
|
||||||
# 本地存储
|
# 获取待转移路径清单
|
||||||
if storage == "local":
|
trans_paths = self.__get_trans_paths(path)
|
||||||
# 获取待转移路径清单
|
if not trans_paths:
|
||||||
trans_paths = self.__get_trans_paths(path)
|
logger.warn(f"{path.name} 没有找到可转移的媒体文件")
|
||||||
if not trans_paths:
|
return False, f"{path.name} 没有找到可转移的媒体文件"
|
||||||
logger.warn(f"{path.name} 没有找到可转移的媒体文件")
|
# 目录所有文件清单
|
||||||
return False, f"{path.name} 没有找到可转移的媒体文件"
|
transfer_files = SystemUtils.list_files(directory=path,
|
||||||
# 目录所有文件清单
|
extensions=settings.RMT_MEDIAEXT,
|
||||||
transfer_files = SystemUtils.list_files(directory=path,
|
min_filesize=min_filesize)
|
||||||
|
if formaterHandler:
|
||||||
|
# 有集自定义格式,过滤文件
|
||||||
|
transfer_files = [f for f in transfer_files if formaterHandler.match(f.name)]
|
||||||
|
|
||||||
|
# 总文件数
|
||||||
|
total_num = len(transfer_files)
|
||||||
|
self.progress.update(value=0,
|
||||||
|
text=f"开始转移 {path},共 {total_num} 个文件 ...",
|
||||||
|
key=ProgressKey.FileTransfer)
|
||||||
|
|
||||||
|
# 处理所有待转移目录或文件,默认一个转移路径或文件只有一个媒体信息
|
||||||
|
for trans_path in trans_paths:
|
||||||
|
# 汇总季集清单
|
||||||
|
season_episodes: Dict[Tuple, List[int]] = {}
|
||||||
|
# 汇总元数据
|
||||||
|
metas: Dict[Tuple, MetaBase] = {}
|
||||||
|
# 汇总媒体信息
|
||||||
|
medias: Dict[Tuple, MediaInfo] = {}
|
||||||
|
# 汇总转移信息
|
||||||
|
transfers: Dict[Tuple, TransferInfo] = {}
|
||||||
|
|
||||||
|
# 如果是目录且不是⼀蓝光原盘,获取所有文件并转移
|
||||||
|
if (not trans_path.is_file()
|
||||||
|
and not SystemUtils.is_bluray_dir(trans_path)):
|
||||||
|
# 遍历获取下载目录所有文件
|
||||||
|
file_paths = SystemUtils.list_files(directory=trans_path,
|
||||||
extensions=settings.RMT_MEDIAEXT,
|
extensions=settings.RMT_MEDIAEXT,
|
||||||
min_filesize=min_filesize)
|
min_filesize=min_filesize)
|
||||||
|
else:
|
||||||
|
file_paths = [trans_path]
|
||||||
|
|
||||||
if formaterHandler:
|
if formaterHandler:
|
||||||
# 有集自定义格式,过滤文件
|
# 有集自定义格式,过滤文件
|
||||||
transfer_files = [f for f in transfer_files if formaterHandler.match(f.name)]
|
file_paths = [f for f in file_paths if formaterHandler.match(f.name)]
|
||||||
|
|
||||||
# 总文件数
|
# 转移所有文件
|
||||||
total_num = len(transfer_files)
|
for file_path in file_paths:
|
||||||
self.progress.update(value=0,
|
# 回收站及隐藏的文件不处理
|
||||||
text=f"开始转移 {path},共 {total_num} 个文件 ...",
|
file_path_str = str(file_path)
|
||||||
key=ProgressKey.FileTransfer)
|
if file_path_str.find('/@Recycle/') != -1 \
|
||||||
|
or file_path_str.find('/#recycle/') != -1 \
|
||||||
|
or file_path_str.find('/.') != -1 \
|
||||||
|
or file_path_str.find('/@eaDir') != -1:
|
||||||
|
logger.debug(f"{file_path_str} 是回收站或隐藏的文件")
|
||||||
|
# 计数
|
||||||
|
processed_num += 1
|
||||||
|
skip_num += 1
|
||||||
|
continue
|
||||||
|
|
||||||
# 处理所有待转移目录或文件,默认一个转移路径或文件只有一个媒体信息
|
# 整理屏蔽词不处理
|
||||||
for trans_path in trans_paths:
|
is_blocked = False
|
||||||
# 汇总季集清单
|
if transfer_exclude_words:
|
||||||
season_episodes: Dict[Tuple, List[int]] = {}
|
for keyword in transfer_exclude_words:
|
||||||
# 汇总元数据
|
if not keyword:
|
||||||
metas: Dict[Tuple, MetaBase] = {}
|
|
||||||
# 汇总媒体信息
|
|
||||||
medias: Dict[Tuple, MediaInfo] = {}
|
|
||||||
# 汇总转移信息
|
|
||||||
transfers: Dict[Tuple, TransferInfo] = {}
|
|
||||||
|
|
||||||
# 如果是目录且不是⼀蓝光原盘,获取所有文件并转移
|
|
||||||
if (not trans_path.is_file()
|
|
||||||
and not SystemUtils.is_bluray_dir(trans_path)):
|
|
||||||
# 遍历获取下载目录所有文件
|
|
||||||
file_paths = SystemUtils.list_files(directory=trans_path,
|
|
||||||
extensions=settings.RMT_MEDIAEXT,
|
|
||||||
min_filesize=min_filesize)
|
|
||||||
else:
|
|
||||||
file_paths = [trans_path]
|
|
||||||
|
|
||||||
if formaterHandler:
|
|
||||||
# 有集自定义格式,过滤文件
|
|
||||||
file_paths = [f for f in file_paths if formaterHandler.match(f.name)]
|
|
||||||
|
|
||||||
# 转移所有文件
|
|
||||||
for file_path in file_paths:
|
|
||||||
# 回收站及隐藏的文件不处理
|
|
||||||
file_path_str = str(file_path)
|
|
||||||
if file_path_str.find('/@Recycle/') != -1 \
|
|
||||||
or file_path_str.find('/#recycle/') != -1 \
|
|
||||||
or file_path_str.find('/.') != -1 \
|
|
||||||
or file_path_str.find('/@eaDir') != -1:
|
|
||||||
logger.debug(f"{file_path_str} 是回收站或隐藏的文件")
|
|
||||||
# 计数
|
|
||||||
processed_num += 1
|
|
||||||
skip_num += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 整理屏蔽词不处理
|
|
||||||
is_blocked = False
|
|
||||||
if transfer_exclude_words:
|
|
||||||
for keyword in transfer_exclude_words:
|
|
||||||
if not keyword:
|
|
||||||
continue
|
|
||||||
if keyword and re.search(r"%s" % keyword, file_path_str, re.IGNORECASE):
|
|
||||||
logger.info(f"{file_path} 命中整理屏蔽词 {keyword},不处理")
|
|
||||||
is_blocked = True
|
|
||||||
break
|
|
||||||
if is_blocked:
|
|
||||||
err_msgs.append(f"{file_path.name} 命中整理屏蔽词")
|
|
||||||
# 计数
|
|
||||||
processed_num += 1
|
|
||||||
skip_num += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 转移成功的不再处理
|
|
||||||
if not force:
|
|
||||||
transferd = self.transferhis.get_by_src(file_path_str)
|
|
||||||
if transferd and transferd.status:
|
|
||||||
logger.info(f"{file_path} 已成功转移过,如需重新处理,请删除历史记录。")
|
|
||||||
# 计数
|
|
||||||
processed_num += 1
|
|
||||||
skip_num += 1
|
|
||||||
continue
|
continue
|
||||||
|
if keyword and re.search(r"%s" % keyword, file_path_str, re.IGNORECASE):
|
||||||
|
logger.info(f"{file_path} 命中整理屏蔽词 {keyword},不处理")
|
||||||
|
is_blocked = True
|
||||||
|
break
|
||||||
|
if is_blocked:
|
||||||
|
err_msgs.append(f"{file_path.name} 命中整理屏蔽词")
|
||||||
|
# 计数
|
||||||
|
processed_num += 1
|
||||||
|
skip_num += 1
|
||||||
|
continue
|
||||||
|
|
||||||
# 更新进度
|
# 转移成功的不再处理
|
||||||
self.progress.update(value=processed_num / total_num * 100,
|
if not force:
|
||||||
text=f"正在转移 ({processed_num + 1}/{total_num}){file_path.name} ...",
|
transferd = self.transferhis.get_by_src(file_path_str)
|
||||||
key=ProgressKey.FileTransfer)
|
if transferd and transferd.status:
|
||||||
|
logger.info(f"{file_path} 已成功转移过,如需重新处理,请删除历史记录。")
|
||||||
if not meta:
|
|
||||||
# 文件元数据
|
|
||||||
file_meta = MetaInfoPath(file_path)
|
|
||||||
else:
|
|
||||||
file_meta = meta
|
|
||||||
|
|
||||||
# 合并季
|
|
||||||
if season is not None:
|
|
||||||
file_meta.begin_season = season
|
|
||||||
|
|
||||||
if not file_meta:
|
|
||||||
logger.error(f"{file_path} 无法识别有效信息")
|
|
||||||
err_msgs.append(f"{file_path} 无法识别有效信息")
|
|
||||||
# 计数
|
# 计数
|
||||||
processed_num += 1
|
processed_num += 1
|
||||||
fail_num += 1
|
skip_num += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 自定义识别
|
# 更新进度
|
||||||
if formaterHandler:
|
self.progress.update(value=processed_num / total_num * 100,
|
||||||
# 开始集、结束集、PART
|
text=f"正在转移 ({processed_num + 1}/{total_num}){file_path.name} ...",
|
||||||
begin_ep, end_ep, part = formaterHandler.split_episode(file_path.name)
|
key=ProgressKey.FileTransfer)
|
||||||
if begin_ep is not None:
|
|
||||||
file_meta.begin_episode = begin_ep
|
|
||||||
file_meta.part = part
|
|
||||||
if end_ep is not None:
|
|
||||||
file_meta.end_episode = end_ep
|
|
||||||
|
|
||||||
if not mediainfo:
|
if not meta:
|
||||||
# 识别媒体信息
|
# 文件元数据
|
||||||
file_mediainfo = self.mediachain.recognize_by_meta(file_meta)
|
file_meta = MetaInfoPath(file_path)
|
||||||
else:
|
else:
|
||||||
file_mediainfo = mediainfo
|
file_meta = meta
|
||||||
|
|
||||||
if not file_mediainfo:
|
# 合并季
|
||||||
logger.warn(f'{file_path} 未识别到媒体信息')
|
if season is not None:
|
||||||
# 新增转移失败历史记录
|
file_meta.begin_season = season
|
||||||
his = self.transferhis.add_fail(
|
|
||||||
src_path=file_path,
|
|
||||||
mode=transfer_type,
|
|
||||||
meta=file_meta,
|
|
||||||
download_hash=download_hash
|
|
||||||
)
|
|
||||||
self.post_message(Notification(
|
|
||||||
mtype=NotificationType.Manual,
|
|
||||||
title=f"{file_path.name} 未识别到媒体信息,无法入库!",
|
|
||||||
text=f"回复:```\n/redo {his.id} [tmdbid]|[类型]\n``` 手动识别转移。",
|
|
||||||
link=settings.MP_DOMAIN('#/history')
|
|
||||||
))
|
|
||||||
# 计数
|
|
||||||
processed_num += 1
|
|
||||||
fail_num += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 如果未开启新增已入库媒体是否跟随TMDB信息变化则根据tmdbid查询之前的title
|
if not file_meta:
|
||||||
if not settings.SCRAP_FOLLOW_TMDB:
|
logger.error(f"{file_path} 无法识别有效信息")
|
||||||
transfer_history = self.transferhis.get_by_type_tmdbid(tmdbid=file_mediainfo.tmdb_id,
|
err_msgs.append(f"{file_path} 无法识别有效信息")
|
||||||
mtype=file_mediainfo.type.value)
|
# 计数
|
||||||
if transfer_history:
|
processed_num += 1
|
||||||
file_mediainfo.title = transfer_history.title
|
fail_num += 1
|
||||||
|
continue
|
||||||
|
|
||||||
logger.info(f"{file_path.name} 识别为:{file_mediainfo.type.value} {file_mediainfo.title_year}")
|
# 自定义识别
|
||||||
|
if formaterHandler:
|
||||||
|
# 开始集、结束集、PART
|
||||||
|
begin_ep, end_ep, part = formaterHandler.split_episode(file_path.name)
|
||||||
|
if begin_ep is not None:
|
||||||
|
file_meta.begin_episode = begin_ep
|
||||||
|
file_meta.part = part
|
||||||
|
if end_ep is not None:
|
||||||
|
file_meta.end_episode = end_ep
|
||||||
|
|
||||||
# 获取集数据
|
if not mediainfo:
|
||||||
if file_mediainfo.type == MediaType.TV:
|
# 识别媒体信息
|
||||||
if file_meta.begin_season is None:
|
file_mediainfo = self.mediachain.recognize_by_meta(file_meta)
|
||||||
file_meta.begin_season = 1
|
else:
|
||||||
file_mediainfo.season = file_mediainfo.season or file_meta.begin_season
|
file_mediainfo = mediainfo
|
||||||
episodes_info = self.tmdbchain.tmdb_episodes(
|
|
||||||
tmdbid=file_mediainfo.tmdb_id,
|
|
||||||
season=file_mediainfo.season
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
episodes_info = None
|
|
||||||
|
|
||||||
# 获取下载hash
|
if not file_mediainfo:
|
||||||
if not download_hash:
|
logger.warn(f'{file_path} 未识别到媒体信息')
|
||||||
download_file = self.downloadhis.get_file_by_fullpath(file_path_str)
|
# 新增转移失败历史记录
|
||||||
if download_file:
|
his = self.transferhis.add_fail(
|
||||||
download_hash = download_file.download_hash
|
src_path=file_path,
|
||||||
|
mode=transfer_type,
|
||||||
|
meta=file_meta,
|
||||||
|
download_hash=download_hash
|
||||||
|
)
|
||||||
|
self.post_message(Notification(
|
||||||
|
mtype=NotificationType.Manual,
|
||||||
|
title=f"{file_path.name} 未识别到媒体信息,无法入库!",
|
||||||
|
text=f"回复:```\n/redo {his.id} [tmdbid]|[类型]\n``` 手动识别转移。",
|
||||||
|
link=settings.MP_DOMAIN('#/history')
|
||||||
|
))
|
||||||
|
# 计数
|
||||||
|
processed_num += 1
|
||||||
|
fail_num += 1
|
||||||
|
continue
|
||||||
|
|
||||||
# 执行转移
|
# 如果未开启新增已入库媒体是否跟随TMDB信息变化则根据tmdbid查询之前的title
|
||||||
transferinfo: TransferInfo = self.transfer(meta=file_meta,
|
if not settings.SCRAP_FOLLOW_TMDB:
|
||||||
mediainfo=file_mediainfo,
|
transfer_history = self.transferhis.get_by_type_tmdbid(tmdbid=file_mediainfo.tmdb_id,
|
||||||
path=file_path,
|
mtype=file_mediainfo.type.value)
|
||||||
transfer_type=transfer_type,
|
if transfer_history:
|
||||||
target=target,
|
file_mediainfo.title = transfer_history.title
|
||||||
episodes_info=episodes_info,
|
|
||||||
scrape=scrape)
|
|
||||||
if not transferinfo:
|
|
||||||
logger.error("文件转移模块运行失败")
|
|
||||||
return False, "文件转移模块运行失败"
|
|
||||||
if not transferinfo.success:
|
|
||||||
# 转移失败
|
|
||||||
logger.warn(f"{file_path.name} 入库失败:{transferinfo.message}")
|
|
||||||
err_msgs.append(f"{file_path.name} {transferinfo.message}")
|
|
||||||
# 新增转移失败历史记录
|
|
||||||
self.transferhis.add_fail(
|
|
||||||
src_path=file_path,
|
|
||||||
mode=transfer_type,
|
|
||||||
download_hash=download_hash,
|
|
||||||
meta=file_meta,
|
|
||||||
mediainfo=file_mediainfo,
|
|
||||||
transferinfo=transferinfo
|
|
||||||
)
|
|
||||||
# 发送消息
|
|
||||||
self.post_message(Notification(
|
|
||||||
mtype=NotificationType.Manual,
|
|
||||||
title=f"{file_mediainfo.title_year} {file_meta.season_episode} 入库失败!",
|
|
||||||
text=f"原因:{transferinfo.message or '未知'}",
|
|
||||||
image=file_mediainfo.get_message_image(),
|
|
||||||
link=settings.MP_DOMAIN('#/history')
|
|
||||||
))
|
|
||||||
# 计数
|
|
||||||
processed_num += 1
|
|
||||||
fail_num += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 汇总信息
|
logger.info(f"{file_path.name} 识别为:{file_mediainfo.type.value} {file_mediainfo.title_year}")
|
||||||
mkey = (file_mediainfo.tmdb_id, file_meta.begin_season)
|
|
||||||
if mkey not in medias:
|
|
||||||
# 新增信息
|
|
||||||
metas[mkey] = file_meta
|
|
||||||
medias[mkey] = file_mediainfo
|
|
||||||
season_episodes[mkey] = file_meta.episode_list
|
|
||||||
transfers[mkey] = transferinfo
|
|
||||||
else:
|
|
||||||
# 合并季集清单
|
|
||||||
season_episodes[mkey] = list(set(season_episodes[mkey] + file_meta.episode_list))
|
|
||||||
# 合并转移数据
|
|
||||||
transfers[mkey].file_count += transferinfo.file_count
|
|
||||||
transfers[mkey].total_size += transferinfo.total_size
|
|
||||||
transfers[mkey].file_list.extend(transferinfo.file_list)
|
|
||||||
transfers[mkey].file_list_new.extend(transferinfo.file_list_new)
|
|
||||||
transfers[mkey].fail_list.extend(transferinfo.fail_list)
|
|
||||||
|
|
||||||
# 新增转移成功历史记录
|
# 获取集数据
|
||||||
self.transferhis.add_success(
|
if file_mediainfo.type == MediaType.TV:
|
||||||
|
if file_meta.begin_season is None:
|
||||||
|
file_meta.begin_season = 1
|
||||||
|
file_mediainfo.season = file_mediainfo.season or file_meta.begin_season
|
||||||
|
episodes_info = self.tmdbchain.tmdb_episodes(
|
||||||
|
tmdbid=file_mediainfo.tmdb_id,
|
||||||
|
season=file_mediainfo.season
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
episodes_info = None
|
||||||
|
|
||||||
|
# 获取下载hash
|
||||||
|
if not download_hash:
|
||||||
|
download_file = self.downloadhis.get_file_by_fullpath(file_path_str)
|
||||||
|
if download_file:
|
||||||
|
download_hash = download_file.download_hash
|
||||||
|
|
||||||
|
# 执行转移
|
||||||
|
transferinfo: TransferInfo = self.transfer(meta=file_meta,
|
||||||
|
mediainfo=file_mediainfo,
|
||||||
|
path=file_path,
|
||||||
|
transfer_type=transfer_type,
|
||||||
|
target=target,
|
||||||
|
episodes_info=episodes_info,
|
||||||
|
scrape=scrape)
|
||||||
|
if not transferinfo:
|
||||||
|
logger.error("文件转移模块运行失败")
|
||||||
|
return False, "文件转移模块运行失败"
|
||||||
|
if not transferinfo.success:
|
||||||
|
# 转移失败
|
||||||
|
logger.warn(f"{file_path.name} 入库失败:{transferinfo.message}")
|
||||||
|
err_msgs.append(f"{file_path.name} {transferinfo.message}")
|
||||||
|
# 新增转移失败历史记录
|
||||||
|
self.transferhis.add_fail(
|
||||||
src_path=file_path,
|
src_path=file_path,
|
||||||
mode=transfer_type,
|
mode=transfer_type,
|
||||||
download_hash=download_hash,
|
download_hash=download_hash,
|
||||||
@ -382,47 +380,83 @@ class TransferChain(ChainBase):
|
|||||||
mediainfo=file_mediainfo,
|
mediainfo=file_mediainfo,
|
||||||
transferinfo=transferinfo
|
transferinfo=transferinfo
|
||||||
)
|
)
|
||||||
# 刮削单个文件
|
# 发送消息
|
||||||
if transferinfo.need_scrape:
|
self.post_message(Notification(
|
||||||
self.scrape_metadata(path=transferinfo.target_path,
|
mtype=NotificationType.Manual,
|
||||||
mediainfo=file_mediainfo,
|
title=f"{file_mediainfo.title_year} {file_meta.season_episode} 入库失败!",
|
||||||
transfer_type=transfer_type,
|
text=f"原因:{transferinfo.message or '未知'}",
|
||||||
metainfo=file_meta)
|
image=file_mediainfo.get_message_image(),
|
||||||
# 更新进度
|
link=settings.MP_DOMAIN('#/history')
|
||||||
|
))
|
||||||
|
# 计数
|
||||||
processed_num += 1
|
processed_num += 1
|
||||||
self.progress.update(value=processed_num / total_num * 100,
|
fail_num += 1
|
||||||
text=f"{file_path.name} 转移完成",
|
continue
|
||||||
key=ProgressKey.FileTransfer)
|
|
||||||
|
|
||||||
# 目录或文件转移完成
|
# 汇总信息
|
||||||
self.progress.update(text=f"{trans_path} 转移完成,正在执行后续处理 ...",
|
mkey = (file_mediainfo.tmdb_id, file_meta.begin_season)
|
||||||
|
if mkey not in medias:
|
||||||
|
# 新增信息
|
||||||
|
metas[mkey] = file_meta
|
||||||
|
medias[mkey] = file_mediainfo
|
||||||
|
season_episodes[mkey] = file_meta.episode_list
|
||||||
|
transfers[mkey] = transferinfo
|
||||||
|
else:
|
||||||
|
# 合并季集清单
|
||||||
|
season_episodes[mkey] = list(set(season_episodes[mkey] + file_meta.episode_list))
|
||||||
|
# 合并转移数据
|
||||||
|
transfers[mkey].file_count += transferinfo.file_count
|
||||||
|
transfers[mkey].total_size += transferinfo.total_size
|
||||||
|
transfers[mkey].file_list.extend(transferinfo.file_list)
|
||||||
|
transfers[mkey].file_list_new.extend(transferinfo.file_list_new)
|
||||||
|
transfers[mkey].fail_list.extend(transferinfo.fail_list)
|
||||||
|
|
||||||
|
# 新增转移成功历史记录
|
||||||
|
self.transferhis.add_success(
|
||||||
|
src_path=file_path,
|
||||||
|
mode=transfer_type,
|
||||||
|
download_hash=download_hash,
|
||||||
|
meta=file_meta,
|
||||||
|
mediainfo=file_mediainfo,
|
||||||
|
transferinfo=transferinfo
|
||||||
|
)
|
||||||
|
# 刮削单个文件
|
||||||
|
if transferinfo.need_scrape:
|
||||||
|
self.scrape_metadata(path=transferinfo.target_path,
|
||||||
|
mediainfo=file_mediainfo,
|
||||||
|
transfer_type=transfer_type,
|
||||||
|
metainfo=file_meta)
|
||||||
|
# 更新进度
|
||||||
|
processed_num += 1
|
||||||
|
self.progress.update(value=processed_num / total_num * 100,
|
||||||
|
text=f"{file_path.name} 转移完成",
|
||||||
key=ProgressKey.FileTransfer)
|
key=ProgressKey.FileTransfer)
|
||||||
|
|
||||||
# 执行后续处理
|
# 目录或文件转移完成
|
||||||
for mkey, media in medias.items():
|
self.progress.update(text=f"{trans_path} 转移完成,正在执行后续处理 ...",
|
||||||
transfer_meta = metas[mkey]
|
key=ProgressKey.FileTransfer)
|
||||||
transfer_info = transfers[mkey]
|
|
||||||
# 媒体目录
|
|
||||||
if transfer_info.target_path.is_file():
|
|
||||||
transfer_info.target_path = transfer_info.target_path.parent
|
|
||||||
# 发送通知
|
|
||||||
se_str = None
|
|
||||||
if media.type == MediaType.TV:
|
|
||||||
se_str = f"{transfer_meta.season} {StringUtils.format_ep(season_episodes[mkey])}"
|
|
||||||
self.send_transfer_message(meta=transfer_meta,
|
|
||||||
mediainfo=media,
|
|
||||||
transferinfo=transfer_info,
|
|
||||||
season_episode=se_str)
|
|
||||||
# 广播事件
|
|
||||||
self.eventmanager.send_event(EventType.TransferComplete, {
|
|
||||||
'meta': transfer_meta,
|
|
||||||
'mediainfo': media,
|
|
||||||
'transferinfo': transfer_info
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
# TODO 网盘整理
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
# 执行后续处理
|
||||||
|
for mkey, media in medias.items():
|
||||||
|
transfer_meta = metas[mkey]
|
||||||
|
transfer_info = transfers[mkey]
|
||||||
|
# 媒体目录
|
||||||
|
if transfer_info.target_path.is_file():
|
||||||
|
transfer_info.target_path = transfer_info.target_path.parent
|
||||||
|
# 发送通知
|
||||||
|
se_str = None
|
||||||
|
if media.type == MediaType.TV:
|
||||||
|
se_str = f"{transfer_meta.season} {StringUtils.format_ep(season_episodes[mkey])}"
|
||||||
|
self.send_transfer_message(meta=transfer_meta,
|
||||||
|
mediainfo=media,
|
||||||
|
transferinfo=transfer_info,
|
||||||
|
season_episode=se_str)
|
||||||
|
# 广播事件
|
||||||
|
self.eventmanager.send_event(EventType.TransferComplete, {
|
||||||
|
'meta': transfer_meta,
|
||||||
|
'mediainfo': media,
|
||||||
|
'transferinfo': transfer_info
|
||||||
|
})
|
||||||
# 结束进度
|
# 结束进度
|
||||||
logger.info(f"{path} 转移完成,共 {total_num} 个文件,"
|
logger.info(f"{path} 转移完成,共 {total_num} 个文件,"
|
||||||
f"失败 {fail_num} 个,跳过 {skip_num} 个")
|
f"失败 {fail_num} 个,跳过 {skip_num} 个")
|
||||||
@ -431,10 +465,215 @@ class TransferChain(ChainBase):
|
|||||||
text=f"{path} 转移完成,共 {total_num} 个文件,"
|
text=f"{path} 转移完成,共 {total_num} 个文件,"
|
||||||
f"失败 {fail_num} 个,跳过 {skip_num} 个",
|
f"失败 {fail_num} 个,跳过 {skip_num} 个",
|
||||||
key=ProgressKey.FileTransfer)
|
key=ProgressKey.FileTransfer)
|
||||||
self.progress.end(ProgressKey.FileTransfer)
|
|
||||||
|
|
||||||
return True, "\n".join(err_msgs)
|
return True, "\n".join(err_msgs)
|
||||||
|
|
||||||
|
def __transfer_remote(self, storage: str, fileitem: schemas.FileItem,
|
||||||
|
meta: MetaBase, mediainfo: MediaInfo) -> Tuple[bool, str]:
|
||||||
|
"""
|
||||||
|
整理一个远程目录
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __list_files(_storage: str, _fileid: str,
|
||||||
|
_path: str = None, _drive_id: str = None) -> List[schemas.FileItem]:
|
||||||
|
"""
|
||||||
|
列出下级文件
|
||||||
|
"""
|
||||||
|
if _storage == "aliyun":
|
||||||
|
return AliyunHelper().list(drive_id=_drive_id, parent_file_id=_fileid, path=_path)
|
||||||
|
elif _storage == "u115":
|
||||||
|
return U115Helper().list(parent_file_id=_fileid, path=_path)
|
||||||
|
return []
|
||||||
|
|
||||||
|
def __rename_file(_storage: str, _fileid: str, _name: str) -> bool:
|
||||||
|
"""
|
||||||
|
重命名文件
|
||||||
|
"""
|
||||||
|
if _storage == "aliyun":
|
||||||
|
return AliyunHelper().rename(file_id=_fileid, name=_name)
|
||||||
|
elif _storage == "u115":
|
||||||
|
return U115Helper().rename(file_id=_fileid, name=_name)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __create_folder(_storage: str, _drive_id: str, _parent_fileid: str,
|
||||||
|
_name: str, _path: str) -> Optional[schemas.FileItem]:
|
||||||
|
"""
|
||||||
|
创建目录
|
||||||
|
"""
|
||||||
|
if _storage == "aliyun":
|
||||||
|
return AliyunHelper().create_folder(drive_id=_drive_id, parent_file_id=_parent_fileid,
|
||||||
|
name=_name, path=_path)
|
||||||
|
elif _storage == "u115":
|
||||||
|
return U115Helper().create_folder(parent_file_id=_parent_fileid, name=_name, path=_path)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __move_file(_storage: str, _drive_id: str, _fileid: str, _target_fileid: str) -> bool:
|
||||||
|
"""
|
||||||
|
移动文件
|
||||||
|
"""
|
||||||
|
if _storage == "aliyun":
|
||||||
|
return AliyunHelper().move(drive_id=_drive_id, file_id=_fileid, target_id=_target_fileid)
|
||||||
|
elif _storage == "u115":
|
||||||
|
return U115Helper().move(file_id=_fileid, target_id=_target_fileid)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __remove_dir(_storage: str, _drive_id: str, _fileid: str) -> bool:
|
||||||
|
"""
|
||||||
|
删除目录
|
||||||
|
"""
|
||||||
|
if _storage == "aliyun":
|
||||||
|
return AliyunHelper().delete(drive_id=_drive_id, file_id=_fileid)
|
||||||
|
elif _storage == "u115":
|
||||||
|
return U115Helper().delete(file_id=_fileid)
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"开始整理 {fileitem.path} ...")
|
||||||
|
self.progress.update(value=0,
|
||||||
|
text=f"正在整理 {fileitem.path} ...",
|
||||||
|
key=ProgressKey.FileTransfer)
|
||||||
|
# 重新识别
|
||||||
|
if not meta:
|
||||||
|
# 文件元数据
|
||||||
|
meta = MetaInfoPath(Path(fileitem.path))
|
||||||
|
if not mediainfo:
|
||||||
|
mediainfo = self.recognize_media(meta=meta)
|
||||||
|
if not mediainfo:
|
||||||
|
logger.warn(f"{fileitem.name} 未识别到媒体信息")
|
||||||
|
return False, f"{fileitem.name} 未识别到媒体信息"
|
||||||
|
# 获取完整的路径命名
|
||||||
|
full_names = self.recommend_name(meta=meta, mediainfo=mediainfo)
|
||||||
|
if not full_names:
|
||||||
|
logger.warn(f"{fileitem.path} 未获取到命名")
|
||||||
|
return False, f"{fileitem.path} 未获取到命名"
|
||||||
|
|
||||||
|
if mediainfo.type == MediaType.TV:
|
||||||
|
# 电视剧
|
||||||
|
[folder_name, season_name, file_name] = Path(full_names).parts
|
||||||
|
else:
|
||||||
|
# 电影
|
||||||
|
season_name = None
|
||||||
|
[folder_name, file_name] = Path(full_names).parts
|
||||||
|
|
||||||
|
# 如果是单个文件,则直接重命名
|
||||||
|
if fileitem.type == "file":
|
||||||
|
# 重命名文件
|
||||||
|
logger.info(f"正在整理 {fileitem.name} => {file_name} ...")
|
||||||
|
if not __rename_file(storage, fileitem.fileid, file_name):
|
||||||
|
logger.error(f"{fileitem.name} 重命名失败")
|
||||||
|
return False, f"{fileitem.name} 重命名失败"
|
||||||
|
logger.info(f"{fileitem.path} 整理完成")
|
||||||
|
else:
|
||||||
|
# 目录处理
|
||||||
|
if mediainfo.type == MediaType.MOVIE:
|
||||||
|
# 电影目录
|
||||||
|
# 重命名当前目录
|
||||||
|
logger.info(f"正在重命名 {fileitem.path} => {folder_name} ...")
|
||||||
|
if not __rename_file(_storage=storage, _fileid=fileitem.fileid, _name=folder_name):
|
||||||
|
logger.error(f"{fileitem.path} 重命名失败")
|
||||||
|
return False, f"{fileitem.path} 重命名失败"
|
||||||
|
logger.info(f"{fileitem.path} 重命名完成")
|
||||||
|
# 处理所有子文件或目录
|
||||||
|
files = __list_files(_storage=storage, _fileid=fileitem.fileid,
|
||||||
|
_drive_id=fileitem.drive_id, _path=fileitem.path)
|
||||||
|
if not files:
|
||||||
|
logger.info(f"{fileitem.path} 未找到文件,删除空目录")
|
||||||
|
if not __remove_dir(_storage=storage, _drive_id=fileitem.drive_id, _fileid=fileitem.fileid):
|
||||||
|
logger.error(f"{fileitem.path} 删除失败")
|
||||||
|
return False, f"{fileitem.path} 删除失败"
|
||||||
|
return True, ""
|
||||||
|
for file in files:
|
||||||
|
# 过滤不处理的文件
|
||||||
|
if file.type == "file" and str(file.extension) in ['nfo', 'jpg', 'png']:
|
||||||
|
continue
|
||||||
|
# 重新识别文件或目录
|
||||||
|
file_meta = MetaInfoPath(Path(file.path))
|
||||||
|
if not file_meta.name:
|
||||||
|
# 过滤掉无效文件
|
||||||
|
continue
|
||||||
|
file_media = self.recognize_media(meta=file_meta)
|
||||||
|
if not file_media:
|
||||||
|
logger.warn(f"{file.name} 未识别到媒体信息")
|
||||||
|
continue
|
||||||
|
# 整理这个文件或目录
|
||||||
|
self.__transfer_remote(storage=storage, fileitem=file, meta=file_meta, mediainfo=file_media)
|
||||||
|
else:
|
||||||
|
# 电视剧目录
|
||||||
|
# 判断当前目录类型
|
||||||
|
folder_meta = MetaInfo(fileitem.name)
|
||||||
|
if folder_meta.begin_season and not folder_meta.name:
|
||||||
|
# 季目录
|
||||||
|
logger.info(f"正在重命名 {fileitem.path} => {season_name} ...")
|
||||||
|
if not __rename_file(_storage=storage, _fileid=fileitem.fileid, _name=season_name):
|
||||||
|
logger.error(f"{fileitem.path} 重命名失败")
|
||||||
|
return False, f"{fileitem.path} 重命名失败"
|
||||||
|
logger.info(f"{fileitem.path} 重命名完成")
|
||||||
|
elif folder_meta.name:
|
||||||
|
# 根目录,重命名当前目录
|
||||||
|
logger.info(f"正在重命名 {fileitem.path} => {folder_name} ...")
|
||||||
|
if not __rename_file(_storage=storage, _fileid=fileitem.fileid, _name=folder_name):
|
||||||
|
logger.error(f"{fileitem.path} 重命名失败")
|
||||||
|
return False, f"{fileitem.path} 重命名失败"
|
||||||
|
logger.info(f"{fileitem.path} 重命名完成")
|
||||||
|
# 是否有季
|
||||||
|
if folder_meta.begin_season:
|
||||||
|
# 创建季目录
|
||||||
|
logger.info(f"正在创建目录 {fileitem.path}{season_name} ...")
|
||||||
|
season_dir = __create_folder(_storage=storage, _drive_id=fileitem.drive_id,
|
||||||
|
_parent_fileid=fileitem.fileid, _name=season_name,
|
||||||
|
_path=fileitem.path)
|
||||||
|
if not season_dir:
|
||||||
|
logger.error(f"{fileitem.path}/{season_name} 创建失败")
|
||||||
|
return False, f"{fileitem.path}/{season_name} 创建失败"
|
||||||
|
logger.info(f"{fileitem.path}/{season_name} 创建完成")
|
||||||
|
# 移动当前目录下的所有文件到季目录
|
||||||
|
files = __list_files(_storage=storage, _fileid=fileitem.fileid,
|
||||||
|
_drive_id=fileitem.drive_id, _path=fileitem.path)
|
||||||
|
if not files:
|
||||||
|
logger.error(f"{fileitem.path} 未找到文件,删除空目录")
|
||||||
|
if not __remove_dir(_storage=storage, _drive_id=fileitem.drive_id, _fileid=fileitem.fileid):
|
||||||
|
logger.error(f"{fileitem.path} 删除失败")
|
||||||
|
return False, f"{fileitem.path} 删除失败"
|
||||||
|
logger.info(f"{fileitem.path} 已删除")
|
||||||
|
return True, ""
|
||||||
|
for file in files:
|
||||||
|
if file.type == "dir":
|
||||||
|
continue
|
||||||
|
logger.info(f"正在移动 {file.path} => {season_dir.path}...")
|
||||||
|
if not __move_file(_storage=storage, _drive_id=fileitem.drive_id,
|
||||||
|
_fileid=file.fileid, _target_fileid=season_dir.fileid):
|
||||||
|
logger.error(f"{file.name} 移动失败")
|
||||||
|
return False, f"{file.name} 移动失败"
|
||||||
|
logger.info(f"{file.path} 移动完成")
|
||||||
|
# 修改当前目录为季目录
|
||||||
|
fileitem = season_dir
|
||||||
|
# 列出当前目录下所有的文件或目录,并进行重命名整理
|
||||||
|
files = __list_files(_storage=storage, _fileid=fileitem.fileid,
|
||||||
|
_drive_id=fileitem.drive_id, _path=fileitem.path)
|
||||||
|
if not files:
|
||||||
|
logger.info(f"{fileitem.path} 未找到文件,删除空目录")
|
||||||
|
if not __remove_dir(_storage=storage, _drive_id=fileitem.drive_id, _fileid=fileitem.fileid):
|
||||||
|
logger.error(f"{fileitem.path} 删除失败")
|
||||||
|
return False, f"{fileitem.path} 删除失败"
|
||||||
|
logger.info(f"{fileitem.path} 已删除")
|
||||||
|
return True, ""
|
||||||
|
for file in files:
|
||||||
|
# 过滤不处理的文件
|
||||||
|
if file.type == "file" and str(file.extension) in ['nfo', 'jpg', 'png']:
|
||||||
|
continue
|
||||||
|
# 重新识别文件或目录
|
||||||
|
file_meta = MetaInfoPath(Path(file.path))
|
||||||
|
file_media = self.recognize_media(meta=file_meta)
|
||||||
|
if not file_media:
|
||||||
|
logger.warn(f"{file.name} 未识别到媒体信息")
|
||||||
|
continue
|
||||||
|
# 整理这个文件或目录
|
||||||
|
self.__transfer_remote(storage=storage, fileitem=file, meta=file_meta, mediainfo=file_media)
|
||||||
|
|
||||||
|
logger.info(f"{fileitem.path} 整理完成")
|
||||||
|
self.progress.update(value=0,
|
||||||
|
text=f"{fileitem.path} 整理完成",
|
||||||
|
key=ProgressKey.FileTransfer)
|
||||||
|
return True, ""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_trans_paths(directory: Path):
|
def __get_trans_paths(directory: Path):
|
||||||
"""
|
"""
|
||||||
@ -565,6 +804,9 @@ class TransferChain(ChainBase):
|
|||||||
def manual_transfer(self,
|
def manual_transfer(self,
|
||||||
storage: str,
|
storage: str,
|
||||||
in_path: Path,
|
in_path: Path,
|
||||||
|
drive_id: str = None,
|
||||||
|
fileid: str = None,
|
||||||
|
filetype: str = None,
|
||||||
target: Path = None,
|
target: Path = None,
|
||||||
tmdbid: int = None,
|
tmdbid: int = None,
|
||||||
doubanid: str = None,
|
doubanid: str = None,
|
||||||
@ -579,6 +821,9 @@ class TransferChain(ChainBase):
|
|||||||
手动转移,支持复杂条件,带进度显示
|
手动转移,支持复杂条件,带进度显示
|
||||||
:param storage: 存储器
|
:param storage: 存储器
|
||||||
:param in_path: 源文件路径
|
:param in_path: 源文件路径
|
||||||
|
:param drive_id: 网盘ID
|
||||||
|
:param fileid: 文件ID
|
||||||
|
:param filetype: 文件类型
|
||||||
:param target: 目标路径
|
:param target: 目标路径
|
||||||
:param tmdbid: TMDB ID
|
:param tmdbid: TMDB ID
|
||||||
:param doubanid: 豆瓣ID
|
:param doubanid: 豆瓣ID
|
||||||
@ -607,6 +852,9 @@ class TransferChain(ChainBase):
|
|||||||
state, errmsg = self.do_transfer(
|
state, errmsg = self.do_transfer(
|
||||||
storage=storage,
|
storage=storage,
|
||||||
path=in_path,
|
path=in_path,
|
||||||
|
drive_id=drive_id,
|
||||||
|
fileid=fileid,
|
||||||
|
filetype=filetype,
|
||||||
mediainfo=mediainfo,
|
mediainfo=mediainfo,
|
||||||
target=target,
|
target=target,
|
||||||
transfer_type=transfer_type,
|
transfer_type=transfer_type,
|
||||||
@ -626,6 +874,9 @@ class TransferChain(ChainBase):
|
|||||||
# 没有输入TMDBID时,按文件识别
|
# 没有输入TMDBID时,按文件识别
|
||||||
state, errmsg = self.do_transfer(storage=storage,
|
state, errmsg = self.do_transfer(storage=storage,
|
||||||
path=in_path,
|
path=in_path,
|
||||||
|
drive_id=drive_id,
|
||||||
|
fileid=fileid,
|
||||||
|
filetype=filetype,
|
||||||
target=target,
|
target=target,
|
||||||
transfer_type=transfer_type,
|
transfer_type=transfer_type,
|
||||||
season=season,
|
season=season,
|
||||||
|
@ -438,7 +438,7 @@ class AliyunHelper:
|
|||||||
self.__handle_error(res, "创建目录")
|
self.__handle_error(res, "创建目录")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def delete(self, file_id: str) -> bool:
|
def delete(self, drive_id: str, file_id: str) -> bool:
|
||||||
"""
|
"""
|
||||||
删除文件
|
删除文件
|
||||||
"""
|
"""
|
||||||
@ -447,7 +447,7 @@ class AliyunHelper:
|
|||||||
return False
|
return False
|
||||||
headers = self.__get_headers(params)
|
headers = self.__get_headers(params)
|
||||||
res = RequestUtils(headers=headers, timeout=10).post_res(self.delete_file_url, json={
|
res = RequestUtils(headers=headers, timeout=10).post_res(self.delete_file_url, json={
|
||||||
"drive_id": params.get("resourceDriveId"),
|
"drive_id": drive_id,
|
||||||
"file_id": file_id
|
"file_id": file_id
|
||||||
})
|
})
|
||||||
if res:
|
if res:
|
||||||
@ -571,7 +571,7 @@ class AliyunHelper:
|
|||||||
return None
|
return None
|
||||||
# 获取上传参数
|
# 获取上传参数
|
||||||
result = res.json()
|
result = res.json()
|
||||||
if result.get("'exist'"):
|
if result.get("exist"):
|
||||||
logger.info(f"文件{result.get('file_name')}已存在,无需上传")
|
logger.info(f"文件{result.get('file_name')}已存在,无需上传")
|
||||||
return schemas.FileItem(
|
return schemas.FileItem(
|
||||||
drive_id=result.get("drive_id"),
|
drive_id=result.get("drive_id"),
|
||||||
|
@ -775,14 +775,15 @@ class DoubanModule(_ModuleBase):
|
|||||||
return None
|
return None
|
||||||
return self.scraper.get_metadata_nfo(mediainfo=mediainfo, season=season)
|
return self.scraper.get_metadata_nfo(mediainfo=mediainfo, season=season)
|
||||||
|
|
||||||
def metadata_img(self, mediainfo: MediaInfo, **kwargs) -> Optional[dict]:
|
def metadata_img(self, mediainfo: MediaInfo, season: int = None) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
获取图片名称和url
|
获取图片名称和url
|
||||||
:param mediainfo: 媒体信息
|
:param mediainfo: 媒体信息
|
||||||
|
:param season: 季号
|
||||||
"""
|
"""
|
||||||
if settings.SCRAP_SOURCE != "douban":
|
if settings.SCRAP_SOURCE != "douban":
|
||||||
return None
|
return None
|
||||||
return self.scraper.get_metadata_img(mediainfo=mediainfo)
|
return self.scraper.get_metadata_img(mediainfo=mediainfo, season=season)
|
||||||
|
|
||||||
def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]:
|
def obtain_images(self, mediainfo: MediaInfo) -> Optional[MediaInfo]:
|
||||||
"""
|
"""
|
||||||
|
@ -39,12 +39,16 @@ class DoubanScraper:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_metadata_img(mediainfo: MediaInfo) -> Optional[dict]:
|
def get_metadata_img(mediainfo: MediaInfo, season: int = None) -> Optional[dict]:
|
||||||
"""
|
"""
|
||||||
获取图片内容
|
获取图片内容
|
||||||
:param mediainfo: 媒体信息
|
:param mediainfo: 媒体信息
|
||||||
|
:param season: 季号
|
||||||
"""
|
"""
|
||||||
ret_dict = {}
|
ret_dict = {}
|
||||||
|
if season:
|
||||||
|
# 豆瓣无季图片
|
||||||
|
return {}
|
||||||
if mediainfo.poster_path:
|
if mediainfo.poster_path:
|
||||||
ret_dict[f"poster{Path(mediainfo.poster_path).suffix}"] = mediainfo.poster_path
|
ret_dict[f"poster{Path(mediainfo.poster_path).suffix}"] = mediainfo.poster_path
|
||||||
if mediainfo.backdrop_path:
|
if mediainfo.backdrop_path:
|
||||||
|
@ -65,24 +65,24 @@ class TmdbScraper:
|
|||||||
:param season: 季号
|
:param season: 季号
|
||||||
"""
|
"""
|
||||||
images = {}
|
images = {}
|
||||||
if mediainfo.type == MediaType.MOVIE:
|
if season:
|
||||||
for attr_name, attr_value in vars(mediainfo).items():
|
# 只需要季的图片
|
||||||
if attr_value \
|
seasoninfo = self.tmdb.get_tv_season_detail(mediainfo.tmdb_id, season)
|
||||||
and attr_name.endswith("_path") \
|
if seasoninfo:
|
||||||
and attr_value \
|
# TMDB季poster图片
|
||||||
and isinstance(attr_value, str) \
|
poster_name, poster_url = self.get_season_poster(seasoninfo, season)
|
||||||
and attr_value.startswith("http"):
|
if poster_name and poster_url:
|
||||||
image_name = attr_name.replace("_path", "") + Path(attr_value).suffix
|
images[poster_name] = poster_url
|
||||||
images[image_name] = attr_value
|
return images
|
||||||
else:
|
# 主媒体图片
|
||||||
if season:
|
for attr_name, attr_value in vars(mediainfo).items():
|
||||||
# 查询季信息
|
if attr_value \
|
||||||
seasoninfo = self.tmdb.get_tv_season_detail(mediainfo.tmdb_id, season)
|
and attr_name.endswith("_path") \
|
||||||
if seasoninfo:
|
and attr_value \
|
||||||
# TMDB季poster图片
|
and isinstance(attr_value, str) \
|
||||||
poster_name, poster_url = self.get_season_poster(seasoninfo, season)
|
and attr_value.startswith("http"):
|
||||||
if poster_name and poster_url:
|
image_name = attr_name.replace("_path", "") + Path(attr_value).suffix
|
||||||
images[poster_name] = poster_url
|
images[image_name] = attr_value
|
||||||
return images
|
return images
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
Loading…
x
Reference in New Issue
Block a user