mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
feat:识别支持后台处理
This commit is contained in:
+206
-130
@@ -70,7 +70,10 @@ class JobManager:
|
|||||||
获取作业ID
|
获取作业ID
|
||||||
"""
|
"""
|
||||||
if task:
|
if task:
|
||||||
return task.mediainfo.tmdb_id or task.mediainfo.douban_id, task.meta.begin_season
|
if task.mediainfo:
|
||||||
|
return task.mediainfo.tmdb_id or task.mediainfo.douban_id, task.meta.begin_season
|
||||||
|
else:
|
||||||
|
return task.meta.name, task.meta.begin_season
|
||||||
else:
|
else:
|
||||||
return media.tmdb_id or media.douban_id, season
|
return media.tmdb_id or media.douban_id, season
|
||||||
|
|
||||||
@@ -79,9 +82,20 @@ class JobManager:
|
|||||||
"""
|
"""
|
||||||
获取媒体信息
|
获取媒体信息
|
||||||
"""
|
"""
|
||||||
mediainfo = deepcopy(task.mediainfo)
|
if task.mediainfo:
|
||||||
mediainfo.clear()
|
# 有媒体信息
|
||||||
return schemas.MediaInfo(**mediainfo.to_dict())
|
mediainfo = deepcopy(task.mediainfo)
|
||||||
|
mediainfo.clear()
|
||||||
|
return schemas.MediaInfo(**mediainfo.to_dict())
|
||||||
|
else:
|
||||||
|
# 没有媒体信息
|
||||||
|
meta: MetaBase = task.meta
|
||||||
|
return schemas.MediaInfo(
|
||||||
|
title=meta.name,
|
||||||
|
year=meta.year,
|
||||||
|
title_year=f"{meta.name} ({meta.year})",
|
||||||
|
type=meta.type.value if meta.type else None
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_meta(task: TransferTask) -> schemas.MetaInfo:
|
def __get_meta(task: TransferTask) -> schemas.MetaInfo:
|
||||||
@@ -90,11 +104,11 @@ class JobManager:
|
|||||||
"""
|
"""
|
||||||
return schemas.MetaInfo(**task.meta.to_dict())
|
return schemas.MetaInfo(**task.meta.to_dict())
|
||||||
|
|
||||||
def add_task(self, task: TransferTask):
|
def add_task(self, task: TransferTask, state: str = "waiting"):
|
||||||
"""
|
"""
|
||||||
添加整理任务
|
添加整理任务
|
||||||
"""
|
"""
|
||||||
if not any([task, task.mediainfo, task.meta, task.fileitem]):
|
if not any([task, task.meta, task.fileitem]):
|
||||||
return
|
return
|
||||||
with job_lock:
|
with job_lock:
|
||||||
__mediaid__ = self.__get_id(task)
|
__mediaid__ = self.__get_id(task)
|
||||||
@@ -103,14 +117,14 @@ class JobManager:
|
|||||||
"media": self.__get_media(task),
|
"media": self.__get_media(task),
|
||||||
"season": task.meta.begin_season,
|
"season": task.meta.begin_season,
|
||||||
"tasks": [{
|
"tasks": [{
|
||||||
"state": "waiting",
|
"state": state,
|
||||||
"fileitem": task.fileitem,
|
"fileitem": task.fileitem,
|
||||||
"meta": self.__get_meta(task)
|
"meta": self.__get_meta(task)
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
self._job_view[__mediaid__]["tasks"].append({
|
self._job_view[__mediaid__]["tasks"].append({
|
||||||
"state": "waiting",
|
"state": state,
|
||||||
"fileitem": task.fileitem,
|
"fileitem": task.fileitem,
|
||||||
"meta": self.__get_meta(task)
|
"meta": self.__get_meta(task)
|
||||||
})
|
})
|
||||||
@@ -153,8 +167,6 @@ class JobManager:
|
|||||||
"""
|
"""
|
||||||
任务失败
|
任务失败
|
||||||
"""
|
"""
|
||||||
if not task or not task.mediainfo or not task.meta:
|
|
||||||
return
|
|
||||||
with job_lock:
|
with job_lock:
|
||||||
__mediaid__ = self.__get_id(task)
|
__mediaid__ = self.__get_id(task)
|
||||||
if __mediaid__ not in self._job_view:
|
if __mediaid__ not in self._job_view:
|
||||||
@@ -170,7 +182,7 @@ class JobManager:
|
|||||||
set(self._season_episodes[__mediaid__]) - set(task.meta.episode_list)
|
set(self._season_episodes[__mediaid__]) - set(task.meta.episode_list)
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove_task(self, fileitem: FileItem):
|
def remove_task(self, fileitem: FileItem) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
移除整理任务
|
移除整理任务
|
||||||
"""
|
"""
|
||||||
@@ -178,7 +190,7 @@ class JobManager:
|
|||||||
for mediaid in list(self._job_view):
|
for mediaid in list(self._job_view):
|
||||||
job = self._job_view[mediaid]
|
job = self._job_view[mediaid]
|
||||||
for task in job["tasks"]:
|
for task in job["tasks"]:
|
||||||
if task["fileitem"].path == fileitem.path and task["fileitem"].storage == fileitem.storage:
|
if task["fileitem"] == fileitem:
|
||||||
job["tasks"].remove(task)
|
job["tasks"].remove(task)
|
||||||
# 如果没有作业了,则移除作业
|
# 如果没有作业了,则移除作业
|
||||||
if not job["tasks"]:
|
if not job["tasks"]:
|
||||||
@@ -188,14 +200,12 @@ class JobManager:
|
|||||||
self._season_episodes[mediaid] = list(
|
self._season_episodes[mediaid] = list(
|
||||||
set(self._season_episodes[mediaid]) - set(task["meta"].episode_list)
|
set(self._season_episodes[mediaid]) - set(task["meta"].episode_list)
|
||||||
)
|
)
|
||||||
break
|
return task
|
||||||
|
|
||||||
def remove_job(self, media: MediaInfo, season: int = None) -> Optional[Dict[str, Any]]:
|
def remove_job(self, media: MediaInfo, season: int = None) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
移除作业
|
移除作业
|
||||||
"""
|
"""
|
||||||
if not media:
|
|
||||||
return
|
|
||||||
__mediaid__ = self.__get_id(media=media, season=season)
|
__mediaid__ = self.__get_id(media=media, season=season)
|
||||||
with job_lock:
|
with job_lock:
|
||||||
# 移除作业
|
# 移除作业
|
||||||
@@ -205,17 +215,45 @@ class JobManager:
|
|||||||
self._season_episodes.pop(__mediaid__)
|
self._season_episodes.pop(__mediaid__)
|
||||||
return self._job_view.pop(__mediaid__)
|
return self._job_view.pop(__mediaid__)
|
||||||
|
|
||||||
def is_finished(self, media: MediaInfo, season: int = None) -> bool:
|
def is_done(self, media: MediaInfo, season: int = None) -> bool:
|
||||||
"""
|
"""
|
||||||
任务检查某项任务是否全部为已完成状态
|
检查某项作业是否整理完成(不管成功还是失败)
|
||||||
"""
|
"""
|
||||||
if not media:
|
|
||||||
return False
|
|
||||||
__mediaid__ = self.__get_id(media=media, season=season)
|
__mediaid__ = self.__get_id(media=media, season=season)
|
||||||
if __mediaid__ not in self._job_view:
|
if __mediaid__ not in self._job_view:
|
||||||
return False
|
return False
|
||||||
with job_lock:
|
with job_lock:
|
||||||
return all([job["state"] in ["completed", "failed"] for job in self._job_view[__mediaid__]["tasks"]])
|
return all(
|
||||||
|
[task["state"] in ["completed", "failed"] for task in self._job_view[__mediaid__]["tasks"]]
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_finished(self, media: MediaInfo, season: int = None) -> bool:
|
||||||
|
"""
|
||||||
|
检查某项作业是否已完成且有成功的记录
|
||||||
|
"""
|
||||||
|
__mediaid__ = self.__get_id(media=media, season=season)
|
||||||
|
if __mediaid__ not in self._job_view:
|
||||||
|
return False
|
||||||
|
with job_lock:
|
||||||
|
# 所有任务都是完成或者失败状态,且有完成的记录
|
||||||
|
tasks = self._job_view[__mediaid__]["tasks"]
|
||||||
|
return True if all(
|
||||||
|
[task["state"] in ["completed", "failed"] for task in tasks]
|
||||||
|
) and any(
|
||||||
|
[task["state"] == "completed" for task in tasks]
|
||||||
|
) else False
|
||||||
|
|
||||||
|
def is_success(self, media: MediaInfo, season: int = None) -> bool:
|
||||||
|
"""
|
||||||
|
检查某项作业是否全部成功
|
||||||
|
"""
|
||||||
|
__mediaid__ = self.__get_id(media=media, season=season)
|
||||||
|
if __mediaid__ not in self._job_view:
|
||||||
|
return False
|
||||||
|
with job_lock:
|
||||||
|
return all(
|
||||||
|
[task["state"] in ["completed"] for task in self._job_view[__mediaid__]["tasks"]]
|
||||||
|
)
|
||||||
|
|
||||||
def count(self, media: MediaInfo, season: int = None) -> int:
|
def count(self, media: MediaInfo, season: int = None) -> int:
|
||||||
"""
|
"""
|
||||||
@@ -233,7 +271,7 @@ class JobManager:
|
|||||||
获取所有task任务总数
|
获取所有task任务总数
|
||||||
"""
|
"""
|
||||||
with job_lock:
|
with job_lock:
|
||||||
return sum([len(jobs) for jobs in self._job_view.values()])
|
return sum([len(job["tasks"]) for job in self._job_view.values()])
|
||||||
|
|
||||||
def list_jobs(self) -> List[dict]:
|
def list_jobs(self) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
@@ -245,8 +283,6 @@ class JobManager:
|
|||||||
"""
|
"""
|
||||||
获取季集清单
|
获取季集清单
|
||||||
"""
|
"""
|
||||||
if not media:
|
|
||||||
return []
|
|
||||||
__mediaid__ = self.__get_id(media=media, season=season)
|
__mediaid__ = self.__get_id(media=media, season=season)
|
||||||
with job_lock:
|
with job_lock:
|
||||||
return self._season_episodes.get(__mediaid__) or []
|
return self._season_episodes.get(__mediaid__) or []
|
||||||
@@ -347,8 +383,8 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
'download_hash': task.download_hash,
|
'download_hash': task.download_hash,
|
||||||
})
|
})
|
||||||
|
|
||||||
# 整理完成一个媒体项时
|
# 全部整理成功时
|
||||||
if self.jobview.is_finished(task.mediainfo, task.meta.begin_season):
|
if self.jobview.is_success(task.mediainfo, task.meta.begin_season):
|
||||||
# 移动模式删除空目录
|
# 移动模式删除空目录
|
||||||
if transferinfo.transfer_type in ["move"]:
|
if transferinfo.transfer_type in ["move"]:
|
||||||
# 下载器hash
|
# 下载器hash
|
||||||
@@ -359,6 +395,8 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
if task.fileitem:
|
if task.fileitem:
|
||||||
self.storagechain.delete_media_file(task.fileitem, delete_self=False)
|
self.storagechain.delete_media_file(task.fileitem, delete_self=False)
|
||||||
|
|
||||||
|
# 整理完成且有成功的任务时
|
||||||
|
if self.jobview.is_finished(task.mediainfo, task.meta.begin_season):
|
||||||
# 发送通知
|
# 发送通知
|
||||||
if transferinfo.need_notify:
|
if transferinfo.need_notify:
|
||||||
se_str = None
|
se_str = None
|
||||||
@@ -425,58 +463,60 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
try:
|
try:
|
||||||
item: TransferQueue = self._queue.get(timeout=self._transfer_interval)
|
item: TransferQueue = self._queue.get(timeout=self._transfer_interval)
|
||||||
if item:
|
if item:
|
||||||
callback = item.callback
|
|
||||||
task = item.task
|
task = item.task
|
||||||
if not task:
|
if not task:
|
||||||
continue
|
continue
|
||||||
mediainfo = task.mediainfo
|
|
||||||
meta = task.meta
|
|
||||||
fileitem = task.fileitem
|
|
||||||
# 正在处理
|
# 正在处理
|
||||||
self.jobview.running_task(task)
|
self.jobview.running_task(task)
|
||||||
|
# 文件信息
|
||||||
|
fileitem = task.fileitem
|
||||||
|
# 开始新队列
|
||||||
if __queue_start:
|
if __queue_start:
|
||||||
logger.info("开始整理队列处理...")
|
logger.info("开始整理队列处理...")
|
||||||
# 启动进度
|
# 启动进度
|
||||||
self.progress.start(ProgressKey.FileTransfer)
|
self.progress.start(ProgressKey.FileTransfer)
|
||||||
|
# 重置计数
|
||||||
|
processed_num = 0
|
||||||
|
fail_num = 0
|
||||||
total_num = self.jobview.total()
|
total_num = self.jobview.total()
|
||||||
__process_msg = f"开始整理队列处理,共 {total_num} 个文件或子目录 ..."
|
__process_msg = f"开始整理队列处理,当前共 {total_num} 个文件 ..."
|
||||||
logger.info(__process_msg)
|
logger.info(__process_msg)
|
||||||
self.progress.update(value=0,
|
self.progress.update(value=0,
|
||||||
text=__process_msg,
|
text=__process_msg,
|
||||||
key=ProgressKey.FileTransfer)
|
key=ProgressKey.FileTransfer)
|
||||||
# 队列已开始
|
# 队列已开始
|
||||||
__queue_start = False
|
__queue_start = False
|
||||||
# 重新计算总数
|
|
||||||
total_num = self.jobview.total()
|
|
||||||
# 更新进度
|
# 更新进度
|
||||||
__process_msg = f"正在整理 ({processed_num + 1}/{total_num}){fileitem.name} ..."
|
__process_msg = f"正在整理 {fileitem.name} ..."
|
||||||
logger.info(__process_msg)
|
logger.info(__process_msg)
|
||||||
self.progress.update(value=processed_num / total_num * 100,
|
self.progress.update(value=processed_num / total_num * 100,
|
||||||
text=__process_msg,
|
text=__process_msg,
|
||||||
key=ProgressKey.FileTransfer)
|
key=ProgressKey.FileTransfer)
|
||||||
# 整理
|
# 整理
|
||||||
state, err_msg = self.__handle_transfer(task=task, callback=callback)
|
state, err_msg = self.__handle_transfer(task=task, callback=item.callback)
|
||||||
if state:
|
if not state:
|
||||||
# 任务成功
|
|
||||||
processed_num += 1
|
|
||||||
else:
|
|
||||||
# 任务失败
|
# 任务失败
|
||||||
logger.warn(f"{fileitem.name} 整理失败:{err_msg}")
|
|
||||||
fail_num += 1
|
fail_num += 1
|
||||||
|
# 更新进度
|
||||||
|
processed_num += 1
|
||||||
|
__process_msg = f"{fileitem.name} 整理完成"
|
||||||
|
logger.info(__process_msg)
|
||||||
|
self.progress.update(value=processed_num / total_num * 100,
|
||||||
|
text=__process_msg,
|
||||||
|
key=ProgressKey.FileTransfer)
|
||||||
# 移除已完成的任务
|
# 移除已完成的任务
|
||||||
if self.jobview.is_finished(mediainfo, meta.begin_season):
|
if self.jobview.is_done(task.mediainfo, task.meta.begin_season):
|
||||||
self.jobview.remove_job(mediainfo, meta.begin_season)
|
self.jobview.remove_job(task.mediainfo, task.meta.begin_season)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
if not __queue_start:
|
if not __queue_start:
|
||||||
# 结束进度
|
# 结束进度
|
||||||
__end_msg = f"整理队列处理完成,共整理 {total_num} 个文件,失败 {fail_num} 个"
|
__end_msg = f"整理队列处理完成,共整理 {processed_num} 个文件,失败 {fail_num} 个"
|
||||||
logger.info(__end_msg)
|
logger.info(__end_msg)
|
||||||
self.progress.update(value=100,
|
self.progress.update(value=100,
|
||||||
text=__end_msg,
|
text=__end_msg,
|
||||||
key=ProgressKey.FileTransfer)
|
key=ProgressKey.FileTransfer)
|
||||||
self.progress.end(ProgressKey.FileTransfer)
|
self.progress.end(ProgressKey.FileTransfer)
|
||||||
# 重置计数
|
# 重置计数
|
||||||
total_num = 0
|
|
||||||
processed_num = 0
|
processed_num = 0
|
||||||
fail_num = 0
|
fail_num = 0
|
||||||
# 标记为新队列
|
# 标记为新队列
|
||||||
@@ -490,6 +530,85 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
"""
|
"""
|
||||||
处理整理任务
|
处理整理任务
|
||||||
"""
|
"""
|
||||||
|
# 识别
|
||||||
|
if not task.mediainfo:
|
||||||
|
download_history = task.download_history
|
||||||
|
# 识别媒体信息
|
||||||
|
if download_history and (download_history.tmdbid or download_history.doubanid):
|
||||||
|
# 下载记录中已存在识别信息
|
||||||
|
mediainfo: MediaInfo = self.recognize_media(mtype=MediaType(download_history.type),
|
||||||
|
tmdbid=download_history.tmdbid,
|
||||||
|
doubanid=download_history.doubanid)
|
||||||
|
if mediainfo:
|
||||||
|
# 更新自定义媒体类别
|
||||||
|
if download_history.media_category:
|
||||||
|
mediainfo.category = download_history.media_category
|
||||||
|
else:
|
||||||
|
# 识别媒体信息
|
||||||
|
mediainfo = self.mediachain.recognize_by_meta(task.meta)
|
||||||
|
# 更新媒体图片
|
||||||
|
if mediainfo:
|
||||||
|
self.obtain_images(mediainfo=mediainfo)
|
||||||
|
if not mediainfo:
|
||||||
|
# 新增整理失败历史记录
|
||||||
|
his = self.transferhis.add_fail(
|
||||||
|
fileitem=task.fileitem,
|
||||||
|
mode=task.transfer_type,
|
||||||
|
meta=task.meta,
|
||||||
|
downloader=task.downloader,
|
||||||
|
download_hash=task.download_hash
|
||||||
|
)
|
||||||
|
self.post_message(Notification(
|
||||||
|
mtype=NotificationType.Manual,
|
||||||
|
title=f"{task.fileitem.name} 未识别到媒体信息,无法入库!",
|
||||||
|
text=f"回复:```\n/redo {his.id} [tmdbid]|[类型]\n``` 手动识别整理。",
|
||||||
|
link=settings.MP_DOMAIN('#/history')
|
||||||
|
))
|
||||||
|
# 任务失败
|
||||||
|
return False, "未识别到媒体信息"
|
||||||
|
|
||||||
|
# 如果未开启新增已入库媒体是否跟随TMDB信息变化则根据tmdbid查询之前的title
|
||||||
|
if not settings.SCRAP_FOLLOW_TMDB:
|
||||||
|
transfer_history = self.transferhis.get_by_type_tmdbid(tmdbid=mediainfo.tmdb_id,
|
||||||
|
mtype=mediainfo.type.value)
|
||||||
|
if transfer_history:
|
||||||
|
mediainfo.title = transfer_history.title
|
||||||
|
|
||||||
|
# 获取集数据
|
||||||
|
if not task.episodes_info and mediainfo.type == MediaType.TV:
|
||||||
|
if task.meta.begin_season is None:
|
||||||
|
task.meta.begin_season = 1
|
||||||
|
mediainfo.season = mediainfo.season or task.meta.begin_season
|
||||||
|
task.episodes_info = self.tmdbchain.tmdb_episodes(
|
||||||
|
tmdbid=mediainfo.tmdb_id,
|
||||||
|
season=mediainfo.season
|
||||||
|
)
|
||||||
|
|
||||||
|
# 更新任务信息
|
||||||
|
task.mediainfo = mediainfo
|
||||||
|
# 更新队列任务
|
||||||
|
curr_task = self.jobview.remove_task(task.fileitem)
|
||||||
|
self.jobview.add_task(task, state=curr_task['state'] if curr_task else "waiting")
|
||||||
|
|
||||||
|
# 查询整理目标目录
|
||||||
|
if not task.target_directory:
|
||||||
|
if task.src_match:
|
||||||
|
# 按源目录匹配,以便找到更合适的目录配置
|
||||||
|
task.target_directory = self.directoryhelper.get_dir(media=task.mediainfo,
|
||||||
|
storage=task.fileitem.storage,
|
||||||
|
src_path=Path(task.fileitem.path),
|
||||||
|
target_storage=task.target_storage)
|
||||||
|
elif task.target_path:
|
||||||
|
# 指定目标路径,`手动整理`场景下使用,忽略源目录匹配,使用指定目录匹配
|
||||||
|
task.target_directory = self.directoryhelper.get_dir(media=task.mediainfo,
|
||||||
|
dest_path=task.target_path,
|
||||||
|
target_storage=task.target_storage)
|
||||||
|
else:
|
||||||
|
# 未指定目标路径,根据媒体信息获取目标目录
|
||||||
|
task.target_directory = self.directoryhelper.get_dir(media=task.mediainfo,
|
||||||
|
storage=task.fileitem.storage,
|
||||||
|
target_storage=task.target_storage)
|
||||||
|
|
||||||
# 执行整理
|
# 执行整理
|
||||||
transferinfo: TransferInfo = self.transfer(fileitem=task.fileitem,
|
transferinfo: TransferInfo = self.transfer(fileitem=task.fileitem,
|
||||||
meta=task.meta,
|
meta=task.meta,
|
||||||
@@ -775,7 +894,21 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
logger.warn(f"{fileitem.path} 没有找到可整理的媒体文件")
|
logger.warn(f"{fileitem.path} 没有找到可整理的媒体文件")
|
||||||
return False, f"{fileitem.name} 没有找到可整理的媒体文件"
|
return False, f"{fileitem.name} 没有找到可整理的媒体文件"
|
||||||
|
|
||||||
logger.info(f"正在计划整理 {len(file_items)} 个文件...")
|
# 总数量
|
||||||
|
total_num = len(file_items)
|
||||||
|
# 已处理数量
|
||||||
|
processed_num = 0
|
||||||
|
# 失败数量
|
||||||
|
fail_num = 0
|
||||||
|
logger.info(f"正在计划整理 {total_num} 个文件...")
|
||||||
|
if not background:
|
||||||
|
# 启动进度
|
||||||
|
self.progress.start(ProgressKey.FileTransfer)
|
||||||
|
__process_msg = f"开始整理,共 {total_num} 个文件 ..."
|
||||||
|
logger.info(__process_msg)
|
||||||
|
self.progress.update(value=0,
|
||||||
|
text=__process_msg,
|
||||||
|
key=ProgressKey.FileTransfer)
|
||||||
|
|
||||||
# 整理所有文件
|
# 整理所有文件
|
||||||
for file_item, bluray_dir in file_items:
|
for file_item, bluray_dir in file_items:
|
||||||
@@ -801,17 +934,27 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
is_blocked = True
|
is_blocked = True
|
||||||
break
|
break
|
||||||
if is_blocked:
|
if is_blocked:
|
||||||
|
fail_num += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 整理成功的不再处理
|
# 整理成功的不再处理
|
||||||
if not force:
|
if not force:
|
||||||
transferd = self.transferhis.get_by_src(file_item.path, storage=file_item.storage)
|
transferd = self.transferhis.get_by_src(file_item.path, storage=file_item.storage)
|
||||||
if transferd and transferd.status:
|
if transferd:
|
||||||
all_success = False
|
all_success = False
|
||||||
logger.info(f"{file_item.path} 已整理过,如需重新处理,请删除整理记录。")
|
logger.info(f"{file_item.path} 已整理过,如需重新处理,请删除整理记录。")
|
||||||
err_msgs.append(f"{file_item.name} 已整理过")
|
err_msgs.append(f"{file_item.name} 已整理过")
|
||||||
|
fail_num += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 更新进度
|
||||||
|
if not background:
|
||||||
|
__process_msg = f"正在整理 ({processed_num + 1}/{total_num}){file_item.name} ..."
|
||||||
|
logger.info(__process_msg)
|
||||||
|
self.progress.update(value=processed_num / total_num * 100,
|
||||||
|
text=__process_msg,
|
||||||
|
key=ProgressKey.FileTransfer)
|
||||||
|
|
||||||
if not meta:
|
if not meta:
|
||||||
# 文件元数据
|
# 文件元数据
|
||||||
file_meta = MetaInfoPath(file_path)
|
file_meta = MetaInfoPath(file_path)
|
||||||
@@ -826,6 +969,7 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
all_success = False
|
all_success = False
|
||||||
logger.error(f"{file_path.name} 无法识别有效信息")
|
logger.error(f"{file_path.name} 无法识别有效信息")
|
||||||
err_msgs.append(f"{file_path.name} 无法识别有效信息")
|
err_msgs.append(f"{file_path.name} 无法识别有效信息")
|
||||||
|
fail_num += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 自定义识别
|
# 自定义识别
|
||||||
@@ -854,102 +998,22 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
downloader = download_history.downloader
|
downloader = download_history.downloader
|
||||||
download_hash = download_history.download_hash
|
download_hash = download_history.download_hash
|
||||||
|
|
||||||
if not mediainfo:
|
|
||||||
# 识别媒体信息
|
|
||||||
if download_history and (download_history.tmdbid or download_history.doubanid):
|
|
||||||
# 下载记录中已存在识别信息
|
|
||||||
file_mediainfo: MediaInfo = self.recognize_media(mtype=MediaType(download_history.type),
|
|
||||||
tmdbid=download_history.tmdbid,
|
|
||||||
doubanid=download_history.doubanid)
|
|
||||||
if file_mediainfo:
|
|
||||||
# 更新自定义媒体类别
|
|
||||||
if download_history.media_category:
|
|
||||||
file_mediainfo.category = download_history.media_category
|
|
||||||
else:
|
|
||||||
# 识别媒体信息
|
|
||||||
file_mediainfo = self.mediachain.recognize_by_meta(file_meta)
|
|
||||||
# 更新媒体图片
|
|
||||||
if file_mediainfo:
|
|
||||||
self.obtain_images(mediainfo=file_mediainfo)
|
|
||||||
else:
|
|
||||||
file_mediainfo = mediainfo
|
|
||||||
|
|
||||||
if not file_mediainfo:
|
|
||||||
all_success = False
|
|
||||||
logger.warn(f'{file_path.name} 未识别到媒体信息')
|
|
||||||
err_msgs.append(f"{file_path.name} 未识别到媒体信息")
|
|
||||||
# 新增整理失败历史记录
|
|
||||||
his = self.transferhis.add_fail(
|
|
||||||
fileitem=file_item,
|
|
||||||
mode=transfer_type,
|
|
||||||
meta=file_meta,
|
|
||||||
downloader=downloader,
|
|
||||||
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')
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 如果未开启新增已入库媒体是否跟随TMDB信息变化则根据tmdbid查询之前的title
|
|
||||||
if not settings.SCRAP_FOLLOW_TMDB:
|
|
||||||
transfer_history = self.transferhis.get_by_type_tmdbid(tmdbid=file_mediainfo.tmdb_id,
|
|
||||||
mtype=file_mediainfo.type.value)
|
|
||||||
if transfer_history:
|
|
||||||
file_mediainfo.title = transfer_history.title
|
|
||||||
|
|
||||||
logger.info(f"{file_path.name} 识别为:{file_mediainfo.type.value} {file_mediainfo.title_year}")
|
|
||||||
|
|
||||||
# 获取集数据
|
|
||||||
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
|
|
||||||
|
|
||||||
# 查询整理目标目录
|
|
||||||
dir_info = None
|
|
||||||
if not target_directory:
|
|
||||||
if src_match:
|
|
||||||
# 按源目录匹配,以便找到更合适的目录配置
|
|
||||||
dir_info = self.directoryhelper.get_dir(media=file_mediainfo,
|
|
||||||
storage=file_item.storage,
|
|
||||||
src_path=file_path,
|
|
||||||
target_storage=target_storage)
|
|
||||||
elif target_path:
|
|
||||||
# 指定目标路径,`手动整理`场景下使用,忽略源目录匹配,使用指定目录匹配
|
|
||||||
dir_info = self.directoryhelper.get_dir(media=file_mediainfo,
|
|
||||||
dest_path=target_path,
|
|
||||||
target_storage=target_storage)
|
|
||||||
else:
|
|
||||||
# 未指定目标路径,根据媒体信息获取目标目录
|
|
||||||
dir_info = self.directoryhelper.get_dir(file_mediainfo,
|
|
||||||
storage=file_item.storage,
|
|
||||||
target_storage=target_storage)
|
|
||||||
|
|
||||||
# 后台整理
|
# 后台整理
|
||||||
transfer_task = TransferTask(
|
transfer_task = TransferTask(
|
||||||
fileitem=file_item,
|
fileitem=file_item,
|
||||||
meta=file_meta,
|
meta=file_meta,
|
||||||
mediainfo=file_mediainfo,
|
mediainfo=mediainfo,
|
||||||
target_directory=target_directory or dir_info,
|
target_directory=target_directory,
|
||||||
target_storage=target_storage,
|
target_storage=target_storage,
|
||||||
target_path=target_path,
|
target_path=target_path,
|
||||||
transfer_type=transfer_type,
|
transfer_type=transfer_type,
|
||||||
episodes_info=episodes_info,
|
src_match=src_match,
|
||||||
scrape=scrape,
|
scrape=scrape,
|
||||||
library_type_folder=library_type_folder,
|
library_type_folder=library_type_folder,
|
||||||
library_category_folder=library_category_folder,
|
library_category_folder=library_category_folder,
|
||||||
downloader=downloader,
|
downloader=downloader,
|
||||||
download_hash=download_hash
|
download_hash=download_hash,
|
||||||
|
download_history=download_history
|
||||||
)
|
)
|
||||||
if background:
|
if background:
|
||||||
self.put_to_queue(
|
self.put_to_queue(
|
||||||
@@ -965,6 +1029,18 @@ class TransferChain(ChainBase, metaclass=Singleton):
|
|||||||
all_success = False
|
all_success = False
|
||||||
logger.warn(f"{file_path.name} {err_msg}")
|
logger.warn(f"{file_path.name} {err_msg}")
|
||||||
err_msgs.append(f"{file_path.name} {err_msg}")
|
err_msgs.append(f"{file_path.name} {err_msg}")
|
||||||
|
fail_num += 1
|
||||||
|
# 完成计数
|
||||||
|
processed_num += 1
|
||||||
|
|
||||||
|
# 整理结束
|
||||||
|
if not background:
|
||||||
|
__end_msg = f"整理队列处理完成,共整理 {total_num} 个文件,失败 {fail_num} 个"
|
||||||
|
logger.info(__end_msg)
|
||||||
|
self.progress.update(value=100,
|
||||||
|
text=__end_msg,
|
||||||
|
key=ProgressKey.FileTransfer)
|
||||||
|
self.progress.end(ProgressKey.FileTransfer)
|
||||||
|
|
||||||
return all_success, ",".join(err_msgs)
|
return all_success, ",".join(err_msgs)
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ class DownloadHistory(BaseModel):
|
|||||||
date: Optional[str] = None
|
date: Optional[str] = None
|
||||||
# 备注
|
# 备注
|
||||||
note: Optional[Any] = None
|
note: Optional[Any] = None
|
||||||
|
# 自定义媒体类别
|
||||||
|
media_category: Optional[str] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional, List, Any, Callable
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.schemas import TmdbEpisode
|
from app.schemas import TmdbEpisode, DownloadHistory
|
||||||
from app.schemas.file import FileItem
|
from app.schemas.file import FileItem
|
||||||
from app.schemas.system import TransferDirectoryConf
|
from app.schemas.system import TransferDirectoryConf
|
||||||
|
|
||||||
@@ -46,20 +46,21 @@ class TransferTask(BaseModel):
|
|||||||
"""
|
"""
|
||||||
文件整理任务
|
文件整理任务
|
||||||
"""
|
"""
|
||||||
fileitem: Optional[FileItem] = None
|
fileitem: FileItem = None
|
||||||
file_path: Optional[Path] = None
|
meta: Any = None
|
||||||
meta: Optional[Any] = None
|
|
||||||
mediainfo: Optional[Any] = None
|
mediainfo: Optional[Any] = None
|
||||||
target_directory: Optional[TransferDirectoryConf] = None
|
target_directory: Optional[TransferDirectoryConf] = None
|
||||||
target_storage: Optional[str] = None
|
target_storage: Optional[str] = None
|
||||||
target_path: Optional[Path] = None
|
target_path: Optional[Path] = None
|
||||||
transfer_type: Optional[str] = None
|
transfer_type: Optional[str] = None
|
||||||
scrape: Optional[bool] = None
|
src_match: Optional[bool] = False
|
||||||
library_type_folder: Optional[bool] = None
|
scrape: Optional[bool] = False
|
||||||
library_category_folder: Optional[bool] = None
|
library_type_folder: Optional[bool] = False
|
||||||
|
library_category_folder: Optional[bool] = False
|
||||||
episodes_info: Optional[List[TmdbEpisode]] = None
|
episodes_info: Optional[List[TmdbEpisode]] = None
|
||||||
downloader: Optional[str] = None
|
downloader: Optional[str] = None
|
||||||
download_hash: Optional[str] = None
|
download_hash: Optional[str] = None
|
||||||
|
download_history: Optional[DownloadHistory] = None
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user