This commit is contained in:
jxxghp
2023-09-25 08:40:19 +08:00
parent b3c0dc813b
commit da4ff99570
4 changed files with 294 additions and 276 deletions

View File

@ -131,130 +131,130 @@ class BestFilmVersion(_PluginBase):
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构 拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
""" """
return [ return [
{ {
'component': 'VForm', 'component': 'VForm',
'content': [ 'content': [
{ {
'component': 'VRow', 'component': 'VRow',
'content': [ 'content': [
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 3 'md': 3
}, },
'content': [ 'content': [
{ {
'component': 'VSwitch', 'component': 'VSwitch',
'props': { 'props': {
'model': 'enabled', 'model': 'enabled',
'label': '启用插件', 'label': '启用插件',
} }
} }
] ]
}, },
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 3 'md': 3
}, },
'content': [ 'content': [
{ {
'component': 'VSwitch', 'component': 'VSwitch',
'props': { 'props': {
'model': 'notify', 'model': 'notify',
'label': '发送通知', 'label': '发送通知',
} }
} }
] ]
}, },
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 3 'md': 3
}, },
'content': [ 'content': [
{ {
'component': 'VSwitch', 'component': 'VSwitch',
'props': { 'props': {
'model': 'only_once', 'model': 'only_once',
'label': '立即运行一次', 'label': '立即运行一次',
} }
} }
] ]
}, },
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 3 'md': 3
}, },
'content': [ 'content': [
{ {
'component': 'VSwitch', 'component': 'VSwitch',
'props': { 'props': {
'model': 'webhook_enabled', 'model': 'webhook_enabled',
'label': 'Webhook', 'label': 'Webhook',
} }
} }
] ]
} }
] ]
}, },
{ {
'component': 'VRow', 'component': 'VRow',
'content': [ 'content': [
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
}, },
'content': [ 'content': [
{ {
'component': 'VTextField', 'component': 'VTextField',
'props': { 'props': {
'model': 'cron', 'model': 'cron',
'label': '执行周期', 'label': '执行周期',
'placeholder': '5位cron表达式留空自动' 'placeholder': '5位cron表达式留空自动'
} }
} }
] ]
} }
] ]
}, },
{ {
'component': 'VRow', 'component': 'VRow',
'content': [ 'content': [
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
}, },
'content': [ 'content': [
{ {
'component': 'VAlert', 'component': 'VAlert',
'props': { 'props': {
'text': '支持主动定时获取媒体库数据和Webhook实时触发两种方式两者只能选其一' 'text': '支持主动定时获取媒体库数据和Webhook实时触发两种方式两者只能选其一'
'Webhook需要在媒体服务器设置发送Webhook报文。' 'Webhook需要在媒体服务器设置发送Webhook报文。'
'Plex使用主动获取时建议执行周期设置大于1小时' 'Plex使用主动获取时建议执行周期设置大于1小时'
'收藏Api调用Plex官网接口有频率限制。' '收藏Api调用Plex官网接口有频率限制。'
} }
} }
] ]
} }
] ]
} }
] ]
} }
], { ], {
"enabled": False, "enabled": False,
"notify": False, "notify": False,
"cron": "*/30 * * * *", "cron": "*/30 * * * *",
"webhook_enabled": False, "webhook_enabled": False,
"only_once": False "only_once": False
} }
def get_page(self) -> List[dict]: def get_page(self) -> List[dict]:
""" """
@ -386,81 +386,85 @@ class BestFilmVersion(_PluginBase):
# 读取历史记录 # 读取历史记录
history = self.get_data('history') or [] history = self.get_data('history') or []
all_item = [] # 媒体服务器类型,多个以,分隔
if not settings.MEDIASERVER:
return
media_servers = settings.MEDIASERVER.split(',')
# 读取收藏 # 读取收藏
if settings.MEDIASERVER == 'jellyfin': all_items = {}
self.jellyfin_get_items(all_item) for media_server in media_servers:
elif settings.MEDIASERVER == 'emby': if media_server == 'jellyfin':
self.emby_get_items(all_item) all_items['jellyfin'] = self.jellyfin_get_items()
else: elif media_server == 'emby':
resp = self.plex_get_watchlist() all_items['emby'] = self.emby_get_items()
if not resp: else:
return all_items['plex'] = self.plex_get_watchlist()
all_item.extend(resp)
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]
# all_item 根据电影名去重 # 处理所有结果
result = reduce(function, all_item, []) for server, all_item in all_items.items():
# all_item 根据电影名去重
for data in result: result = reduce(function, all_item, [])
# 检查缓存 for data in result:
if data.get('Name') in caches: # 检查缓存
continue if data.get('Name') in caches:
# 获取详情
if settings.MEDIASERVER == 'jellyfin':
item_info_resp = Jellyfin().get_iteminfo(itemid=data.get('Id'))
elif settings.MEDIASERVER == 'emby':
item_info_resp = Emby().get_iteminfo(itemid=data.get('Id'))
else:
item_info_resp = self.plex_get_iteminfo(itemid=data.get('Id'))
logger.info(f'BestFilmVersion插件 item打印 {item_info_resp}')
if not item_info_resp:
continue
# 只接受Movie类型
if data.get('Type') != 'Movie':
continue
# 获取tmdb_id
media_info_ids = item_info_resp.get('ExternalUrls')
if not media_info_ids:
continue
for media_info_id in media_info_ids:
if 'TheMovieDb' != media_info_id.get('Name'):
continue continue
tmdb_find_id = str(media_info_id.get('Url')).split('/')
tmdb_find_id.reverse() # 获取详情
tmdb_id = tmdb_find_id[0] if server == 'jellyfin':
# 识别媒体信息 item_info_resp = Jellyfin().get_iteminfo(itemid=data.get('Id'))
mediainfo: MediaInfo = self.chain.recognize_media(tmdbid=tmdb_id, mtype=MediaType.MOVIE) elif server == 'emby':
if not mediainfo: item_info_resp = Emby().get_iteminfo(itemid=data.get('Id'))
logger.warn(f'未识别到媒体信息,标题:{data.get("Name")}tmdbID{tmdb_id}') else:
item_info_resp = self.plex_get_iteminfo(itemid=data.get('Id'))
logger.info(f'BestFilmVersion插件 item打印 {item_info_resp}')
if not item_info_resp:
continue continue
# 添加订阅
self.subscribechain.add(mtype=MediaType.MOVIE, # 只接受Movie类型
title=mediainfo.title, if data.get('Type') != 'Movie':
year=mediainfo.year, continue
tmdbid=mediainfo.tmdb_id,
best_version=True, # 获取tmdb_id
username="收藏洗版", media_info_ids = item_info_resp.get('ExternalUrls')
exist_ok=True) if not media_info_ids:
# 加入缓存 continue
caches.append(data.get('Name')) for media_info_id in media_info_ids:
# 存储历史记录 if 'TheMovieDb' != media_info_id.get('Name'):
if mediainfo.tmdb_id not in [h.get("tmdbid") for h in history]: continue
history.append({ tmdb_find_id = str(media_info_id.get('Url')).split('/')
"title": mediainfo.title, tmdb_find_id.reverse()
"type": mediainfo.type.value, tmdb_id = tmdb_find_id[0]
"year": mediainfo.year, # 识别媒体信息
"poster": mediainfo.get_poster_image(), mediainfo: MediaInfo = self.chain.recognize_media(tmdbid=tmdb_id, mtype=MediaType.MOVIE)
"overview": mediainfo.overview, if not mediainfo:
"tmdbid": mediainfo.tmdb_id, logger.warn(f'未识别到媒体信息,标题:{data.get("Name")}tmdbID{tmdb_id}')
"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S") continue
}) # 添加订阅
self.subscribechain.add(mtype=MediaType.MOVIE,
title=mediainfo.title,
year=mediainfo.year,
tmdbid=mediainfo.tmdb_id,
best_version=True,
username="收藏洗版",
exist_ok=True)
# 加入缓存
caches.append(data.get('Name'))
# 存储历史记录
if mediainfo.tmdb_id not in [h.get("tmdbid") for h in history]:
history.append({
"title": mediainfo.title,
"type": mediainfo.type.value,
"year": mediainfo.year,
"poster": mediainfo.get_poster_image(),
"overview": mediainfo.overview,
"tmdbid": mediainfo.tmdb_id,
"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
})
# 保存历史记录 # 保存历史记录
self.save_data('history', history) self.save_data('history', history)
# 保存缓存 # 保存缓存
@ -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

