Merge pull request #343 from hotlcc/develop-20240529

This commit is contained in:
jxxghp
2024-05-29 18:44:23 +08:00
committed by GitHub
3 changed files with 509 additions and 70 deletions

View File

@@ -640,11 +640,12 @@
"name": "下载器助手",
"description": "自动做种、站点标签、自动删种。",
"labels": "下载管理,仪表板",
"version": "2.5",
"version": "2.6",
"icon": "DownloaderHelper.png",
"author": "hotlcc",
"level": 1,
"history": {
"v2.6": "新增仪表板实时速率组件支持单独展示qb和tr的实时速率tr未测试有问题提Issue并@hotlcc。",
"v2.5": "优化通知类型降低认证级别要求使MP非认证用户可用但无法使用【站点名称优先】功能。主程序需升级至v1.9.2及以上版本,否则插件功能异常!",
"v2.4": "修复tr活动种子仪表板的种子排序的bug优化插件的消息发送。",
"v2.3": "仪表板支持多个下载器活动种子组件主程序版本需大于v1.9.1)。",

View File

@@ -9,6 +9,7 @@ from urllib.parse import urlparse
import pytz
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from cachetools import TTLCache
from qbittorrentapi import TorrentDictionary, TorrentState
from transmission_rpc.torrent import Torrent, Status as TorrentStatus
from ruamel.yaml.comments import CommentedMap
@@ -21,7 +22,7 @@ from app.log import logger
from app.modules.qbittorrent.qbittorrent import Qbittorrent
from app.modules.transmission.transmission import Transmission
from app.plugins import _PluginBase
from app.plugins.downloaderhelper.module import TaskContext, TaskResult, Downloader, TorrentField, TorrentFieldMap, DownloaderMap
from app.plugins.downloaderhelper.module import TaskContext, TaskResult, Downloader, TorrentField, TorrentFieldMap, DownloaderMap, DownloaderTransferInfo
from app.schemas import NotificationType
from app.schemas.types import EventType
from app.utils.string import StringUtils
@@ -35,7 +36,7 @@ class DownloaderHelper(_PluginBase):
# 插件图标
plugin_icon = "DownloaderHelper.png"
# 插件版本
plugin_version = "2.5"
plugin_version = "2.6"
# 插件作者
plugin_author = "hotlcc"
# 作者主页
@@ -57,6 +58,8 @@ class DownloaderHelper(_PluginBase):
__exit_event: ThreadEvent = ThreadEvent()
# 任务锁
__task_lock: RLock = RLock()
# 缓存
__ttl_cache = TTLCache(maxsize=128, ttl=1800)
# 配置相关
# 插件缺省配置
@@ -77,7 +80,8 @@ class DownloaderHelper(_PluginBase):
TorrentField.TAGS.name,
TorrentField.ADD_TIME.name,
TorrentField.UPLOADED.name,
]
],
'dashboard_speed_widget_target_downloaders': ['default'],
}
# 插件用户配置
__config: Dict[str, Any] = {}
@@ -93,6 +97,12 @@ class DownloaderHelper(_PluginBase):
__exclude_tags: Set[str] = set()
# 多级根域名,用于在打标时做特殊处理
__multi_level_root_domain: List[str] = ['edu.cn', 'com.cn', 'net.cn', 'org.cn']
# vuetifyjs mdi 图标 svg path 值
__mdi_icon_svg_path = {
'mdi-cloud-upload': 'M11 20H6.5q-2.28 0-3.89-1.57Q1 16.85 1 14.58q0-1.95 1.17-3.48q1.18-1.53 3.08-1.95q.63-2.3 2.5-3.72Q9.63 4 12 4q2.93 0 4.96 2.04Q19 8.07 19 11q1.73.2 2.86 1.5q1.14 1.28 1.14 3q0 1.88-1.31 3.19T18.5 20H13v-7.15l1.6 1.55L16 13l-4-4l-4 4l1.4 1.4l1.6-1.55Z',
'mdi-download-box': 'M5 3h14a2 2 0 0 1 2 2v14c0 1.11-.89 2-2 2H5a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2m3 14h8v-2H8zm8-7h-2.5V7h-3v3H8l4 4z',
'mdi-content-save': 'M15 9H5V5h10m-3 14a3 3 0 0 1-3-3a3 3 0 0 1 3-3a3 3 0 0 1 3 3a3 3 0 0 1-3 3m5-16H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V7z',
}
def init_plugin(self, config: dict = None):
"""
@@ -143,7 +153,8 @@ class DownloaderHelper(_PluginBase):
)
and self.__check_enable_any_task()
)
or self.__check_enable_dashboard_widget()
or self.__check_enable_dashboard_active_torrent_widget()
or self.__check_enable_dashboard_speed_widget()
) else False
return state
@@ -439,9 +450,23 @@ class DownloaderHelper(_PluginBase):
'content': [{
'component': 'VSwitch',
'props': {
'model': '_config_dashboard_dialog_closed',
'model': '_config_dashboard_active_torrent_dialog_closed',
'label': '配置仪表板活动种子组件',
'hint': '点击展开仪表板组件配置窗口。'
'hint': '点击展开仪表板活动种子组件配置窗口。'
}
}]
}, {
'component': 'VCol',
'props': {
'cols': 12,
'xxl': 4, 'xl': 4, 'lg': 4, 'md': 4, 'sm': 6, 'xs': 12
},
'content': [{
'component': 'VSwitch',
'props': {
'model': '_config_dashboard_speed_dialog_closed',
'label': '配置仪表板实时速率组件',
'hint': '点击展开仪表板实时速率组件配置窗口。'
}
}]
}]
@@ -489,7 +514,7 @@ class DownloaderHelper(_PluginBase):
}, {
'component': 'VDialog',
'props': {
'model': '_config_dashboard_dialog_closed',
'model': '_config_dashboard_active_torrent_dialog_closed',
'max-width': '40rem'
},
'content': [{
@@ -503,7 +528,7 @@ class DownloaderHelper(_PluginBase):
'content': [{
'component': 'VDialogCloseBtn',
'props': {
'model': '_config_dashboard_dialog_closed'
'model': '_config_dashboard_active_torrent_dialog_closed'
}
}, {
'component': 'VRow',
@@ -517,8 +542,8 @@ class DownloaderHelper(_PluginBase):
'component': 'VSwitch',
'props': {
'model': 'enable_dashboard_widget',
'label': '启用仪表板组件',
'hint': '是否启用仪表板组件。'
'label': '启用组件',
'hint': '是否启用仪表板活动种子组件。'
}
}]
}, {
@@ -597,6 +622,83 @@ class DownloaderHelper(_PluginBase):
}]
}]
}]
}, {
'component': 'VDialog',
'props': {
'model': '_config_dashboard_speed_dialog_closed',
'max-width': '40rem'
},
'content': [{
'component': 'VCard',
'props': {
'title': '配置仪表板实时速率组件',
'style': {
'padding': '0 20px 20px 20px'
}
},
'content': [{
'component': 'VDialogCloseBtn',
'props': {
'model': '_config_dashboard_speed_dialog_closed'
}
}, {
'component': 'VRow',
'content': [{
'component': 'VCol',
'props': {
'cols': 12,
'xxl': 6, 'xl': 6, 'lg': 6, 'md': 6, 'sm': 6, 'xs': 12
},
'content': [{
'component': 'VSwitch',
'props': {
'model': 'enable_dashboard_speed_widget',
'label': '启用组件',
'hint': '是否启用仪表板实时速率组件。'
}
}]
}]
}, {
'component': 'VRow',
'content': [{
'component': 'VCol',
'props': {
'cols': 12,
'xxl': 6, 'xl': 6, 'lg': 6, 'md': 6, 'sm': 6, 'xs': 12
},
'content': [{
'component': 'VTextField',
'props': {
'model': 'dashboard_speed_widget_refresh',
'label': '刷新间隔(秒)',
'placeholder': '5',
'type': 'number',
'hint': '组件刷新时间间隔,单位为秒,缺省时不刷新。请合理配置,间隔太短可能会导致下载器假死。'
}
}]
}, {
'component': 'VCol',
'props': {
'cols': 12,
'xxl': 6, 'xl': 6, 'lg': 6, 'md': 6, 'sm': 6, 'xs': 12
},
'content': [{
'component': 'VSelect',
'props': {
'model': 'dashboard_speed_widget_target_downloaders',
'label': '目标下载器',
'multiple': True,
'items': [
{'title': '系统默认下载器', 'value': 'default'},
{'title': Downloader.QB.name_, 'value': Downloader.QB.id},
{'title': Downloader.TR.name_, 'value': Downloader.TR.id}
],
'hint': '选择要展示的目标下载器。'
}
}]
}]
}]
}]
}, {
'component': 'VRow',
'content': [{
@@ -668,15 +770,28 @@ class DownloaderHelper(_PluginBase):
}]
"""
dashboard_meta = []
target_downloader_ids = self.__get_target_downloader_ids()
for target_downloader_id in target_downloader_ids:
downloader = self.__get_downloader_enum_by_id(downloader_id=target_downloader_id)
if not downloader:
continue
dashboard_meta.append({
"key": downloader.id,
"name": f"活动种子 #{downloader.short_name}",
})
if not self.get_state():
return dashboard_meta
if self.__check_enable_dashboard_active_torrent_widget():
target_downloader_ids = self.__get_dashboard_active_torrent_widget_target_downloader_ids()
for target_downloader_id in target_downloader_ids:
downloader = self.__get_downloader_enum_by_id(downloader_id=target_downloader_id)
if not downloader:
continue
dashboard_meta.append({
"key": downloader.id,
"name": f"活动种子 #{downloader.short_name}",
})
if self.__check_enable_dashboard_speed_widget():
target_downloader_ids = self.__get_dashboard_speed_widget_target_downloader_ids()
for target_downloader_id in target_downloader_ids:
downloader = self.__get_downloader_enum_by_id(downloader_id=target_downloader_id)
if not downloader:
continue
dashboard_meta.append({
"key": f"{downloader.id}_speed",
"name": f"实时速率 #{downloader.short_name}",
})
return dashboard_meta
def get_dashboard(self, key: str = None, **kwargs) -> Optional[Tuple[Dict[str, Any], Dict[str, Any], List[dict]]]:
@@ -696,21 +811,35 @@ class DownloaderHelper(_PluginBase):
:param key: 仪表盘key根据指定的key返回相应的仪表盘数据缺省时返回一个固定的仪表盘数据兼容旧版
"""
if not self.get_state() or not self.__check_enable_dashboard_widget():
if not self.get_state():
return None
target_downloader_ids = self.__get_target_downloader_ids()
enable_dashboard_active_torrent_widget = self.__check_enable_dashboard_active_torrent_widget()
enable_dashboard_speed_widget = self.__check_enable_dashboard_speed_widget()
if not enable_dashboard_active_torrent_widget and not enable_dashboard_speed_widget:
return None
# 无key兼容历史
dashboard_active_torrent_widget_target_downloader_ids = self.__get_dashboard_active_torrent_widget_target_downloader_ids()
if not key:
if not target_downloader_ids:
if enable_dashboard_active_torrent_widget and dashboard_active_torrent_widget_target_downloader_ids:
return self.__get_dashboard_active_torrent_widget(downloader_id=dashboard_active_torrent_widget_target_downloader_ids[0])
else:
return None
return self.__get_active_torrent_dashboard(downloader_id=target_downloader_ids[0])
if key in target_downloader_ids:
return self.__get_active_torrent_dashboard(downloader_id=key)
# 有key
dashboard_speed_widget_target_downloader_ids = self.__get_dashboard_speed_widget_target_downloader_ids()
if key == Downloader.QB.id and enable_dashboard_active_torrent_widget and Downloader.QB.id in dashboard_active_torrent_widget_target_downloader_ids:
return self.__get_dashboard_active_torrent_widget(downloader_id=Downloader.QB.id)
if key == Downloader.TR.id and enable_dashboard_active_torrent_widget and Downloader.TR.id in dashboard_active_torrent_widget_target_downloader_ids:
return self.__get_dashboard_active_torrent_widget(downloader_id=Downloader.TR.id)
if key == f"{Downloader.QB.id}_speed" and enable_dashboard_speed_widget and Downloader.QB.id in dashboard_speed_widget_target_downloader_ids:
return self.__get_dashboard_speed_widget(downloader_id=Downloader.QB.id)
if key == f"{Downloader.TR.id}_speed" and enable_dashboard_speed_widget and Downloader.TR.id in dashboard_speed_widget_target_downloader_ids:
return self.__get_dashboard_speed_widget(downloader_id=Downloader.TR.id)
return None
def __get_active_torrent_dashboard(self,
downloader_id: str) -> Optional[Tuple[Dict[str, Any], Dict[str, Any], List[dict]]]:
def __get_dashboard_active_torrent_widget(self,
downloader_id: str) -> Optional[Tuple[Dict[str, Any], Dict[str, Any], List[dict]]]:
"""
获取活动种子仪表板
获取仪表板活动种子组件
"""
downloader = self.__get_downloader_enum_by_id(downloader_id=downloader_id)
if not downloader:
@@ -739,7 +868,42 @@ class DownloaderHelper(_PluginBase):
attrs['refresh'] = self.__get_config_item('dashboard_widget_refresh')
# 页面元素
elements = self.__get_dashboard_elememts(downloader_id=downloader_id)
elements = self.__get_dashboard_active_torrent_widget_elememts(downloader_id=downloader_id)
return cols, attrs, elements
def __get_dashboard_speed_widget(self,
downloader_id: str) -> Optional[Tuple[Dict[str, Any], Dict[str, Any], List[dict]]]:
"""
获取仪表板实时速率组件
"""
downloader = self.__get_downloader_enum_by_id(downloader_id=downloader_id)
if not downloader:
return None
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
return None
# 列配置
cols = {
'cols': 12,
'xxl': 4,
'xl': 4,
'lg': 4,
'md': 4,
'sm': 12,
'xs': 12
}
# 全局配置
attrs = {
'title': f'实时速率 #{downloader.short_name}'
}
if self.__check_target_downloader(downloader_id=downloader_id):
attrs['refresh'] = self.__get_config_item('dashboard_speed_widget_refresh')
# 页面元素
elements = self.__get_dashboard_speed_widget_elememts(downloader_id=downloader_id)
return cols, attrs, elements
@@ -751,6 +915,7 @@ class DownloaderHelper(_PluginBase):
logger.info('尝试停止插件服务...')
self.__exit_event.set()
self.__stop_scheduler()
self.__clear_cache()
logger.info('插件服务停止完成')
except Exception as e:
logger.error(f"插件服务停止异常: {str(e)}", exc_info=True)
@@ -855,6 +1020,20 @@ class DownloaderHelper(_PluginBase):
except Exception as e:
logger.error(f"插件服务调度器停止异常: {str(e)}", exc_info=True)
def __clear_cache(self):
"""
清除缓存
"""
try:
logger.info('尝试清除插件缓存...')
if self.__ttl_cache:
self.__ttl_cache.clear()
logger.info('插件缓存清除成功')
else:
logger.info('插件未启用缓存,无须清除')
except Exception as e:
logger.error(f"插件缓存清除异常: {str(e)}", exc_info=True)
def __fix_config(self, config: dict) -> dict:
"""
修正配置
@@ -993,13 +1172,20 @@ class DownloaderHelper(_PluginBase):
return True if self.__check_enable_qb_task() \
or self.__check_enable_tr_task() else False
def __check_enable_dashboard_widget(self) -> bool:
def __check_enable_dashboard_active_torrent_widget(self) -> bool:
"""
判断是否启用了仪表板组件
:return: 是否启用了仪表板组件
判断是否启用了仪表板活动种子组件
:return: 是否启用了仪表板活动种子组件
"""
return True if self.__get_config_item('enable_dashboard_widget') else False
def __check_enable_dashboard_speed_widget(self) -> bool:
"""
判断是否启用了仪表板实时速率组件
:return: 是否启用了仪表板实时速率组件
"""
return True if self.__get_config_item('enable_dashboard_speed_widget') else False
@classmethod
def __parse_tracker_for_qbittorrent(cls, torrent: TorrentDictionary) -> Optional[str]:
"""
@@ -1868,13 +2054,13 @@ class DownloaderHelper(_PluginBase):
result.append(field)
return result
def __build_dashboard_widget_table_head_content(self,
def __build_dashboard_widget_torrent_table_head_content(self,
fields: List[TorrentField] = None) -> list:
"""
构造仪表板组件表头内容
构造仪表板组件种子表头内容
"""
if not fields:
fields = self.__get_dashboard_widget_display_fields()
fields = self.__get_dashboard_active_torrent_widget_display_fields()
if not fields:
return []
return [{
@@ -1885,22 +2071,22 @@ class DownloaderHelper(_PluginBase):
'text': field.name_
} for field in fields if field]
def __build_dashboard_widget_table_head(self,
def __build_dashboard_widget_torrent_table_head(self,
fields: List[TorrentField] = None) -> dict:
"""
构造仪表板组件表头
构造仪表板组件种子表头
"""
return {
'component': 'thead',
'content': self.__build_dashboard_widget_table_head_content(fields=fields)
'content': self.__build_dashboard_widget_torrent_table_head_content(fields=fields)
}
def __build_dashboard_widget_table_body_content(self,
def __build_dashboard_widget_torrent_table_body_content(self,
data: List[List[Any]],
field_count: int,
downloader_id: str) -> list:
"""
构造仪表板组件表体内容
构造仪表板组件种子表体内容
:param downloader_id: 下载器ID
:param data: 表格数据
:param field_count: 字段数量
@@ -1936,24 +2122,26 @@ class DownloaderHelper(_PluginBase):
}]
}]
def __build_dashboard_widget_table_body(self,
def __build_dashboard_widget_torrent_table_body(self,
data: List[List[Any]],
field_count: int,
downloader_id: str) -> dict:
"""
构造仪表板组件表体内容
构造仪表板组件种子表体
"""
return {
'component': 'tbody',
'content': self.__build_dashboard_widget_table_body_content(data=data, field_count=field_count, downloader_id=downloader_id)
'content': self.__build_dashboard_widget_torrent_table_body_content(data=data, field_count=field_count, downloader_id=downloader_id)
}
def __get_target_downloader_ids(self) -> List[str]:
def __get_dashboard_widget_target_downloader_ids(self, config_key: str) -> List[str]:
"""
获取目标下载器ids
获取仪表板组件目标下载器ids
"""
target_downloader_ids = []
target_downloaders = self.__get_config_item('dashboard_widget_target_downloaders')
if not config_key:
return target_downloader_ids
target_downloaders = self.__get_config_item(config_key)
if not target_downloaders:
return target_downloader_ids
for target_downloader in target_downloaders:
@@ -1963,9 +2151,21 @@ class DownloaderHelper(_PluginBase):
target_downloader_ids.append(target_downloader)
return target_downloader_ids
def __get_dashboard_widget_display_fields(self) -> List[TorrentField]:
def __get_dashboard_active_torrent_widget_target_downloader_ids(self) -> List[str]:
"""
获取仪表板组件展示字段
获取仪表板活动种子组件目标下载器ids
"""
return self.__get_dashboard_widget_target_downloader_ids(config_key='dashboard_widget_target_downloaders')
def __get_dashboard_speed_widget_target_downloader_ids(self) -> List[str]:
"""
获取仪表板实时速率组件目标下载器ids
"""
return self.__get_dashboard_widget_target_downloader_ids(config_key='dashboard_speed_widget_target_downloaders')
def __get_dashboard_active_torrent_widget_display_fields(self) -> List[TorrentField]:
"""
获取仪表板活动种子组件展示字段
"""
fields = self.__get_config_item('dashboard_widget_display_fields')
return self.__ensure_torrent_fields(fields=fields)
@@ -1992,28 +2192,28 @@ class DownloaderHelper(_PluginBase):
else:
return False
def __get_downloader_torrent_data(self,
downloader_id: str,
fields: List[TorrentField] = None):
def __get_downloader_active_torrent_data(self,
downloader_id: str,
fields: List[TorrentField] = None):
"""
获取下载器种子数据
获取下载器活动种子数据
"""
if not downloader_id:
return None
# 字段
if not fields:
fields = self.__get_dashboard_widget_display_fields()
fields = self.__get_dashboard_active_torrent_widget_display_fields()
if downloader_id == Downloader.QB.id:
return self.__get_qbittorrent_torrent_data(fields=fields)
return self.__get_qbittorrent_active_torrent_data(fields=fields)
elif downloader_id == Downloader.TR.id:
return self.__get_transmission_torrent_data(fields=fields)
return self.__get_transmission_active_torrent_data(fields=fields)
else:
return None
def __get_qbittorrent_torrent_data(self,
fields: List[TorrentField] = None):
def __get_qbittorrent_active_torrent_data(self,
fields: List[TorrentField] = None):
"""
获取qb种子数据
获取qb活动种子数据
"""
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
@@ -2023,7 +2223,7 @@ class DownloaderHelper(_PluginBase):
return None
# 字段
if not fields:
fields = self.__get_dashboard_widget_display_fields()
fields = self.__get_dashboard_active_torrent_widget_display_fields()
# 活动种子
torrents, error = qbittorrent.get_torrents(status=['active'])
if error:
@@ -2153,10 +2353,10 @@ class DownloaderHelper(_PluginBase):
arguments.append('uploadLimited')
return list(set(arguments))
def __get_transmission_torrent_data(self,
fields: List[TorrentField] = None):
def __get_transmission_active_torrent_data(self,
fields: List[TorrentField] = None):
"""
获取tr种子数据
获取tr活动种子数据
"""
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
@@ -2166,7 +2366,7 @@ class DownloaderHelper(_PluginBase):
return None
# 字段
if not fields:
fields = self.__get_dashboard_widget_display_fields()
fields = self.__get_dashboard_active_torrent_widget_display_fields()
torrents, _ = transmission.trc.get_recently_active_torrents(arguments=self.__build_transmission_field_arguments(fields=fields))
if not torrents:
return None
@@ -2282,18 +2482,18 @@ class DownloaderHelper(_PluginBase):
logger.error(f'从tr种子中提取值异常: {str(e)}, torrent = {str(torrent.fields)}', exc_info=True)
return None
def __get_dashboard_elememts(self, downloader_id: str) -> list:
def __get_dashboard_active_torrent_widget_elememts(self, downloader_id: str) -> list:
"""
获取仪表板元素
获取仪表板活动种子组件元素
"""
if not downloader_id:
return None
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
return None
fields = self.__get_dashboard_widget_display_fields()
fields = self.__get_dashboard_active_torrent_widget_display_fields()
field_count=len(fields)
data = self.__get_downloader_torrent_data(downloader_id=downloader_id, fields=fields)
data = self.__get_downloader_active_torrent_data(downloader_id=downloader_id, fields=fields)
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
return None
@@ -2308,11 +2508,232 @@ class DownloaderHelper(_PluginBase):
}
},
'content': [
self.__build_dashboard_widget_table_head(fields=fields),
self.__build_dashboard_widget_table_body(data=data, field_count=field_count, downloader_id=downloader_id)
self.__build_dashboard_widget_torrent_table_head(fields=fields),
self.__build_dashboard_widget_torrent_table_body(data=data, field_count=field_count, downloader_id=downloader_id)
]
}]
def __get_downloader_transfer_info(self,
downloader_id: str) -> DownloaderTransferInfo:
"""
获取下载器传输信息
"""
if downloader_id == Downloader.QB.id:
return self.__get_qbittorrent_transfer_info()
elif downloader_id == Downloader.TR.id:
return self.__get_transmission_transfer_info()
else:
return DownloaderTransferInfo()
def __get_qbittorrent_transfer_info(self) -> DownloaderTransferInfo:
"""
获取qb下载器传输信息
"""
result = DownloaderTransferInfo()
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
return result
qbittorrent = self.__get_qbittorrent()
if not qbittorrent:
return result
info = qbittorrent.transfer_info()
if info:
result.download_speed = f'{StringUtils.str_filesize(info.get("dl_info_speed"))}/s'
result.upload_speed = f'{StringUtils.str_filesize(info.get("up_info_speed"))}/s'
result.download_size = StringUtils.str_filesize(info.get("dl_info_data"))
result.upload_size = StringUtils.str_filesize(info.get("up_info_data"))
maindata = self.__get_qbittorrent_maindata()
if maindata:
server_state = maindata.get("server_state")
if server_state:
result.free_space = StringUtils.str_filesize(server_state.get("free_space_on_disk"))
return result
def __get_qbittorrent_maindata(self):
"""
获取qb的maindata
"""
cache_key = "qbittorrent_maindata"
maindata = self.__ttl_cache.get(cache_key)
if not maindata:
qbittorrent = self.__get_qbittorrent()
if qbittorrent:
maindata = qbittorrent.qbc.sync_maindata()
self.__ttl_cache[cache_key] = maindata
return maindata
def __get_transmission_transfer_info(self) -> DownloaderTransferInfo:
"""
获取qb下载器传输信息
"""
result = DownloaderTransferInfo()
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
return result
transmission = self.__get_transmission()
if not transmission:
return result
info = transmission.transfer_info()
if info:
result.download_speed = f"{StringUtils.str_filesize(info.download_speed)}/s"
result.upload_speed = f"{StringUtils.str_filesize(info.upload_speed,)}/s"
result.download_size = StringUtils.str_filesize(info.current_stats.downloaded_bytes)
result.upload_size = StringUtils.str_filesize(info.current_stats.uploaded_bytes)
session = self.__get_transmission_session()
if session:
result.free_space = StringUtils.str_filesize(session.download_dir_free_space)
return result
def __get_transmission_session(self):
"""
获取tr的session
"""
cache_key = "transmission_session"
session = self.__ttl_cache.get(cache_key)
if not session:
transmission = self.__get_transmission()
if transmission:
session = transmission.get_session()
self.__ttl_cache[cache_key] = session
return session
def __build_mdi_icon_svg_elememt(self, mdi_icon: str) -> dict:
"""
构造 svg mdi 图标元素
"""
if not mdi_icon:
return None
path = self.__mdi_icon_svg_path.get(mdi_icon)
if not path:
return None
return {
'component': 'svg',
'props': {
'class': 'v-icon notranslate v-theme--light v-icon--size-default iconify iconify--mdi',
'rounded': True,
'width': '1em',
'height': '1em',
'viewBox': '0 0 24 24',
'style': {
'top': '-1px'
}
},
'content': [{
'component': 'path',
'props': {
'fill': 'currentColor',
'd': path
}
}]
}
def __build_dashboard_speed_widget_list_item_element(self, mdi_icon: str, label: str, value: str) -> dict:
"""
构造仪表板实时速率组件列表item元素
"""
if not mdi_icon or not label or not value:
return None
return {
'component': 'div',
'props': {
'style': {
'display': 'grid',
'grid-template-areas': '"prepend content append"',
'grid-template-columns': 'max-content 1fr auto',
'padding-bottom': '16px'
}
},
'content': [{
'component': 'div',
'props': {
'style': {
'grid-area': 'prepend',
'height': '21px',
'color': '#6a6670'
}
},
'content': [self.__build_mdi_icon_svg_elememt(mdi_icon=mdi_icon)]
}, {
'component': 'div',
'props': {
'style': {
'grid-area': 'content',
'margin-left': '15px'
}
},
'content': [{
'component': 'h6',
'props': {
'class': 'text-sm font-weight-medium mb-1'
},
'text': label
}]
}, {
'component': 'div',
'props': {
'style': {
'grid-area': 'append'
}
},
'content': [{
'component': 'h6',
'props': {
'class': 'text-sm font-weight-medium mb-2'
},
'text': value
}]
}]
}
def __get_dashboard_speed_widget_elememts(self, downloader_id: str) -> list:
"""
获取仪表板实时速率组件元素
"""
if not downloader_id:
return None
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
return None
data = self.__get_downloader_transfer_info(downloader_id=downloader_id)
if self.__exit_event.is_set():
logger.warn('插件服务正在退出,操作取消')
return None
list_items = [
self.__build_dashboard_speed_widget_list_item_element(mdi_icon='mdi-cloud-upload', label='总上传量', value=data.upload_size),
self.__build_dashboard_speed_widget_list_item_element(mdi_icon='mdi-download-box', label='总下载量', value=data.download_size),
self.__build_dashboard_speed_widget_list_item_element(mdi_icon='mdi-content-save', label='磁盘剩余空间', value=data.free_space),
]
return [{
'component': 'div',
'props': {
'style': {
'padding': '16px 0 20px 0'
}
},
'content': [{
'component': 'div',
'content': [{
'component': 'p',
'props': {
'class': 'text-h5 me-2'
},
'text': f'{data.upload_speed}'
}, {
'component': 'p',
'props': {
'class': 'text-h4 me-2'
},
'text': f'{data.download_speed}'
}]
}, {
'component': 'div',
'props': {
'class': 'card-list mt-9'
},
'content': list_items
}]
}]
@eventmanager.register(EventType.DownloadAdded)
def listen_download_added_event(self, event: Event = None):
"""

View File

@@ -308,3 +308,20 @@ class TorrentField(Enum):
# TorrentField 映射
TorrentFieldMap = dict((field.name, field) for field in TorrentField)
class DownloaderTransferInfo():
"""
下载器传输信息
"""
# 下载速度
download_speed: Optional[str] = '0.00B/s'
# 上传速度
upload_speed: Optional[str] = '0.00B/s'
# 下载量
download_size: Optional[str] = '0.00B'
# 上传量
upload_size: Optional[str] = '0.00B'
# 剩余空间
free_space: Optional[str] = '0.00B'