mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-20 07:26:53 +00:00
Merge branch 'main' into main
This commit is contained in:
83
package.json
83
package.json
@@ -2,6 +2,7 @@
|
||||
"AutoSignIn": {
|
||||
"name": "站点自动签到",
|
||||
"description": "自动模拟登录、签到站点。",
|
||||
"labels": "站点",
|
||||
"version": "2.1",
|
||||
"icon": "signin.png",
|
||||
"author": "thsrite",
|
||||
@@ -15,6 +16,7 @@
|
||||
"CustomSites": {
|
||||
"name": "自定义站点",
|
||||
"description": "增加自定义站点为签到和统计使用。",
|
||||
"labels": "站点",
|
||||
"version": "1.0",
|
||||
"icon": "world.png",
|
||||
"author": "lightolly",
|
||||
@@ -23,6 +25,7 @@
|
||||
"SiteStatistic": {
|
||||
"name": "站点数据统计",
|
||||
"description": "自动统计和展示站点数据。",
|
||||
"labels": "站点",
|
||||
"version": "2.9",
|
||||
"icon": "statistic.png",
|
||||
"author": "lightolly",
|
||||
@@ -37,6 +40,7 @@
|
||||
"SiteRefresh": {
|
||||
"name": "站点自动更新",
|
||||
"description": "使用浏览器模拟登录站点获取Cookie和UA。",
|
||||
"labels": "站点",
|
||||
"version": "1.2",
|
||||
"icon": "Chrome_A.png",
|
||||
"author": "thsrite",
|
||||
@@ -45,6 +49,7 @@
|
||||
"DoubanSync": {
|
||||
"name": "豆瓣想看",
|
||||
"description": "同步豆瓣想看数据,自动添加订阅。",
|
||||
"labels": "订阅",
|
||||
"version": "1.7",
|
||||
"icon": "douban.png",
|
||||
"author": "jxxghp",
|
||||
@@ -58,6 +63,7 @@
|
||||
"DirMonitor": {
|
||||
"name": "目录监控",
|
||||
"description": "监控目录文件发生变化时实时整理到媒体库。",
|
||||
"labels": "文件整理",
|
||||
"version": "2.0",
|
||||
"icon": "directory.png",
|
||||
"author": "jxxghp",
|
||||
@@ -70,6 +76,7 @@
|
||||
"ChineseSubFinder": {
|
||||
"name": "ChineseSubFinder",
|
||||
"description": "整理入库时通知ChineseSubFinder下载字幕。",
|
||||
"labels": "字幕",
|
||||
"version": "1.1",
|
||||
"icon": "chinesesubfinder.png",
|
||||
"author": "jxxghp",
|
||||
@@ -78,6 +85,7 @@
|
||||
"DoubanRank": {
|
||||
"name": "豆瓣榜单订阅",
|
||||
"description": "监控豆瓣热门榜单,自动添加订阅。",
|
||||
"labels": "订阅",
|
||||
"version": "1.9",
|
||||
"icon": "movie.jpg",
|
||||
"author": "jxxghp",
|
||||
@@ -90,6 +98,7 @@
|
||||
"LibraryScraper": {
|
||||
"name": "媒体库刮削",
|
||||
"description": "定时对媒体库进行刮削,补齐缺失元数据和图片。",
|
||||
"labels": "刮削",
|
||||
"version": "1.4",
|
||||
"icon": "scraper.png",
|
||||
"author": "jxxghp",
|
||||
@@ -98,6 +107,7 @@
|
||||
"TorrentRemover": {
|
||||
"name": "自动删种",
|
||||
"description": "自动删除下载器中的下载任务。",
|
||||
"labels": "做种",
|
||||
"version": "1.2.2",
|
||||
"icon": "delete.jpg",
|
||||
"author": "jxxghp",
|
||||
@@ -106,6 +116,7 @@
|
||||
"MediaSyncDel": {
|
||||
"name": "媒体文件同步删除",
|
||||
"description": "同步删除历史记录、源文件和下载任务。",
|
||||
"labels": "文件整理",
|
||||
"version": "1.4",
|
||||
"icon": "mediasyncdel.png",
|
||||
"author": "thsrite",
|
||||
@@ -114,6 +125,7 @@
|
||||
"CustomHosts": {
|
||||
"name": "自定义Hosts",
|
||||
"description": "修改系统hosts文件,加速网络访问。",
|
||||
"labels": "网络",
|
||||
"version": "1.0",
|
||||
"icon": "hosts.png",
|
||||
"author": "thsrite",
|
||||
@@ -122,6 +134,7 @@
|
||||
"SpeedLimiter": {
|
||||
"name": "播放限速",
|
||||
"description": "外网播放媒体库视频时,自动对下载器进行限速。",
|
||||
"labels": "网络",
|
||||
"version": "1.1",
|
||||
"icon": "Librespeed_A.png",
|
||||
"author": "Shurelol",
|
||||
@@ -130,6 +143,7 @@
|
||||
"CloudflareSpeedTest": {
|
||||
"name": "Cloudflare IP优选",
|
||||
"description": "🌩 测试 Cloudflare CDN 延迟和速度,自动优选IP。",
|
||||
"labels": "网络,站点",
|
||||
"version": "1.2",
|
||||
"icon": "cloudflare.jpg",
|
||||
"author": "thsrite",
|
||||
@@ -141,6 +155,7 @@
|
||||
"BestFilmVersion": {
|
||||
"name": "收藏洗版",
|
||||
"description": "Jellyfin/Emby/Plex点击收藏电影后,自动订阅洗版。",
|
||||
"labels": "订阅",
|
||||
"version": "2.1",
|
||||
"icon": "like.jpg",
|
||||
"author": "wlj",
|
||||
@@ -149,6 +164,7 @@
|
||||
"MediaServerMsg": {
|
||||
"name": "媒体库服务器通知",
|
||||
"description": "发送Emby/Jellyfin/Plex服务器的播放、入库等通知消息。",
|
||||
"labels": "消息通知,媒体库",
|
||||
"version": "1.1",
|
||||
"icon": "mediaplay.png",
|
||||
"author": "jxxghp",
|
||||
@@ -157,6 +173,7 @@
|
||||
"MediaServerRefresh": {
|
||||
"name": "媒体库服务器刷新",
|
||||
"description": "入库后自动刷新Emby/Jellyfin/Plex服务器海报墙。",
|
||||
"labels": "媒体库",
|
||||
"version": "1.2",
|
||||
"icon": "refresh2.png",
|
||||
"author": "jxxghp",
|
||||
@@ -173,6 +190,7 @@
|
||||
"ChatGPT": {
|
||||
"name": "ChatGPT",
|
||||
"description": "消息交互支持与ChatGPT对话。",
|
||||
"labels": "消息通知,识别",
|
||||
"version": "1.3",
|
||||
"icon": "Chatgpt_A.png",
|
||||
"author": "jxxghp",
|
||||
@@ -189,6 +207,7 @@
|
||||
"MessageForward": {
|
||||
"name": "消息转发",
|
||||
"description": "根据正则转发通知到其他WeChat应用。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.1",
|
||||
"icon": "forward.png",
|
||||
"author": "thsrite",
|
||||
@@ -197,6 +216,7 @@
|
||||
"AutoBackup": {
|
||||
"name": "自动备份",
|
||||
"description": "自动备份数据和配置文件。",
|
||||
"labels": "系统设置",
|
||||
"version": "1.2",
|
||||
"icon": "Time_machine_B.png",
|
||||
"author": "thsrite",
|
||||
@@ -208,14 +228,19 @@
|
||||
"IYUUAutoSeed": {
|
||||
"name": "IYUU自动辅种",
|
||||
"description": "基于IYUU官方Api实现自动辅种。",
|
||||
"version": "1.3",
|
||||
"labels": "做种,IYUU",
|
||||
"version": "1.5",
|
||||
"icon": "IYUU.png",
|
||||
"author": "jxxghp",
|
||||
"level": 2
|
||||
"level": 2,
|
||||
"history": {
|
||||
"v1.5": "支持馒头新架构辅种"
|
||||
}
|
||||
},
|
||||
"CrossSeed": {
|
||||
"name": "青蛙辅种助手",
|
||||
"description": "参考ReseedPuppy和IYUU辅种插件实现自动辅种,支持站点:青蛙、AGSVPT、麒麟、UBits、聆音、憨憨等。",
|
||||
"labels": "做种",
|
||||
"version": "2.3",
|
||||
"icon": "qingwa.png",
|
||||
"author": "233@qingwa",
|
||||
@@ -228,6 +253,7 @@
|
||||
"VCBAnimeMonitor": {
|
||||
"name": "整理VCB动漫压制组作品",
|
||||
"description": "提高部分VCB-Studio作品的识别准确率,将VCB-Studio的作品统一转移到指定目录同时进行刮削整理",
|
||||
"labels": "文件整理,识别",
|
||||
"version": "1.7",
|
||||
"icon": "vcbmonitor.png",
|
||||
"author": "pixel@qingwa",
|
||||
@@ -236,6 +262,7 @@
|
||||
"TorrentTransfer": {
|
||||
"name": "自动转移做种",
|
||||
"description": "定期转移下载器中的做种任务到另一个下载器。",
|
||||
"labels": "做种",
|
||||
"version": "1.3",
|
||||
"icon": "seed.png",
|
||||
"author": "jxxghp",
|
||||
@@ -244,6 +271,7 @@
|
||||
"RssSubscribe": {
|
||||
"name": "自定义订阅",
|
||||
"description": "定时刷新RSS报文,识别内容后添加订阅或直接下载。",
|
||||
"labels": "订阅",
|
||||
"version": "1.3",
|
||||
"icon": "rss.png",
|
||||
"author": "jxxghp",
|
||||
@@ -255,6 +283,7 @@
|
||||
"SyncDownloadFiles": {
|
||||
"name": "下载器文件同步",
|
||||
"description": "同步下载器的文件信息到数据库,删除文件时联动删除下载任务。",
|
||||
"labels": "下载管理",
|
||||
"version": "1.1",
|
||||
"icon": "Youtube-dl_A.png",
|
||||
"author": "thsrite",
|
||||
@@ -263,6 +292,7 @@
|
||||
"BrushFlow": {
|
||||
"name": "站点刷流",
|
||||
"description": "自动托管刷流,将会提高对应站点的访问频率。",
|
||||
"labels": "刷流",
|
||||
"version": "2.9",
|
||||
"icon": "brush.jpg",
|
||||
"author": "jxxghp,InfinityPacer",
|
||||
@@ -278,6 +308,7 @@
|
||||
"DownloadingMsg": {
|
||||
"name": "下载进度推送",
|
||||
"description": "定时推送正在下载进度。",
|
||||
"labels": "消息通知,下载管理",
|
||||
"version": "1.1",
|
||||
"icon": "downloadmsg.png",
|
||||
"author": "thsrite",
|
||||
@@ -286,6 +317,7 @@
|
||||
"AutoClean": {
|
||||
"name": "定时清理媒体库",
|
||||
"description": "定时清理用户下载的种子、源文件、媒体库文件。",
|
||||
"labels": "媒体库",
|
||||
"version": "1.1",
|
||||
"icon": "clean.png",
|
||||
"author": "thsrite",
|
||||
@@ -294,6 +326,7 @@
|
||||
"InvitesSignin": {
|
||||
"name": "药丸签到",
|
||||
"description": "药丸论坛签到。",
|
||||
"labels": "站点",
|
||||
"version": "1.4",
|
||||
"icon": "invites.png",
|
||||
"author": "thsrite",
|
||||
@@ -305,6 +338,7 @@
|
||||
"PersonMeta": {
|
||||
"name": "演职人员刮削",
|
||||
"description": "刮削演职人员图片以及中文名称。",
|
||||
"labels": "媒体库,刮削",
|
||||
"version": "1.2",
|
||||
"icon": "actor.png",
|
||||
"author": "jxxghp",
|
||||
@@ -313,6 +347,7 @@
|
||||
"MoviePilotUpdateNotify": {
|
||||
"name": "MoviePilot更新推送",
|
||||
"description": "MoviePilot推送release更新通知、自动重启。",
|
||||
"labels": "消息通知,自动更新",
|
||||
"version": "1.4",
|
||||
"icon": "Moviepilot_A.png",
|
||||
"author": "thsrite",
|
||||
@@ -325,6 +360,7 @@
|
||||
"CloudDiskDel": {
|
||||
"name": "云盘文件删除",
|
||||
"description": "媒体库删除strm文件后同步删除云盘资源。",
|
||||
"labels": "媒体库",
|
||||
"version": "1.3",
|
||||
"icon": "clouddisk.png",
|
||||
"author": "thsrite",
|
||||
@@ -333,6 +369,7 @@
|
||||
"BarkMsg": {
|
||||
"name": "Bark消息推送",
|
||||
"description": "支持使用Bark发送消息通知。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.1",
|
||||
"icon": "Bark_A.png",
|
||||
"author": "jxxghp",
|
||||
@@ -341,6 +378,7 @@
|
||||
"IyuuMsg": {
|
||||
"name": "IYUU消息推送",
|
||||
"description": "支持使用IYUU发送消息通知。",
|
||||
"labels": "消息通知,IYUU",
|
||||
"version": "1.2",
|
||||
"icon": "Iyuu_A.png",
|
||||
"author": "jxxghp",
|
||||
@@ -349,6 +387,7 @@
|
||||
"PushDeerMsg": {
|
||||
"name": "PushDeer消息推送",
|
||||
"description": "支持使用PushDeer发送消息通知。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.1",
|
||||
"icon": "pushdeer.png",
|
||||
"author": "jxxghp",
|
||||
@@ -357,6 +396,7 @@
|
||||
"ConfigCenter": {
|
||||
"name": "配置中心",
|
||||
"description": "快速调整部分系统设定。",
|
||||
"labels": "系统设置",
|
||||
"version": "2.4",
|
||||
"icon": "setting.png",
|
||||
"author": "jxxghp",
|
||||
@@ -365,6 +405,7 @@
|
||||
"WorkWechatMsg": {
|
||||
"name": "企微机器人消息推送",
|
||||
"description": "支持使用企业微信群聊机器人发送消息通知。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.0",
|
||||
"icon": "Wecom_A.png",
|
||||
"author": "叮叮当",
|
||||
@@ -373,6 +414,7 @@
|
||||
"EpisodeGroupMeta": {
|
||||
"name": "TMDB剧集组刮削",
|
||||
"description": "从TMDB剧集组刮削季集的实际顺序。",
|
||||
"labels": "刮削",
|
||||
"version": "1.1",
|
||||
"icon": "Element_A.png",
|
||||
"author": "叮叮当",
|
||||
@@ -381,6 +423,7 @@
|
||||
"CustomIndexer": {
|
||||
"name": "自定义索引站点",
|
||||
"description": "修改或扩展内建索引器支持的站点。",
|
||||
"labels": "站点",
|
||||
"version": "1.0",
|
||||
"icon": "spider.png",
|
||||
"author": "jxxghp",
|
||||
@@ -389,6 +432,7 @@
|
||||
"FFmpegThumb": {
|
||||
"name": "FFmpeg缩略图",
|
||||
"description": "TheMovieDb没有背景图片时使用FFmpeg截取视频文件缩略图",
|
||||
"labels": "刮削",
|
||||
"version": "1.2",
|
||||
"icon": "ffmpeg.png",
|
||||
"author": "jxxghp",
|
||||
@@ -397,6 +441,7 @@
|
||||
"PushPlusMsg": {
|
||||
"name": "PushPlus消息推送",
|
||||
"description": "支持使用PushPlus发送消息通知。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.0",
|
||||
"icon": "Pushplus_A.png",
|
||||
"author": "cheng",
|
||||
@@ -405,6 +450,7 @@
|
||||
"DownloadSiteTag": {
|
||||
"name": "下载任务分类与标签",
|
||||
"description": "自动给下载任务分类与打站点标签、剧集名称标签",
|
||||
"labels": "下载管理",
|
||||
"version": "2.1",
|
||||
"icon": "Youtube-dl_B.png",
|
||||
"author": "叮叮当",
|
||||
@@ -416,6 +462,7 @@
|
||||
"RemoveLink": {
|
||||
"name": "清理硬链接",
|
||||
"description": "监控目录内文件被删除时,同步删除监控目录内所有和它硬链接的文件",
|
||||
"labels": "文件整理",
|
||||
"version": "1.9",
|
||||
"icon": "Ombi_A.png",
|
||||
"author": "DzAvril",
|
||||
@@ -430,6 +477,7 @@
|
||||
"LinkMonitor": {
|
||||
"name": "实时硬链接",
|
||||
"description": "监控目录文件变化,实时硬链接。",
|
||||
"labels": "文件整理",
|
||||
"version": "1.6",
|
||||
"icon": "Linkace_C.png",
|
||||
"author": "jxxghp",
|
||||
@@ -441,6 +489,7 @@
|
||||
"CategoryEditor": {
|
||||
"name": "二级分类策略",
|
||||
"description": "编辑下载目录和媒体库目录的二级分类规则。",
|
||||
"labels": "文件整理",
|
||||
"version": "1.1",
|
||||
"icon": "Bookstack_A.png",
|
||||
"author": "jxxghp",
|
||||
@@ -449,6 +498,7 @@
|
||||
"RemoteIdentifiers": {
|
||||
"name": "共享识别词",
|
||||
"description": "从Github、Etherpad等远程文件中获取共享识别词并应用。",
|
||||
"labels": "识别",
|
||||
"version": "2.2",
|
||||
"icon": "words.png",
|
||||
"author": "honue",
|
||||
@@ -457,6 +507,7 @@
|
||||
"NeoDBSync": {
|
||||
"name": "NeoDB 想看",
|
||||
"description": "同步 NeoDB 想看条目,自动添加订阅。",
|
||||
"labels": "订阅",
|
||||
"version": "1.1",
|
||||
"icon": "NeoDB.jpeg",
|
||||
"author": "hcplantern",
|
||||
@@ -468,6 +519,7 @@
|
||||
"PlayletCategory": {
|
||||
"name": "短剧自动分类",
|
||||
"description": "网络短剧自动整理到独立的分类目录。",
|
||||
"labels": "文件整理",
|
||||
"version": "1.4",
|
||||
"icon": "Amule_A.png",
|
||||
"author": "jxxghp",
|
||||
@@ -476,6 +528,7 @@
|
||||
"DiagParamAdjust": {
|
||||
"name": "诊断参数调整",
|
||||
"description": "Emby专用插件|暂时性解决emby字幕偏移问题,需要emby安装Diagnostics插件。",
|
||||
"labels": "Emby",
|
||||
"version": "1.3",
|
||||
"icon": "Gatus_A.png",
|
||||
"author": "jeblove",
|
||||
@@ -484,14 +537,19 @@
|
||||
"QbCommand": {
|
||||
"name": "QB远程操作",
|
||||
"description": "通过定时任务或交互命令远程操作QB暂停/开始/限速等。",
|
||||
"version": "1.3",
|
||||
"labels": "下载管理,Qbittorrent",
|
||||
"version": "1.4",
|
||||
"icon": "Qbittorrent_A.png",
|
||||
"author": "DzAvril",
|
||||
"level": 1
|
||||
"level": 1,
|
||||
"history": {
|
||||
"v1.4": "可选某些站点不再做种(暂停做种后不会被恢复)"
|
||||
}
|
||||
},
|
||||
"TrCommand": {
|
||||
"name": "TR远程操作",
|
||||
"description": "通过定时任务或交互命令远程操作TR暂停/开始/限速等。",
|
||||
"labels": "下载管理,Transmission",
|
||||
"version": "1.1",
|
||||
"icon": "Transmission_A.png",
|
||||
"author": "Hoey",
|
||||
@@ -500,6 +558,7 @@
|
||||
"IpDetect": {
|
||||
"name": "本地IP检测",
|
||||
"description": "如果QB、TR等服务在本地部署,当本地IP改变时自动修改其Server IP。",
|
||||
"labels": "系统设置",
|
||||
"version": "1.1",
|
||||
"icon": "ipAddress.png",
|
||||
"author": "DzAvril",
|
||||
@@ -508,6 +567,7 @@
|
||||
"TrackerEditor": {
|
||||
"name": "Tracker替换",
|
||||
"description": "批量替换种子tracker,支持周期性巡检(如为TR,仅支持4.0以上版本)。",
|
||||
"labels": "做种",
|
||||
"version": "1.5",
|
||||
"icon": "trackereditor_A.png",
|
||||
"author": "honue",
|
||||
@@ -516,6 +576,7 @@
|
||||
"ContractCheck": {
|
||||
"name": "契约检查",
|
||||
"description": "定时检查保种契约达成情况。",
|
||||
"labels": "做种",
|
||||
"version": "1.1",
|
||||
"icon": "contract.png",
|
||||
"author": "DzAvril",
|
||||
@@ -524,6 +585,7 @@
|
||||
"DownloaderHelper": {
|
||||
"name": "下载器助手",
|
||||
"description": "自动做种、站点标签、自动删种。",
|
||||
"labels": "下载管理",
|
||||
"version": "1.6",
|
||||
"icon": "DownloaderHelper.png",
|
||||
"author": "hotlcc",
|
||||
@@ -535,6 +597,7 @@
|
||||
"FeiShuMsg": {
|
||||
"name": "飞书机器人消息通知",
|
||||
"description": "支持使用飞书群聊机器人发送消息通知。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.0",
|
||||
"icon": "FeiShu_A.png",
|
||||
"author": "InfinityPacer",
|
||||
@@ -543,6 +606,7 @@
|
||||
"IyuuAuth": {
|
||||
"name": "IYUU站点绑定",
|
||||
"description": "为IYUU账号绑定认证站点,以便用于用户认证和辅种。",
|
||||
"labels": "IYUU",
|
||||
"version": "1.1",
|
||||
"icon": "Iyuu_A.png",
|
||||
"author": "jxxghp",
|
||||
@@ -554,6 +618,7 @@
|
||||
"NtfyMsg": {
|
||||
"name": "ntfy消息推送",
|
||||
"description": "支持使用ntfy发送消息通知。",
|
||||
"labels": "消息通知",
|
||||
"version": "1.0",
|
||||
"icon": "Ntfy_A.png",
|
||||
"author": "lethargicScribe",
|
||||
@@ -562,9 +627,19 @@
|
||||
"PluginAutoUpgrade": {
|
||||
"name": "插件自动升级",
|
||||
"description": "定时检测、升级插件。",
|
||||
"labels": "自动更新",
|
||||
"version": "1.4",
|
||||
"icon": "PluginAutoUpgrade.png",
|
||||
"author": "hotlcc",
|
||||
"level": 1
|
||||
},
|
||||
"MergeSiteSwitch": {
|
||||
"name": "聚合站点开关",
|
||||
"description": "统一管理所有与站点相关的开关。",
|
||||
"labels": "系统设置",
|
||||
"version": "1.0",
|
||||
"icon": "world.png",
|
||||
"author": "hotlcc",
|
||||
"level": 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class IYUUAutoSeed(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "IYUU.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.3"
|
||||
plugin_version = "1.5"
|
||||
# 插件作者
|
||||
plugin_author = "jxxghp"
|
||||
# 作者主页
|
||||
@@ -760,7 +760,7 @@ class IYUUAutoSeed(_PluginBase):
|
||||
site_domain = StringUtils.get_url_domain(site_url)
|
||||
# 站点信息
|
||||
site_info = self.sites.get_indexer(site_domain)
|
||||
if not site_info:
|
||||
if not site_info or not site_info.get('url'):
|
||||
logger.debug(f"没有维护种子对应的站点:{site_url}")
|
||||
return False
|
||||
if self._sites and site_info.get('id') not in self._sites:
|
||||
@@ -892,7 +892,36 @@ class IYUUAutoSeed(_PluginBase):
|
||||
拼装种子下载链接
|
||||
"""
|
||||
|
||||
def __is_special_site(url):
|
||||
def __is_mteam(url: str):
|
||||
"""
|
||||
判断是否为mteam站点
|
||||
"""
|
||||
return True if "m-team." in url else False
|
||||
|
||||
def __get_mteam_enclosure(tid: str):
|
||||
"""
|
||||
获取mteam种子下载链接
|
||||
"""
|
||||
_apikey = self.systemconfig.get(f"site.m-team.apikey")
|
||||
if not _apikey:
|
||||
logger.error("m-team站点的apikey未配置")
|
||||
return None
|
||||
with RequestUtils(
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': f'{site.get("ua")}',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'x-api-key': _apikey
|
||||
}
|
||||
).post_res(f"{site.get('url')}api/torrent/genDlToken", params={
|
||||
'id': tid
|
||||
}) as res:
|
||||
if not res:
|
||||
logger.warn(f"m-team 获取种子下载链接失败:{tid}")
|
||||
return None
|
||||
return res.json().get("data")
|
||||
|
||||
def __is_special_site(url: str):
|
||||
"""
|
||||
判断是否为特殊站点
|
||||
"""
|
||||
@@ -910,7 +939,10 @@ class IYUUAutoSeed(_PluginBase):
|
||||
return False
|
||||
|
||||
try:
|
||||
if __is_special_site(site.get('url')):
|
||||
if __is_mteam(site.get('url')):
|
||||
# 调用mteam接口获取下载链接
|
||||
return __get_mteam_enclosure(seed.get("torrent_id"))
|
||||
elif __is_special_site(site.get('url')):
|
||||
# 从详情页面获取下载链接
|
||||
return self.__get_torrent_url_from_page(seed=seed, site=site)
|
||||
else:
|
||||
@@ -940,7 +972,7 @@ class IYUUAutoSeed(_PluginBase):
|
||||
flags=re.IGNORECASE)
|
||||
return f"{site.get('url')}{download_url}"
|
||||
except Exception as e:
|
||||
logger.warn(f"站点 {site.get('name')} Url转换失败:{str(e)},尝试通过详情页面获取种子下载链接 ...")
|
||||
logger.warn(f"{site.get('name')} Url转换失败,{str(e)}:site_url={site.get('url')},base_url={base_url}, seed={seed}")
|
||||
return self.__get_torrent_url_from_page(seed=seed, site=site)
|
||||
|
||||
def __get_torrent_url_from_page(self, seed: dict, site: dict):
|
||||
|
||||
912
plugins/mergesiteswitch/__init__.py
Normal file
912
plugins/mergesiteswitch/__init__.py
Normal file
@@ -0,0 +1,912 @@
|
||||
import inspect
|
||||
import os
|
||||
from typing import Any, List, Dict, Tuple
|
||||
|
||||
from app.core.event import eventmanager, Event
|
||||
from app.db.models.site import Site
|
||||
from app.db.site_oper import SiteOper
|
||||
from app.db.systemconfig_oper import SystemConfigOper
|
||||
from app.log import logger
|
||||
from app.plugins import _PluginBase
|
||||
from app.schemas.types import SystemConfigKey, EventType
|
||||
|
||||
|
||||
class MergeSiteSwitch(_PluginBase):
|
||||
# 插件名称
|
||||
plugin_name = "聚合站点开关"
|
||||
# 插件描述
|
||||
plugin_desc = "统一管理所有与站点相关的开关。"
|
||||
# 插件图标
|
||||
plugin_icon = "world.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.0"
|
||||
# 插件作者
|
||||
plugin_author = "hotlcc"
|
||||
# 作者主页
|
||||
author_url = "https://github.com/hotlcc"
|
||||
# 插件配置项ID前缀
|
||||
plugin_config_prefix = "com.hotlcc.mergesiteswitch."
|
||||
# 加载顺序
|
||||
plugin_order = 66
|
||||
# 可使用的用户级别
|
||||
auth_level = 2
|
||||
|
||||
# 依赖组件
|
||||
# 站点操作
|
||||
__site_oper: SiteOper = SiteOper()
|
||||
# 系统配置操作
|
||||
__system_config_oper: SystemConfigOper = SystemConfigOper()
|
||||
|
||||
# 其它插件ID
|
||||
# 站点自动签到
|
||||
__plugin_id_auto_signin: str = 'AutoSignIn'
|
||||
# 站点数据统计
|
||||
__plugin_id_site_statistic: str = 'SiteStatistic'
|
||||
# IYUU自动辅种
|
||||
__plugin_id_iyuu_auto_seed: str = 'IYUUAutoSeed'
|
||||
# 站点刷流
|
||||
__plugin_id_brush_flow: str = 'BrushFlow'
|
||||
|
||||
# 配置相关
|
||||
# 插件缺省配置
|
||||
__config_default: Dict[str, Any] = {}
|
||||
# 插件用户配置
|
||||
__config: Dict[str, Any] = {}
|
||||
|
||||
def init_plugin(self, config: dict = None):
|
||||
"""
|
||||
初始化插件
|
||||
"""
|
||||
# 加载配置
|
||||
self.__config = config
|
||||
# 当页面通过调用接口保存配置时保存其它各项配置
|
||||
if self.__check_stack_is_save_config_request():
|
||||
self.__set_config(config=config)
|
||||
|
||||
def get_state(self) -> bool:
|
||||
"""
|
||||
获取插件状态
|
||||
"""
|
||||
return self.__check_any_follow_enable_sites()
|
||||
|
||||
@staticmethod
|
||||
def get_command() -> List[Dict[str, Any]]:
|
||||
"""
|
||||
定义远程控制命令
|
||||
:return: 命令关键字、事件、描述、附带数据
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_api(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
获取插件API
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_service(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
注册插件公共服务
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
|
||||
"""
|
||||
拼装插件配置页面,需要返回两块数据:1、页面配置;2、数据结构
|
||||
"""
|
||||
# 站点选项
|
||||
site_options = self.__get_site_options()
|
||||
# 已安装的插件IDs
|
||||
installed_plugin_ids = self.__get_installed_plugin_ids()
|
||||
# 建议的配置
|
||||
config_suggest = {}
|
||||
|
||||
# 表单内容
|
||||
form_content = [{
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 12, 'xl': 12, 'lg': 12, 'md': 12, 'sm': 12, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'enable_sites',
|
||||
'label': '启用的站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '对应功能【站点管理 / 添加编辑站点 / 启用】'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'search_sites',
|
||||
'label': '设定 / 搜索 / 搜索站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '只有选中的站点才会在搜索中使用。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'search_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'rss_sites',
|
||||
'label': '设定 / 订阅 / 订阅站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '只有选中的站点才会在订阅中使用。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'rss_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
# 站点自动签到
|
||||
if self.__plugin_id_auto_signin in installed_plugin_ids:
|
||||
form_content.append({
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'signin_sites',
|
||||
'label': '插件 / 站点自动签到 / 签到站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '只有选中的站点才会在签到中使用。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'signin_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'login_sites',
|
||||
'label': '插件 / 站点自动签到 / 登录站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '只有选中的站点才会在登录中使用。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'login_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
})
|
||||
# 站点数据统计
|
||||
if self.__plugin_id_site_statistic in installed_plugin_ids:
|
||||
form_content.append({
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'statistic_sites',
|
||||
'label': '插件 / 站点数据统计 / 统计站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '缺省时默认全部站点。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'statistic_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
})
|
||||
# IYUU自动辅种
|
||||
if self.__plugin_id_iyuu_auto_seed in installed_plugin_ids:
|
||||
form_content.append({
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'iyuu_seed_sites',
|
||||
'label': '插件 / IYUU自动辅种 / 辅种站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '缺省时默认全部站点。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'iyuu_seed_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
})
|
||||
# 站点刷流
|
||||
if self.__plugin_id_brush_flow in installed_plugin_ids:
|
||||
form_content.append({
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 9, 'xl': 9, 'lg': 9, 'md': 9, 'sm': 8, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSelect',
|
||||
'props': {
|
||||
'model': 'brush_flow_sites',
|
||||
'label': '插件 / 站点刷流 / 刷流站点',
|
||||
'multiple': True,
|
||||
'chips': True,
|
||||
'items': site_options,
|
||||
'hint': '只有选中的站点才会在刷流中使用。'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 3, 'xl': 3, 'lg': 3, 'md': 3, 'sm': 4, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VSwitch',
|
||||
'props': {
|
||||
'model': 'brush_flow_follow_enable_sites',
|
||||
'label': '跟随启用的站点',
|
||||
'hint': '与站点的启用状态保持一致,保存时会立即生效,并在后台监听站点状态变化实时生效。'
|
||||
}
|
||||
}]
|
||||
}]
|
||||
})
|
||||
# 提示信息
|
||||
form_content.append({
|
||||
'component': 'VRow',
|
||||
'content': [{
|
||||
'component': 'VCol',
|
||||
'props': {
|
||||
'cols': 12,
|
||||
'xxl': 12, 'xl': 12, 'lg': 12, 'md': 12, 'sm': 12, 'xs': 12
|
||||
},
|
||||
'content': [{
|
||||
'component': 'VAlert',
|
||||
'props': {
|
||||
'type': 'warning',
|
||||
'variant': 'tonal'
|
||||
},
|
||||
'text': '点击保存后会立即生效,各项站点开关配置即会更新!'
|
||||
}]
|
||||
}]
|
||||
})
|
||||
# 表单
|
||||
form = [{
|
||||
'component': 'VForm',
|
||||
'content': form_content
|
||||
}]
|
||||
|
||||
# 重载配置
|
||||
self.__get_config()
|
||||
return form, config_suggest
|
||||
|
||||
def get_page(self) -> List[dict]:
|
||||
pass
|
||||
|
||||
def stop_service(self):
|
||||
"""
|
||||
退出插件
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def __check_stack_exist_function(cls, package_name: str, function_name: str) -> bool:
|
||||
"""
|
||||
判断当前调用栈是否存在指定的函数
|
||||
"""
|
||||
if not package_name or not function_name:
|
||||
return False
|
||||
for stack in inspect.stack():
|
||||
if stack.function != 'set_plugin_config':
|
||||
continue
|
||||
package_path = package_name.replace('.', os.sep)
|
||||
if stack.filename.endswith(f"{package_path}.py") or stack.filename.endswith(f"{package_path}{os.sep}__init__.py"):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def __check_stack_is_save_config_request(cls) -> bool:
|
||||
"""
|
||||
判断当前调用栈是否来源于“插件配置保存”接口
|
||||
"""
|
||||
return cls.__check_stack_exist_function('app.api.endpoints.plugin', 'set_plugin_config')
|
||||
|
||||
def __get_config_value(self, config_key: str, use_default: bool = True) -> Any:
|
||||
"""
|
||||
获取插件配置值
|
||||
:param config_key: 配置键
|
||||
:param use_default: 是否使用缺省值
|
||||
:return: 配置值
|
||||
"""
|
||||
if not config_key:
|
||||
return None
|
||||
config = self.__config if self.__config else {}
|
||||
config_value = config.get(config_key)
|
||||
if config_value is None and use_default:
|
||||
config_default = self.__config_default if self.__config_default else {}
|
||||
config_value = config_default.get(config_key)
|
||||
return config_value
|
||||
|
||||
def __check_follow_enable_sites(self, config_key: str, plugin_id: str = None, installed_plugin_ids: List[str] = None) -> bool:
|
||||
"""
|
||||
判断某个跟随按钮是否打开
|
||||
:param plugin_id: 传插件ID时还要同时根据插件是否安装进行判断
|
||||
"""
|
||||
if not self.__get_config_value(config_key=config_key):
|
||||
return False
|
||||
if not plugin_id:
|
||||
return True
|
||||
if not installed_plugin_ids:
|
||||
installed_plugin_ids = self.__get_installed_plugin_ids()
|
||||
return plugin_id in installed_plugin_ids
|
||||
|
||||
def __check_search_follow_enable_sites(self) -> bool:
|
||||
"""
|
||||
判断搜索站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='search_follow_enable_sites')
|
||||
|
||||
def __check_rss_follow_enable_sites(self) -> bool:
|
||||
"""
|
||||
判断订阅站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='rss_follow_enable_sites')
|
||||
|
||||
def __check_signin_follow_enable_sites(self, installed_plugin_ids: List[str] = None) -> bool:
|
||||
"""
|
||||
判断签到站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='signin_follow_enable_sites', plugin_id=self.__plugin_id_auto_signin, installed_plugin_ids=installed_plugin_ids)
|
||||
|
||||
def __check_login_follow_enable_sites(self, installed_plugin_ids: List[str] = None) -> bool:
|
||||
"""
|
||||
判断登录站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='login_follow_enable_sites', plugin_id=self.__plugin_id_auto_signin, installed_plugin_ids=installed_plugin_ids)
|
||||
|
||||
def __check_statistic_follow_enable_sites(self, installed_plugin_ids: List[str] = None) -> bool:
|
||||
"""
|
||||
判断统计站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='statistic_follow_enable_sites', plugin_id=self.__plugin_id_site_statistic, installed_plugin_ids=installed_plugin_ids)
|
||||
|
||||
def __check_iyuu_seed_follow_enable_sites(self, installed_plugin_ids: List[str] = None) -> bool:
|
||||
"""
|
||||
判断iyuu辅种站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='iyuu_seed_follow_enable_sites', plugin_id=self.__plugin_id_iyuu_auto_seed, installed_plugin_ids=installed_plugin_ids)
|
||||
|
||||
def __check_brush_flow_follow_enable_sites(self, installed_plugin_ids: List[str] = None) -> bool:
|
||||
"""
|
||||
判断刷流站点的跟随按钮是否打开
|
||||
"""
|
||||
return self.__check_follow_enable_sites(config_key='brush_flow_follow_enable_sites', plugin_id=self.__plugin_id_brush_flow, installed_plugin_ids=installed_plugin_ids)
|
||||
|
||||
def __check_any_follow_enable_sites(self) -> bool:
|
||||
"""
|
||||
判断是否开启任意跟随按钮
|
||||
"""
|
||||
# 已安装的插件IDs
|
||||
installed_plugin_ids = self.__get_installed_plugin_ids()
|
||||
return True if self.__check_search_follow_enable_sites() \
|
||||
or self.__check_rss_follow_enable_sites() \
|
||||
or self.__check_signin_follow_enable_sites(installed_plugin_ids=installed_plugin_ids) \
|
||||
or self.__check_login_follow_enable_sites(installed_plugin_ids=installed_plugin_ids) \
|
||||
or self.__check_statistic_follow_enable_sites(installed_plugin_ids=installed_plugin_ids) \
|
||||
or self.__check_iyuu_seed_follow_enable_sites(installed_plugin_ids=installed_plugin_ids) \
|
||||
or self.__check_brush_flow_follow_enable_sites(installed_plugin_ids=installed_plugin_ids) else False
|
||||
|
||||
def __get_site_options(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
获取站点选项
|
||||
"""
|
||||
sites = self.__site_oper.list_order_by_pri()
|
||||
if not sites:
|
||||
return []
|
||||
return [{
|
||||
'title': site.name,
|
||||
'value': site.id
|
||||
} for site in sites if site]
|
||||
|
||||
def __get_installed_plugin_ids(self):
|
||||
"""
|
||||
获取已安装的插件IDs
|
||||
"""
|
||||
installed_plugin_ids = self.__system_config_oper.get(SystemConfigKey.UserInstalledPlugins)
|
||||
return installed_plugin_ids if installed_plugin_ids else []
|
||||
|
||||
def __get_config(self):
|
||||
"""
|
||||
获取配置,包含聚合外部配置
|
||||
"""
|
||||
config = self.get_config()
|
||||
if not config:
|
||||
config = {}
|
||||
config.update({
|
||||
'enable_sites': self.__get_enable_site_ids(),
|
||||
'search_sites': self.__get_search_site_ids(),
|
||||
'rss_sites': self.__get_rss_site_ids(),
|
||||
})
|
||||
# 已安装的插件IDs
|
||||
installed_plugin_ids = self.__get_installed_plugin_ids()
|
||||
if self.__plugin_id_auto_signin in installed_plugin_ids:
|
||||
config.update({
|
||||
'signin_sites': self.__get_signin_site_ids(),
|
||||
'login_sites': self.__get_login_site_ids(),
|
||||
})
|
||||
if self.__plugin_id_site_statistic in installed_plugin_ids:
|
||||
config.update({
|
||||
'statistic_sites': self.__get_statistic_site_ids(),
|
||||
})
|
||||
if self.__plugin_id_iyuu_auto_seed in installed_plugin_ids:
|
||||
config.update({
|
||||
'iyuu_seed_sites': self.__get_iyuu_seed_site_ids(),
|
||||
})
|
||||
if self.__plugin_id_brush_flow in installed_plugin_ids:
|
||||
config.update({
|
||||
'brush_flow_sites': self.__get_brush_flow_site_ids(),
|
||||
})
|
||||
self.update_config(config=config)
|
||||
return config
|
||||
|
||||
def __pre_config_follow_enable_sites(self, config: dict) -> dict:
|
||||
"""
|
||||
处理跟随站点
|
||||
"""
|
||||
if not config:
|
||||
config = {}
|
||||
enable_sites = config.get('enable_sites') or []
|
||||
if config.get('search_follow_enable_sites'):
|
||||
config.update({"search_sites": enable_sites.copy()})
|
||||
if config.get('rss_follow_enable_sites'):
|
||||
config.update({"rss_sites": enable_sites.copy()})
|
||||
if config.get('signin_follow_enable_sites'):
|
||||
config.update({"signin_sites": enable_sites.copy()})
|
||||
if config.get('login_follow_enable_sites'):
|
||||
config.update({"login_sites": enable_sites.copy()})
|
||||
if config.get('statistic_follow_enable_sites'):
|
||||
config.update({"statistic_sites": enable_sites.copy()})
|
||||
if config.get('iyuu_seed_follow_enable_sites'):
|
||||
config.update({"iyuu_seed_sites": enable_sites.copy()})
|
||||
if config.get('brush_flow_follow_enable_sites'):
|
||||
config.update({"brush_flow_sites": enable_sites.copy()})
|
||||
return config
|
||||
|
||||
def __pre_config(self, config: dict) -> dict:
|
||||
"""
|
||||
预处理配置
|
||||
"""
|
||||
config = self.__pre_config_follow_enable_sites(config=config)
|
||||
logger.debug(f"配置预处理完成: {config}")
|
||||
return config
|
||||
|
||||
def __set_config(self, config: dict):
|
||||
"""
|
||||
保存配置,包含保存外部配置到各自表
|
||||
"""
|
||||
# 预处理配置
|
||||
config = self.__pre_config(config=config)
|
||||
# 更新各项配置
|
||||
self.update_config(config=config)
|
||||
logger.info("插件配置更新完成")
|
||||
self.__set_enable_site_ids(config.get('enable_sites'))
|
||||
self.__set_search_site_ids(config.get('search_sites'))
|
||||
self.__set_rss_site_ids(config.get('rss_sites'))
|
||||
# 已安装的插件IDs
|
||||
installed_plugin_ids = self.__get_installed_plugin_ids()
|
||||
if self.__plugin_id_auto_signin in installed_plugin_ids:
|
||||
self.__set_signin_site_ids(config.get('signin_sites'))
|
||||
self.__set_login_site_ids(config.get('login_sites'))
|
||||
if self.__plugin_id_site_statistic in installed_plugin_ids:
|
||||
self.__set_statistic_site_ids(config.get('statistic_sites'))
|
||||
if self.__plugin_id_iyuu_auto_seed in installed_plugin_ids:
|
||||
self.__set_iyuu_seed_site_ids(config.get('iyuu_seed_sites'))
|
||||
if self.__plugin_id_brush_flow in installed_plugin_ids:
|
||||
self.__set_brush_flow_site_ids(config.get('brush_flow_sites'))
|
||||
return config
|
||||
|
||||
def __get_enable_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取启用的站点IDs
|
||||
"""
|
||||
sites = self.__site_oper.list_order_by_pri()
|
||||
if not sites:
|
||||
return []
|
||||
return [site.id for site in sites if site and site.is_active]
|
||||
|
||||
def __set_enable_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置启用的站点IDs
|
||||
"""
|
||||
sites = self.__site_oper.list_order_by_pri()
|
||||
if not sites:
|
||||
return
|
||||
for site in sites:
|
||||
if not site_ids or site.id not in site_ids:
|
||||
if site.is_active:
|
||||
self.__site_oper.update(site.id, {'is_active': False})
|
||||
else:
|
||||
if not site.is_active:
|
||||
self.__site_oper.update(site.id, {'is_active': True})
|
||||
logger.info("启用的站点配置完成")
|
||||
|
||||
def __get_search_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取搜索站点IDs
|
||||
"""
|
||||
sites = self.__system_config_oper.get(SystemConfigKey.IndexerSites)
|
||||
return sites if sites else []
|
||||
|
||||
def __set_search_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置搜索站点IDs
|
||||
"""
|
||||
self.__system_config_oper.set(SystemConfigKey.IndexerSites, site_ids)
|
||||
logger.info("搜索站点配置完成")
|
||||
|
||||
def __get_rss_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取订阅站点IDs
|
||||
"""
|
||||
sites = self.__system_config_oper.get(SystemConfigKey.RssSites)
|
||||
return sites if sites else []
|
||||
|
||||
def __set_rss_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置订阅站点IDs
|
||||
"""
|
||||
self.__system_config_oper.set(SystemConfigKey.RssSites, site_ids)
|
||||
logger.info("订阅站点配置完成")
|
||||
|
||||
def __get_plugin_config_value(self, plugin_id: str, config_key: str) -> Any:
|
||||
"""
|
||||
获取插件配置值
|
||||
"""
|
||||
if not plugin_id or not config_key:
|
||||
return None
|
||||
config = self.get_config(plugin_id)
|
||||
if not config:
|
||||
return None
|
||||
return config.get(config_key)
|
||||
|
||||
def __set_plugin_config_value(self, plugin_id: str, config_key: str, config_value: Any) -> Any:
|
||||
"""
|
||||
设置插件配置值
|
||||
"""
|
||||
if not plugin_id or not config_key:
|
||||
return
|
||||
config = self.get_config(plugin_id)
|
||||
if not config:
|
||||
config = {}
|
||||
config.update({config_key: config_value})
|
||||
self.update_config(plugin_id=plugin_id, config=config)
|
||||
|
||||
def __get_signin_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取签到站点IDs
|
||||
"""
|
||||
sites = self.__get_plugin_config_value(self.__plugin_id_auto_signin, 'sign_sites')
|
||||
return sites if sites else []
|
||||
|
||||
def __set_signin_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置签到站点IDs
|
||||
"""
|
||||
self.__set_plugin_config_value(self.__plugin_id_auto_signin, 'sign_sites', site_ids)
|
||||
logger.info("签到站点配置完成")
|
||||
|
||||
def __get_login_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取登录站点IDs
|
||||
"""
|
||||
sites = self.__get_plugin_config_value(self.__plugin_id_auto_signin, 'login_sites')
|
||||
return sites if sites else []
|
||||
|
||||
def __set_login_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置登录站点IDs
|
||||
"""
|
||||
self.__set_plugin_config_value(self.__plugin_id_auto_signin, 'login_sites', site_ids)
|
||||
logger.info("登录站点配置完成")
|
||||
|
||||
def __get_statistic_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取统计站点IDs
|
||||
"""
|
||||
sites = self.__get_plugin_config_value(self.__plugin_id_site_statistic, 'statistic_sites')
|
||||
return sites if sites else []
|
||||
|
||||
def __set_statistic_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置统计站点IDs
|
||||
"""
|
||||
self.__set_plugin_config_value(self.__plugin_id_site_statistic, 'statistic_sites', site_ids)
|
||||
logger.info("统计站点配置完成")
|
||||
|
||||
def __get_iyuu_seed_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取iyuu自动辅种站点IDs
|
||||
"""
|
||||
sites = self.__get_plugin_config_value(self.__plugin_id_iyuu_auto_seed, 'sites')
|
||||
return sites if sites else []
|
||||
|
||||
def __set_iyuu_seed_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置iyuu自动辅种站点IDs
|
||||
"""
|
||||
self.__set_plugin_config_value(self.__plugin_id_iyuu_auto_seed, 'sites', site_ids)
|
||||
logger.info("IYUU辅种站点配置完成")
|
||||
|
||||
def __get_brush_flow_site_ids(self) -> List[int]:
|
||||
"""
|
||||
获取刷流站点IDs
|
||||
"""
|
||||
sites = self.__get_plugin_config_value(self.__plugin_id_brush_flow, 'brushsites')
|
||||
return sites if sites else []
|
||||
|
||||
def __set_brush_flow_site_ids(self, site_ids: List[int]):
|
||||
"""
|
||||
设置刷流站点IDs
|
||||
"""
|
||||
self.__set_plugin_config_value(self.__plugin_id_brush_flow, 'brushsites', site_ids)
|
||||
logger.info("刷流站点配置完成")
|
||||
|
||||
def __update_search_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_search_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_search_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_search_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_rss_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_rss_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_rss_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_rss_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_signin_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_signin_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_signin_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_signin_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_login_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_login_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_login_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_login_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_statistic_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_statistic_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_statistic_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_statistic_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_iyuu_seed_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_iyuu_seed_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_iyuu_seed_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_iyuu_seed_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_brush_flow_site_ids_by_site(self, site_id: int, site_status: bool):
|
||||
if site_id == None:
|
||||
return
|
||||
site_ids = self.__get_brush_flow_site_ids() or []
|
||||
if site_id not in site_ids and site_status:
|
||||
site_ids.append(site_id)
|
||||
self.__set_brush_flow_site_ids(site_ids=site_ids)
|
||||
elif site_id in site_ids and not site_status:
|
||||
site_ids.remove(site_id)
|
||||
self.__set_brush_flow_site_ids(site_ids=site_ids)
|
||||
|
||||
def __update_site_ids_for_site_event(self, site_id: int, site_status: bool):
|
||||
"""
|
||||
针对站点事件更新各项配置
|
||||
"""
|
||||
if site_id == None:
|
||||
return
|
||||
if self.__check_search_follow_enable_sites():
|
||||
self.__update_search_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
if self.__check_rss_follow_enable_sites():
|
||||
self.__update_rss_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
# 已安装的插件IDs
|
||||
installed_plugin_ids = self.__get_installed_plugin_ids()
|
||||
if self.__check_signin_follow_enable_sites(installed_plugin_ids=installed_plugin_ids):
|
||||
self.__update_signin_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
if self.__check_login_follow_enable_sites(installed_plugin_ids=installed_plugin_ids):
|
||||
self.__update_login_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
if self.__check_statistic_follow_enable_sites(installed_plugin_ids=installed_plugin_ids):
|
||||
self.__update_statistic_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
if self.__check_iyuu_seed_follow_enable_sites(installed_plugin_ids=installed_plugin_ids):
|
||||
self.__update_iyuu_seed_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
if self.__check_brush_flow_follow_enable_sites(installed_plugin_ids=installed_plugin_ids):
|
||||
self.__update_brush_flow_site_ids_by_site(site_id=site_id, site_status=site_status)
|
||||
|
||||
@eventmanager.register(EventType.SiteUpdated)
|
||||
def listen_site_updated_event(self, event: Event = None):
|
||||
"""
|
||||
监听站点更新事件
|
||||
"""
|
||||
logger.info('监听到站点更新事件')
|
||||
if not event or not event.event_data:
|
||||
logger.warn('事件信息无效,忽略事件')
|
||||
return
|
||||
domain = event.event_data.get("domain")
|
||||
if not domain:
|
||||
logger.warn('事件信息无效,忽略事件')
|
||||
return
|
||||
if not self.__check_any_follow_enable_sites():
|
||||
logger.warn('未打开任一【跟随启用的站点】开关,忽略事件')
|
||||
return
|
||||
site = self.__site_oper.get_by_domain(domain=domain)
|
||||
if not site:
|
||||
logger.warn(f'目标站点不存在,忽略事件: domain = {domain}')
|
||||
return
|
||||
self.__update_site_ids_for_site_event(site_id=site.id, site_status=site.is_active)
|
||||
logger.info('站点更新事件监听任务执行完成')
|
||||
|
||||
@eventmanager.register(EventType.SiteDeleted)
|
||||
def listen_site_deleted_event(self, event: Event = None):
|
||||
"""
|
||||
监听站点删除事件
|
||||
"""
|
||||
logger.info('监听到站点删除事件')
|
||||
if not event or not event.event_data:
|
||||
logger.warn('事件信息无效,忽略事件')
|
||||
return
|
||||
site_id = event.event_data.get("site_id")
|
||||
if site_id == None:
|
||||
logger.warn('事件信息无效,忽略事件')
|
||||
return
|
||||
if not self.__check_any_follow_enable_sites():
|
||||
logger.warn('未打开任一【跟随启用的站点】开关,忽略事件')
|
||||
return
|
||||
self.__update_site_ids_for_site_event(site_id=site_id, site_status=False)
|
||||
logger.info('站点删除事件监听任务执行完成')
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import List, Tuple, Dict, Any
|
||||
from enum import Enum
|
||||
from urllib.parse import urlparse
|
||||
import urllib
|
||||
from app.log import logger
|
||||
from app.modules.qbittorrent import Qbittorrent
|
||||
from app.plugins import _PluginBase
|
||||
@@ -7,6 +9,13 @@ from app.schemas import NotificationType
|
||||
from app.schemas.types import EventType
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
from app.core.event import eventmanager, Event
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from app.core.config import settings
|
||||
from app.helper.sites import SitesHelper
|
||||
from app.db.site_oper import SiteOper
|
||||
from app.utils.string import StringUtils
|
||||
from datetime import datetime, timedelta
|
||||
import pytz
|
||||
import time
|
||||
|
||||
|
||||
@@ -18,7 +27,7 @@ class QbCommand(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "Qbittorrent_A.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.3"
|
||||
plugin_version = "1.4"
|
||||
# 插件作者
|
||||
plugin_author = "DzAvril"
|
||||
# 作者主页
|
||||
@@ -31,6 +40,8 @@ class QbCommand(_PluginBase):
|
||||
auth_level = 1
|
||||
|
||||
# 私有属性
|
||||
_sites = None
|
||||
_siteoper = None
|
||||
_qb = None
|
||||
_enabled: bool = False
|
||||
_notify: bool = False
|
||||
@@ -45,8 +56,14 @@ class QbCommand(_PluginBase):
|
||||
_enable_upload_limit = False
|
||||
_download_limit = 0
|
||||
_enable_download_limit = False
|
||||
_op_site_ids = []
|
||||
_op_sites = []
|
||||
_multi_level_root_domain = ["edu.cn", "com.cn", "net.cn", "org.cn"]
|
||||
_scheduler = None
|
||||
|
||||
def init_plugin(self, config: dict = None):
|
||||
self._sites = SitesHelper()
|
||||
self._siteoper = SiteOper()
|
||||
# 停止现有任务
|
||||
self.stop_service()
|
||||
# 读取配置
|
||||
@@ -65,14 +82,33 @@ class QbCommand(_PluginBase):
|
||||
self._enable_download_limit = config.get("enable_download_limit")
|
||||
self._enable_upload_limit = config.get("enable_upload_limit")
|
||||
self._qb = Qbittorrent()
|
||||
self._op_site_ids = config.get("op_site_ids") or []
|
||||
# 查询所有站点
|
||||
all_sites = [site for site in self._sites.get_indexers() if not site.get("public")] + self.__custom_sites()
|
||||
# 过滤掉没有选中的站点
|
||||
self._op_sites = [site for site in all_sites if site.get("id") in self._op_site_ids]
|
||||
|
||||
if self._only_pause_once or self._only_resume_once:
|
||||
if self._only_pause_once and self._only_resume_once:
|
||||
logger.warning("只能选择一个: 立即暂停或立即开始所有任务")
|
||||
elif self._only_pause_once:
|
||||
self.pause_torrent()
|
||||
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
|
||||
logger.info(f"立即运行一次暂停所有任务")
|
||||
self._scheduler.add_job(
|
||||
self.pause_torrent,
|
||||
"date",
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
|
||||
+ timedelta(seconds=3),
|
||||
)
|
||||
elif self._only_resume_once:
|
||||
self.resume_torrent()
|
||||
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
|
||||
logger.info(f"立即运行一次开始所有任务")
|
||||
self._scheduler.add_job(
|
||||
self.resume_torrent,
|
||||
"date",
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
|
||||
+ timedelta(seconds=3),
|
||||
)
|
||||
|
||||
self._only_resume_once = False
|
||||
self._only_pause_once = False
|
||||
@@ -84,16 +120,56 @@ class QbCommand(_PluginBase):
|
||||
"notify": self._notify,
|
||||
"pause_cron": self._pause_cron,
|
||||
"resume_cron": self._resume_cron,
|
||||
"op_site_ids": self._op_site_ids,
|
||||
}
|
||||
)
|
||||
|
||||
if self._only_pause_upload or self._only_pause_download or self._only_pause_checking:
|
||||
# 启动任务
|
||||
if self._scheduler.get_jobs():
|
||||
self._scheduler.print_jobs()
|
||||
self._scheduler.start()
|
||||
|
||||
if (
|
||||
self._only_pause_upload
|
||||
or self._only_pause_download
|
||||
or self._only_pause_checking
|
||||
):
|
||||
if self._only_pause_upload:
|
||||
self.pause_torrent(self.TorrentType.UPLOADING)
|
||||
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
|
||||
logger.info(f"立即运行一次暂停所有上传任务")
|
||||
self._scheduler.add_job(
|
||||
self.pause_torrent,
|
||||
"date",
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
|
||||
+ timedelta(seconds=3),
|
||||
kwargs={
|
||||
'type': self.TorrentType.UPLOADING
|
||||
}
|
||||
)
|
||||
if self._only_pause_download:
|
||||
self.pause_torrent(self.TorrentType.DOWNLOADING)
|
||||
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
|
||||
logger.info(f"立即运行一次暂停所有下载任务")
|
||||
self._scheduler.add_job(
|
||||
self.pause_torrent,
|
||||
"date",
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
|
||||
+ timedelta(seconds=3),
|
||||
kwargs={
|
||||
'type': self.TorrentType.DOWNLOADING
|
||||
}
|
||||
)
|
||||
if self._only_pause_checking:
|
||||
self.pause_torrent(self.TorrentType.CHECKING)
|
||||
self._scheduler = BackgroundScheduler(timezone=settings.TZ)
|
||||
logger.info(f"立即运行一次暂停所有检查任务")
|
||||
self._scheduler.add_job(
|
||||
self.pause_torrent,
|
||||
"date",
|
||||
run_date=datetime.now(tz=pytz.timezone(settings.TZ))
|
||||
+ timedelta(seconds=3),
|
||||
kwargs={
|
||||
'type': self.TorrentType.CHECKING
|
||||
}
|
||||
)
|
||||
|
||||
self._only_pause_upload = False
|
||||
self._only_pause_download = False
|
||||
@@ -107,9 +183,15 @@ class QbCommand(_PluginBase):
|
||||
"notify": self._notify,
|
||||
"pause_cron": self._pause_cron,
|
||||
"resume_cron": self._resume_cron,
|
||||
"op_site_ids": self._op_site_ids,
|
||||
}
|
||||
)
|
||||
|
||||
# 启动任务
|
||||
if self._scheduler.get_jobs():
|
||||
self._scheduler.print_jobs()
|
||||
self._scheduler.start()
|
||||
|
||||
self.set_limit(self._upload_limit, self._download_limit)
|
||||
|
||||
def get_state(self) -> bool:
|
||||
@@ -186,6 +268,13 @@ class QbCommand(_PluginBase):
|
||||
},
|
||||
]
|
||||
|
||||
def __custom_sites(self) -> List[Any]:
|
||||
custom_sites = []
|
||||
custom_sites_config = self.get_config("CustomSites")
|
||||
if custom_sites_config and custom_sites_config.get("enabled"):
|
||||
custom_sites = custom_sites_config.get("sites")
|
||||
return custom_sites
|
||||
|
||||
def get_api(self) -> List[Dict[str, Any]]:
|
||||
pass
|
||||
|
||||
@@ -427,6 +516,7 @@ class QbCommand(_PluginBase):
|
||||
return
|
||||
|
||||
all_torrents = self.get_all_torrents()
|
||||
all_torrents = self.filter_torrents(all_torrents)
|
||||
hash_downloading, hash_uploading, hash_paused, hash_checking, hash_error = (
|
||||
self.get_torrents_status(all_torrents)
|
||||
)
|
||||
@@ -489,6 +579,41 @@ class QbCommand(_PluginBase):
|
||||
f"错误数量: {len(hash_error)}\n",
|
||||
)
|
||||
|
||||
def filter_torrents(self, all_torrents):
|
||||
"""
|
||||
过滤掉不参与保种的种子
|
||||
"""
|
||||
if len(self._op_sites) == 0:
|
||||
return all_torrents
|
||||
|
||||
urls = [site.get("url") for site in self._op_sites]
|
||||
op_sites_main_domains = []
|
||||
for url in urls:
|
||||
domain = StringUtils.get_url_netloc(url)
|
||||
main_domain = self.get_main_domain(domain[1])
|
||||
op_sites_main_domains.append(main_domain)
|
||||
|
||||
torrents = []
|
||||
for torrent in all_torrents:
|
||||
if torrent.get("state") == "pausedUP":
|
||||
tracker_url = self.get_torrent_tracker(torrent)
|
||||
if not tracker_url:
|
||||
logger.info(f"获取种子 {torrent.name} Tracker失败,不过滤该种子")
|
||||
torrents.append(torrent)
|
||||
_, tracker_domain = StringUtils.get_url_netloc(tracker_url)
|
||||
if not tracker_domain:
|
||||
logger.info(f"获取种子 {torrent.name} Tracker失败,不过滤该种子")
|
||||
torrents.append(torrent)
|
||||
tracker_main_domain = self.get_main_domain(domain=tracker_domain)
|
||||
if tracker_main_domain in op_sites_main_domains:
|
||||
logger.info(
|
||||
f"种子 {torrent.name} 属于站点{tracker_main_domain},不执行操作"
|
||||
)
|
||||
continue
|
||||
|
||||
torrents.append(torrent)
|
||||
return torrents
|
||||
|
||||
@eventmanager.register(EventType.PluginAction)
|
||||
def handle_qb_status(self, event: Event):
|
||||
if not self._enabled:
|
||||
@@ -508,14 +633,13 @@ class QbCommand(_PluginBase):
|
||||
self.get_torrents_status(all_torrents)
|
||||
)
|
||||
logger.info(
|
||||
f"QB开始任务启动 \n"
|
||||
f"QB任务状态 \n"
|
||||
f"种子总数: {len(all_torrents)} \n"
|
||||
f"做种数量: {len(hash_uploading)}\n"
|
||||
f"下载数量: {len(hash_downloading)}\n"
|
||||
f"检查数量: {len(hash_checking)}\n"
|
||||
f"暂停数量: {len(hash_paused)}\n"
|
||||
f"错误数量: {len(hash_error)}\n"
|
||||
f"开始操作中请稍等...\n",
|
||||
)
|
||||
if self._notify:
|
||||
self.post_message(
|
||||
@@ -648,7 +772,67 @@ class QbCommand(_PluginBase):
|
||||
text=f"设置QB限速失败",
|
||||
)
|
||||
|
||||
def get_torrent_tracker(self, torrent):
|
||||
"""
|
||||
qb解析 tracker
|
||||
:return: tracker url
|
||||
"""
|
||||
if not torrent:
|
||||
return None
|
||||
tracker = torrent.get("tracker")
|
||||
if tracker and len(tracker) > 0:
|
||||
return tracker
|
||||
magnet_uri = torrent.get("magnet_uri")
|
||||
if not magnet_uri or len(magnet_uri) <= 0:
|
||||
return None
|
||||
magnet_uri_obj = urlparse(magnet_uri)
|
||||
query = urllib.parse.parse_qs(magnet_uri_obj.query)
|
||||
tr = query["tr"]
|
||||
if not tr or len(tr) <= 0:
|
||||
return None
|
||||
return tr[0]
|
||||
|
||||
def get_main_domain(self, domain):
|
||||
"""
|
||||
获取域名的主域名
|
||||
:param domain: 原域名
|
||||
:return: 主域名
|
||||
"""
|
||||
if not domain:
|
||||
return None
|
||||
domain_arr = domain.split(".")
|
||||
domain_len = len(domain_arr)
|
||||
if domain_len < 2:
|
||||
return None
|
||||
root_domain, root_domain_len = self.match_multi_level_root_domain(domain=domain)
|
||||
if root_domain:
|
||||
return f"{domain_arr[-root_domain_len - 1]}.{root_domain}"
|
||||
else:
|
||||
return f"{domain_arr[-2]}.{domain_arr[-1]}"
|
||||
|
||||
def match_multi_level_root_domain(self, domain):
|
||||
"""
|
||||
匹配多级根域名
|
||||
:param domain: 被匹配的域名
|
||||
:return: 匹配的根域名, 匹配的根域名长度
|
||||
"""
|
||||
if not domain or not self._multi_level_root_domain:
|
||||
return None, 0
|
||||
for root_domain in self._multi_level_root_domain:
|
||||
if domain.endswith("." + root_domain):
|
||||
root_domain_len = len(root_domain.split("."))
|
||||
return root_domain, root_domain_len
|
||||
return None, 0
|
||||
|
||||
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
|
||||
customSites = self.__custom_sites()
|
||||
|
||||
site_options = [
|
||||
{"title": site.name, "value": site.id}
|
||||
for site in self._siteoper.list_order_by_pri()
|
||||
] + [
|
||||
{"title": site.get("name"), "value": site.get("id")} for site in customSites
|
||||
]
|
||||
return [
|
||||
{
|
||||
"component": "VForm",
|
||||
@@ -849,6 +1033,27 @@ class QbCommand(_PluginBase):
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"component": "VRow",
|
||||
"content": [
|
||||
{
|
||||
"component": "VCol",
|
||||
"props": {"cols": 12},
|
||||
"content": [
|
||||
{
|
||||
"component": "VSelect",
|
||||
"props": {
|
||||
"chips": True,
|
||||
"multiple": True,
|
||||
"model": "op_site_ids",
|
||||
"label": "停止保种站点(暂停保种后不会被恢复)",
|
||||
"items": site_options,
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"component": "VRow",
|
||||
"content": [
|
||||
@@ -884,22 +1089,6 @@ class QbCommand(_PluginBase):
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"component": "VCol",
|
||||
"props": {
|
||||
"cols": 12,
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"component": "VAlert",
|
||||
"props": {
|
||||
"type": "info",
|
||||
"variant": "tonal",
|
||||
"text": "PT精神重在分享,请勿恶意限速,因此导致账号被封禁作者概不负责",
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -916,10 +1105,21 @@ class QbCommand(_PluginBase):
|
||||
"download_limit": 0,
|
||||
"enable_upload_limit": False,
|
||||
"enable_download_limit": False,
|
||||
"op_site_ids": [],
|
||||
}
|
||||
|
||||
def get_page(self) -> List[dict]:
|
||||
pass
|
||||
|
||||
def stop_service(self):
|
||||
pass
|
||||
"""
|
||||
退出插件
|
||||
"""
|
||||
try:
|
||||
if self._scheduler:
|
||||
self._scheduler.remove_all_jobs()
|
||||
if self._scheduler.running:
|
||||
self._scheduler.shutdown()
|
||||
self._scheduler = None
|
||||
except Exception as e:
|
||||
logger.error("退出插件失败:%s" % str(e))
|
||||
|
||||
Reference in New Issue
Block a user