Merge branch 'main' into main
This commit is contained in:
commit
6c59a5ebb0
@ -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):
|
||||||
"""
|
"""
|
||||||
据tmdbid、season、season_episode查询转移记录
|
据tmdbid、season、season_episode查询转移记录
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
据tmdbid、season、season_episode查询转移记录
|
据tmdbid、season、season_episode查询转移记录
|
||||||
"""
|
"""
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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} 刮削完成")
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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}"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user