Files
MoviePilot-Plugins/plugins/mpserverstatus/__init__.py
2025-04-05 14:47:22 +08:00

468 lines
23 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import re
import time
from typing import List, Tuple, Dict, Any, Optional
from app.log import logger
from app.plugins import _PluginBase
from app.utils.http import RequestUtils
class MPServerStatus(_PluginBase):
# 插件名称
plugin_name = "MoviePilot服务监控"
# 插件描述
plugin_desc = "在仪表板中实时显示MoviePilot公共服务器状态。"
# 插件图标
plugin_icon = "Duplicati_A.png"
# 插件版本
plugin_version = "1.2"
# 插件作者
plugin_author = "jxxghp"
# 作者主页
author_url = "https://github.com/jxxghp"
# 插件配置项ID前缀
plugin_config_prefix = "MPServer_"
# 加载顺序
plugin_order = 99
# 可使用的用户级别
auth_level = 1
_enable: bool = False
_server_base = "https://movie-pilot.org/status"
def init_plugin(self, config: dict = None):
self._enable = config.get("enable") if config.get("enable") else False
@staticmethod
def get_command() -> List[Dict[str, Any]]:
pass
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
return [
{
'component': 'VForm',
'content': [
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'enable',
'label': '启用插件',
}
}
]
}
]
}
]
}
], {
"enable": self._enable,
}
def get_page(self) -> List[dict]:
"""
获取插件页面
"""
if not self._enable:
return [
{
'component': 'div',
'text': '插件未启用',
'props': {
'class': 'text-center',
}
}
]
_, _, elements = self.get_dashboard()
return elements
def get_dashboard(self) -> Optional[Tuple[Dict[str, Any], Dict[str, Any], List[dict]]]:
"""
获取插件仪表盘页面需要返回1、仪表板col配置字典2、全局配置自动刷新等3、仪表板页面元素配置json含数据
1、col配置参考
{
"cols": 12, "md": 6
}
2、全局配置参考
{
"refresh": 10 // 自动刷新时间,单位秒
}
3、页面配置使用Vuetify组件拼装参考https://vuetifyjs.com/
"""
# 列配置
cols = {
"cols": 12,
"md": 8
}
# 全局配置
attrs = {
"refresh": 10
}
# 读取服务器文本
start_time = time.time()
logger.info(f"请求服务器状态 {self._server_base}...")
res = RequestUtils().get_res(self._server_base)
seconds = time.time() - start_time
logger.info(f"请求耗时:{seconds}")
if not res:
logger.warn(f"请求服务器状态失败:{res.status_code if res is not None else '网络错误'}")
elements = [
{
'component': 'div',
'text': '无法连接服务器',
'props': {
'class': 'text-center',
}
}
]
else:
"""
Active connections: 62
server accepts handled requests
468843 468843 1368256
Reading: 0 Writing: 1 Waiting: 61
"""
status_lines = res.text.strip().split('\n')
active_connections = int(status_lines[0].split(':')[1].strip())
accepts, handled, requests = map(int, status_lines[2].split())
reading, writing, waiting = map(int, re.findall(r'\d+', status_lines[3]))
elements = [
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 6,
'md': 3
},
'content': [
{
'component': 'VCard',
'props': {
'variant': 'tonal',
},
'content': [
{
'component': 'VCardText',
'props': {
'class': 'd-flex align-center',
},
'content': [
{
'component': 'div',
'content': [
{
'component': 'span',
'props': {
'class': 'text-caption'
},
'text': '连接耗时'
},
{
'component': 'div',
'props': {
'class': 'd-flex align-center flex-wrap'
},
'content': [
{
'component': 'span',
'props': {
'class': 'text-h6'
},
'text': f"{seconds:.2f}"
}
]
}
]
}
]
}
]
},
]
},
{
'component': 'VCol',
'props': {
'cols': 6,
'md': 3
},
'content': [
{
'component': 'VCard',
'props': {
'variant': 'tonal',
},
'content': [
{
'component': 'VCardText',
'props': {
'class': 'd-flex align-center',
},
'content': [
{
'component': 'div',
'content': [
{
'component': 'span',
'props': {
'class': 'text-caption'
},
'text': '活跃连接'
},
{
'component': 'div',
'props': {
'class': 'd-flex align-center flex-wrap'
},
'content': [
{
'component': 'span',
'props': {
'class': 'text-h6'
},
'text': active_connections
}
]
}
]
}
]
}
]
},
]
},
{
'component': 'VCol',
'props': {
'cols': 6,
'md': 3
},
'content': [
{
'component': 'VCard',
'props': {
'variant': 'tonal',
},
'content': [
{
'component': 'VCardText',
'props': {
'class': 'd-flex align-center',
},
'content': [
{
'component': 'div',
'content': [
{
'component': 'span',
'props': {
'class': 'text-caption'
},
'text': '等待连接'
},
{
'component': 'div',
'props': {
'class': 'd-flex align-center flex-wrap'
},
'content': [
{
'component': 'span',
'props': {
'class': 'text-h6'
},
'text': waiting
}
]
}
]
}
]
}
]
},
]
},
{
'component': 'VCol',
'props': {
'cols': 6,
'md': 3
},
'content': [
{
'component': 'VCard',
'props': {
'variant': 'tonal',
},
'content': [
{
'component': 'VCardText',
'props': {
'class': 'd-flex align-center',
},
'content': [
{
'component': 'div',
'content': [
{
'component': 'span',
'props': {
'class': 'text-caption'
},
'text': '处理中连接'
},
{
'component': 'div',
'props': {
'class': 'd-flex align-center flex-wrap'
},
'content': [
{
'component': 'span',
'props': {
'class': 'text-h6'
},
'text': reading + writing
}
]
}
]
}
]
}
]
},
]
},
{
'component': 'VCol',
'props': {
'cols': 6,
'md': 3
},
'content': [
{
'component': 'VCard',
'props': {
'variant': 'tonal',
},
'content': [
{
'component': 'VCardText',
'props': {
'class': 'd-flex align-center',
},
'content': [
{
'component': 'div',
'content': [
{
'component': 'span',
'props': {
'class': 'text-caption'
},
'text': '总请求数'
},
{
'component': 'div',
'props': {
'class': 'd-flex align-center flex-wrap'
},
'content': [
{
'component': 'span',
'props': {
'class': 'text-h6'
},
'text': f"{requests:,}"
}
]
}
]
}
]
}
]
},
]
},
{
'component': 'VCol',
'props': {
'cols': 6,
'md': 3
},
'content': [
{
'component': 'VCard',
'props': {
'variant': 'tonal',
},
'content': [
{
'component': 'VCardText',
'props': {
'class': 'd-flex align-center',
},
'content': [
{
'component': 'div',
'content': [
{
'component': 'span',
'props': {
'class': 'text-caption'
},
'text': '总连接数'
},
{
'component': 'div',
'props': {
'class': 'd-flex align-center flex-wrap'
},
'content': [
{
'component': 'span',
'props': {
'class': 'text-h6'
},
'text': f"{accepts:,}"
}
]
}
]
}
]
}
]
},
]
}
]
}]
return cols, attrs, elements
def get_state(self) -> bool:
return self._enable
def stop_service(self):
pass