Merge pull request #350 from thsrite/main

feat 播放限速插件支持智能限速、不限速地址
This commit is contained in:
jxxghp 2023-08-31 19:48:48 +08:00 committed by GitHub
commit 2a7fc7bbe6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,3 +1,4 @@
import ipaddress
from typing import List, Tuple, Dict, Any from typing import List, Tuple, Dict, Any
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
@ -50,6 +51,10 @@ class SpeedLimiter(_PluginBase):
_play_down_speed: float = 0 _play_down_speed: float = 0
_noplay_up_speed: float = 0 _noplay_up_speed: float = 0
_noplay_down_speed: float = 0 _noplay_down_speed: float = 0
_bandwidth: float = 0
_allocation_ratio: str = ""
_auto_limit: bool = False
_limit_enabled: bool = False
# 当前限速状态 # 当前限速状态
_current_state = "" _current_state = ""
@ -62,6 +67,18 @@ class SpeedLimiter(_PluginBase):
self._play_down_speed = float(config.get("play_down_speed")) if config.get("play_down_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_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._noplay_down_speed = float(config.get("noplay_down_speed")) if config.get("noplay_down_speed") else 0
try:
# 总带宽
self._bandwidth = int(float(config.get("bandwidth") or 0)) * 1000000
if self._bandwidth > 0:
# 自动限速开关
self._auto_limit = True
except Exception:
self._bandwidth = 0
# 限速服务开关
self._limit_enabled = True if self._play_up_speed or self._play_down_speed or self._auto_limit else False
self._allocation_ratio = config.get("allocation_ratio") or ""
self._downloader = config.get("downloader") or [] self._downloader = config.get("downloader") or []
if self._downloader: if self._downloader:
if 'qbittorrent' in self._downloader: if 'qbittorrent' in self._downloader:
@ -73,7 +90,7 @@ class SpeedLimiter(_PluginBase):
self.stop_service() self.stop_service()
# 启动限速任务 # 启动限速任务
if self._enabled: if self._enabled and self._limit_enabled:
self._scheduler = BackgroundScheduler(timezone=settings.TZ) self._scheduler = BackgroundScheduler(timezone=settings.TZ)
self._scheduler.add_job(func=self.check_playing_sessions, self._scheduler.add_job(func=self.check_playing_sessions,
trigger='interval', trigger='interval',
@ -95,158 +112,199 @@ class SpeedLimiter(_PluginBase):
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]: def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
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': 6 'md': 6
}, },
'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': 6 'md': 6
}, },
'content': [ 'content': [
{ {
'component': 'VSwitch', 'component': 'VSwitch',
'props': { 'props': {
'model': 'notify', 'model': 'notify',
'label': '发送通知', 'label': '发送通知',
} }
} }
] ]
} }
] ]
}, },
{ {
'component': 'VRow', 'component': 'VRow',
'content': [ 'content': [
{ {
'component': 'VCol', 'component': 'VCol',
'content': [ 'content': [
{ {
'component': 'VSelect', 'component': 'VSelect',
'props': { 'props': {
'chips': True, 'chips': True,
'multiple': True, 'multiple': True,
'model': 'downloader', 'model': 'downloader',
'label': '下载器', 'label': '下载器',
'items': [ 'items': [
{'title': 'Qbittorrent', 'value': 'qbittorrent'}, {'title': 'Qbittorrent', 'value': 'qbittorrent'},
{'title': 'Transmission', 'value': 'transmission'}, {'title': 'Transmission', 'value': 'transmission'},
] ]
} }
} }
] ]
} }
] ]
}, },
{ {
'component': 'VRow', 'component': 'VRow',
'content': [ 'content': [
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 6 'md': 6
}, },
'content': [ 'content': [
{ {
'component': 'VTextField', 'component': 'VTextField',
'props': { 'props': {
'model': 'play_up_speed', 'model': 'play_up_speed',
'label': '播放限速(上传)', 'label': '播放限速(上传)',
'placeholder': 'KB/s' 'placeholder': 'KB/s'
} }
} }
] ]
}, },
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 6 'md': 6
}, },
'content': [ 'content': [
{ {
'component': 'VTextField', 'component': 'VTextField',
'props': { 'props': {
'model': 'play_down_speed', 'model': 'play_down_speed',
'label': '播放限速(下载)', 'label': '播放限速(下载)',
'placeholder': 'KB/s' 'placeholder': 'KB/s'
} }
} }
] ]
} }
] ]
}, },
{ {
'component': 'VRow', 'component': 'VRow',
'content': [ 'content': [
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 6 'md': 6
}, },
'content': [ 'content': [
{ {
'component': 'VTextField', 'component': 'VTextField',
'props': { 'props': {
'model': 'noplay_up_speed', 'model': 'noplay_up_speed',
'label': '未播放限速(上传)', 'label': '未播放限速(上传)',
'placeholder': 'KB/s' 'placeholder': 'KB/s'
} }
} }
] ]
}, },
{ {
'component': 'VCol', 'component': 'VCol',
'props': { 'props': {
'cols': 12, 'cols': 12,
'md': 6 'md': 6
}, },
'content': [ 'content': [
{ {
'component': 'VTextField', 'component': 'VTextField',
'props': { 'props': {
'model': 'noplay_down_speed', 'model': 'noplay_down_speed',
'label': '未播放限速(下载)', 'label': '未播放限速(下载)',
'placeholder': 'KB/s' 'placeholder': 'KB/s'
} }
} }
] ]
} }
] ]
} },
] {
} 'component': 'VRow',
], { 'content': [
"enabled": False, {
"notify": True, 'component': 'VCol',
"downloader": [], 'props': {
"play_up_speed": 0, 'cols': 12,
"play_down_speed": 0, 'md': 6
"noplay_up_speed": 0, },
"noplay_down_speed": 0, 'content': [
} {
'component': 'VTextField',
'props': {
'model': 'bandwidth',
'label': '智能限速上行带宽',
'placeholder': '设置上行带宽后,媒体服务器有媒体播放时根据上行带宽和媒体播放占用带宽计算上传限速数值。'
}
}
]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VTextField',
'props': {
'model': 'allocation_ratio',
'label': '智能限速分配比例',
'placeholder': '多个下载器设置分配比例如两个下载器设置1:2,留空均分'
}
}
]
}
]
},
]
}
], {
"enabled": False,
"notify": True,
"downloader": [],
"play_up_speed": 0,
"play_down_speed": 0,
"noplay_up_speed": 0,
"noplay_down_speed": 0,
"bandwidth": 0,
"allocation_ratio": "",
}
def get_page(self) -> List[dict]: def get_page(self) -> List[dict]:
pass pass
@ -313,19 +371,37 @@ class SpeedLimiter(_PluginBase):
}) })
# 计算有效比特率 # 计算有效比特率
for session in playing_sessions: for session in playing_sessions:
if not IpUtils.is_private_ip(session.get("address")) \ if 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)
if total_bit_rate: if total_bit_rate:
# 开启智能限速计算上传限速
if self._auto_limit:
play_up_speed = self.__calc_limit(total_bit_rate)
else:
play_up_speed = self._play_up_speed
# 当前正在播放,开始限速 # 当前正在播放,开始限速
self.__set_limiter(limit_type="播放", upload_limit=self._play_up_speed, self.__set_limiter(limit_type="播放", upload_limit=play_up_speed,
download_limit=self._play_down_speed) download_limit=self._play_down_speed)
else: else:
# 当前没有播放,开始限速 # 当前没有播放,取消限速
self.__set_limiter(limit_type="未播放", upload_limit=self._noplay_up_speed, self.__set_limiter(limit_type="未播放", upload_limit=self._noplay_up_speed,
download_limit=self._noplay_down_speed) download_limit=self._noplay_down_speed)
def __calc_limit(self, total_bit_rate):
"""
计算智能上传限速
"""
residual_bandwidth = (self._bandwidth - total_bit_rate)
if residual_bandwidth < 0:
play_up_speed = 10
else:
play_up_speed = round(residual_bandwidth / 8 / 1024, 2)
return play_up_speed
def __set_limiter(self, limit_type: str, upload_limit: float, download_limit: float): def __set_limiter(self, limit_type: str, upload_limit: float, download_limit: float):
""" """
设置限速 设置限速
@ -348,42 +424,55 @@ class SpeedLimiter(_PluginBase):
else: else:
text = f"{text}\n下载:未限速" text = f"{text}\n下载:未限速"
try: try:
if self._qb: cnt = 0
self._qb.set_speed_limit(download_limit=download_limit, upload_limit=upload_limit) for download in self._downloader:
# 发送通知 if self._auto_limit and limit_type == "播放":
if self._notify: # 开启了播放智能限速
title = "【播放限速】" allocation_count = sum(self._allocation_ratio) if self._allocation_ratio else len(self._downloader)
if upload_limit or download_limit: if not self._allocation_ratio:
subtitle = f"Qbittorrent 开始{limit_type}限速" upload_limit = int(upload_limit / allocation_count)
self.post_message(
mtype=NotificationType.MediaServer,
title=title,
text=f"{subtitle}\n{text}"
)
else: else:
self.post_message( upload_limit = int(upload_limit * float(self._allocation_ratio[cnt]) / allocation_count)
mtype=NotificationType.MediaServer, cnt += 1
title=title,
text=f"Qbittorrent 已取消限速" if str(download) == 'qbittorrent':
) if self._qb:
if self._tr: self._qb.set_speed_limit(download_limit=download_limit, upload_limit=upload_limit)
self._tr.set_speed_limit(download_limit=download_limit, upload_limit=upload_limit) # 发送通知
# 发送通知 if self._notify:
if self._notify: title = "【播放限速】"
title = "【播放限速】" if upload_limit or download_limit:
if upload_limit or download_limit: subtitle = f"Qbittorrent 开始{limit_type}限速"
subtitle = f"Transmission 开始{limit_type}限速" self.post_message(
self.post_message( mtype=NotificationType.MediaServer,
mtype=NotificationType.MediaServer, title=title,
title=title, text=f"{subtitle}\n{text}"
text=f"{subtitle}\n{text}" )
) else:
self.post_message(
mtype=NotificationType.MediaServer,
title=title,
text=f"Qbittorrent 已取消限速"
)
else: else:
self.post_message( if self._tr:
mtype=NotificationType.MediaServer, self._tr.set_speed_limit(download_limit=download_limit, upload_limit=upload_limit)
title=title, # 发送通知
text=f"Transmission 已取消限速" if self._notify:
) title = "【播放限速】"
if upload_limit or download_limit:
subtitle = f"Transmission 开始{limit_type}限速"
self.post_message(
mtype=NotificationType.MediaServer,
title=title,
text=f"{subtitle}\n{text}"
)
else:
self.post_message(
mtype=NotificationType.MediaServer,
title=title,
text=f"Transmission 已取消限速"
)
except Exception as e: except Exception as e:
logger.error(f"设置限速失败:{str(e)}") logger.error(f"设置限速失败:{str(e)}")