mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-14 07:26:51 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
12
package.json
12
package.json
@@ -34,7 +34,7 @@
|
||||
"DoubanSync": {
|
||||
"name": "豆瓣想看",
|
||||
"description": "同步豆瓣想看数据,自动添加订阅。",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"icon": "douban.png",
|
||||
"author": "jxxghp",
|
||||
"level": 2
|
||||
@@ -58,7 +58,7 @@
|
||||
"DoubanRank": {
|
||||
"name": "豆瓣榜单订阅",
|
||||
"description": "监控豆瓣热门榜单,自动添加订阅。",
|
||||
"version": "1.2",
|
||||
"version": "1.4",
|
||||
"icon": "movie.jpg",
|
||||
"author": "jxxghp",
|
||||
"level": 2
|
||||
@@ -66,7 +66,7 @@
|
||||
"LibraryScraper": {
|
||||
"name": "媒体库刮削",
|
||||
"description": "定时对媒体库进行刮削,补齐缺失元数据和图片。",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"icon": "scraper.png",
|
||||
"author": "jxxghp",
|
||||
"level": 1
|
||||
@@ -210,7 +210,7 @@
|
||||
"BrushFlow": {
|
||||
"name": "站点刷流",
|
||||
"description": "自动托管刷流,将会提高对应站点的访问频率。",
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"icon": "brush.jpg",
|
||||
"author": "jxxghp",
|
||||
"level": 2
|
||||
@@ -258,7 +258,7 @@
|
||||
"CloudDiskDel": {
|
||||
"name": "云盘文件删除",
|
||||
"description": "媒体库删除strm文件后同步删除云盘资源。",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"icon": "clouddisk.png",
|
||||
"author": "thsrite",
|
||||
"level": 1
|
||||
@@ -322,7 +322,7 @@
|
||||
"FFmpegThumb": {
|
||||
"name": "FFmpeg缩略图",
|
||||
"description": "TheMovieDb没有背景图片时使用FFmpeg截取视频文件缩略图",
|
||||
"version": "1.0",
|
||||
"version": "1.2",
|
||||
"icon": "ffmpeg.png",
|
||||
"author": "jxxghp",
|
||||
"level": 1
|
||||
|
||||
@@ -31,7 +31,7 @@ class BrushFlow(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "brush.jpg"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
plugin_version = "1.1"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp"
|
||||
# 作者主页
|
||||
@@ -64,6 +64,7 @@ class BrushFlow(_PluginBase):
|
||||
_downloader = "qbittorrent"
|
||||
_disksize = 0
|
||||
_freeleech = "free"
|
||||
_hr = "no"
|
||||
_maxupspeed = 0
|
||||
_maxdlspeed = 0
|
||||
_maxdlcount = 0
|
||||
@@ -96,6 +97,7 @@ class BrushFlow(_PluginBase):
|
||||
self._downloader = config.get("downloader")
|
||||
self._disksize = config.get("disksize")
|
||||
self._freeleech = config.get("freeleech")
|
||||
self._hr = config.get("hr")
|
||||
self._maxupspeed = config.get("maxupspeed")
|
||||
self._maxdlspeed = config.get("maxdlspeed")
|
||||
self._maxdlcount = config.get("maxdlcount")
|
||||
@@ -411,6 +413,26 @@ class BrushFlow(_PluginBase):
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
"cols": 12,
|
||||
"md": 4
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'hr',
|
||||
'label': '排除H&R',
|
||||
'items': [
|
||||
{'title': '是', 'value': 'yes'},
|
||||
{'title': '否', 'value': 'no'},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
@@ -732,7 +754,28 @@ class BrushFlow(_PluginBase):
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
'component': 'VRow',
|
||||
'content': [
|
||||
{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
},
|
||||
'content': [
|
||||
{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'info',
|
||||
'variant': 'tonal',
|
||||
'text': '注意:排除H&R并不保证能完全适配所有站点(部分站点在列表页不显示H&R标志,但实际上是有H&R的),请注意核对使用!'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
], {
|
||||
@@ -740,7 +783,8 @@ class BrushFlow(_PluginBase):
|
||||
"notify": True,
|
||||
"onlyonce": False,
|
||||
"clear_task": False,
|
||||
"freeleech": "free"
|
||||
"freeleech": "free",
|
||||
"hr": "yes",
|
||||
}
|
||||
|
||||
def get_page(self) -> List[dict]:
|
||||
@@ -1215,6 +1259,7 @@ class BrushFlow(_PluginBase):
|
||||
"downloader": self._downloader,
|
||||
"disksize": self._disksize,
|
||||
"freeleech": self._freeleech,
|
||||
"hr": self._hr,
|
||||
"maxupspeed": self._maxupspeed,
|
||||
"maxdlspeed": self._maxdlspeed,
|
||||
"maxdlcount": self._maxdlcount,
|
||||
@@ -1284,6 +1329,9 @@ class BrushFlow(_PluginBase):
|
||||
continue
|
||||
if self._freeleech == "2xfree" and torrent.uploadvolumefactor != 2:
|
||||
continue
|
||||
# H&R
|
||||
if self._hr == "yes" and torrent.hit_and_run:
|
||||
continue
|
||||
# 包含规则
|
||||
if self._include and not re.search(r"%s" % self._include, torrent.title, re.I):
|
||||
continue
|
||||
|
||||
@@ -20,7 +20,7 @@ class CloudDiskDel(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "clouddisk.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.1"
|
||||
plugin_version = "1.2"
|
||||
# 插件作者
|
||||
plugin_author = "thsrite"
|
||||
# 作者主页
|
||||
@@ -84,9 +84,13 @@ class CloudDiskDel(_PluginBase):
|
||||
season_num = event_data.get("season_num")
|
||||
episode_num = event_data.get("episode_num")
|
||||
|
||||
# 不是网盘监控路径,直接排除
|
||||
cloud_file_flag = False
|
||||
|
||||
# 判断删除媒体路径是否与配置的媒体库路径相符,相符则继续删除,不符则跳过
|
||||
for library_path in list(self._paths.keys()):
|
||||
if str(media_path).startswith(library_path):
|
||||
cloud_file_flag = True
|
||||
# 替换网盘路径
|
||||
media_path = str(media_path).replace(library_path, self._paths.get(library_path))
|
||||
logger.info(f"获取到moviepilot本地云盘挂载路径 {media_path}")
|
||||
@@ -128,63 +132,64 @@ class CloudDiskDel(_PluginBase):
|
||||
|
||||
break
|
||||
|
||||
# 发送消息
|
||||
image = 'https://emby.media/notificationicon.png'
|
||||
media_type = MediaType.MOVIE if media_type in ["Movie", "MOV"] else MediaType.TV
|
||||
if self._notify:
|
||||
backrop_image = self.chain.obtain_specific_image(
|
||||
if cloud_file_flag:
|
||||
# 发送消息
|
||||
image = 'https://emby.media/notificationicon.png'
|
||||
media_type = MediaType.MOVIE if media_type in ["Movie", "MOV"] else MediaType.TV
|
||||
if self._notify:
|
||||
backrop_image = self.chain.obtain_specific_image(
|
||||
mediaid=tmdb_id,
|
||||
mtype=media_type,
|
||||
image_type=MediaImageType.Backdrop,
|
||||
season=season_num,
|
||||
episode=episode_num
|
||||
) or image
|
||||
|
||||
# 类型
|
||||
if media_type == MediaType.MOVIE:
|
||||
msg = f'电影 {media_name} {tmdb_id}'
|
||||
# 删除电视剧
|
||||
elif media_type == MediaType.TV and not season_num and not episode_num:
|
||||
msg = f'剧集 {media_name} {tmdb_id}'
|
||||
# 删除季 S02
|
||||
elif media_type == MediaType.TV and season_num and not episode_num:
|
||||
msg = f'剧集 {media_name} S{season_num} {tmdb_id}'
|
||||
# 删除剧集S02E02
|
||||
elif media_type == MediaType.TV and season_num and episode_num:
|
||||
msg = f'剧集 {media_name} S{season_num}E{episode_num} {tmdb_id}'
|
||||
else:
|
||||
msg = media_name
|
||||
|
||||
# 发送通知
|
||||
self.post_message(
|
||||
mtype=NotificationType.MediaServer,
|
||||
title="云盘同步删除任务完成",
|
||||
image=backrop_image,
|
||||
text=f"{msg}\n"
|
||||
f"时间 {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}"
|
||||
)
|
||||
|
||||
# 读取历史记录
|
||||
history = self.get_data('history') or []
|
||||
|
||||
# 获取poster
|
||||
poster_image = self.chain.obtain_specific_image(
|
||||
mediaid=tmdb_id,
|
||||
mtype=media_type,
|
||||
image_type=MediaImageType.Backdrop,
|
||||
season=season_num,
|
||||
episode=episode_num
|
||||
image_type=MediaImageType.Poster,
|
||||
) or image
|
||||
history.append({
|
||||
"type": media_type.value,
|
||||
"title": media_name,
|
||||
"path": media_path,
|
||||
"season": season_num if season_num and str(season_num).isdigit() else None,
|
||||
"episode": episode_num if episode_num and str(episode_num).isdigit() else None,
|
||||
"image": poster_image,
|
||||
"del_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
|
||||
})
|
||||
|
||||
# 类型
|
||||
if media_type == MediaType.MOVIE:
|
||||
msg = f'电影 {media_name} {tmdb_id}'
|
||||
# 删除电视剧
|
||||
elif media_type == MediaType.TV and not season_num and not episode_num:
|
||||
msg = f'剧集 {media_name} {tmdb_id}'
|
||||
# 删除季 S02
|
||||
elif media_type == MediaType.TV and season_num and not episode_num:
|
||||
msg = f'剧集 {media_name} S{season_num} {tmdb_id}'
|
||||
# 删除剧集S02E02
|
||||
elif media_type == MediaType.TV and season_num and episode_num:
|
||||
msg = f'剧集 {media_name} S{season_num}E{episode_num} {tmdb_id}'
|
||||
else:
|
||||
msg = media_name
|
||||
|
||||
# 发送通知
|
||||
self.post_message(
|
||||
mtype=NotificationType.MediaServer,
|
||||
title="云盘同步删除任务完成",
|
||||
image=backrop_image,
|
||||
text=f"{msg}\n"
|
||||
f"时间 {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}"
|
||||
)
|
||||
|
||||
# 读取历史记录
|
||||
history = self.get_data('history') or []
|
||||
|
||||
# 获取poster
|
||||
poster_image = self.chain.obtain_specific_image(
|
||||
mediaid=tmdb_id,
|
||||
mtype=media_type,
|
||||
image_type=MediaImageType.Poster,
|
||||
) or image
|
||||
history.append({
|
||||
"type": media_type.value,
|
||||
"title": media_name,
|
||||
"path": media_path,
|
||||
"season": season_num if season_num and str(season_num).isdigit() else None,
|
||||
"episode": episode_num if episode_num and str(episode_num).isdigit() else None,
|
||||
"image": poster_image,
|
||||
"del_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
|
||||
})
|
||||
|
||||
# 保存历史
|
||||
self.save_data("history", history)
|
||||
# 保存历史
|
||||
self.save_data("history", history)
|
||||
|
||||
def get_state(self) -> bool:
|
||||
return self._enabled
|
||||
|
||||
@@ -27,7 +27,7 @@ class DoubanRank(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "movie.jpg"
|
||||
# 插件版本
|
||||
plugin_version = "1.2"
|
||||
plugin_version = "1.4"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp"
|
||||
# 作者主页
|
||||
@@ -48,7 +48,7 @@ class DoubanRank(_PluginBase):
|
||||
_douban_address = {
|
||||
'movie-ustop': 'https://rsshub.app/douban/movie/ustop',
|
||||
'movie-weekly': 'https://rsshub.app/douban/movie/weekly',
|
||||
'movie-real-time': 'https://rsshub.app/douban/movie/weekly/subject_real_time_hotest',
|
||||
'movie-real-time': 'https://rsshub.app/douban/movie/weekly/movie_real_time_hotest',
|
||||
'show-domestic': 'https://rsshub.app/douban/movie/weekly/show_domestic',
|
||||
'movie-hot-gaia': 'https://rsshub.app/douban/movie/weekly/movie_hot_gaia',
|
||||
'tv-hot': 'https://rsshub.app/douban/movie/weekly/tv_hot',
|
||||
@@ -101,10 +101,8 @@ class DoubanRank(_PluginBase):
|
||||
logger.error(f"豆瓣榜单订阅服务启动失败,错误信息:{str(e)}")
|
||||
self.systemmessage.put(f"豆瓣榜单订阅服务启动失败,错误信息:{str(e)}")
|
||||
else:
|
||||
self._scheduler.add_job(func=self.__refresh_rss, trigger='date',
|
||||
run_date=datetime.datetime.now(
|
||||
tz=pytz.timezone(settings.TZ)) + datetime.timedelta(seconds=3)
|
||||
)
|
||||
self._scheduler.add_job(func=self.__refresh_rss, trigger=CronTrigger.from_crontab("0 8 * * *"),
|
||||
name="豆瓣榜单订阅")
|
||||
logger.info("豆瓣榜单订阅服务启动,周期:每天 08:00")
|
||||
|
||||
if self._onlyonce:
|
||||
@@ -484,12 +482,14 @@ class DoubanRank(_PluginBase):
|
||||
|
||||
title = rss_info.get('title')
|
||||
douban_id = rss_info.get('doubanid')
|
||||
year = rss_info.get('year')
|
||||
unique_flag = f"doubanrank: {title} (DB:{douban_id})"
|
||||
# 检查是否已处理过
|
||||
if unique_flag in [h.get("unique") for h in history]:
|
||||
continue
|
||||
# 元数据
|
||||
meta = MetaInfo(title)
|
||||
meta.year = year
|
||||
# 识别媒体信息
|
||||
if douban_id:
|
||||
# 识别豆瓣信息
|
||||
@@ -565,25 +565,36 @@ class DoubanRank(_PluginBase):
|
||||
items = rootNode.getElementsByTagName("item")
|
||||
for item in items:
|
||||
try:
|
||||
rss_info = {}
|
||||
|
||||
# 标题
|
||||
title = DomUtils.tag_value(item, "title", default="")
|
||||
# 链接
|
||||
link = DomUtils.tag_value(item, "link", default="")
|
||||
# 年份
|
||||
description = DomUtils.tag_value(item, "description", default="")
|
||||
|
||||
if not title and not link:
|
||||
logger.warn(f"条目标题和链接均为空,无法处理")
|
||||
continue
|
||||
rss_info['title'] = title
|
||||
rss_info['link'] = link
|
||||
|
||||
doubanid = re.findall(r"/(\d+)/", link)
|
||||
if doubanid:
|
||||
doubanid = doubanid[0]
|
||||
if doubanid and not str(doubanid).isdigit():
|
||||
logger.warn(f"解析的豆瓣ID格式不正确:{doubanid}")
|
||||
continue
|
||||
rss_info['doubanid'] = doubanid
|
||||
|
||||
# 匹配4位独立数字1900-2099年
|
||||
year = re.findall(r"\b(19\d{2}|20\d{2})\b", description)
|
||||
if year:
|
||||
rss_info['year'] = year[0]
|
||||
|
||||
# 返回对象
|
||||
ret_array.append({
|
||||
'title': title,
|
||||
'link': link,
|
||||
'doubanid': doubanid
|
||||
})
|
||||
ret_array.append(rss_info)
|
||||
except Exception as e1:
|
||||
logger.error("解析RSS条目失败:" + str(e1))
|
||||
continue
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Optional, Any, List, Dict, Tuple
|
||||
import pytz
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
from app.schemas.types import MediaType
|
||||
|
||||
from app.chain.download import DownloadChain
|
||||
from app.chain.search import SearchChain
|
||||
@@ -30,7 +31,7 @@ class DoubanSync(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "douban.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.1"
|
||||
plugin_version = "1.2"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp"
|
||||
# 作者主页
|
||||
@@ -475,6 +476,9 @@ class DoubanSync(_PluginBase):
|
||||
continue
|
||||
# 识别媒体信息
|
||||
meta = MetaInfo(title=title)
|
||||
douban_info = self.chain.douban_info(doubanid=douban_id)
|
||||
meta.year = douban_info.get("year")
|
||||
meta.type = MediaType.MOVIE if douban_info.get("type") == "movie" else MediaType.TV
|
||||
mediainfo = self.chain.recognize_media(meta=meta, doubanid=douban_id)
|
||||
if not mediainfo:
|
||||
logger.warn(f'未识别到媒体信息,标题:{title},豆瓣ID:{douban_id}')
|
||||
|
||||
@@ -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.2"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp"
|
||||
# 作者主页
|
||||
@@ -328,18 +331,18 @@ 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}")
|
||||
except Exception as err:
|
||||
logger.error(f"FFmpeg处理文件 {file_path} 时发生错误:{str(err)}")
|
||||
|
||||
def stop_service(self):
|
||||
"""
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user