Merge remote-tracking branch 'origin/wlj0909' into wlj0909

This commit is contained in:
mayun110 2023-09-27 18:05:46 +08:00
commit a89bd8b816
11 changed files with 347 additions and 316 deletions

View File

@ -85,15 +85,18 @@ def delete_transfer_history(history_in: schemas.TransferHistory,
@router.post("/transfer", summary="历史记录重新转移", response_model=schemas.Response) @router.post("/transfer", summary="历史记录重新转移", response_model=schemas.Response)
def redo_transfer_history(history_in: schemas.TransferHistory, def redo_transfer_history(history_in: schemas.TransferHistory,
mtype: str, mtype: str = None,
new_tmdbid: int, new_tmdbid: int = None,
db: Session = Depends(get_db), db: Session = Depends(get_db),
_: schemas.TokenPayload = Depends(verify_token)) -> Any: _: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
历史记录重新转移 历史记录重新转移不输入 mtype new_tmdbid 自动使用文件名重新识别
""" """
if mtype and new_tmdbid:
state, errmsg = TransferChain(db).re_transfer(logid=history_in.id, state, errmsg = TransferChain(db).re_transfer(logid=history_in.id,
mtype=MediaType(mtype), tmdbid=new_tmdbid) mtype=MediaType(mtype), tmdbid=new_tmdbid)
else:
state, errmsg = TransferChain(db).re_transfer(logid=history_in.id)
if state: if state:
return schemas.Response(success=True) return schemas.Response(success=True)
else: else:

View File

@ -4,7 +4,7 @@ from typing import Optional, List, Tuple
from app.chain import ChainBase from app.chain import ChainBase
from app.core.context import Context, MediaInfo from app.core.context import Context, MediaInfo
from app.core.meta import MetaBase from app.core.meta import MetaBase
from app.core.metainfo import MetaInfo from app.core.metainfo import MetaInfo, MetaInfoPath
from app.log import logger from app.log import logger
from app.utils.string import StringUtils from app.utils.string import StringUtils
@ -38,12 +38,8 @@ class MediaChain(ChainBase):
""" """
logger.info(f'开始识别媒体信息,文件:{path} ...') logger.info(f'开始识别媒体信息,文件:{path} ...')
file_path = Path(path) file_path = Path(path)
# 上级目录元数据 # 元数据
dir_meta = MetaInfo(title=file_path.parent.name) file_meta = MetaInfoPath(file_path)
# 文件元数据,不包含后缀
file_meta = MetaInfo(title=file_path.stem)
# 合并元数据
file_meta.merge(dir_meta)
# 识别媒体信息 # 识别媒体信息
mediainfo = self.recognize_media(meta=file_meta) mediainfo = self.recognize_media(meta=file_meta)
if not mediainfo: if not mediainfo:

View File

@ -12,7 +12,7 @@ from app.chain.media import MediaChain
from app.core.config import settings from app.core.config import settings
from app.core.context import MediaInfo from app.core.context import MediaInfo
from app.core.meta import MetaBase from app.core.meta import MetaBase
from app.core.metainfo import MetaInfo from app.core.metainfo import MetaInfoPath
from app.db.downloadhistory_oper import DownloadHistoryOper from app.db.downloadhistory_oper import DownloadHistoryOper
from app.db.models.downloadhistory import DownloadHistory from app.db.models.downloadhistory import DownloadHistory
from app.db.models.transferhistory import TransferHistory from app.db.models.transferhistory import TransferHistory
@ -202,12 +202,8 @@ class TransferChain(ChainBase):
key=ProgressKey.FileTransfer) key=ProgressKey.FileTransfer)
if not meta: if not meta:
# 上级目录元数据 # 文件元数据
dir_meta = MetaInfo(title=file_path.parent.name) file_meta = MetaInfoPath(file_path)
# 文件元数据,不包含后缀
file_meta = MetaInfo(title=file_path.stem)
# 合并元数据
file_meta.merge(dir_meta)
else: else:
file_meta = meta file_meta = meta
@ -474,7 +470,8 @@ class TransferChain(ChainBase):
text=errmsg, userid=userid)) text=errmsg, userid=userid))
return return
def re_transfer(self, logid: int, mtype: MediaType, tmdbid: int) -> Tuple[bool, str]: def re_transfer(self, logid: int,
mtype: MediaType = None, tmdbid: int = None) -> Tuple[bool, str]:
""" """
根据历史记录重新识别转移只处理对应的src目录 根据历史记录重新识别转移只处理对应的src目录
:param logid: 历史记录ID :param logid: 历史记录ID
@ -492,11 +489,15 @@ class TransferChain(ChainBase):
return False, f"源目录不存在:{src_path}" return False, f"源目录不存在:{src_path}"
dest_path = Path(history.dest) if history.dest else None dest_path = Path(history.dest) if history.dest else None
# 查询媒体信息 # 查询媒体信息
if mtype and tmdbid:
mediainfo = self.recognize_media(mtype=mtype, tmdbid=tmdbid) mediainfo = self.recognize_media(mtype=mtype, tmdbid=tmdbid)
else:
meta = MetaInfoPath(src_path)
mediainfo = self.recognize_media(meta=meta)
if not mediainfo: if not mediainfo:
return False, f"未识别到媒体信息,类型:{mtype.value}tmdbid{tmdbid}" return False, f"未识别到媒体信息,类型:{mtype.value}tmdbid{tmdbid}"
# 重新执行转移 # 重新执行转移
logger.info(f"{mtype.value} {tmdbid} 识别为:{mediainfo.title_year}") logger.info(f"{src_path.name} 识别为:{mediainfo.title_year}")
# 更新媒体图片 # 更新媒体图片
self.obtain_images(mediainfo=mediainfo) self.obtain_images(mediainfo=mediainfo)

