mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-13 15:09:12 +00:00
Merge pull request #343 from hotlcc/develop-20240529
This commit is contained in:
@@ -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)。",
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user