@ -598,7 +598,7 @@ class DirMonitor(_PluginBase):
'rows': 5, 'rows': 5,
'placeholder': '每一行一个目录,支持两种配置方式:\n' 'placeholder': '每一行一个目录,支持两种配置方式:\n'
'监控目录\n' '监控目录\n'
'监控目录:转移目的目录' '监控目录:转移目的目录(需同时在媒体库目录中配置该目的目录)'
} }
} }
] ]

View File

@ -718,19 +718,21 @@ 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 media_server == 'emby':
del_medias = self.parse_emby_log(last_time) # 媒体服务器类型,多个以,分隔
elif media_server == 'jellyfin': if not settings.MEDIASERVER:
del_medias = self.parse_jellyfin_log(last_time)
elif media_server == 'plex':
# TODO plex解析日志
return return
media_servers = settings.MEDIASERVER.split(',')
for media_server in media_servers:
if media_server == 'emby':
del_medias.extend(self.parse_emby_log(last_time))
elif media_server == 'jellyfin':
del_medias.extend(self.parse_jellyfin_log(last_time))
elif media_server == 'plex':
# TODO plex解析日志
return
if not del_medias: if not del_medias:
logger.error("未解析到已删除媒体信息") logger.error("未解析到已删除媒体信息")

View File

