Merge branch 'jxxghp:main' into main

This commit is contained in:
thsrite
2023-08-07 11:44:44 +08:00
committed by GitHub
12 changed files with 343 additions and 113 deletions

View File

@ -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 {}

View File

@ -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
@ -597,3 +599,17 @@ class Emby(metaclass=Singleton):
image_type="Backdrop")
return eventItem
def get_data(self, url: str) -> Optional[Response]:
"""
自定义URL从媒体服务器获取数据其中{HOST}{APIKEY}{USER}会被替换成实际的值
:param url: 请求地址
"""
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"连接Emby出错" + str(e))
return None

View File

@ -2,6 +2,8 @@ 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
@ -437,3 +439,17 @@ class Jellyfin(metaclass=Singleton):
except Exception as e:
logger.error(f"连接Users/Items出错" + str(e))
yield {}
def get_data(self, url: str) -> Optional[Response]:
"""
自定义URL从媒体服务器获取数据其中{HOST}{APIKEY}{USER}会被替换成实际的值
:param url: 请求地址
"""
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

View File

@ -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,8 +299,29 @@ class Qbittorrent(metaclass=Singleton):
"""
获取传输信息
"""
if not self.qbc:
return None
try:
return self.qbc.transfer_info()
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

View File

@ -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,8 +228,39 @@ class Transmission(metaclass=Singleton):
"""
获取传输信息
"""
if not self.trc:
return None
try:
return self.trc.session_stats()
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

View File

@ -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
@ -12,12 +12,8 @@ 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
lock = Lock()
class BestFilmVersion(_PluginBase):
@ -46,14 +42,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 +51,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 +199,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 +300,48 @@ class BestFilmVersion(_PluginBase):
# 读取历史记录
history = self.get_data('history') or []
if self.jellyfin:
# 读取收藏
if settings.MEDIASERVER == '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 = "{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':
# 根据加入日期 降序排序
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}"
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
# 获取收藏数据
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}')
@ -424,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 []

View File

@ -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):
"""

View File

@ -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()

View File

@ -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():

View File

@ -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()

View File

@ -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,18 +470,8 @@ 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)
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 []
@ -551,19 +542,9 @@ 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 日志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)
# 根据加入日期 降序排序
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 []

View File

@ -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):