View File

@ -9,7 +9,7 @@ from app.core.meta.words import WordsMatcher
def MetaInfo(title: str, subtitle: str = None) -> MetaBase: def MetaInfo(title: str, subtitle: str = None) -> MetaBase:
""" """
媒体整理入口根据名称和副标题判断是哪种类型的识别返回对应对象 根据标题和副标题识别元数据
:param title: 标题种子名文件名 :param title: 标题种子名文件名
:param subtitle: 副标题描述 :param subtitle: 副标题描述
:return: MetaAnimeMetaVideo :return: MetaAnimeMetaVideo
@ -33,6 +33,20 @@ def MetaInfo(title: str, subtitle: str = None) -> MetaBase:
return meta return meta
def MetaInfoPath(path: Path) -> MetaBase:
"""
根据路径识别元数据
:param path: 路径
"""
# 上级目录元数据
dir_meta = MetaInfo(title=path.parent.name)
# 文件元数据,不包含后缀
file_meta = MetaInfo(title=path.stem)
# 合并元数据
file_meta.merge(dir_meta)
return file_meta
def is_anime(name: str) -> bool: def is_anime(name: str) -> bool:
""" """
判断是否为动漫 判断是否为动漫

View File

@ -536,16 +536,14 @@ class FileTransferModule(_ModuleBase):
@staticmethod @staticmethod
def get_library_path(path: Path): def get_library_path(path: Path):
""" """
根据目录查询其所在的媒体库目录 根据目录查询其所在的媒体库目录查询不到的返回输入目录
""" """
if not path: if not path:
return None return None
if not settings.LIBRARY_PATHS: if not settings.LIBRARY_PATHS:
return None return path
# 目的路径,多路径以,分隔 # 目的路径,多路径以,分隔
dest_paths = settings.LIBRARY_PATHS dest_paths = settings.LIBRARY_PATHS
if len(dest_paths) == 1:
return dest_paths[0]
for libpath in dest_paths: for libpath in dest_paths:
try: try:
if path.is_relative_to(libpath): if path.is_relative_to(libpath):
@ -553,7 +551,7 @@ class FileTransferModule(_ModuleBase):
except Exception as e: except Exception as e:
logger.debug(f"计算媒体库路径时出错:{e}") logger.debug(f"计算媒体库路径时出错:{e}")
continue continue
return None return path
@staticmethod @staticmethod
def get_target_path(in_path: Path = None) -> Optional[Path]: def get_target_path(in_path: Path = None) -> Optional[Path]:

View File

