Merge branch 'main' into main

This commit is contained in:
jxxghp 2023-09-07 14:29:01 +08:00 committed by GitHub
commit 6c59a5ebb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 147 additions and 73 deletions

View File

@ -52,7 +52,7 @@ class DownloadHistory(Base):
@staticmethod @staticmethod
def get_last_by(db: Session, mtype: str = None, title: str = None, year: int = None, season: str = None, def get_last_by(db: Session, mtype: str = None, title: str = None, year: int = None, season: str = None,
episode: str = None, tmdbid: str = None): episode: str = None, tmdbid: int = None):
""" """
据tmdbidseasonseason_episode查询转移记录 据tmdbidseasonseason_episode查询转移记录
""" """

View File

@ -86,7 +86,7 @@ class TransferHistory(Base):
@staticmethod @staticmethod
def list_by(db: Session, type: str = None, title: str = None, year: int = None, season: str = None, def list_by(db: Session, type: str = None, title: str = None, year: int = None, season: str = None,
episode: str = None, tmdbid: str = None): episode: str = None, tmdbid: int = None):
""" """
据tmdbidseasonseason_episode查询转移记录 据tmdbidseasonseason_episode查询转移记录
""" """

View File

@ -590,7 +590,7 @@ class FileTransferModule(_ModuleBase):
continue continue
# 检索媒体文件 # 检索媒体文件
media_files = SystemUtils.list_files(directory=media_path, extensions=settings.RMT_VIDEOEXT) media_files = SystemUtils.list_files(directory=media_path, extensions=settings.RMT_MEDIAEXT)
if not media_files: if not media_files:
continue continue

View File

@ -194,12 +194,17 @@ class TheMovieDbModule(_ModuleBase):
scrape_path = path / path.name scrape_path = path / path.name
self.scraper.gen_scraper_files(mediainfo=mediainfo, self.scraper.gen_scraper_files(mediainfo=mediainfo,
file_path=scrape_path) file_path=scrape_path)
elif path.is_file():
# 单个文件
logger.info(f"开始刮削媒体库文件:{path} ...")
self.scraper.gen_scraper_files(mediainfo=mediainfo,
file_path=path)
else: else:
# 目录下的所有文件 # 目录下的所有文件
logger.info(f"开始刮削目录:{path} ...")
for file in SystemUtils.list_files(path, settings.RMT_MEDIAEXT): for file in SystemUtils.list_files(path, settings.RMT_MEDIAEXT):
if not file: if not file:
continue continue
logger.info(f"开始刮削媒体库文件:{file} ...")
self.scraper.gen_scraper_files(mediainfo=mediainfo, self.scraper.gen_scraper_files(mediainfo=mediainfo,
file_path=file) file_path=file)
logger.info(f"{path} 刮削完成") logger.info(f"{path} 刮削完成")

View File

