diff --git a/app/plugins/bestfilmversion/__init__.py b/app/plugins/bestfilmversion/__init__.py index 1e516e6ac..69063912b 100644 --- a/app/plugins/bestfilmversion/__init__.py +++ b/app/plugins/bestfilmversion/__init__.py @@ -131,130 +131,130 @@ def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: 拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构 """ return [ - { - 'component': 'VForm', - 'content': [ - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'enabled', - 'label': '启用插件', - } - } - ] - }, - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'notify', - 'label': '发送通知', - } - } - ] - }, - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'only_once', - 'label': '立即运行一次', - } - } - ] - }, - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 3 - }, - 'content': [ - { - 'component': 'VSwitch', - 'props': { - 'model': 'webhook_enabled', - 'label': 'Webhook', - } - } - ] - } - ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - }, - 'content': [ - { - 'component': 'VTextField', - 'props': { - 'model': 'cron', - 'label': '执行周期', - 'placeholder': '5位cron表达式,留空自动' - } - } - ] - } - ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'props': { - 'cols': 12, - }, - 'content': [ - { - 'component': 'VAlert', - 'props': { - 'text': '支持主动定时获取媒体库数据和Webhook实时触发两种方式,两者只能选其一,' - 'Webhook需要在媒体服务器设置发送Webhook报文。' - 'Plex使用主动获取时,建议执行周期设置大于1小时,' - '收藏Api调用Plex官网接口,有频率限制。' - } - } - ] - } - ] - } - ] - } - ], { - "enabled": False, - "notify": False, - "cron": "*/30 * * * *", - "webhook_enabled": False, - "only_once": False - } + { + 'component': 'VForm', + 'content': [ + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'enabled', + 'label': '启用插件', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'notify', + 'label': '发送通知', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'only_once', + 'label': '立即运行一次', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 3 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'webhook_enabled', + 'label': 'Webhook', + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'cron', + 'label': '执行周期', + 'placeholder': '5位cron表达式,留空自动' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + }, + 'content': [ + { + 'component': 'VAlert', + 'props': { + 'text': '支持主动定时获取媒体库数据和Webhook实时触发两种方式,两者只能选其一,' + 'Webhook需要在媒体服务器设置发送Webhook报文。' + 'Plex使用主动获取时,建议执行周期设置大于1小时,' + '收藏Api调用Plex官网接口,有频率限制。' + } + } + ] + } + ] + } + ] + } + ], { + "enabled": False, + "notify": False, + "cron": "*/30 * * * *", + "webhook_enabled": False, + "only_once": False + } def get_page(self) -> List[dict]: """ @@ -386,81 +386,85 @@ def sync(self): # 读取历史记录 history = self.get_data('history') or [] - all_item = [] + # 媒体服务器类型,多个以,分隔 + if not settings.MEDIASERVER: + return + media_servers = settings.MEDIASERVER.split(',') + # 读取收藏 - 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 - all_item.extend(resp) + 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): 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 data in result: - # 检查缓存 - if data.get('Name') in caches: - continue - - # 获取详情 - 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')) + # 处理所有结果 + for server, all_item in all_items.items(): + # all_item 根据电影名去重 + result = reduce(function, all_item, []) + for data in result: + # 检查缓存 + if data.get('Name') in caches: + continue - logger.info(f'BestFilmVersion插件 item打印 {item_info_resp}') - if not item_info_resp: - continue + # 获取详情 + if server == 'jellyfin': + item_info_resp = Jellyfin().get_iteminfo(itemid=data.get('Id')) + elif server == 'emby': + item_info_resp = Emby().get_iteminfo(itemid=data.get('Id')) + else: + item_info_resp = self.plex_get_iteminfo(itemid=data.get('Id')) - # 只接受Movie类型 - if data.get('Type') != 'Movie': - continue + logger.info(f'BestFilmVersion插件 item打印 {item_info_resp}') + if not item_info_resp: + 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'): + # 只接受Movie类型 + if data.get('Type') != 'Movie': continue - tmdb_find_id = str(media_info_id.get('Url')).split('/') - tmdb_find_id.reverse() - tmdb_id = tmdb_find_id[0] - # 识别媒体信息 - mediainfo: MediaInfo = self.chain.recognize_media(tmdbid=tmdb_id, mtype=MediaType.MOVIE) - if not mediainfo: - logger.warn(f'未识别到媒体信息,标题:{data.get("Name")},tmdbID:{tmdb_id}') + + # 获取tmdb_id + media_info_ids = item_info_resp.get('ExternalUrls') + if not media_info_ids: 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") - }) + for media_info_id in media_info_ids: + if 'TheMovieDb' != media_info_id.get('Name'): + continue + tmdb_find_id = str(media_info_id.get('Url')).split('/') + tmdb_find_id.reverse() + tmdb_id = tmdb_find_id[0] + # 识别媒体信息 + mediainfo: MediaInfo = self.chain.recognize_media(tmdbid=tmdb_id, mtype=MediaType.MOVIE) + if not mediainfo: + logger.warn(f'未识别到媒体信息,标题:{data.get("Name")},tmdbID:{tmdb_id}') + 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) # 保存缓存 @@ -468,13 +472,14 @@ def function(y, x): finally: lock.release() - def jellyfin_get_items(self, all_item): + def jellyfin_get_items(self) -> List[dict]: # 获取所有user users_url = "{HOST}Users?&apikey={APIKEY}" users = self.get_users(Jellyfin().get_data(users_url)) if not users: logger.info(f"bestfilmversion/users_url: {users_url}") - return + return [] + all_items = [] for user in users: # 根据加入日期 降序排序 url = "{HOST}Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ @@ -490,14 +495,16 @@ def jellyfin_get_items(self, all_item): resp = self.get_items(Jellyfin().get_data(url)) if not resp: 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 get_users_url = "{HOST}Users?&api_key={APIKEY}" users = self.get_users(Emby().get_data(get_users_url)) if not users: - return + return [] + all_items = [] for user in users: # 根据加入日期 降序排序 url = "{HOST}emby/Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ @@ -512,7 +519,8 @@ def emby_get_items(self, all_item): resp = self.get_items(Emby().get_data(url)) if not resp: continue - all_item.extend(resp) + all_items.extend(resp) + return all_items @staticmethod def get_items(resp: Response): @@ -538,7 +546,7 @@ def get_users(resp: Response): return [] @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" \ f"&X-Plex-Container-Start=0&X-Plex-Container-Size=50" \ diff --git a/app/plugins/dirmonitor/__init__.py b/app/plugins/dirmonitor/__init__.py index b0e4396dd..ce6e4a89f 100644 --- a/app/plugins/dirmonitor/__init__.py +++ b/app/plugins/dirmonitor/__init__.py @@ -598,7 +598,7 @@ def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: 'rows': 5, 'placeholder': '每一行一个目录,支持两种配置方式:\n' '监控目录\n' - '监控目录:转移目的目录' + '监控目录:转移目的目录(需同时在媒体库目录中配置该目的目录)' } } ] diff --git a/app/plugins/mediasyncdel/__init__.py b/app/plugins/mediasyncdel/__init__.py index 5d355fbbe..43c12a8d6 100644 --- a/app/plugins/mediasyncdel/__init__.py +++ b/app/plugins/mediasyncdel/__init__.py @@ -718,19 +718,21 @@ def sync_del_by_log(self): """ # 读取历史记录 history = self.get_data('history') or [] - - # 媒体服务器类型 - media_server = settings.MEDIASERVER - last_time = self.get_data("last_time") del_medias = [] - if media_server == 'emby': - del_medias = self.parse_emby_log(last_time) - elif media_server == 'jellyfin': - del_medias = self.parse_jellyfin_log(last_time) - elif media_server == 'plex': - # TODO plex解析日志 + + # 媒体服务器类型,多个以,分隔 + if not settings.MEDIASERVER: 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: logger.error("未解析到已删除媒体信息") diff --git a/app/plugins/speedlimiter/__init__.py b/app/plugins/speedlimiter/__init__.py index 47cb72a1c..501d869a1 100644 --- a/app/plugins/speedlimiter/__init__.py +++ b/app/plugins/speedlimiter/__init__.py @@ -383,78 +383,86 @@ def check_playing_sessions(self, event: Event = None): return # 当前播放的总比特率 total_bit_rate = 0 - # 查询播放中会话 - playing_sessions = [] - if settings.MEDIASERVER == "emby": - req_url = "{HOST}emby/Sessions?api_key={APIKEY}" - try: - res = Emby().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"获取Emby播放会话失败:{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")) \ + # 媒体服务器类型,多个以,分隔 + if not settings.MEDIASERVER: + return + media_servers = settings.MEDIASERVER.split(',') + # 查询所有媒体服务器状态 + for media_server in media_servers: + # 查询播放中会话 + playing_sessions = [] + if media_server == "emby": + req_url = "{HOST}emby/Sessions?api_key={APIKEY}" + try: + res = Emby().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"获取Emby播放会话失败:{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": + 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) - # 未设置不限速范围,则默认不限速内网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 - }) + 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("address")) \ + 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": 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: # 开启智能限速计算上传限速