@ -386,33 +386,37 @@ class BestFilmVersion(_PluginBase):
# 读取历史记录 # 读取历史记录
history = self.get_data('history') or [] history = self.get_data('history') or []
all_item = [] # 媒体服务器类型,多个以,分隔
# 读取收藏 if not settings.MEDIASERVER:
if settings.MEDIASERVER == 'jellyfin':
self.jellyfin_get_items(all_item)
elif settings.MEDIASERVER == 'emby':
self.emby_get_items(all_item)
else:
resp = self.plex_get_watchlist()
if not resp:
return return
all_item.extend(resp) media_servers = settings.MEDIASERVER.split(',')
# 读取收藏
all_items = {}
for media_server in media_servers:
if media_server == 'jellyfin':
all_items['jellyfin'] = self.jellyfin_get_items()
elif media_server == 'emby':
all_items['emby'] = self.emby_get_items()
else:
all_items['plex'] = self.plex_get_watchlist()
def function(y, x): def function(y, x):
return y if (x['Name'] in [i['Name'] for i in y]) else (lambda z, u: (z.append(u), z))(y, x)[1] return y if (x['Name'] in [i['Name'] for i in y]) else (lambda z, u: (z.append(u), z))(y, x)[1]
# 处理所有结果
for server, all_item in all_items.items():
# all_item 根据电影名去重 # all_item 根据电影名去重
result = reduce(function, all_item, []) result = reduce(function, all_item, [])
for data in result: for data in result:
# 检查缓存 # 检查缓存
if data.get('Name') in caches: if data.get('Name') in caches:
continue continue
# 获取详情 # 获取详情
if settings.MEDIASERVER == 'jellyfin': if server == 'jellyfin':
item_info_resp = Jellyfin().get_iteminfo(itemid=data.get('Id')) item_info_resp = Jellyfin().get_iteminfo(itemid=data.get('Id'))
elif settings.MEDIASERVER == 'emby': elif server == 'emby':
item_info_resp = Emby().get_iteminfo(itemid=data.get('Id')) item_info_resp = Emby().get_iteminfo(itemid=data.get('Id'))
else: else:
item_info_resp = self.plex_get_iteminfo(itemid=data.get('Id')) item_info_resp = self.plex_get_iteminfo(itemid=data.get('Id'))
@ -468,13 +472,14 @@ class BestFilmVersion(_PluginBase):
finally: finally:
lock.release() lock.release()
def jellyfin_get_items(self, all_item): def jellyfin_get_items(self) -> List[dict]:
# 获取所有user # 获取所有user
users_url = "{HOST}Users?&apikey={APIKEY}" users_url = "{HOST}Users?&apikey={APIKEY}"
users = self.get_users(Jellyfin().get_data(users_url)) users = self.get_users(Jellyfin().get_data(users_url))
if not users: if not users:
logger.info(f"bestfilmversion/users_url: {users_url}") logger.info(f"bestfilmversion/users_url: {users_url}")
return return []
all_items = []
for user in users: for user in users:
# 根据加入日期 降序排序 # 根据加入日期 降序排序
url = "{HOST}Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ url = "{HOST}Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \
@ -490,14 +495,16 @@ class BestFilmVersion(_PluginBase):
resp = self.get_items(Jellyfin().get_data(url)) resp = self.get_items(Jellyfin().get_data(url))
if not resp: if not resp:
continue continue
all_item.extend(resp) all_items.extend(resp)
return all_items
def emby_get_items(self, all_item): def emby_get_items(self) -> List[dict]:
# 获取所有user # 获取所有user
get_users_url = "{HOST}Users?&api_key={APIKEY}" get_users_url = "{HOST}Users?&api_key={APIKEY}"
users = self.get_users(Emby().get_data(get_users_url)) users = self.get_users(Emby().get_data(get_users_url))
if not users: if not users:
return return []
all_items = []
for user in users: for user in users:
# 根据加入日期 降序排序 # 根据加入日期 降序排序
url = "{HOST}emby/Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ url = "{HOST}emby/Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \
@ -512,7 +519,8 @@ class BestFilmVersion(_PluginBase):
resp = self.get_items(Emby().get_data(url)) resp = self.get_items(Emby().get_data(url))
if not resp: if not resp:
continue continue
all_item.extend(resp) all_items.extend(resp)
return all_items
@staticmethod @staticmethod
def get_items(resp: Response): def get_items(resp: Response):
@ -538,7 +546,7 @@ class BestFilmVersion(_PluginBase):
return [] return []
@staticmethod @staticmethod
def plex_get_watchlist(): def plex_get_watchlist() -> List[dict]:
# 根据加入日期 降序排序 # 根据加入日期 降序排序
url = f"https://metadata.provider.plex.tv/library/sections/watchlist/all?type=1&sort=addedAt%3Adesc" \ url = f"https://metadata.provider.plex.tv/library/sections/watchlist/all?type=1&sort=addedAt%3Adesc" \
f"&X-Plex-Container-Start=0&X-Plex-Container-Size=50" \ f"&X-Plex-Container-Start=0&X-Plex-Container-Size=50" \