@ -8,7 +8,6 @@ from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.cron import CronTrigger
from app.core.config import settings from app.core.config import settings
from app.core.context import MediaInfo
from app.core.metainfo import MetaInfo from app.core.metainfo import MetaInfo
from app.db.transferhistory_oper import TransferHistoryOper from app.db.transferhistory_oper import TransferHistoryOper
from app.helper.nfo import NfoReader from app.helper.nfo import NfoReader
@ -49,6 +48,7 @@ class LibraryScraper(_PluginBase):
_enabled = False _enabled = False
_onlyonce = False _onlyonce = False
_cron = None _cron = None
_mode = ""
_scraper_paths = "" _scraper_paths = ""
_exclude_paths = "" _exclude_paths = ""
# 退出事件 # 退出事件
@ -60,6 +60,7 @@ class LibraryScraper(_PluginBase):
self._enabled = config.get("enabled") self._enabled = config.get("enabled")
self._onlyonce = config.get("onlyonce") self._onlyonce = config.get("onlyonce")
self._cron = config.get("cron") self._cron = config.get("cron")
self._mode = config.get("mode") or ""
self._scraper_paths = config.get("scraper_paths") or "" self._scraper_paths = config.get("scraper_paths") or ""
self._exclude_paths = config.get("exclude_paths") or "" self._exclude_paths = config.get("exclude_paths") or ""
@ -95,6 +96,7 @@ class LibraryScraper(_PluginBase):
"onlyonce": False, "onlyonce": False,
"enabled": self._enabled, "enabled": self._enabled,
"cron": self._cron, "cron": self._cron,
"mode": self._mode,
"scraper_paths": self._scraper_paths, "scraper_paths": self._scraper_paths,
"exclude_paths": self._exclude_paths "exclude_paths": self._exclude_paths
}) })
@ -158,6 +160,28 @@ class LibraryScraper(_PluginBase):
{ {
'component': 'VRow', 'component': 'VRow',
'content': [ 'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VSelect',
'props': {
'model': 'mode',
'label': '刮削模式',
'items': [
{'title': '仅刮削缺失元数据和图片', 'value': ''},
{'title': '覆盖所有元数据和图片', 'value': 'force_all'},
{'title': '覆盖所有元数据', 'value': 'force_nfo'},
{'title': '覆盖所有图片', 'value': 'force_image'},
]
}
}
]
},
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
@ -192,7 +216,7 @@ class LibraryScraper(_PluginBase):
'model': 'scraper_paths', 'model': 'scraper_paths',
'label': '削刮路径', 'label': '削刮路径',
'rows': 5, 'rows': 5,
'placeholder': '每一行一个目录' 'placeholder': '每一行一个目录,需配置到媒体文件的上级目录,即开了二级分类时需要配置到二级分类目录'
} }
} }
] ]
@ -226,6 +250,7 @@ class LibraryScraper(_PluginBase):
], { ], {
"enabled": False, "enabled": False,
"cron": "0 0 */7 * *", "cron": "0 0 */7 * *",
"mode": "",
"scraper_paths": "", "scraper_paths": "",
"err_hosts": "" "err_hosts": ""
} }
@ -239,49 +264,66 @@ class LibraryScraper(_PluginBase):
""" """
if not self._scraper_paths: if not self._scraper_paths:
return return
# 排除目录
exclude_paths = self._exclude_paths.split("\n")
# 已选择的目录 # 已选择的目录
paths = self._scraper_paths.split("\n") paths = self._scraper_paths.split("\n")
for path in paths: for path in paths:
if not path: if not path:
continue continue
if not Path(path).exists(): scraper_path = Path(path)
if not scraper_path.exists():
logger.warning(f"媒体库刮削路径不存在:{path}") logger.warning(f"媒体库刮削路径不存在:{path}")
continue continue
logger.info(f"开始刮削媒体库:{path} ...") logger.info(f"开始刮削媒体库:{path} ...")
if self._event.is_set(): # 遍历一层文件夹
logger.info(f"媒体库刮削服务停止") for sub_path in scraper_path.iterdir():
return
# 刮削目录
self.__scrape_dir(Path(path))
logger.info(f"媒体库刮削完成")
def __scrape_dir(self, path: Path):
"""
削刮一个目录
"""
exclude_paths = self._exclude_paths.split("\n")
# 查找目录下所有的文件
files = SystemUtils.list_files(path, settings.RMT_MEDIAEXT)
for file in files:
if self._event.is_set(): if self._event.is_set():
logger.info(f"媒体库刮削服务停止") logger.info(f"媒体库刮削服务停止")
return return
# 排除目录 # 排除目录
exclude_flag = False exclude_flag = False
for exclude_path in exclude_paths: for exclude_path in exclude_paths:
if file.is_relative_to(Path(exclude_path)): try:
if sub_path.is_relative_to(Path(exclude_path)):
exclude_flag = True exclude_flag = True
break break
except Exception as err:
print(str(err))
if exclude_flag: if exclude_flag:
logger.debug(f"{file} 在排除目录中,跳过 ...") logger.debug(f"{sub_path} 在排除目录中,跳过 ...")
continue continue
# 识别媒体文件 # 开始刮削目录
if sub_path.is_dir():
logger.info(f"开始刮削目录:{sub_path} ...")
self.__scrape_dir(sub_path)
logger.info(f"目录 {sub_path} 刮削完成")
logger.info(f"媒体库 {path} 刮削完成")
def __scrape_dir(self, path: Path):
"""
削刮一个目录该目录必须是媒体文件目录
"""
# 目录识别
dir_meta = MetaInfo(path.name)
# 媒体信息
mediainfo = None
# 查找目录下所有的文件
files = SystemUtils.list_files(path, settings.RMT_MEDIAEXT)
for file in files:
if self._event.is_set():
logger.info(f"媒体库刮削服务停止")
return
# 识别元数据
meta_info = MetaInfo(file.name) meta_info = MetaInfo(file.name)
if meta_info.type == MediaType.TV: # 合并
dir_info = MetaInfo(file.parent.parent.name) meta_info.merge(dir_meta)
else:
dir_info = MetaInfo(file.parent.name) # 识别媒体信息
meta_info.merge(dir_info) if not mediainfo:
# 优先读取本地nfo文件 # 优先读取本地nfo文件
tmdbid = None tmdbid = None
if meta_info.type == MediaType.MOVIE: if meta_info.type == MediaType.MOVIE:
@ -298,21 +340,45 @@ class LibraryScraper(_PluginBase):
if tv_nfo.exists(): if tv_nfo.exists():
tmdbid = self.__get_tmdbid_from_nfo(tv_nfo) tmdbid = self.__get_tmdbid_from_nfo(tv_nfo)
if tmdbid: if tmdbid:
# 按TMDBID识别
logger.info(f"读取到本地nfo文件的tmdbid{tmdbid}") logger.info(f"读取到本地nfo文件的tmdbid{tmdbid}")
# 识别媒体信息 mediainfo = self.chain.recognize_media(tmdbid=tmdbid, mtype=meta_info.type)
mediainfo: MediaInfo = self.chain.recognize_media(tmdbid=tmdbid, mtype=meta_info.type)
else: else:
# 识别媒体信息 # 按名称识别
mediainfo: MediaInfo = self.chain.recognize_media(meta=meta_info) mediainfo = self.chain.recognize_media(meta=meta_info)
if not mediainfo: if not mediainfo:
logger.warn(f"未识别到媒体信息:{file}") logger.warn(f"未识别到媒体信息:{file}")
continue continue
# 如果未开启新增已入库媒体是否跟随TMDB信息变化则根据tmdbid查询之前的title # 如果未开启新增已入库媒体是否跟随TMDB信息变化则根据tmdbid查询之前的title
if not settings.SCRAP_FOLLOW_TMDB: if not settings.SCRAP_FOLLOW_TMDB:
transfer_historys = self.transferhis.get_by(tmdbid=mediainfo.tmdb_id, transfer_historys = self.transferhis.get_by(tmdbid=mediainfo.tmdb_id,
type=mediainfo.type.value) type=mediainfo.type.value)
if transfer_historys: if transfer_historys:
mediainfo.title = transfer_historys[0].title mediainfo.title = transfer_historys[0].title
# 覆盖模式时提前删除nfo
if self._mode in ["force_all", "force_nfo"]:
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"]:
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))
# 开始刮削 # 开始刮削
self.chain.scrape_metadata(path=file, mediainfo=mediainfo) self.chain.scrape_metadata(path=file, mediainfo=mediainfo)