@ -383,78 +383,86 @@ class SpeedLimiter(_PluginBase):
return return
# 当前播放的总比特率 # 当前播放的总比特率
total_bit_rate = 0 total_bit_rate = 0
# 查询播放中会话 # 媒体服务器类型,多个以,分隔
playing_sessions = [] if not settings.MEDIASERVER:
if settings.MEDIASERVER == "emby": return
req_url = "{HOST}emby/Sessions?api_key={APIKEY}" media_servers = settings.MEDIASERVER.split(',')
try: # 查询所有媒体服务器状态
res = Emby().get_data(req_url) for media_server in media_servers:
if res and res.status_code == 200: # 查询播放中会话
sessions = res.json() playing_sessions = []
for session in sessions: if media_server == "emby":
if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"): req_url = "{HOST}emby/Sessions?api_key={APIKEY}"
playing_sessions.append(session) try:
except Exception as e: res = Emby().get_data(req_url)
logger.error(f"获取Emby播放会话失败{str(e)}") if res and res.status_code == 200:
# 计算有效比特率 sessions = res.json()
for session in playing_sessions: for session in sessions:
# 设置了不限速范围则判断session ip是否在不限速范围内 if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"):
if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]: playing_sessions.append(session)
if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \ except Exception as e:
and session.get("NowPlayingItem", {}).get("MediaType") == "Video": logger.error(f"获取Emby播放会话失败{str(e)}")
total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0) continue
# 未设置不限速范围则默认不限速内网ip
elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0)
elif settings.MEDIASERVER == "jellyfin":
req_url = "{HOST}Sessions?api_key={APIKEY}"
try:
res = Jellyfin().get_data(req_url)
if res and res.status_code == 200:
sessions = res.json()
for session in sessions:
if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"):
playing_sessions.append(session)
except Exception as e:
logger.error(f"获取Jellyfin播放会话失败{str(e)}")
# 计算有效比特率
for session in playing_sessions:
# 设置了不限速范围则判断session ip是否在不限速范围内
if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]:
if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or []
for media_stream in media_streams:
total_bit_rate += int(media_stream.get("BitRate") or 0)
# 未设置不限速范围则默认不限速内网ip
elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or []
for media_stream in media_streams:
total_bit_rate += int(media_stream.get("BitRate") or 0)
elif settings.MEDIASERVER == "plex":
_plex = Plex().get_plex()
if _plex:
sessions = _plex.sessions()
for session in sessions:
bitrate = sum([m.bitrate or 0 for m in session.media])
playing_sessions.append({
"type": session.TAG,
"bitrate": bitrate,
"address": session.player.address
})
# 计算有效比特率 # 计算有效比特率
for session in playing_sessions: for session in playing_sessions:
# 设置了不限速范围则判断session ip是否在不限速范围内 # 设置了不限速范围则判断session ip是否在不限速范围内
if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]: if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]:
if not self.__allow_access(self._unlimited_ips, session.get("address")) \ if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0)
# 未设置不限速范围则默认不限速内网ip
elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
total_bit_rate += int(session.get("NowPlayingItem", {}).get("Bitrate") or 0)
elif media_server == "jellyfin":
req_url = "{HOST}Sessions?api_key={APIKEY}"
try:
res = Jellyfin().get_data(req_url)
if res and res.status_code == 200:
sessions = res.json()
for session in sessions:
if session.get("NowPlayingItem") and not session.get("PlayState", {}).get("IsPaused"):
playing_sessions.append(session)
except Exception as e:
logger.error(f"获取Jellyfin播放会话失败{str(e)}")
continue
# 计算有效比特率
for session in playing_sessions:
# 设置了不限速范围则判断session ip是否在不限速范围内
if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]:
if not self.__allow_access(self._unlimited_ips, session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or []
for media_stream in media_streams:
total_bit_rate += int(media_stream.get("BitRate") or 0)
# 未设置不限速范围则默认不限速内网ip
elif not IpUtils.is_private_ip(session.get("RemoteEndPoint")) \
and session.get("NowPlayingItem", {}).get("MediaType") == "Video":
media_streams = session.get("NowPlayingItem", {}).get("MediaStreams") or []
for media_stream in media_streams:
total_bit_rate += int(media_stream.get("BitRate") or 0)
elif media_server == "plex":
_plex = Plex().get_plex()
if _plex:
sessions = _plex.sessions()
for session in sessions:
bitrate = sum([m.bitrate or 0 for m in session.media])
playing_sessions.append({
"type": session.TAG,
"bitrate": bitrate,
"address": session.player.address
})
# 计算有效比特率
for session in playing_sessions:
# 设置了不限速范围则判断session ip是否在不限速范围内
if self._unlimited_ips["ipv4"] or self._unlimited_ips["ipv6"]:
if not self.__allow_access(self._unlimited_ips, session.get("address")) \
and session.get("type") == "Video":
total_bit_rate += int(session.get("bitrate") or 0)
# 未设置不限速范围则默认不限速内网ip
elif not IpUtils.is_private_ip(session.get("address")) \
and session.get("type") == "Video": and session.get("type") == "Video":
total_bit_rate += int(session.get("bitrate") or 0) total_bit_rate += int(session.get("bitrate") or 0)
# 未设置不限速范围则默认不限速内网ip
elif not IpUtils.is_private_ip(session.get("address")) \
and session.get("type") == "Video":
total_bit_rate += int(session.get("bitrate") or 0)
if total_bit_rate: if total_bit_rate:
# 开启智能限速计算上传限速 # 开启智能限速计算上传限速