View File

@ -43,7 +43,7 @@ class BrushFlow(_PluginBase):
# 加载顺序 # 加载顺序
plugin_order = 21 plugin_order = 21
# 可使用的用户级别 # 可使用的用户级别
auth_level = 3 auth_level = 2
# 私有属性 # 私有属性
siteshelper = None siteshelper = None

View File

@ -15,7 +15,7 @@ from watchdog.observers.polling import PollingObserver
from app.chain.transfer import TransferChain from app.chain.transfer import TransferChain
from app.core.config import settings from app.core.config import settings
from app.core.context import MediaInfo from app.core.context import MediaInfo
from app.core.metainfo import MetaInfo from app.core.metainfo import MetaInfoPath
from app.db.downloadhistory_oper import DownloadHistoryOper from app.db.downloadhistory_oper import DownloadHistoryOper
from app.db.transferhistory_oper import TransferHistoryOper from app.db.transferhistory_oper import TransferHistoryOper
from app.log import logger from app.log import logger
@ -237,13 +237,8 @@ class DirMonitor(_PluginBase):
logger.info(f"{event_path} 已整理过") logger.info(f"{event_path} 已整理过")
return return
# 上级目录元数据 # 元数据
meta = MetaInfo(title=file_path.parent.name) file_meta = MetaInfoPath(file_path)
# 文件元数据,不包含后缀
file_meta = MetaInfo(title=file_path.stem)
# 合并元数据
file_meta.merge(meta)
if not file_meta.name: if not file_meta.name:
logger.error(f"{file_path.name} 无法识别有效信息") logger.error(f"{file_path.name} 无法识别有效信息")
return return
@ -343,7 +338,7 @@ class DirMonitor(_PluginBase):
} }
""" """
# 发送消息汇总 # 发送消息汇总
media_list = self._medias.get(mediainfo.title_year + " " + meta.season) or {} media_list = self._medias.get(mediainfo.title_year + " " + file_meta.season) or {}
if media_list: if media_list:
media_files = media_list.get("files") or [] media_files = media_list.get("files") or []
if media_files: if media_files:
@ -384,7 +379,7 @@ class DirMonitor(_PluginBase):
], ],
"time": datetime.now() "time": datetime.now()
} }
self._medias[mediainfo.title_year + " " + meta.season] = media_list self._medias[mediainfo.title_year + " " + file_meta.season] = media_list
# 汇总刷新媒体库 # 汇总刷新媒体库
if settings.REFRESH_MEDIASERVER: if settings.REFRESH_MEDIASERVER:
@ -598,7 +593,7 @@ class DirMonitor(_PluginBase):
'rows': 5, 'rows': 5,
'placeholder': '每一行一个目录,支持两种配置方式:\n' 'placeholder': '每一行一个目录,支持两种配置方式:\n'
'监控目录\n' '监控目录\n'
'监控目录:转移目的目录' '监控目录:转移目的目录(需同时在媒体库目录中配置该目的目录)'
} }
} }
] ]

View File

@ -718,16 +718,18 @@ class MediaSyncDel(_PluginBase):
""" """
# 读取历史记录 # 读取历史记录
history = self.get_data('history') or [] history = self.get_data('history') or []
# 媒体服务器类型
media_server = settings.MEDIASERVER
last_time = self.get_data("last_time") last_time = self.get_data("last_time")
del_medias = [] del_medias = []
# 媒体服务器类型,多个以,分隔
if not settings.MEDIASERVER:
return
media_servers = settings.MEDIASERVER.split(',')
for media_server in media_servers:
if media_server == 'emby': if media_server == 'emby':
del_medias = self.parse_emby_log(last_time) del_medias.extend(self.parse_emby_log(last_time))
elif media_server == 'jellyfin': elif media_server == 'jellyfin':
del_medias = self.parse_jellyfin_log(last_time) del_medias.extend(self.parse_jellyfin_log(last_time))
elif media_server == 'plex': elif media_server == 'plex':
# TODO plex解析日志 # TODO plex解析日志
return return

View File

@ -383,9 +383,15 @@ class SpeedLimiter(_PluginBase):
return return
# 当前播放的总比特率 # 当前播放的总比特率
total_bit_rate = 0 total_bit_rate = 0
# 媒体服务器类型,多个以,分隔
if not settings.MEDIASERVER:
return
media_servers = settings.MEDIASERVER.split(',')
# 查询所有媒体服务器状态
for media_server in media_servers:
# 查询播放中会话 # 查询播放中会话
playing_sessions = [] playing_sessions = []
if settings.MEDIASERVER == "emby": if media_server == "emby":
req_url = "{HOST}emby/Sessions?api_key={APIKEY}" req_url = "{HOST}emby/Sessions?api_key={APIKEY}"
try: try:
res = Emby().get_data(req_url) res = Emby().get_data(req_url)
@ -396,6 +402,7 @@ class SpeedLimiter(_PluginBase):
playing_sessions.append(session) playing_sessions.append(session)
except Exception as e: except Exception as e:
logger.error(f"获取Emby播放会话失败{str(e)}") logger.error(f"获取Emby播放会话失败{str(e)}")
continue
# 计算有效比特率 # 计算有效比特率
for session in playing_sessions: for session in playing_sessions:
# 设置了不限速范围则判断session ip是否在不限速范围内 # 设置了不限速范围则判断session ip是否在不限速范围内
@ -407,7 +414,7 @@ class SpeedLimiter(_PluginBase):
elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \ elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video": and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0) total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0)
elif settings.MEDIASERVER == "jellyfin": elif media_server == "jellyfin":
req_url = "{HOST}Sessions?api_key={APIKEY}" req_url = "{HOST}Sessions?api_key={APIKEY}"
try: try:
res = Jellyfin().get_data(req_url) res = Jellyfin().get_data(req_url)
@ -418,6 +425,7 @@ class SpeedLimiter(_PluginBase):
playing_sessions.append(session) playing_sessions.append(session)
except Exception as e: except Exception as e:
logger.error(f"获取Jellyfin播放会话失败{str(e)}") logger.error(f"获取Jellyfin播放会话失败{str(e)}")
continue
# 计算有效比特率 # 计算有效比特率
for session in playing_sessions: for session in playing_sessions:
# 设置了不限速范围则判断session ip是否在不限速范围内 # 设置了不限速范围则判断session ip是否在不限速范围内
@ -433,7 +441,7 @@ class SpeedLimiter(_PluginBase):
media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or [] media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or []
for media_stream in media_streams: for media_stream in media_streams:
total_bit_rate += int(media_stream.get("BitRate") or 0) total_bit_rate += int(media_stream.get("BitRate") or 0)
elif settings.MEDIASERVER == "plex": elif media_server == "plex":
_plex = Plex().get_plex() _plex = Plex().get_plex()
if _plex: if _plex:
sessions = _plex.sessions() sessions = _plex.sessions()

View File

@ -61,7 +61,9 @@ class SystemUtils:
移动 移动
""" """
try: try:
# 当前目录改名
temp = src.replace(src.parent / dest.name) temp = src.replace(src.parent / dest.name)
# 移动到目标目录
shutil.move(temp, dest) shutil.move(temp, dest)
return 0, "" return 0, ""
except Exception as err: except Exception as err:
@ -74,7 +76,11 @@ class SystemUtils:
硬链接 硬链接
""" """
try: try:
dest.hardlink_to(src) # link到当前目录并改名
tmp_path = src.parent / dest.name
tmp_path.hardlink_to(src)
# 移动到目标目录
shutil.move(tmp_path, dest)
return 0, "" return 0, ""
except Exception as err: except Exception as err:
print(str(err)) print(str(err))