View File

@ -418,7 +418,7 @@ class MediaSyncDel(_PluginBase):
] ]
@eventmanager.register(EventType.WebhookMessage) @eventmanager.register(EventType.WebhookMessage)
def sync_del_by_webhook(self, event): def sync_del_by_webhook(self, event: Event):
""" """
emby删除媒体库同步删除历史记录 emby删除媒体库同步删除历史记录
webhook webhook
@ -507,7 +507,8 @@ class MediaSyncDel(_PluginBase):
season_num=season_num, season_num=season_num,
episode_num=episode_num) episode_num=episode_num)
def __sync_del(self, media_type, media_name, media_path, tmdb_id, season_num, episode_num): def __sync_del(self, media_type: str, media_name: str, media_path: str,
tmdb_id: int, season_num: int, episode_num: int):
""" """
执行删除逻辑 执行删除逻辑
""" """
@ -524,13 +525,6 @@ class MediaSyncDel(_PluginBase):
logger.info(f"媒体路径 {media_path} 已被排除,暂不处理") logger.info(f"媒体路径 {media_path} 已被排除,暂不处理")
return return
# 季数
if season_num and str(season_num).isdigit() and int(season_num) < 10:
season_num = f'0{season_num}'
# 集数
if episode_num and str(episode_num).isdigit() and int(episode_num) < 10:
episode_num = f'0{episode_num}'
# 查询转移记录 # 查询转移记录
msg, transfer_history = self.__get_transfer_his(media_type=media_type, msg, transfer_history = self.__get_transfer_his(media_type=media_type,
media_name=media_name, media_name=media_name,
@ -632,10 +626,19 @@ class MediaSyncDel(_PluginBase):
# 保存历史 # 保存历史
self.save_data("history", history) self.save_data("history", history)
def __get_transfer_his(self, media_type, media_name, tmdb_id, season_num, episode_num): def __get_transfer_his(self, media_type: str, media_name: str,
tmdb_id: int, season_num: int, episode_num: int):
""" """
查询转移记录 查询转移记录
""" """
# 季数
if season_num:
season_num = str(season_num).rjust(2, '0')
# 集数
if episode_num:
episode_num = str(episode_num).rjust(2, '0')
# 删除电影 # 删除电影
if media_type == "Movie" or media_type == "MOV": if media_type == "Movie" or media_type == "MOV":
msg = f'电影 {media_name} {tmdb_id}' msg = f'电影 {media_name} {tmdb_id}'
@ -1061,7 +1064,7 @@ class MediaSyncDel(_PluginBase):
return del_medias return del_medias
@staticmethod @staticmethod
def parse_jellyfin_log(last_time): def parse_jellyfin_log(last_time: datetime):
# 根据加入日期 降序排序 # 根据加入日期 降序排序
log_url = "{HOST}System/Logs/Log?name=log_%s.log&api_key={APIKEY}" % datetime.date.today().strftime("%Y%m%d") log_url = "{HOST}System/Logs/Log?name=log_%s.log&api_key={APIKEY}" % datetime.date.today().strftime("%Y%m%d")
log_res = Jellyfin().get_data(log_url) log_res = Jellyfin().get_data(log_url)
@ -1134,7 +1137,7 @@ class MediaSyncDel(_PluginBase):
return del_medias return del_medias
@staticmethod @staticmethod
def delete_media_file(filedir, filename): def delete_media_file(filedir: str, filename: str):
""" """
删除媒体文件空目录也会被删除 删除媒体文件空目录也会被删除
""" """
@ -1202,7 +1205,7 @@ class MediaSyncDel(_PluginBase):
title="媒体库同步删除完成!", userid=event.event_data.get("user")) title="媒体库同步删除完成!", userid=event.event_data.get("user"))
@staticmethod @staticmethod
def get_tmdbimage_url(path, prefix="w500"): def get_tmdbimage_url(path: str, prefix="w500"):
if not path: if not path:
return "" return ""
tmdb_image_url = f"https://{settings.TMDB_IMAGE_DOMAIN}" tmdb_image_url = f"https://{settings.TMDB_IMAGE_DOMAIN}"