From acb6a0f5914dd5c7e74470236a460cb49d9e0dbd Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 17 Dec 2023 10:15:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat=EF=BC=9AFFmpeg=E5=8D=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- plugins/ffmpegthumb/__init__.py | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 00e0de0..d489aa3 100644 --- a/package.json +++ b/package.json @@ -322,7 +322,7 @@ "FFmpegThumb": { "name": "FFmpeg缩略图", "description": "TheMovieDb没有背景图片时使用FFmpeg截取视频文件缩略图", - "version": "1.0", + "version": "1.1", "icon": "ffmpeg.png", "author": "jxxghp", "level": 1 diff --git a/plugins/ffmpegthumb/__init__.py b/plugins/ffmpegthumb/__init__.py index b9ebff9..9c8e472 100644 --- a/plugins/ffmpegthumb/__init__.py +++ b/plugins/ffmpegthumb/__init__.py @@ -1,3 +1,4 @@ +import threading from datetime import datetime, timedelta from pathlib import Path from threading import Event as ThreadEvent @@ -16,6 +17,8 @@ from app.schemas import TransferInfo from app.schemas.types import EventType from app.utils.system import SystemUtils +ffmpeg_lock = threading.Lock() + class FFmpegThumb(_PluginBase): # 插件名称 @@ -25,7 +28,7 @@ class FFmpegThumb(_PluginBase): # 插件图标 plugin_icon = "ffmpeg.png" # 插件版本 - plugin_version = "1.0" + plugin_version = "1.1" # 插件作者 plugin_author = "jxxghp" # 作者主页 @@ -328,18 +331,20 @@ class FFmpegThumb(_PluginBase): """ 处理一个文件 """ - try: - thumb_path = file_path.with_name(file_path.stem + "-thumb.jpg") - if thumb_path.exists(): - logger.info(f"缩略图已存在:{thumb_path}") - return - if FfmpegHelper.get_thumb(video_path=str(file_path), - image_path=str(thumb_path), frames=self._timeline): - logger.info(f"{file_path} 缩略图已生成:{thumb_path}") - else: - logger.warn(f"{file_path} 缩略图生成失败!") - except Exception as err: - logger.error(f"FFmpeg处理文件 {file_path} 时发生错误:{str(err)}") + # 单线程处理 + with ffmpeg_lock: + try: + thumb_path = file_path.with_name(file_path.stem + "-thumb.jpg") + if thumb_path.exists(): + logger.info(f"缩略图已存在:{thumb_path}") + return + if FfmpegHelper.get_thumb(video_path=str(file_path), + image_path=str(thumb_path), frames=self._timeline): + logger.info(f"{file_path} 缩略图已生成:{thumb_path}") + else: + logger.warn(f"{file_path} 缩略图生成失败!") + except Exception as err: + logger.error(f"FFmpeg处理文件 {file_path} 时发生错误:{str(err)}") def stop_service(self): """ From 55327f357de659425ce260ab25c56e204e83e418 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 17 Dec 2023 10:53:48 +0800 Subject: [PATCH 2/2] fix LibraryScraper --- package.json | 2 +- plugins/libraryscraper/__init__.py | 177 +++++++++-------------------- 2 files changed, 57 insertions(+), 122 deletions(-) diff --git a/package.json b/package.json index d489aa3..21628ef 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "LibraryScraper": { "name": "媒体库刮削", "description": "定时对媒体库进行刮削,补齐缺失元数据和图片。", - "version": "1.1", + "version": "1.2", "icon": "scraper.png", "author": "jxxghp", "level": 1 diff --git a/plugins/libraryscraper/__init__.py b/plugins/libraryscraper/__init__.py index 85938d8..7b78bb5 100644 --- a/plugins/libraryscraper/__init__.py +++ b/plugins/libraryscraper/__init__.py @@ -8,8 +8,7 @@ from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger from app.core.config import settings -from app.core.meta import MetaBase -from app.core.metainfo import MetaInfo +from app.core.metainfo import MetaInfoPath from app.db.transferhistory_oper import TransferHistoryOper from app.helper.nfo import NfoReader from app.log import logger @@ -26,7 +25,7 @@ class LibraryScraper(_PluginBase): # 插件图标 plugin_icon = "scraper.png" # 插件版本 - plugin_version = "1.1" + plugin_version = "1.2" # 插件作者 plugin_author = "jxxghp" # 作者主页 @@ -214,7 +213,7 @@ class LibraryScraper(_PluginBase): 'model': 'scraper_paths', 'label': '削刮路径', 'rows': 5, - 'placeholder': '每一行一个目录,需配置到媒体文件的上级目录,即开了二级分类时需要配置到二级分类目录' + 'placeholder': '每一行一个目录' } } ] @@ -243,27 +242,6 @@ class LibraryScraper(_PluginBase): } ] }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - }, - 'content': [ - { - 'component': 'VAlert', - 'props': { - 'type': 'info', - 'variant': 'tonal', - 'text': '刮削路径要配置到二级分类路径(如果配置了LIBRARY_CATEGORY=true);开启插件后默认会实时处理增量整理的媒体文件,需要处理存量媒体文件时才需开启定时。' - } - } - ] - } - ] - }, { 'component': 'VRow', 'content': [ @@ -325,8 +303,9 @@ class LibraryScraper(_PluginBase): logger.warning(f"媒体库刮削路径不存在:{path}") continue logger.info(f"开始刮削媒体库:{path} {mtype} ...") - # 遍历一层文件夹 - for sub_path in scraper_path.iterdir(): + # 遍历所有文件 + files = SystemUtils.list_files(scraper_path, settings.RMT_MEDIAEXT) + for file_path in files: if self._event.is_set(): logger.info(f"媒体库刮削服务停止") return @@ -334,115 +313,71 @@ class LibraryScraper(_PluginBase): exclude_flag = False for exclude_path in exclude_paths: try: - if sub_path.is_relative_to(Path(exclude_path)): + if file_path.is_relative_to(Path(exclude_path)): exclude_flag = True break except Exception as err: print(str(err)) if exclude_flag: - logger.debug(f"{sub_path} 在排除目录中,跳过 ...") + logger.debug(f"{file_path} 在排除目录中,跳过 ...") continue - # 开始刮削目录 - if sub_path.is_dir(): - # 判断目录是不是媒体目录 - dir_meta = MetaInfo(sub_path.name) - if not dir_meta.name or not dir_meta.year: - logger.warn(f"{sub_path} 可能不是媒体目录,请检查刮削目录配置,跳过 ...") - continue - logger.info(f"开始刮削目录:{sub_path} ...") - self.__scrape_dir(path=sub_path, dir_meta=dir_meta, mtype=mtype) - logger.info(f"目录 {sub_path} 刮削完成") + # 开始刮削文件 + self.__scrape_file(file=file_path, mtype=mtype) logger.info(f"媒体库 {path} 刮削完成") - def __scrape_dir(self, path: Path, dir_meta: MetaBase, mtype: MediaType = None): + def __scrape_file(self, file: Path, mtype: MediaType = None): """ 削刮一个目录,该目录必须是媒体文件目录 """ + # 识别元数据 + meta_info = MetaInfoPath(file) + # 强制指定类型 + if mtype: + meta_info.type = mtype - # 媒体信息 - mediainfo = None + # 是否刮削 + force_nfo = self._mode in ["force_all", "force_nfo"] + force_img = self._mode in ["force_all", "force_image"] - # 查找目录下所有的文件 - files = SystemUtils.list_files(path, settings.RMT_MEDIAEXT) - for file in files: - if self._event.is_set(): - logger.info(f"媒体库刮削服务停止") - return + # 优先读取本地nfo文件 + tmdbid = None + if meta_info.type == MediaType.MOVIE: + # 电影 + movie_nfo = file.parent / "movie.nfo" + if movie_nfo.exists(): + tmdbid = self.__get_tmdbid_from_nfo(movie_nfo) + file_nfo = file.with_suffix(".nfo") + if not tmdbid and file_nfo.exists(): + tmdbid = self.__get_tmdbid_from_nfo(file_nfo) + else: + # 电视剧 + tv_nfo = file.parent.parent / "tvshow.nfo" + if tv_nfo.exists(): + tmdbid = self.__get_tmdbid_from_nfo(tv_nfo) + if tmdbid: + # 按TMDBID识别 + logger.info(f"读取到本地nfo文件的tmdbid:{tmdbid}") + mediainfo = self.chain.recognize_media(tmdbid=tmdbid, mtype=meta_info.type) + else: + # 按名称识别 + mediainfo = self.chain.recognize_media(meta=meta_info) + if not mediainfo: + logger.warn(f"未识别到媒体信息:{file}") + return - # 识别元数据 - meta_info = MetaInfo(file.stem) - # 合并 - meta_info.merge(dir_meta) - # 强制指定类型 - if mtype: - meta_info.type = mtype + # 如果未开启新增已入库媒体是否跟随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 - # 是否刮削 - scrap_metadata = settings.SCRAP_METADATA - - # 没有媒体信息或者名字出现变化时,需要重新识别 - if not mediainfo \ - or meta_info.name != dir_meta.name: - # 优先读取本地nfo文件 - tmdbid = None - if meta_info.type == MediaType.MOVIE: - # 电影 - movie_nfo = file.parent / "movie.nfo" - if movie_nfo.exists(): - tmdbid = self.__get_tmdbid_from_nfo(movie_nfo) - file_nfo = file.with_suffix(".nfo") - if not tmdbid and file_nfo.exists(): - tmdbid = self.__get_tmdbid_from_nfo(file_nfo) - else: - # 电视剧 - tv_nfo = file.parent.parent / "tvshow.nfo" - if tv_nfo.exists(): - tmdbid = self.__get_tmdbid_from_nfo(tv_nfo) - if tmdbid: - # 按TMDBID识别 - logger.info(f"读取到本地nfo文件的tmdbid:{tmdbid}") - mediainfo = self.chain.recognize_media(tmdbid=tmdbid, mtype=meta_info.type) - else: - # 按名称识别 - mediainfo = self.chain.recognize_media(meta=meta_info) - if not mediainfo: - logger.warn(f"未识别到媒体信息:{file}") - continue - - # 如果未开启新增已入库媒体是否跟随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 - - # 覆盖模式时,提前删除nfo - if self._mode in ["force_all", "force_nfo"]: - scrap_metadata = True - nfo_files = SystemUtils.list_files(path, [".nfo"]) - for nfo_file in nfo_files: - try: - logger.warn(f"删除nfo文件:{nfo_file}") - nfo_file.unlink() - except Exception as err: - print(str(err)) - - # 覆盖模式时,提前删除图片文件 - if self._mode in ["force_all", "force_image"]: - scrap_metadata = True - image_files = SystemUtils.list_files(path, [".jpg", ".png"]) - for image_file in image_files: - if ".actors" in str(image_file): - continue - try: - logger.warn(f"删除图片文件:{image_file}") - image_file.unlink() - except Exception as err: - print(str(err)) - - # 刮削单个文件 - if scrap_metadata: - self.chain.scrape_metadata(path=file, mediainfo=mediainfo, transfer_type=settings.TRANSFER_TYPE) + # 刮削 + self.chain.scrape_metadata(path=file, + mediainfo=mediainfo, + transfer_type=settings.TRANSFER_TYPE, + force_nfo=force_nfo, + force_img=force_img) @staticmethod def __get_tmdbid_from_nfo(file_path: Path):