diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index 8e6b3370..e1a0134a 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -866,16 +866,33 @@ class Emby(metaclass=Singleton): def get_data(self, url: str) -> Optional[Response]: """ - 自定义URL从媒体服务器获取数据,其中{HOST}、{APIKEY}、{USER}会被替换成实际的值 + 自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值 :param url: 请求地址 """ if not self._host or not self._apikey: return None - url = url.replace("{HOST}", self._host) \ - .replace("{APIKEY}", self._apikey) \ - .replace("{USER}", self.user) + url = url.replace("[HOST]", self._host) \ + .replace("[APIKEY]", self._apikey) \ + .replace("[USER]", self.user) try: - return RequestUtils().get_res(url=url) + return RequestUtils(content_type="application/json").get_res(url=url) + except Exception as e: + logger.error(f"连接Emby出错:" + str(e)) + return None + + def post_data(self, url: str, data: str = None): + """ + 自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值 + :param url: 请求地址 + :param data: 请求数据 + """ + if not self._host or not self._apikey: + return None + url = url.replace("[HOST]", self._host) \ + .replace("[APIKEY]", self._apikey) \ + .replace("[USER]", self.user) + try: + return RequestUtils(content_type="application/json").post_res(url=url, data=data) except Exception as e: logger.error(f"连接Emby出错:" + str(e)) return None diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index d6a7745c..f9e51dd0 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -74,7 +74,7 @@ class Jellyfin(metaclass=Singleton): continue libraries.append( schemas.MediaServerLibrary( - server="emby", + server="jellyfin", id=library.get("Id"), name=library.get("Name"), path=library.get("Path"), @@ -252,7 +252,7 @@ class Jellyfin(metaclass=Singleton): for item in res_items: item_tmdbid = item.get("ProviderIds", {}).get("Tmdb") mediaserver_item = schemas.MediaServerItem( - server="emby", + server="jellyfin", library=item.get("ParentId"), item_id=item.get("Id"), item_type=item.get("Type"), @@ -509,7 +509,7 @@ class Jellyfin(metaclass=Singleton): item = res.json() tmdbid = item.get("ProviderIds", {}).get("Tmdb") return schemas.MediaServerItem( - server="emby", + server="jellyfin", library=item.get("ParentId"), item_id=item.get("Id"), item_type=item.get("Type"), @@ -552,16 +552,33 @@ class Jellyfin(metaclass=Singleton): def get_data(self, url: str) -> Optional[Response]: """ - 自定义URL从媒体服务器获取数据,其中{HOST}、{APIKEY}、{USER}会被替换成实际的值 + 自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值 :param url: 请求地址 """ if not self._host or not self._apikey: return None - url = url.replace("{HOST}", self._host) \ - .replace("{APIKEY}", self._apikey) \ - .replace("{USER}", self.user) + url = url.replace("[HOST]", self._host) \ + .replace("[APIKEY]", self._apikey) \ + .replace("[USER]", self.user) try: return RequestUtils().get_res(url=url) except Exception as e: logger.error(f"连接Jellyfin出错:" + str(e)) return None + + def post_data(self, url: str, data: str = None): + """ + 自定义URL从媒体服务器获取数据,其中[HOST]、[APIKEY]、[USER]会被替换成实际的值 + :param url: 请求地址 + :param data: 请求数据 + """ + if not self._host or not self._apikey: + return None + url = url.replace("[HOST]", self._host) \ + .replace("[APIKEY]", self._apikey) \ + .replace("[USER]", self.user) + try: + return RequestUtils().post_res(url=url, data=data) + except Exception as e: + logger.error(f"连接Jellyfin出错:" + str(e)) + return None diff --git a/app/plugins/bestfilmversion/__init__.py b/app/plugins/bestfilmversion/__init__.py index 41a88f46..9d78cb4a 100644 --- a/app/plugins/bestfilmversion/__init__.py +++ b/app/plugins/bestfilmversion/__init__.py @@ -467,7 +467,7 @@ class BestFilmVersion(_PluginBase): def jellyfin_get_items(self) -> List[dict]: # 获取所有user - users_url = "{HOST}Users?&apikey={APIKEY}" + 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}") @@ -475,7 +475,7 @@ class BestFilmVersion(_PluginBase): all_items = [] for user in users: # 根据加入日期 降序排序 - url = "{HOST}Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ + url = "[HOST]Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ "&SortOrder=Descending" \ "&Filters=IsFavorite" \ "&Recursive=true" \ @@ -484,7 +484,7 @@ class BestFilmVersion(_PluginBase): "&ExcludeLocationTypes=Virtual" \ "&EnableTotalRecordCount=false" \ "&Limit=20" \ - "&apikey={APIKEY}" + "&apikey=[APIKEY]" resp = self.get_items(Jellyfin().get_data(url)) if not resp: continue @@ -493,14 +493,14 @@ class BestFilmVersion(_PluginBase): def emby_get_items(self) -> List[dict]: # 获取所有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)) if not users: return [] all_items = [] for user in users: # 根据加入日期 降序排序 - url = "{HOST}emby/Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ + url = "[HOST]emby/Users/" + user + "/Items?SortBy=DateCreated%2CSortName" \ "&SortOrder=Descending" \ "&Filters=IsFavorite" \ "&Recursive=true" \ @@ -508,7 +508,7 @@ class BestFilmVersion(_PluginBase): "&CollapseBoxSetItems=false" \ "&ExcludeLocationTypes=Virtual" \ "&EnableTotalRecordCount=false" \ - "&Limit=20&api_key={APIKEY}" + "&Limit=20&api_key=[APIKEY]" resp = self.get_items(Emby().get_data(url)) if not resp: continue diff --git a/app/plugins/cloudflarespeedtest/__init__.py b/app/plugins/cloudflarespeedtest/__init__.py index b6461d78..f520999a 100644 --- a/app/plugins/cloudflarespeedtest/__init__.py +++ b/app/plugins/cloudflarespeedtest/__init__.py @@ -725,9 +725,9 @@ class CloudflareSpeedTest(_PluginBase): new_entrys.append(host_entry) except Exception as err: err_hosts.append(host + "\n") - logger.error(f"{host} 格式转换错误:{str(err)}") + logger.error(f"[HOST] 格式转换错误:{str(err)}") # 推送实时消息 - self.systemmessage.put(f"{host} 格式转换错误:{str(err)}") + self.systemmessage.put(f"[HOST] 格式转换错误:{str(err)}") # 写入系统hosts if new_entrys: diff --git a/app/plugins/customhosts/__init__.py b/app/plugins/customhosts/__init__.py index 4a69b6d0..4cf5e762 100644 --- a/app/plugins/customhosts/__init__.py +++ b/app/plugins/customhosts/__init__.py @@ -199,9 +199,9 @@ class CustomHosts(_PluginBase): new_entrys.append(host_entry) except Exception as err: err_hosts.append(host + "\n") - logger.error(f"{host} 格式转换错误:{str(err)}") + logger.error(f"[HOST] 格式转换错误:{str(err)}") # 推送实时消息 - self.systemmessage.put(f"{host} 格式转换错误:{str(err)}") + self.systemmessage.put(f"[HOST] 格式转换错误:{str(err)}") # 写入系统hosts if new_entrys: diff --git a/app/plugins/mediasyncdel/__init__.py b/app/plugins/mediasyncdel/__init__.py index e311e28c..06fdcb59 100644 --- a/app/plugins/mediasyncdel/__init__.py +++ b/app/plugins/mediasyncdel/__init__.py @@ -1043,7 +1043,7 @@ class MediaSyncDel(_PluginBase): @staticmethod def parse_emby_log(last_time): - log_url = "{HOST}System/Logs/embyserver.txt?api_key={APIKEY}" + log_url = "[HOST]System/Logs/embyserver.txt?api_key=[APIKEY]" log_res = Emby().get_data(log_url) if not log_res or log_res.status_code != 200: logger.error("获取emby日志失败,请检查服务器配置") @@ -1116,7 +1116,7 @@ class MediaSyncDel(_PluginBase): @staticmethod 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) if not log_res or log_res.status_code != 200: logger.error("获取jellyfin日志失败,请检查服务器配置") diff --git a/app/plugins/personmeta/__init__.py b/app/plugins/personmeta/__init__.py index 733f08bc..ae1054eb 100644 --- a/app/plugins/personmeta/__init__.py +++ b/app/plugins/personmeta/__init__.py @@ -1,5 +1,7 @@ +import json import threading import time +from pathlib import Path from typing import Any, List, Dict, Tuple import zhconv @@ -11,6 +13,9 @@ from app.chain.tmdb import TmdbChain from app.core.config import settings from app.core.event import eventmanager, Event from app.log import logger +from app.modules.emby import Emby +from app.modules.jellyfin import Jellyfin +from app.modules.plex import Plex from app.plugins import _PluginBase from app.schemas import MediaInfo, MediaServerItem from app.schemas.types import EventType, MediaType @@ -223,10 +228,6 @@ class PersonMeta(_PluginBase): if not existsinfo or not existsinfo.itemid: logger.warn(f"演职人员刮削 {mediainfo.title_year} 在媒体库中不存在") return - # 初始化媒体服务器 - if existsinfo.server == "plex": - logger.warn(f"演职人员刮削 不支持{existsinfo.server}媒体服务器") - return # 查询条目详情 iteminfo = self.mschain.iteminfo(server=existsinfo.server, item_id=existsinfo.itemid) if not iteminfo: @@ -243,9 +244,6 @@ class PersonMeta(_PluginBase): if not settings.MEDIASERVER: return for server in settings.MEDIASERVER.split(","): - if server == "plex": - logger.warn(f"演职人员刮削 不支持{server}媒体服务器") - continue # 扫描所有媒体库 logger.info(f"开始刮削服务器 {server} 的演员信息 ...") for library in self.mschain.librarys(server): @@ -291,9 +289,307 @@ class PersonMeta(_PluginBase): # 下载图片 # 更新演员图片 + + @staticmethod + def get_iteminfo(server: str, itemid: str) -> dict: + """ + 获得媒体项详情 + """ + + def __get_emby_iteminfo() -> dict: + """ + 获得Emby媒体项详情 + """ + try: + url = f'[HOST]emby/Users/[USER]/Items/{itemid}?' \ + f'Fields=ChannelMappingInfo&api_key=[APIKEY]' + res = Emby().get_data(url=url) + if res: + return res.json() + except Exception as err: + logger.error(f"获取Emby媒体项详情失败:{err}") + return {} + + def __get_jellyfin_iteminfo() -> dict: + """ + 获得Jellyfin媒体项详情 + """ + try: + url = f'[HOST]Users/[USER]/Items/{itemid}?Fields=ChannelMappingInfo&api_key=[APIKEY]' + res = Jellyfin().get_data(url=url) + if res: + result = res.json() + if result: + result['FileName'] = Path(result['Path']).name + return result + except Exception as err: + logger.error(f"获取Jellyfin媒体项详情失败:{err}") + return {} + + def __get_plex_iteminfo() -> dict: + """ + 获得Plex媒体项详情 + """ + iteminfo = {} + try: + plexitem = Plex().get_plex().library.fetchItem(ekey=itemid) + if 'movie' in plexitem.METADATA_TYPE: + iteminfo['Type'] = 'Movie' + iteminfo['IsFolder'] = False + elif 'episode' in plexitem.METADATA_TYPE: + iteminfo['Type'] = 'Series' + iteminfo['IsFolder'] = False + if 'show' in plexitem.TYPE: + iteminfo['ChildCount'] = plexitem.childCount + iteminfo['Name'] = plexitem.title + iteminfo['Id'] = plexitem.key + iteminfo['ProductionYear'] = plexitem.year + iteminfo['ProviderIds'] = {} + for guid in plexitem.guids: + idlist = str(guid.id).split(sep='://') + if len(idlist) < 2: + continue + iteminfo['ProviderIds'][idlist[0]] = idlist[1] + for location in plexitem.locations: + iteminfo['Path'] = location + iteminfo['FileName'] = Path(location).name + iteminfo['Overview'] = plexitem.summary + iteminfo['CommunityRating'] = plexitem.audienceRating + return iteminfo + except Exception as err: + logger.error(f"获取Plex媒体项详情失败:{err}") + return {} + + if server == "emby": + return __get_emby_iteminfo() + elif server == "jellyfin": + return __get_jellyfin_iteminfo() + else: + return __get_plex_iteminfo() + + @staticmethod + def get_items(server: str, parentid: str) -> dict: + """ + 获得媒体的所有子媒体项 + """ pass - def __get_chinese_name(self, person: dict): + def __get_emby_items() -> dict: + """ + 获得Emby媒体的所有子媒体项 + """ + try: + if parentid: + url = f'[HOST]emby/Users/[USER]/Items?ParentId={parentid}&api_key=[APIKEY]' + else: + url = '[HOST]emby/Users/[USER]/Items?api_key=[APIKEY]' + res = Emby().get_data(url=url) + if res: + return res.json() + except Exception as err: + logger.error(f"获取Emby媒体的所有子媒体项失败:{err}") + return {} + + def __get_jellyfin_items() -> dict: + """ + 获得Jellyfin媒体的所有子媒体项 + """ + try: + if parentid: + url = f'[HOST]Users/[USER]/Items?ParentId={parentid}&api_key=[APIKEY]' + else: + url = '[HOST]Users/[USER]/Items?api_key=[APIKEY]' + res = Jellyfin().get_data(url=url) + if res: + return res.json() + except Exception as err: + logger.error(f"获取Jellyfin媒体的所有子媒体项失败:{err}") + return {} + + def __get_plex_items() -> dict: + """ + 获得Plex媒体的所有子媒体项 + """ + items = {} + try: + plex = Plex().get_plex() + items['Items'] = [] + if parentid: + if type and 'Season' in type: + plexitem = plex.library.fetchItem(ekey=parentid) + items['Items'] = [] + for season in plexitem.seasons(): + item = { + 'Name': season.title, + 'Id': season.key, + 'IndexNumber': season.seasonNumber, + 'Overview': season.summary + } + items['Items'].append(item) + elif type and 'Episode' in type: + plexitem = plex.library.fetchItem(ekey=parentid) + items['Items'] = [] + for episode in plexitem.episodes(): + item = { + 'Name': episode.title, + 'Id': episode.key, + 'IndexNumber': episode.episodeNumber, + 'Overview': episode.summary, + 'CommunityRating': episode.audienceRating + } + items['Items'].append(item) + else: + plexitems = plex.library.sectionByID(sectionID=parentid) + for plexitem in plexitems.all(): + item = {} + if 'movie' in plexitem.METADATA_TYPE: + item['Type'] = 'Movie' + item['IsFolder'] = False + elif 'episode' in plexitem.METADATA_TYPE: + item['Type'] = 'Series' + item['IsFolder'] = False + item['Name'] = plexitem.title + item['Id'] = plexitem.key + items['Items'].append(item) + else: + plexitems = plex.library.sections() + for plexitem in plexitems: + item = {} + if 'Directory' in plexitem.TAG: + item['Type'] = 'Folder' + item['IsFolder'] = True + elif 'movie' in plexitem.METADATA_TYPE: + item['Type'] = 'Movie' + item['IsFolder'] = False + elif 'episode' in plexitem.METADATA_TYPE: + item['Type'] = 'Series' + item['IsFolder'] = False + item['Name'] = plexitem.title + item['Id'] = plexitem.key + items['Items'].append(item) + return items + except Exception as err: + logger.error(f"获取Plex媒体的所有子媒体项失败:{err}") + return {} + + if server == "emby": + return __get_emby_items() + elif server == "jellyfin": + return __get_jellyfin_items() + else: + return __get_plex_items() + + @staticmethod + def set_iteminfo(server: str, itemid: str, iteminfo: dict): + """ + 更新媒体项详情 + """ + + def __set_emby_iteminfo(): + """ + 更新Emby媒体项详情 + """ + try: + res = Emby().post_data( + url=f'[HOST]emby/Items/{itemid}?api_key=[APIKEY]', + data=json.dumps(iteminfo) + ) + return True if res else False + except Exception as err: + logger.error(f"更新Emby媒体项详情失败:{err}") + return False + + def __set_jellyfin_iteminfo(): + """ + 更新Jellyfin媒体项详情 + """ + try: + res = Jellyfin().post_data( + url=f'[HOST]Items/{itemid}?api_key=[APIKEY]', + data=json.dumps(iteminfo) + ) + return True if res else False + except Exception as err: + logger.error(f"更新Jellyfin媒体项详情失败:{err}") + return False + + def __set_plex_iteminfo(): + """ + 更新Plex媒体项详情 + """ + try: + plexitem = Plex().get_plex().library.fetchItem(ekey=itemid) + if 'CommunityRating' in iteminfo: + edits = { + 'audienceRating.value': iteminfo['CommunityRating'], + 'audienceRating.locked': 1 + } + plexitem.edit(**edits) + plexitem.editTitle(iteminfo['Name']).editSummary(iteminfo['Overview']).reload() + return True + except Exception as err: + logger.error(f"更新Plex媒体项详情失败:{err}") + return False + + if server == "emby": + return __set_emby_iteminfo() + elif server == "jellyfin": + return __set_jellyfin_iteminfo() + else: + return __set_plex_iteminfo() + + @staticmethod + def __set_item_image(server: str, itemid: str, imageurl: str): + """ + 更新媒体项图片 + """ + + def __set_emby_item_image(): + """ + 更新Emby媒体项图片 + """ + try: + url = f'[HOST]emby/Items/{itemid}/Images/Primary/0/Url?api_key=[APIKEY]' + data = json.dumps({'Url': imageurl}) + res = Emby().post_data(url=url, data=data) + return True if res else False + except Exception as result: + logger.error(f"更新Emby媒体项图片失败:{result}") + return False + + def __set_jellyfin_item_image(): + """ + 更新Jellyfin媒体项图片 + """ + try: + url = f'[HOST]Items/{itemid}/RemoteImages/Download?' \ + f'Type=Primary&ImageUrl={imageurl}&ProviderName=TheMovieDb&api_key=[APIKEY]' + res = Jellyfin().post_data(url=url) + return True if res else None + except Exception as err: + logger.error(f"更新Jellyfin媒体项图片失败:{err}") + return False + + def __set_plex_item_image(): + """ + 更新Plex媒体项图片 + """ + try: + plexitem = Plex().get_plex().library.fetchItem(ekey=itemid) + plexitem.uploadPoster(url=imageurl) + return True + except Exception as err: + logger.error(f"更新Plex媒体项图片失败:{err}") + return False + + if server == "emby": + return __set_emby_item_image() + elif server == "jellyfin": + return __set_jellyfin_item_image() + else: + return __set_plex_item_image() + + def __get_chinese_name(self, person: dict) -> str: """ 获取TMDB别名中的中文名 """ diff --git a/app/plugins/speedlimiter/__init__.py b/app/plugins/speedlimiter/__init__.py index e019b491..3f74c7c4 100644 --- a/app/plugins/speedlimiter/__init__.py +++ b/app/plugins/speedlimiter/__init__.py @@ -396,7 +396,7 @@ class SpeedLimiter(_PluginBase): # 查询播放中会话 playing_sessions = [] if media_server == "emby": - req_url = "{HOST}emby/Sessions?api_key={APIKEY}" + req_url = "[HOST]emby/Sessions?api_key=[APIKEY]" try: res = Emby().get_data(req_url) if res and res.status_code == 200: @@ -419,7 +419,7 @@ class SpeedLimiter(_PluginBase): 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}" + req_url = "[HOST]Sessions?api_key=[APIKEY]" try: res = Jellyfin().get_data(req_url) if res and res.status_code == 200: