From cde105854a0d5ee350881a410ba836bed1f22366 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 7 Aug 2023 06:59:00 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20=5F=5Finit=5F=5F.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/plugins/chatgpt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/plugins/chatgpt/__init__.py b/app/plugins/chatgpt/__init__.py index c0e06958..1d6eb938 100644 --- a/app/plugins/chatgpt/__init__.py +++ b/app/plugins/chatgpt/__init__.py @@ -143,7 +143,7 @@ class ChatGPT(_PluginBase): return response = self.openai.get_response(text=text, userid=userid) if response: - self.post_message(channel=channel, title=text, userid=userid) + self.post_message(channel=channel, title=response, userid=userid) def stop_service(self): """ From f46af991cdcf928cf8c6640fddf51b84da5f8be6 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 7 Aug 2023 08:30:06 +0800 Subject: [PATCH 2/6] fix speedlimiter --- app/modules/qbittorrent/qbittorrent.py | 10 + app/modules/transmission/transmission.py | 10 + app/plugins/cloudflarespeedtest/__init__.py | 5 +- app/plugins/doubanrank/__init__.py | 9 +- app/plugins/libraryscraper/__init__.py | 10 +- app/plugins/speedlimiter/__init__.py | 192 +++++++++++++++++++- 6 files changed, 217 insertions(+), 19 deletions(-) diff --git a/app/modules/qbittorrent/qbittorrent.py b/app/modules/qbittorrent/qbittorrent.py index f4d9f20d..d341f38e 100644 --- a/app/modules/qbittorrent/qbittorrent.py +++ b/app/modules/qbittorrent/qbittorrent.py @@ -106,6 +106,8 @@ class Qbittorrent(metaclass=Singleton): :param ids: 种子Hash列表 :param tag: 标签内容 """ + if not self.qbc: + return False try: self.qbc.torrents_delete_tags(torrent_hashes=ids, tags=tag) return True @@ -129,6 +131,8 @@ class Qbittorrent(metaclass=Singleton): """ 设置强制作种 """ + if not self.qbc: + return try: self.qbc.torrents_set_force_start(enable=True, torrent_hashes=ids) except Exception as err: @@ -266,6 +270,8 @@ class Qbittorrent(metaclass=Singleton): """ 获取种子文件清单 """ + if not self.qbc: + return None try: return self.qbc.torrents_files(torrent_hash=tid) except Exception as err: @@ -276,6 +282,8 @@ class Qbittorrent(metaclass=Singleton): """ 设置下载文件的状态,priority为0为不下载,priority为1为下载 """ + if not self.qbc: + return False if not kwargs.get("torrent_hash") or not kwargs.get("file_ids"): return False try: @@ -291,6 +299,8 @@ class Qbittorrent(metaclass=Singleton): """ 获取传输信息 """ + if not self.qbc: + return None try: return self.qbc.transfer_info() except Exception as err: diff --git a/app/modules/transmission/transmission.py b/app/modules/transmission/transmission.py index 3d6577ab..e0dfe222 100644 --- a/app/modules/transmission/transmission.py +++ b/app/modules/transmission/transmission.py @@ -115,6 +115,8 @@ class Transmission(metaclass=Singleton): """ 设置种子标签 """ + if not self.trc: + return False if not ids or not tags: return False try: @@ -138,6 +140,8 @@ class Transmission(metaclass=Singleton): :param cookie: 站点Cookie用于辅助下载种子 :return: Torrent """ + if not self.trc: + return None try: return self.trc.add_torrent(torrent=content, download_dir=download_dir, @@ -193,6 +197,8 @@ class Transmission(metaclass=Singleton): """ 获取种子文件列表 """ + if not self.trc: + return None if not tid: return None try: @@ -209,6 +215,8 @@ class Transmission(metaclass=Singleton): """ 设置下载文件的状态 """ + if not self.trc: + return False try: self.trc.change_torrent(ids=tid, files_wanted=file_ids) return True @@ -220,6 +228,8 @@ class Transmission(metaclass=Singleton): """ 获取传输信息 """ + if not self.trc: + return None try: return self.trc.session_stats() except Exception as err: diff --git a/app/plugins/cloudflarespeedtest/__init__.py b/app/plugins/cloudflarespeedtest/__init__.py index fe11f290..aedb0bf6 100644 --- a/app/plugins/cloudflarespeedtest/__init__.py +++ b/app/plugins/cloudflarespeedtest/__init__.py @@ -87,8 +87,9 @@ class CloudflareSpeedTest(_PluginBase): if self._onlyonce: logger.info(f"Cloudflare CDN优选服务启动,立即运行一次") - self._scheduler.add_job(self.__cloudflareSpeedTest, 'date', - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3)) + self._scheduler.add_job(func=self.__cloudflareSpeedTest, trigger='date', + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3), + name="Cloudflare优选") # 关闭一次性开关 self._onlyonce = False self.__update_config() diff --git a/app/plugins/doubanrank/__init__.py b/app/plugins/doubanrank/__init__.py index dd9b2f4d..35709df9 100644 --- a/app/plugins/doubanrank/__init__.py +++ b/app/plugins/doubanrank/__init__.py @@ -88,13 +88,16 @@ class DoubanRank(_PluginBase): if self._cron: logger.info(f"豆瓣榜单订阅服务启动,周期:{self._cron}") try: - self._scheduler.add_job(self.__refresh_rss, - CronTrigger.from_crontab(self._cron)) + self._scheduler.add_job(func=self.__refresh_rss, + trigger=CronTrigger.from_crontab(self._cron), + name="豆瓣榜单订阅") except Exception as e: logger.error(f"豆瓣榜单订阅服务启动失败,错误信息:{str(e)}") self.systemmessage.put(f"豆瓣榜单订阅服务启动失败,错误信息:{str(e)}") else: - self._scheduler.add_job(self.__refresh_rss, CronTrigger.from_crontab("0 8 * * *")) + self._scheduler.add_job(func=self.__refresh_rss, + trigger=CronTrigger.from_crontab("0 8 * * *"), + name="豆瓣榜单订阅") logger.info("豆瓣榜单订阅服务启动,周期:每天 08:00") if self._scheduler.get_jobs(): diff --git a/app/plugins/libraryscraper/__init__.py b/app/plugins/libraryscraper/__init__.py index ef3ae102..3efe8e7a 100644 --- a/app/plugins/libraryscraper/__init__.py +++ b/app/plugins/libraryscraper/__init__.py @@ -66,15 +66,17 @@ class LibraryScraper(_PluginBase): if self._cron: logger.info(f"媒体库刮削服务启动,周期:{self._cron}") try: - self._scheduler.add_job(self.__libraryscraper, - CronTrigger.from_crontab(self._cron)) + self._scheduler.add_job(func=self.__libraryscraper, + trigger=CronTrigger.from_crontab(self._cron), + name="媒体库刮削") except Exception as e: logger.error(f"媒体库刮削服务启动失败,原因:{e}") self.systemmessage.put(f"媒体库刮削服务启动失败,原因:{e}") else: logger.info(f"媒体库刮削服务启动,周期:每7天") - self._scheduler.add_job(self.__libraryscraper, - CronTrigger.from_crontab("0 0 */7 * *")) + self._scheduler.add_job(func=self.__libraryscraper, + trigger=CronTrigger.from_crontab("0 0 */7 * *"), + name="媒体库刮削") if self._scheduler.get_jobs(): # 启动服务 self._scheduler.print_jobs() diff --git a/app/plugins/speedlimiter/__init__.py b/app/plugins/speedlimiter/__init__.py index 5a85045b..8da88fd9 100644 --- a/app/plugins/speedlimiter/__init__.py +++ b/app/plugins/speedlimiter/__init__.py @@ -4,6 +4,8 @@ from apscheduler.schedulers.background import BackgroundScheduler from app.core.config import settings from app.log import logger +from app.modules.qbittorrent import Qbittorrent +from app.modules.transmission import Transmission from app.plugins import _PluginBase @@ -31,22 +33,32 @@ class SpeedLimiter(_PluginBase): # 私有属性 _scheduler = None + _qb = None + _tr = None _enabled: bool = False _notify: bool = False - _bandwidth: int = 0 _interval: int = 60 + _downloader: list = [] + _play_up_speed: float = 0 + _play_down_speed: float = 0 + _noplay_up_speed: float = 0 + _noplay_down_speed: float = 0 def init_plugin(self, config: dict = None): # 读取配置 if config: self._enabled = config.get("enabled") self._notify = config.get("notify") - try: - # 总带宽 - self._bandwidth = int(float(config.get("bandwidth") or 0)) * 1000000 - except Exception as e: - logger.error(f"总带宽配置错误:{e}") - self._bandwidth = 0 + self._play_up_speed = float(config.get("play_up_speed")) if config.get("play_up_speed") else 0 + self._play_down_speed = float(config.get("play_down_speed")) if config.get("play_down_speed") else 0 + self._noplay_up_speed = float(config.get("noplay_up_speed")) if config.get("noplay_up_speed") else 0 + self._noplay_down_speed = float(config.get("noplay_down_speed")) if config.get("noplay_down_speed") else 0 + self._downloader = config.get("downloader") or [] + if self._downloader: + if 'qbittorrent' in self._downloader: + self._qb = Qbittorrent() + if 'transmission' in self._downloader: + self._tr = Transmission() # 移出现有任务 self.stop_service() @@ -56,10 +68,11 @@ class SpeedLimiter(_PluginBase): self._scheduler = BackgroundScheduler(timezone=settings.TZ) self._scheduler.add_job(func=self.__check_playing_sessions, trigger='interval', - seconds=self._interval) + seconds=self._interval, + name="播放限速检查") self._scheduler.print_jobs() self._scheduler.start() - logger.info("播放限速服务启动") + logger.info("播放限速检查服务启动") def get_state(self) -> bool: return self._enabled @@ -72,7 +85,159 @@ class SpeedLimiter(_PluginBase): pass def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: - pass + return [ + { + 'component': 'VForm', + 'content': [ + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'enabled', + 'label': '启用插件', + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VSwitch', + 'props': { + 'model': 'notify', + 'label': '发送通知', + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'content': [ + { + 'component': 'VSelect', + 'props': { + 'chips': True, + 'multiple': True, + 'model': 'sign_sites', + 'label': '下载器', + 'items': [ + {'title': 'Qbittorrent', 'value': 'qbittorrent'}, + {'title': 'Transmission', 'value': 'transmission'}, + ] + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'play_up_speed', + 'label': '播放限速(上传)', + 'placeholder': 'KB/s' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'play_down_speed', + 'label': '播放限速(下载)', + 'placeholder': 'KB/s' + } + } + ] + } + ] + }, + { + 'component': 'VRow', + 'content': [ + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'noplay_up_speed', + 'label': '未播放限速(上传)', + 'placeholder': 'KB/s' + } + } + ] + }, + { + 'component': 'VCol', + 'props': { + 'cols': 12, + 'md': 6 + }, + 'content': [ + { + 'component': 'VTextField', + 'props': { + 'model': 'noplay_down_speed', + 'label': '未播放限速(下载)', + 'placeholder': 'KB/s' + } + } + ] + } + ] + } + ] + } + ], { + "enabled": False, + "notify": True, + "downloader": [], + "play_up_speed": 0, + "play_down_speed": 0, + "noplay_up_speed": 0, + "noplay_down_speed": 0, + } def get_page(self) -> List[dict]: pass @@ -81,6 +246,13 @@ class SpeedLimiter(_PluginBase): """ 检查播放会话 """ + if not self._qb and not self._tr: + return + + def __set_limiter(self): + """ + 设置限速 + """ pass def stop_service(self): From df5a5fc3c22cf1a156865a99ce46b8bc9e8c1cd2 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 7 Aug 2023 08:33:57 +0800 Subject: [PATCH 3/6] fix --- app/chain/subscribe.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 5232fac5..1c26ba71 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -352,6 +352,11 @@ class SubscribeChain(ChainBase): """ 刷新站点最新资源 """ + # 所有订阅 + subscribes = self.subscribehelper.list('R') + if not subscribes: + # 没有订阅不运行 + return # 读取缓存 torrents_cache: Dict[str, List[Context]] = self.load_cache(self._cache_file) or {} From e58d45923a7dbbe26127ec19b8f35468e70faa13 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 7 Aug 2023 10:33:35 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix=20=E8=BF=81=E7=A7=BB=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=8A=9F=E8=83=BD=E5=88=B0Module=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/modules/emby/emby.py | 39 ++++++++++++++ app/modules/jellyfin/jellyfin.py | 43 +++++++++++++++ app/plugins/bestfilmversion/__init__.py | 70 ++++--------------------- app/plugins/mediasyncdel/__init__.py | 39 ++++---------- 4 files changed, 102 insertions(+), 89 deletions(-) diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index 5dd14369..3eee6ef1 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -597,3 +597,42 @@ class Emby(metaclass=Singleton): image_type="Backdrop") return eventItem + + def get_log(self): + """ + 获取日志 + """ + if not self._host or not self._apikey: + return "" + # emby 日志url + log_url = "%sSystem/Logs/embyserver.txt?api_key=%s" % (self._host, self._apikey) + log_res = RequestUtils().get_res(url=log_url) + + if not log_res or log_res.status_code != 200: + return "" + return log_res.text + + def get_fav_items(self, user: str = None) -> list: + """ + 获取已收藏媒体列表 + """ + # 根据加入日期 降序排序 + url = f"{self._host}emby/Users/{user or self._user}/Items?SortBy=DateCreated%2CSortName" \ + f"&SortOrder=Descending" \ + f"&Filters=IsFavorite" \ + f"&Recursive=true" \ + f"&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ + f"&CollapseBoxSetItems=false" \ + f"&ExcludeLocationTypes=Virtual" \ + f"&EnableTotalRecordCount=false" \ + f"&Limit=20&api_key={self._apikey}" + try: + resp = RequestUtils().get_res(url=url) + if resp: + return resp.json().get("Items") + else: + logger.error(f"User/Items 未获取到返回数据") + return [] + except Exception as e: + logger.error(f"连接User/Items 出错:" + str(e)) + return [] diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index df5db170..9e40bdae 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -1,3 +1,4 @@ +import datetime import json import re from typing import List, Union, Optional, Dict, Generator @@ -437,3 +438,45 @@ class Jellyfin(metaclass=Singleton): except Exception as e: logger.error(f"连接Users/Items出错:" + str(e)) yield {} + + def get_log(self): + """ + 获取日志 + """ + if not self._host or not self._apikey: + return "" + # 日志url + log_url = "%sSystem/Logs/Log?name=log_%s.log&api_key=%s" % ( + self._host, datetime.date.today().strftime("%Y%m%d"), self._apikey) + log_res = RequestUtils().get_res(url=log_url) + + if not log_res or log_res.status_code != 200: + return "" + else: + return log_res.text + + def get_fav_items(self, user: str = None) -> list: + """ + 获取已收藏媒体列表 + """ + # 根据加入日期 降序排序 + url = f"{self._host}Users/{user or self._user}/Items?SortBy=DateCreated%2CSortName" \ + f"&SortOrder=Descending" \ + f"&Filters=IsFavorite" \ + f"&Recursive=true" \ + f"&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ + f"&CollapseBoxSetItems=false" \ + f"&ExcludeLocationTypes=Virtual" \ + f"&EnableTotalRecordCount=false" \ + f"&Limit=20" \ + f"&apikey={self._apikey}" + try: + resp = RequestUtils().get_res(url=url) + if resp: + return resp.json().get("Items") + else: + logger.error(f"User/Items 未获取到返回数据") + return [] + except Exception as e: + logger.error(f"连接User/Items 出错:" + str(e)) + return [] diff --git a/app/plugins/bestfilmversion/__init__.py b/app/plugins/bestfilmversion/__init__.py index ea58498a..ea80a928 100644 --- a/app/plugins/bestfilmversion/__init__.py +++ b/app/plugins/bestfilmversion/__init__.py @@ -12,7 +12,6 @@ from app.core.context import MediaInfo 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.types import MediaType from app.utils.http import RequestUtils @@ -46,14 +45,6 @@ class BestFilmVersion(_PluginBase): _scheduler: Optional[BackgroundScheduler] = None _cache_path: Optional[Path] = None subscribechain = None - jellyfin = None - jellyfin_user = None - emby = None - emby_user = None - plex = None - plex_user = None - service_host = None - service_apikey = None # 配置属性 _enabled: bool = False @@ -63,25 +54,6 @@ class BestFilmVersion(_PluginBase): def init_plugin(self, config: dict = None): self._cache_path = settings.TEMP_PATH / "__best_film_version_cache__" self.subscribechain = SubscribeChain() - if settings.MEDIASERVER == 'jellyfin': - self.jellyfin = Jellyfin() - self.jellyfin_user = self.jellyfin.get_user() - self.service_apikey = settings.JELLYFIN_API_KEY - self.service_host = settings.JELLYFIN_HOST - if settings.MEDIASERVER == 'emby': - self.emby = Emby() - self.emby_user = self.emby.get_user() - self.service_apikey = settings.EMBY_API_KEY - self.service_host = settings.EMBY_HOST - if settings.MEDIASERVER == 'plex': - self.plex = Plex() - self.service_apikey = settings.PLEX_TOKEN - self.service_host = settings.PLEX_HOST - if self.service_host: - if not self.service_host.endswith("/"): - self.service_host += "/" - if not self.service_host.startswith("http"): - self.service_host = "http://" + self.service_host # 停止现有任务 self.stop_service() @@ -230,7 +202,6 @@ class BestFilmVersion(_PluginBase): poster = history.get("poster") mtype = history.get("type") time_str = history.get("time") - overview = history.get("overview") tmdbid = history.get("tmdbid") contents.append( { @@ -332,48 +303,27 @@ class BestFilmVersion(_PluginBase): # 读取历史记录 history = self.get_data('history') or [] - if self.jellyfin: - # 根据加入日期 降序排序 - url = f"{self.service_host}Users/{self.jellyfin_user}/Items?SortBy=DateCreated%2CSortName" \ - f"&SortOrder=Descending" \ - f"&Filters=IsFavorite" \ - f"&Recursive=true" \ - f"&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ - f"&CollapseBoxSetItems=false" \ - f"&ExcludeLocationTypes=Virtual" \ - f"&EnableTotalRecordCount=false" \ - f"&Limit=20" \ - f"&apikey={self.service_apikey}" - elif self.emby: - # 根据加入日期 降序排序 - url = f"{self.service_host}emby/Users/{self.emby_user}/Items?SortBy=DateCreated%2CSortName" \ - f"&SortOrder=Descending" \ - f"&Filters=IsFavorite" \ - f"&Recursive=true" \ - f"&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ - f"&CollapseBoxSetItems=false" \ - f"&ExcludeLocationTypes=Virtual" \ - f"&EnableTotalRecordCount=false" \ - f"&Limit=20&api_key={self.service_apikey}" + # 读取收藏 + if settings.MEDIASERVER == 'jellyfin': + resp = Jellyfin().get_fav_items() + elif settings.MEDIASERVER == 'emby': + resp = Emby().get_fav_items() else: # TODO plex待开发 return - # 获取收藏数据 - resp = self.media_simple_filter(url) - logger.info(f'BestFilmVersion插件 resp打印 {resp}') - for data in resp: # 检查缓存 if data.get('Name') in caches: continue # 获取详情 - if self.jellyfin: - item_info_resp = self.jellyfin.get_iteminfo(itemid=data.get('Id')) - elif self.emby: - item_info_resp = self.emby.get_iteminfo(itemid=data.get('Id')) + 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: + # TODO plex待开发 return logger.info(f'BestFilmVersion插件 item打印 {item_info_resp}') diff --git a/app/plugins/mediasyncdel/__init__.py b/app/plugins/mediasyncdel/__init__.py index 6e5215b5..40552aca 100644 --- a/app/plugins/mediasyncdel/__init__.py +++ b/app/plugins/mediasyncdel/__init__.py @@ -13,9 +13,10 @@ from app.core.event import eventmanager, Event from app.db.models.transferhistory import TransferHistory from app.db.transferhistory_oper import TransferHistoryOper from app.log import logger +from app.modules.emby import Emby +from app.modules.jellyfin import Jellyfin from app.plugins import _PluginBase from app.schemas.types import NotificationType, EventType -from app.utils.http import RequestUtils class MediaSyncDel(_PluginBase): @@ -469,25 +470,15 @@ class MediaSyncDel(_PluginBase): @staticmethod def parse_emby_log(last_time): - # emby host - emby_host = settings.EMBY_HOST - if emby_host: - if not emby_host.endswith("/"): - emby_host += "/" - if not emby_host.startswith("http"): - emby_host = "http://" + emby_host - - # emby 日志url - log_url = "%sSystem/Logs/embyserver.txt?api_key=%s" % (emby_host, settings.EMBY_API_KEY) - log_res = RequestUtils().get_res(url=log_url) - - if not log_res or log_res.status_code != 200: + # emby + log_text = Emby().get_log() + if not log_text: logger.error("获取emby日志失败,请检查服务器配置") return [] # 正则解析删除的媒体信息 pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}) Info App: Removing item from database, Type: (\w+), Name: (.*), Path: (.*), Id: (\d+)' - matches = re.findall(pattern, log_res.text) + matches = re.findall(pattern, log_text) del_medias = [] # 循环获取媒体信息 @@ -551,26 +542,16 @@ class MediaSyncDel(_PluginBase): @staticmethod def parse_jellyfin_log(last_time): - # jellyfin host - jellyfin_host = settings.JELLYFIN_HOST - if jellyfin_host: - if not jellyfin_host.endswith("/"): - jellyfin_host += "/" - if not jellyfin_host.startswith("http"): - jellyfin_host = "http://" + jellyfin_host + # jellyfin + log_text = Jellyfin().get_log() - # jellyfin 日志url - log_url = "%sSystem/Logs/Log?name=log_%s.log&api_key=%s" % ( - jellyfin_host, datetime.date.today().strftime("%Y%m%d"), settings.JELLYFIN_API_KEY) - log_res = RequestUtils().get_res(url=log_url) - - if not log_res or log_res.status_code != 200: + if not log_text: logger.error("获取jellyfin日志失败,请检查服务器配置") return [] # 正则解析删除的媒体信息 pattern = r'\[(.*?)\].*?Removing item, Type: "(.*?)", Name: "(.*?)", Path: "(.*?)"' - matches = re.findall(pattern, log_res.text) + matches = re.findall(pattern, log_text) del_medias = [] # 循环获取媒体信息 From 3258e71a5ffcd389f2d764d65052298b73b09099 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 7 Aug 2023 10:56:57 +0800 Subject: [PATCH 5/6] fix --- app/modules/emby/emby.py | 45 ++++++----------------- app/modules/jellyfin/jellyfin.py | 49 ++++++------------------- app/plugins/bestfilmversion/__init__.py | 38 +++++++++++++------ app/plugins/mediasyncdel/__init__.py | 18 ++++----- 4 files changed, 58 insertions(+), 92 deletions(-) diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index 3eee6ef1..ed693125 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -3,6 +3,8 @@ import re from pathlib import Path from typing import List, Optional, Union, Dict, Generator +from requests import Response + from app.core.config import settings from app.log import logger from app.schemas import RefreshMediaItem, WebhookEventInfo @@ -598,41 +600,16 @@ class Emby(metaclass=Singleton): return eventItem - def get_log(self): + def get_data(self, url: str) -> Optional[Response]: """ - 获取日志 + 自定义URL从媒体服务器获取数据,其中{HOST}、{APIKEY}、{USER}会被替换成实际的值 + :param url: 请求地址 """ - if not self._host or not self._apikey: - return "" - # emby 日志url - log_url = "%sSystem/Logs/embyserver.txt?api_key=%s" % (self._host, self._apikey) - log_res = RequestUtils().get_res(url=log_url) - - if not log_res or log_res.status_code != 200: - return "" - return log_res.text - - def get_fav_items(self, user: str = None) -> list: - """ - 获取已收藏媒体列表 - """ - # 根据加入日期 降序排序 - url = f"{self._host}emby/Users/{user or self._user}/Items?SortBy=DateCreated%2CSortName" \ - f"&SortOrder=Descending" \ - f"&Filters=IsFavorite" \ - f"&Recursive=true" \ - f"&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ - f"&CollapseBoxSetItems=false" \ - f"&ExcludeLocationTypes=Virtual" \ - f"&EnableTotalRecordCount=false" \ - f"&Limit=20&api_key={self._apikey}" + url = url.replace("{HOST}", self._host)\ + .replace("{APIKEY}", self._apikey)\ + .replace("{USER}", self._user) try: - resp = RequestUtils().get_res(url=url) - if resp: - return resp.json().get("Items") - else: - logger.error(f"User/Items 未获取到返回数据") - return [] + return RequestUtils().get_res(url=url) except Exception as e: - logger.error(f"连接User/Items 出错:" + str(e)) - return [] + logger.error(f"连接Emby出错:" + str(e)) + return None diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index 9e40bdae..035868c9 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -1,8 +1,9 @@ -import datetime import json import re from typing import List, Union, Optional, Dict, Generator +from requests import Response + from app.core.config import settings from app.log import logger from app.schemas import MediaType, WebhookEventInfo @@ -439,44 +440,16 @@ class Jellyfin(metaclass=Singleton): logger.error(f"连接Users/Items出错:" + str(e)) yield {} - def get_log(self): + def get_data(self, url: str) -> Optional[Response]: """ - 获取日志 + 自定义URL从媒体服务器获取数据,其中{HOST}、{APIKEY}、{USER}会被替换成实际的值 + :param url: 请求地址 """ - if not self._host or not self._apikey: - return "" - # 日志url - log_url = "%sSystem/Logs/Log?name=log_%s.log&api_key=%s" % ( - self._host, datetime.date.today().strftime("%Y%m%d"), self._apikey) - log_res = RequestUtils().get_res(url=log_url) - - if not log_res or log_res.status_code != 200: - return "" - else: - return log_res.text - - def get_fav_items(self, user: str = None) -> list: - """ - 获取已收藏媒体列表 - """ - # 根据加入日期 降序排序 - url = f"{self._host}Users/{user or self._user}/Items?SortBy=DateCreated%2CSortName" \ - f"&SortOrder=Descending" \ - f"&Filters=IsFavorite" \ - f"&Recursive=true" \ - f"&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ - f"&CollapseBoxSetItems=false" \ - f"&ExcludeLocationTypes=Virtual" \ - f"&EnableTotalRecordCount=false" \ - f"&Limit=20" \ - f"&apikey={self._apikey}" + url = url.replace("{HOST}", self._host)\ + .replace("{APIKEY}", self._apikey)\ + .replace("{USER}", self._user) try: - resp = RequestUtils().get_res(url=url) - if resp: - return resp.json().get("Items") - else: - logger.error(f"User/Items 未获取到返回数据") - return [] + return RequestUtils().get_res(url=url) except Exception as e: - logger.error(f"连接User/Items 出错:" + str(e)) - return [] + logger.error(f"连接Jellyfin出错:" + str(e)) + return None diff --git a/app/plugins/bestfilmversion/__init__.py b/app/plugins/bestfilmversion/__init__.py index ea80a928..8990dd22 100644 --- a/app/plugins/bestfilmversion/__init__.py +++ b/app/plugins/bestfilmversion/__init__.py @@ -1,10 +1,10 @@ import datetime from pathlib import Path -from threading import Lock from typing import Optional, Any, List, Dict, Tuple from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger +from requests import Response from app.chain.subscribe import SubscribeChain from app.core.config import settings @@ -14,9 +14,6 @@ from app.modules.emby import Emby from app.modules.jellyfin import Jellyfin from app.plugins import _PluginBase from app.schemas.types import MediaType -from app.utils.http import RequestUtils - -lock = Lock() class BestFilmVersion(_PluginBase): @@ -305,9 +302,30 @@ class BestFilmVersion(_PluginBase): # 读取收藏 if settings.MEDIASERVER == 'jellyfin': - resp = Jellyfin().get_fav_items() + # 根据加入日期 降序排序 + url = "{HOST}Users/{USER}/Items?SortBy=DateCreated%2CSortName" \ + "&SortOrder=Descending" \ + "&Filters=IsFavorite" \ + "&Recursive=true" \ + "&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ + "&CollapseBoxSetItems=false" \ + "&ExcludeLocationTypes=Virtual" \ + "&EnableTotalRecordCount=false" \ + "&Limit=20" \ + "&apikey={APIKEY}" + resp = self.get_items(Jellyfin().get_data(url)) elif settings.MEDIASERVER == 'emby': - resp = Emby().get_fav_items() + # 根据加入日期 降序排序 + url = "{HOST}emby/Users/{USER}/Items?SortBy=DateCreated%2CSortName" \ + "&SortOrder=Descending" \ + "&Filters=IsFavorite" \ + "&Recursive=true" \ + "&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo" \ + "&CollapseBoxSetItems=false" \ + "&ExcludeLocationTypes=Virtual" \ + "&EnableTotalRecordCount=false" \ + "&Limit=20&api_key={APIKEY}" + resp = self.get_items(Emby().get_data(url)) else: # TODO plex待开发 return @@ -374,14 +392,12 @@ class BestFilmVersion(_PluginBase): self._cache_path.write_text("\n".join(caches)) @staticmethod - def media_simple_filter(url): + def get_items(resp: Response): try: - resp = RequestUtils().get_res(url=url) if resp: - return resp.json().get("Items") + return resp.json().get("Items") or [] else: - logger.error(f"User/Items 未获取到返回数据") return [] except Exception as e: - logger.error(f"连接User/Items 出错:" + str(e)) + print(str(e)) return [] diff --git a/app/plugins/mediasyncdel/__init__.py b/app/plugins/mediasyncdel/__init__.py index 40552aca..bda983ae 100644 --- a/app/plugins/mediasyncdel/__init__.py +++ b/app/plugins/mediasyncdel/__init__.py @@ -470,15 +470,15 @@ class MediaSyncDel(_PluginBase): @staticmethod def parse_emby_log(last_time): - # emby - log_text = Emby().get_log() - if not log_text: + 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日志失败,请检查服务器配置") return [] # 正则解析删除的媒体信息 pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}) Info App: Removing item from database, Type: (\w+), Name: (.*), Path: (.*), Id: (\d+)' - matches = re.findall(pattern, log_text) + matches = re.findall(pattern, log_res.text) del_medias = [] # 循环获取媒体信息 @@ -542,16 +542,16 @@ class MediaSyncDel(_PluginBase): @staticmethod def parse_jellyfin_log(last_time): - # jellyfin - log_text = Jellyfin().get_log() - - if not log_text: + # 根据加入日期 降序排序 + 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日志失败,请检查服务器配置") return [] # 正则解析删除的媒体信息 pattern = r'\[(.*?)\].*?Removing item, Type: "(.*?)", Name: "(.*?)", Path: "(.*?)"' - matches = re.findall(pattern, log_text) + matches = re.findall(pattern, log_res.text) del_medias = [] # 循环获取媒体信息 From 505bfb4c2ec3ebcc13334c06ca3a350b3f1ba775 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Mon, 7 Aug 2023 11:01:55 +0800 Subject: [PATCH 6/6] =?UTF-8?q?feat=20=E4=B8=8B=E8=BD=BD=E5=99=A8=E9=99=90?= =?UTF-8?q?=E9=80=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/modules/qbittorrent/qbittorrent.py | 19 ++++++++++++++++ app/modules/transmission/transmission.py | 29 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/app/modules/qbittorrent/qbittorrent.py b/app/modules/qbittorrent/qbittorrent.py index d341f38e..6cdd5cd7 100644 --- a/app/modules/qbittorrent/qbittorrent.py +++ b/app/modules/qbittorrent/qbittorrent.py @@ -306,3 +306,22 @@ class Qbittorrent(metaclass=Singleton): except Exception as err: logger.error(f"获取传输信息出错:{err}") return None + + def set_speed_limit(self, download_limit: float = None, upload_limit: float = None) -> bool: + """ + 设置速度限制 + :param download_limit: 下载速度限制,单位KB/s + :param upload_limit: 上传速度限制,单位kB/s + """ + if not self.qbc: + return False + download_limit = download_limit * 1024 + upload_limit = upload_limit * 1024 + try: + if self.qbc.transfer.upload_limit != upload_limit: + self.qbc.transfer.upload_limit = upload_limit + if self.qbc.transfer.download_limit != download_limit: + self.qbc.transfer.download_limit = download_limit + except Exception as err: + logger.error(f"设置速度限制出错:{err}") + return False diff --git a/app/modules/transmission/transmission.py b/app/modules/transmission/transmission.py index e0dfe222..7f8ee5b2 100644 --- a/app/modules/transmission/transmission.py +++ b/app/modules/transmission/transmission.py @@ -235,3 +235,32 @@ class Transmission(metaclass=Singleton): except Exception as err: logger.error(f"获取传输信息出错:{err}") return None + + def set_speed_limit(self, download_limit: float = None, upload_limit: float = None): + """ + 设置速度限制 + :param download_limit: 下载速度限制,单位KB/s + :param upload_limit: 上传速度限制,单位kB/s + """ + if not self.trc: + return + try: + session = self.trc.get_session() + download_limit_enabled = True if download_limit else False + upload_limit_enabled = True if upload_limit else False + if download_limit_enabled == session.speed_limit_down_enabled and \ + upload_limit_enabled == session.speed_limit_up_enabled and \ + download_limit == session.speed_limit_down and \ + upload_limit == session.speed_limit_up: + return + self.trc.set_session( + speed_limit_down=download_limit if download_limit != session.speed_limit_down + else session.speed_limit_down, + speed_limit_up=upload_limit if upload_limit != session.speed_limit_up + else session.speed_limit_up, + speed_limit_down_enabled=download_limit_enabled, + speed_limit_up_enabled=upload_limit_enabled + ) + except Exception as err: + logger.error(f"设置速度限制出错:{err}") + return False