Compare commits

...

19 Commits

Author SHA1 Message Date
jxxghp
2102a03740 Merge pull request #885 from wumode/clashruleprovider 2025-08-24 18:46:30 +08:00
wumode
0a9cadf7ab update(ClashRuleProvider): 通过emoji识别国家 2025-08-24 18:06:43 +08:00
jxxghp
279efe8000 Merge pull request #883 from wumode/lexiannot 2025-08-23 17:18:18 +08:00
wumode
fd92e58f81 update(ImdbSource) 修复错误 2025-08-23 16:58:00 +08:00
wumode
fe93e46e02 update(ImdbSource) 修改UA 2025-08-23 00:01:13 +08:00
wumode
cbf541992f update(LexiAnnot): 添加任务页面 2025-08-22 17:03:07 +08:00
jxxghp
8e1d336250 add 统一缓存使用说明 2025-08-21 16:06:23 +08:00
jxxghp
12e0e2b9f5 Merge pull request #881 from wumode/imdbsource 2025-08-20 00:34:31 +08:00
wumode
ac914f70f3 update: ImdbSource&ToBypassTrackers 2025-08-20 00:10:09 +08:00
jxxghp
a07b8a4f4a Merge pull request #878 from wumode/lexiannot 2025-08-17 20:13:53 +08:00
wumode
6960b3f7aa update(LexiAnnot): 支持考试词汇标注 2025-08-17 19:50:30 +08:00
jxxghp
fe83ff1be8 Merge pull request #876 from liuhangbin/multiclass 2025-08-14 19:40:50 +08:00
Hangbin Liu
6357dc8e4a plugins.v2: 添加多级分类插件
目前MP默认只支持二级分类,但是部分用户有多级目录的需求,比如增加按照年代
或者评分度分类。因此增加一个支持多级分类的插件, 目前仅支持电影多级分类。

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
2025-08-14 19:16:45 +08:00
jxxghp
f1d94d0aa3 Merge pull request #875 from yelantf/main 2025-08-12 12:08:56 +08:00
夜阑听风
53dd3bc796 Update dingdingmsg in package.json 2025-08-12 10:59:53 +08:00
夜阑听风
a9d528fc05 Update dingdingmsg version 2025-08-12 10:58:25 +08:00
夜阑听风
0388c437b1 update dingdingmsg to support breakline 2025-08-12 10:56:24 +08:00
jxxghp
ac4b53e745 AutoSignIn v2.7 2025-08-12 08:25:09 +08:00
jxxghp
53297fccaf 更新 release.yml 2025-08-10 13:48:02 +08:00
64 changed files with 5212 additions and 3702 deletions

View File

@@ -90,15 +90,18 @@ jobs:
rm -f "$asset"
(cd "$(dirname "$plugin_dir")" && zip -r "$GITHUB_WORKSPACE/$asset" "$(basename "$plugin_dir")" -x "*/__pycache__/*" -x "*.pyc") >/dev/null
# If same tag exists, delete release and remote tag first
# If same tag exists, delete release and both remote/local tag first
if gh release view "$tag" >/dev/null 2>&1; then
echo "Release $tag exists, deleting..."
gh release delete "$tag" -y
git push origin :refs/tags/"$tag" || true
fi
# Ensure no stale local tag remains
git tag -d "$tag" >/dev/null 2>&1 || true
echo "Creating release $tag"
gh release create "$tag" "$asset" --title "$tag" --notes "Automated release of $plugin_id $plugin_version" --latest
gh release create "$tag" "$asset" --title "$tag" --notes "Automated release of $plugin_id $plugin_version" --latest --target "$GITHUB_SHA"
echo "$tag" >> processed_tags.txt
done

185
README.md
View File

@@ -23,6 +23,7 @@ MoviePilot官方插件市场https://github.com/jxxghp/MoviePilot-Plugins
- [12. 如何通过插件扩展支持的存储类型?](#12-如何通过插件扩展支持的存储类型)
- [13. 如何将插件功能集成到工作流?](#13-如何将插件功能集成到工作流)
- [14. 如何在插件中通过消息持续与用户交互?](#14-如何在插件中通过消息持续与用户交互)
- [15. 如何在插件中使用系统级统一缓存?](#15-如何在插件中使用系统级统一缓存)
- [版本发布](#版本发布)
- [1. 如何发布插件版本?](#1-如何发布插件版本)
- [2. 如何开发V2版本的插件以及实现插件多版本兼容](#2-如何开发v2版本的插件以及实现插件多版本兼容)
@@ -1167,6 +1168,190 @@ def get_actions(self) -> List[Dict[str, Any]]:
- 建议在交互中保存用户状态数据,以支持复杂的多步骤操作
- 可以结合插件数据存储功能保存用户的交互历史和偏好设置
### 15. 如何在插件中使用系统级统一缓存?
**(仅支持 `v2.7.4+` 版本)**
- MoviePilot提供了统一的缓存系统支持内存缓存、文件系统缓存和Redis缓存自动管理当有Redis时优先使用Redis否则使用内存或文件系统。插件可以通过系统提供的缓存接口实现高效的缓存管理无需关心系统设置。
- 1. 使用缓存装饰器:
```python
from app.core.cache import cached
class MyPlugin(_PluginBase):
@cached(region="my_plugin", ttl=3600)
def get_data(self, key: str):
"""
使用缓存装饰器缓存结果1小时
"""
# 复杂的计算或网络请求
return expensive_operation(key)
@cached(region="my_plugin_async", ttl=1800, skip_none=True)
async def get_async_data(self, key: str):
"""
异步函数缓存跳过None值
"""
return await async_expensive_operation(key)
```
- 2. 使用TTLCache类
```python
from app.core.cache import TTLCache
class MyPlugin(_PluginBase):
def __init__(self):
super().__init__()
# 创建缓存实例最大128项TTL 30分钟
self.cache = TTLCache(region="my_plugin", maxsize=128, ttl=1800)
def process_data(self, key: str):
# 检查缓存
if key in self.cache:
return self.cache[key]
# 计算并缓存结果
result = expensive_operation(key)
self.cache[key] = result
return result
def clear_cache(self):
"""
清理插件缓存
"""
self.cache.clear()
```
- 3. 使用文件缓存后端(适用于大文件缓存):
```python
from app.core.cache import FileCache, AsyncFileCache
from pathlib import Path
class MyPlugin(_PluginBase):
def __init__(self):
super().__init__()
# 获取文件缓存后端支持Redis和文件系统
self.file_cache = FileCache(
base=Path("/tmp/my_plugin_cache"),
ttl=86400 # 24小时
)
def cache_large_file(self, key: str, data: bytes):
"""
缓存大文件数据
"""
self.file_cache.set(key, data, region="large_files")
def get_cached_file(self, key: str) -> Optional[bytes]:
"""
获取缓存的文件数据
"""
return self.file_cache.get(key, region="large_files")
async def async_cache_operations(self):
"""
异步文件缓存操作
"""
async_cache = AsyncFileCache(
base=Path("/tmp/my_plugin_async_cache"),
ttl=3600
)
# 异步设置缓存
await async_cache.set("async_key", b"async_data", region="async_files")
# 异步获取缓存
data = await async_cache.get("async_key", region="async_files")
await async_cache.close()
```
- 4. 直接使用缓存后端(高级用法):
```python
from app.core.cache import Cache
class MyPlugin(_PluginBase):
def __init__(self):
super().__init__()
# 直接获取缓存后端实例系统自动选择Redis或内存缓存
self.cache_backend = Cache(maxsize=256, ttl=3600)
def custom_cache_operation(self, key: str, value: Any):
"""
自定义缓存操作
"""
# 设置缓存
self.cache_backend.set(key, value, region="custom_region")
# 检查缓存是否存在
if self.cache_backend.exists(key, region="custom_region"):
# 获取缓存
cached_value = self.cache_backend.get(key, region="custom_region")
return cached_value
return None
def iterate_cache_items(self):
"""
遍历缓存项
"""
for key, value in self.cache_backend.items(region="custom_region"):
print(f"缓存键: {key}, 值: {value}")
def cleanup(self):
"""
清理缓存
"""
self.cache_backend.clear(region="custom_region")
self.cache_backend.close()
```
- 5. 缓存装饰器参数说明:
```python
@cached(
region="my_plugin", # 缓存区域,用于隔离不同插件的缓存
maxsize=512, # 最大缓存条目数(仅内存缓存有效)
ttl=1800, # 缓存存活时间(秒)
skip_none=True, # 是否跳过None值缓存
skip_empty=False # 是否跳过空值缓存(空列表、空字典等)
)
def my_function(self, param):
pass
```
- 6. 缓存管理功能:
```python
class MyPlugin(_PluginBase):
@cached(region="my_plugin")
def cached_function(self, param):
return expensive_operation(param)
def clear_my_cache(self):
"""
清理指定区域的缓存
"""
self.cached_function.cache_clear()
def get_cache_info(self):
"""
获取缓存信息
"""
cache_region = self.cached_function.cache_region
return f"缓存区域: {cache_region}"
```
- 7. 缓存后端自动选择:
- 系统会根据配置自动选择缓存后端:
- `CACHE_BACKEND_TYPE=redis`使用Redis作为缓存后端
- `CACHE_BACKEND_TYPE=memory`使用内存缓存cachetools
- 插件代码无需修改,系统会自动处理缓存后端的切换
- 8. 最佳实践:
- 为每个插件使用独立的缓存区域region避免缓存键冲突
- 合理设置TTL避免缓存过期时间过长导致数据过期
- 对于频繁访问的数据使用较长的TTL对于实时性要求高的数据使用较短的TTL
- 使用`skip_none=True`避免缓存无意义的None值
- 大文件或二进制数据建议使用文件缓存后端
- 在插件卸载时清理相关缓存,避免内存泄漏
## 版本发布

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -943,11 +943,14 @@
"name": "钉钉机器人",
"description": "支持使用钉钉机器人发送消息通知。",
"labels": "消息通知,钉钉机器人",
"version": "1.12",
"version": "1.13",
"icon": "Dingding_A.png",
"author": "nnlegenda",
"level": 1,
"v2": true
"v2": true,
"history": {
"v1.13": "优化钉钉消息换行"
}
},
"DynamicWeChat": {
"name": "动态企微可信IP",

View File

@@ -42,12 +42,13 @@
"name": "站点自动签到",
"description": "自动模拟登录、签到站点。",
"labels": "站点",
"version": "2.6",
"version": "2.7",
"icon": "signin.png",
"author": "thsrite",
"level": 2,
"release": true,
"history": {
"v2.7": "站点请求使用站点设置的超时时间",
"v2.6": "感谢madrays佬提供的UI!",
"v2.5.4": "增加保号风险提示",
"v2.5.3": "优化执行周期输入需要MoviePilot v2.2.1+",
@@ -350,6 +351,18 @@
"v2.0": "适配新的目录结构变化,短剧分类名称调整为配置目录路径,升级后需要重新调整设置后才能使用。"
}
},
"MultiClass": {
"name": "视频多级分类",
"description": "支持视频多级分类",
"labels": "文件整理",
"version": "0.1",
"icon": "Calibreweb_B.png",
"author": "liuhangbin",
"level": 1,
"history": {
"v0.1": "视频多级分类插件, 目前仅支持电影按评分,年代,系列分类。"
}
},
"MoviePilotUpdateNotify": {
"name": "MoviePilot更新推送",
"description": "MoviePilot推送release更新通知、自动重启。",
@@ -419,11 +432,12 @@
"name": "绕过Trackers",
"description": "提供tracker服务器IP地址列表帮助IPv6连接绕过OpenClash。",
"labels": "工具",
"version": "1.4.2",
"version": "1.4.3",
"icon": "Clash_A.png",
"author": "wumode",
"level": 2,
"history": {
"v1.4.3": "修复 bug",
"v1.4.2": "修复插件动作",
"v1.4.1": "修复通知类型错误",
"v1.4": "异步查询DNS",
@@ -437,11 +451,13 @@
"name": "IMDb源",
"description": "让探索推荐和媒体识别支持IMDb数据源。",
"labels": "探索",
"version": "1.5.6",
"version": "1.5.8",
"icon": "IMDb_IOS-OSX_App.png",
"author": "wumode",
"level": 1,
"history": {
"v1.5.8": "修改UA",
"v1.5.7": "改进异常处理",
"v1.5.6": "固定仪表盘组件海报比例; 修复 bug",
"v1.5.5": "修复初始化错误",
"v1.5.4": "改进媒体识别",
@@ -467,12 +483,13 @@
"name": "Clash Rule Provider",
"description": "随时为Clash添加一些额外的规则。",
"labels": "工具",
"version": "1.3.2",
"version": "1.3.3",
"icon": "Mihomo_Meta_A.png",
"author": "wumode",
"level": 1,
"release": true,
"history": {
"v1.3.3": "通过emoji识别国家; 按国家分组节点; mrs格式支持",
"v1.3.2": "注册插件动作",
"v1.3.1": "支持配置 Hosts",
"v1.2.8": "改进导入界面",
@@ -495,11 +512,13 @@
"name": "美剧生词标注",
"description": "根据CEFR等级为英语影视剧标注高级词汇。",
"labels": "英语",
"version": "1.0.1",
"version": "1.1.1",
"icon": "LexiAnnot.png",
"author": "wumode",
"level": 1,
"history": {
"v1.1.1": "添加任务页面; 改进 spaCy 模型加载逻辑",
"v1.1.0": "支持考试词汇标注; 优化分词处理; 修复错误",
"v1.0.1": "合并连字符词; 避免ARM平台依赖问题",
"v1.0": "新增LexiAnnot"
}

View File

@@ -7,10 +7,6 @@ from typing import Any, List, Dict, Tuple, Optional
from urllib.parse import urljoin
import pytz
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from ruamel.yaml import CommentedMap
from app import schemas
from app.core.config import settings
from app.core.event import eventmanager, Event
@@ -26,6 +22,9 @@ from app.utils.http import RequestUtils
from app.utils.site import SiteUtils
from app.utils.string import StringUtils
from app.utils.timer import TimerUtils
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from ruamel.yaml import CommentedMap
class AutoSignIn(_PluginBase):
@@ -36,7 +35,7 @@ class AutoSignIn(_PluginBase):
# 插件图标
plugin_icon = "signin.png"
# 插件版本
plugin_version = "2.6"
plugin_version = "2.7"
# 插件作者
plugin_author = "thsrite"
# 作者主页
@@ -1545,6 +1544,7 @@ class AutoSignIn(_PluginBase):
render = site_info.get("render")
proxies = settings.PROXY if site_info.get("proxy") else None
proxy_server = settings.PROXY_SERVER if site_info.get("proxy") else None
timeout = site_info.get("timeout") or 60
if not site_url or not site_cookie:
logger.warn(f"未配置 {site} 的站点地址或Cookie无法签到")
return False, ""
@@ -1560,7 +1560,8 @@ class AutoSignIn(_PluginBase):
page_source = PlaywrightHelper().get_page_source(url=checkin_url,
cookies=site_cookie,
ua=ua,
proxies=proxy_server)
proxies=proxy_server,
timeout=timeout)
if not SiteUtils.is_logged_in(page_source):
if under_challenge(page_source):
return False, f"无法通过Cloudflare"
@@ -1574,13 +1575,15 @@ class AutoSignIn(_PluginBase):
else:
res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=proxies
proxies=proxies,
timeout=timeout
).get_res(url=checkin_url)
if not res and site_url != checkin_url:
logger.info(f"开始站点模拟登录:{site},地址:{site_url}...")
res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=proxies
proxies=proxies,
timeout=timeout
).get_res(url=site_url)
# 判断登录状态
if res and res.status_code in [200, 500, 403]:
@@ -1647,6 +1650,7 @@ class AutoSignIn(_PluginBase):
render = site_info.get("render")
proxies = settings.PROXY if site_info.get("proxy") else None
proxy_server = settings.PROXY_SERVER if site_info.get("proxy") else None
timeout = site_info.get("timeout") or 60
if not site_url or not site_cookie:
logger.warn(f"未配置 {site} 的站点地址或Cookie无法签到")
return False, ""
@@ -1659,7 +1663,8 @@ class AutoSignIn(_PluginBase):
page_source = PlaywrightHelper().get_page_source(url=site_url,
cookies=site_cookie,
ua=ua,
proxies=proxy_server)
proxies=proxy_server,
timeout=timeout)
if not SiteUtils.is_logged_in(page_source):
if under_challenge(page_source):
return False, f"无法通过Cloudflare"
@@ -1669,7 +1674,8 @@ class AutoSignIn(_PluginBase):
else:
res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=proxies
proxies=proxies,
timeout=timeout
).get_res(url=site_url)
# 判断登录状态
if res and res.status_code in [200, 500, 403]:

View File

@@ -2,13 +2,12 @@ import random
import re
from typing import Tuple
from lxml import etree
from app.core.config import settings
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.http import RequestUtils
from app.utils.string import StringUtils
from lxml import etree
class Pt52(_ISiteSigninHandler):
@@ -46,14 +45,16 @@ class Pt52(_ISiteSigninHandler):
ua = site_info.get("ua")
render = site_info.get("render")
proxy = site_info.get("proxy")
timeout = site_info.get("timeout")
# 判断今日是否已签到
html_text = self.get_page_source(url='https://52pt.site/bakatest.php',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'
@@ -97,14 +98,16 @@ class Pt52(_ISiteSigninHandler):
site_cookie=site_cookie,
ua=ua,
proxy=proxy,
site=site)
site=site,
timeout=timeout)
def __signin(self, questionid: str,
choice: list,
site: str,
site_cookie: str,
ua: str,
proxy: bool) -> Tuple[bool, str]:
proxy: bool,
timeout: int) -> Tuple[bool, str]:
"""
签到请求
questionid: 450
@@ -124,7 +127,8 @@ class Pt52(_ISiteSigninHandler):
sign_res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=settings.PROXY if proxy else None
proxies=settings.PROXY if proxy else None,
timeout=timeout
).post_res(url='https://52pt.site/bakatest.php', data=data)
if not sign_res or sign_res.status_code != 200:
logger.error(f"{site} 签到失败,签到接口请求失败")

View File

@@ -42,7 +42,8 @@ class _ISiteSigninHandler(metaclass=ABCMeta):
pass
@staticmethod
def get_page_source(url: str, cookie: str, ua: str, proxy: bool, render: bool, token: str = None) -> str:
def get_page_source(url: str, cookie: str, ua: str, proxy: bool, render: bool,
token: str = None, timeout: int = None) -> str:
"""
获取页面源码
:param url: Url地址
@@ -51,13 +52,15 @@ class _ISiteSigninHandler(metaclass=ABCMeta):
:param proxy: 是否使用代理
:param render: 是否渲染
:param token: JWT Token
:param timeout: 请求超时时间,单位秒
:return: 页面源码,错误信息
"""
if render:
return PlaywrightHelper().get_page_source(url=url,
cookies=cookie,
ua=ua,
proxies=settings.PROXY_SERVER if proxy else None)
proxies=settings.PROXY_SERVER if proxy else None,
timeout=timeout or 60)
else:
if token:
headers = {
@@ -70,7 +73,8 @@ class _ISiteSigninHandler(metaclass=ABCMeta):
"Cookie": cookie
}
res = RequestUtils(headers=headers,
proxies=settings.PROXY if proxy else None).get_res(url=url)
proxies=settings.PROXY if proxy else None,
timeout=timeout or 20).get_res(url=url)
if res is not None:
# 使用chardet检测字符编码
raw_data = res.content

View File

@@ -37,6 +37,7 @@ class BTSchool(_ISiteSigninHandler):
ua = site_info.get("ua")
render = site_info.get("render")
proxy = site_info.get("proxy")
timeout = site_info.get("timeout")
logger.info(f"{site} 开始签到")
# 判断今日是否已签到
@@ -44,7 +45,8 @@ class BTSchool(_ISiteSigninHandler):
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
@@ -63,7 +65,8 @@ class BTSchool(_ISiteSigninHandler):
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,签到接口请求失败")

View File

@@ -47,13 +47,15 @@ class CHDBits(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 判断今日是否已签到
html_text = self.get_page_source(url='https://ptchdbits.co/bakatest.php',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")

View File

@@ -37,21 +37,24 @@ class HaiDan(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 签到
# 签到页会重定向到index.php由于302重定向特性导致index.php没有携带cookie
self.get_page_source(url='https://www.haidan.video/signin.php',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render,
timeout=timeout)
# 重新携带cookie获取index.php查看签到结果
html_text = self.get_page_source(url='https://www.haidan.video/index.php',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'

View File

@@ -40,13 +40,15 @@ class Hares(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 获取页面html
html_text = self.get_page_source(url='https://club.hares.top',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 模拟访问失败,请检查站点连通性")
@@ -66,7 +68,8 @@ class Hares(_ISiteSigninHandler):
}
sign_res = RequestUtils(cookies=site_cookie,
headers=headers,
proxies=settings.PROXY if proxy else None
proxies=settings.PROXY if proxy else None,
timeout=timeout
).get_res(url="https://club.hares.top/attendance.php?action=sign")
if not sign_res or sign_res.status_code != 200:
logger.error(f"{site} 签到失败,签到接口请求失败")

View File

@@ -40,6 +40,7 @@ class HDArea(_ISiteSigninHandler):
site_cookie = site_info.get("cookie")
ua = site_info.get("ua")
proxies = settings.PROXY if site_info.get("proxy") else None
timeout = site_info.get("timeout")
# 获取页面html
data = {
@@ -47,7 +48,8 @@ class HDArea(_ISiteSigninHandler):
}
html_res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=proxies
proxies=proxies,
timeout=timeout
).post_res(url="https://hdarea.club/sign_in.php", data=data)
if not html_res or html_res.status_code != 200:
logger.error(f"{site} 签到失败,请检查站点连通性")

View File

@@ -40,6 +40,7 @@ class HDChina(_ISiteSigninHandler):
site_cookie = site_info.get("cookie")
ua = site_info.get("ua")
proxies = settings.PROXY if site_info.get("proxy") else None
timeout = site_info.get("timeout")
# 尝试解决瓷器cookie每天签到后过期,只保留hdchina=部分
cookie = ""
@@ -59,7 +60,8 @@ class HDChina(_ISiteSigninHandler):
# 获取页面html
html_res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=proxies
proxies=proxies,
timeout=timeout
).get_res(url="https://hdchina.org/index.php")
if not html_res or html_res.status_code != 200:
logger.error(f"{site} 签到失败,请检查站点连通性")
@@ -99,7 +101,8 @@ class HDChina(_ISiteSigninHandler):
}
sign_res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=proxies
proxies=proxies,
timeout=timeout
).post_res(url="https://hdchina.org/plugin_sign-in.php?cmd=signin", data=data)
if not sign_res or sign_res.status_code != 200:
logger.error(f"{site} 签到失败,签到接口请求失败")

View File

@@ -39,13 +39,15 @@ class HDCity(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 获取页面html
html_text = self.get_page_source(url='https://hdcity.city/sign',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'

View File

@@ -43,13 +43,15 @@ class HDSky(_ISiteSigninHandler):
proxy = site_info.get("proxy")
render = site_info.get("render")
referer = site_info.get("url")
timeout = site_info.get("timeout")
# 判断今日是否已签到
html_text = self.get_page_source(url='https://hdsky.me',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'
@@ -73,7 +75,8 @@ class HDSky(_ISiteSigninHandler):
content_type='application/x-www-form-urlencoded; charset=UTF-8',
referer="https://hdsky.me/index.php",
accept_type="*/*",
proxies=settings.PROXY if proxy else None
proxies=settings.PROXY if proxy else None,
timeout=timeout
).post_res(url='https://hdsky.me/image_code_ajax.php',
data={'action': 'new'})
if image_res and image_res.status_code == 200:

View File

@@ -41,13 +41,15 @@ class HDUpt(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 获取页面html
html_text = self.get_page_source(url='https://pt.hdupt.com',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'
@@ -67,7 +69,8 @@ class HDUpt(_ISiteSigninHandler):
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'

View File

@@ -1,5 +1,4 @@
from typing import Tuple
from urllib.parse import urljoin
from ruamel.yaml import CommentedMap
@@ -38,10 +37,11 @@ class MTorrent(_ISiteSigninHandler):
"Authorization": site_info.get("token")
}
url = site_info.get('url')
timeout = site_info.get("timeout")
domain = StringUtils.get_url_domain(url)
# 更新最后访问时间
res = RequestUtils(headers=headers,
timeout=60,
timeout=timeout,
proxies=settings.PROXY if site_info.get("proxy") else None,
referer=f"{url}index"
).post_res(url=f"https://api.{domain}/api/member/updateLastBrowse")

View File

@@ -40,6 +40,7 @@ class NexusHD(_ISiteSigninHandler):
site_cookie = site_info.get("cookie")
ua = site_info.get("ua")
proxies = settings.PROXY if site_info.get("proxy") else None
timeout = site_info.get("timeout")
# 获取页面html
data = {
@@ -48,7 +49,8 @@ class NexusHD(_ISiteSigninHandler):
}
html_res = RequestUtils(cookies=site_cookie,
ua=ua,
proxies=proxies
proxies=proxies,
timeout=timeout
).post_res(url="https://v6.nexushd.org/signin.php", data=data)
if not html_res or html_res.status_code != 200:
logger.error(f"{site} 签到失败,请检查站点连通性")

View File

@@ -43,13 +43,15 @@ class Opencd(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 判断今日是否已签到
html_text = self.get_page_source(url='https://www.open.cd',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'

View File

@@ -35,13 +35,15 @@ class PTerClub(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 签到
html_text = self.get_page_source(url='https://pterclub.com/attendance-ajax.php',
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'

View File

@@ -37,6 +37,7 @@ class PTTime(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 签到
# 签到返回:<html><head></head><body>签到成功</body></html>
@@ -44,7 +45,8 @@ class PTTime(_ISiteSigninHandler):
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")

View File

@@ -57,6 +57,7 @@ class Tjupt(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 创建正确答案存储目录
if not os.path.exists(os.path.dirname(self._answer_file)):
@@ -67,7 +68,8 @@ class Tjupt(_ISiteSigninHandler):
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
# 获取签到后返回html判断是否签到成功
if not html_text:

View File

@@ -44,13 +44,15 @@ class TTG(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 获取页面html
html_text = self.get_page_source(url="https://totheglory.im",
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'

View File

@@ -50,6 +50,7 @@ class U2(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
now = datetime.datetime.now()
# 判断当前时间是否小于9点
@@ -62,7 +63,8 @@ class U2(_ISiteSigninHandler):
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 签到失败,请检查站点连通性")
return False, '签到失败,请检查站点连通性'

View File

@@ -37,7 +37,7 @@ class YemaPT(_ISiteSigninHandler):
}
# 获取用户信息,更新最后访问时间
res = (RequestUtils(headers=headers,
timeout=15,
timeout=site_info.get("timeout"),
cookies=site_info.get("cookie"),
proxies=settings.PROXY if site_info.get("proxy") else None,
referer=site_info.get('url')
@@ -64,7 +64,7 @@ class YemaPT(_ISiteSigninHandler):
}
# 获取用户信息,更新最后访问时间
res = (RequestUtils(headers=headers,
timeout=15,
timeout=site_info.get("timeout"),
cookies=site_info.get("cookie"),
proxies=settings.PROXY if site_info.get("proxy") else None,
referer=site_info.get('url')

View File

@@ -38,13 +38,15 @@ class ZhuQue(_ISiteSigninHandler):
ua = site_info.get("ua")
proxy = site_info.get("proxy")
render = site_info.get("render")
timeout = site_info.get("timeout")
# 获取页面html
html_text = self.get_page_source(url="https://zhuque.in",
cookie=site_cookie,
ua=ua,
proxy=proxy,
render=render)
render=render,
timeout=timeout)
if not html_text:
logger.error(f"{site} 模拟登录失败,请检查站点连通性")
return False, '模拟登录失败,请检查站点连通性'
@@ -73,7 +75,8 @@ class ZhuQue(_ISiteSigninHandler):
}
skill_res = RequestUtils(cookies=site_cookie,
headers=headers,
proxies=settings.PROXY if proxy else None
proxies=settings.PROXY if proxy else None,
timeout=timeout
).post_res(url="https://zhuque.in/api/gaming/fireGenshinCharacterMagic", json=data)
if not skill_res or skill_res.status_code != 200:
logger.error(f"模拟登录失败,释放技能失败")

View File

@@ -39,7 +39,7 @@ class ClashRuleProvider(_PluginBase):
# 插件图标
plugin_icon = "Mihomo_Meta_A.png"
# 插件版本
plugin_version = "1.3.2"
plugin_version = "1.3.3"
# 插件作者
plugin_author = "wumode"
# 作者主页
@@ -71,8 +71,10 @@ class ClashRuleProvider(_PluginBase):
_auto_update_subscriptions = True
_ruleset_prefix: str = '📂<='
_group_by_region: bool = False
_group_by_country: bool = False
_refresh_delay: int = 5
_discard_rules: bool = False
_discard_proxy_groups: bool = False
_enable_acl4ssr: bool = False
_dashboard_components: List[str] = []
_clash_template_yaml: str = ''
@@ -122,15 +124,15 @@ class ClashRuleProvider(_PluginBase):
self._notify = config.get("notify"),
self._sub_links = config.get("sub_links") or []
self._clash_dashboard_url = config.get("clash_dashboard_url") or ''
if self._clash_dashboard_url and self._clash_dashboard_url[-1] == '/':
self._clash_dashboard_url = self._clash_dashboard_url[:-1]
if self._clash_dashboard_url:
self._clash_dashboard_url = self._clash_dashboard_url.rstrip("/")
if not (self._clash_dashboard_url.startswith('http://') or
self._clash_dashboard_url.startswith('https://')):
self._clash_dashboard_url = 'http://' + self._clash_dashboard_url
self._clash_dashboard_secret = config.get("clash_dashboard_secret")
self._movie_pilot_url = config.get("movie_pilot_url")
if self._movie_pilot_url and self._movie_pilot_url[-1] == '/':
self._movie_pilot_url = self._movie_pilot_url[:-1]
if self._movie_pilot_url:
self._movie_pilot_url = self._movie_pilot_url.rstrip("/")
self._cron = config.get("cron_string") or '30 12 * * *'
self._timeout = config.get("timeout")
self._retry_times = config.get("retry_times") or 3
@@ -139,8 +141,10 @@ class ClashRuleProvider(_PluginBase):
self._acl4ssr_prefix = config.get("acl4ssr_prefix", "🗂️=>")
self._auto_update_subscriptions = config.get("auto_update_subscriptions")
self._group_by_region = config.get("group_by_region")
self._group_by_country = config.get("group_by_country") or False
self._refresh_delay = config.get("refresh_delay") or 5
self._discard_rules = config.get("discard_rules") or False
self._discard_proxy_groups = config.get("discard_proxy_groups") or False
self._enable_acl4ssr = config.get("enable_acl4ssr") or False
self._dashboard_components = config.get("dashboard_components") or []
self._clash_template_yaml = config.get("clash_template") or ''
@@ -816,6 +820,8 @@ class ClashRuleProvider(_PluginBase):
for key, value in new_value.items():
if key == 'name' or value is None:
continue
if key == 'payload' and params.get('type') != 'inline':
continue
if value == '' or value is None:
continue
item[key] = value
@@ -1119,8 +1125,6 @@ class ClashRuleProvider(_PluginBase):
def delete_rule_by_priority(self, priority: int, rule_parser: ClashRuleParser
) -> Optional[Union[ClashRule, LogicRule, MatchRule]]:
if not isinstance(priority, int):
return None
res = rule_parser.remove_rule_at_priority(priority)
self.__save_data()
return res
@@ -1263,6 +1267,8 @@ class ClashRuleProvider(_PluginBase):
logger.info(f"已更新: {url}. 节点数量: {len(rs['proxies'])}")
if rs.get('rules') is None:
rs['rules'] = []
if self._discard_proxy_groups:
rs['proxy-groups'] = []
rs = self.__remove_nodes_by_keywords(rs)
except Exception as e:
logger.error(f"解析配置出错: {e}")
@@ -1290,7 +1296,7 @@ class ClashRuleProvider(_PluginBase):
).put(url)
def proxy_groups_by_region(self) -> List[Dict[str, Any]]:
return ClashRuleProvider.__group_by_region(self._countries, self.all_proxies())
return self.__group_by_region(self._countries, self.all_proxies())
@staticmethod
def __load_countries(file_path: str) -> List:
@@ -1301,40 +1307,71 @@ class ClashRuleProvider(_PluginBase):
return []
return countries
@staticmethod
def __group_by_region(countries: List, proxies) -> List[Dict[str, Any]]:
continents_nodes = {'Asia': [], 'Europe': [], 'SouthAmerica': [], 'NorthAmerica': [], 'Africa': [],
'Oceania': [], 'AsiaExceptChina': []}
def __group_by_region(self, countries: List, proxies) -> List[Dict[str, Any]]:
continent_groups = {}
country_groups = {}
continent_map = {
'欧洲': 'Europe',
'亚洲': 'Asia',
'大洋洲': 'Oceania',
'非洲': 'Africa',
'北美洲': 'NorthAmerica',
'南美洲': 'SouthAmerica'
}
proxy_groups = []
hk = next(filter(lambda c: c['abbr'] == 'HK', countries),
{"abbr": "HK", "chinese": "中国香港特别行政区", "emoji": "🇭🇰"})
tw = next(filter(lambda c: c['abbr'] == 'TW', countries),
{"abbr": "TW", "chinese": "中国台湾", "emoji": "🇹🇼"})
for proxy_node in proxies:
continent = ClashRuleProvider.__continent_name_from_node(countries, proxy_node['name'])
if not continent:
country = ClashRuleProvider.__country_from_node(countries, proxy_node['name'])
if not country:
continue
continents_nodes[continent].append(proxy_node['name'])
for continent_nodes in continents_nodes:
if len(continents_nodes[continent_nodes]):
proxy_group = {'name': continent_nodes, 'type': 'select', 'proxies': continents_nodes[continent_nodes]}
if country.get("abbr") == "CN":
if any(key in proxy_node["name"] for key in ("🇭🇰", "HK", "香港")):
country = hk
if any(key in proxy_node["name"] for key in ("🇹🇼", "TW", "台湾")):
country = tw
continent = continent_map[country.get('continent')]
if self._group_by_region:
continent_groups.setdefault(continent, []).append(proxy_node['name'])
if self._group_by_country:
country_groups.setdefault(f"{country.get('emoji')} {country.get('chinese')}", []).append(
proxy_node['name'])
for continent, nodes in continent_groups.items():
if len(nodes):
proxy_group = {'name': continent, 'type': 'select', 'proxies': nodes}
proxy_groups.append(proxy_group)
for continent_node in continents_nodes['Asia']:
if any(x in continent_node for x in ('中国', '香港', 'CN')):
excluded = ('中国', '香港', 'CN', 'HK', '🇨🇳', '🇭🇰')
for continent_node in continent_groups.get('Asia', []):
if any(x in continent_node for x in excluded):
continue
continents_nodes['AsiaExceptChina'].append(continent_node)
if continents_nodes['AsiaExceptChina']:
proxy_group = {'name': 'AsiaExceptChina', 'type': 'select', 'proxies': continents_nodes['AsiaExceptChina']}
continent_groups.setdefault('AsiaExceptChina', []).append(continent_node)
if continent_groups.get('AsiaExceptChina'):
proxy_group = {'name': 'AsiaExceptChina', 'type': 'select', 'proxies': continent_groups['AsiaExceptChina']}
proxy_groups.append(proxy_group)
for country, nodes in country_groups.items():
if len(nodes):
proxy_group = {'name': country, 'type': 'select', 'proxies': nodes}
proxy_groups.append(proxy_group)
country_group = list(country_groups.keys())
if country_group:
proxy_groups.append({'name': '🏴‍☠️国家分组', 'type': 'select', 'proxies': country_group})
return proxy_groups
@staticmethod
def __continent_name_from_node(countries: List[Dict[str, str]], node_name: str) -> Optional[str]:
continents_names = {'欧洲': 'Europe',
'亚洲': 'Asia',
'大洋洲': 'Oceania',
'非洲': 'Africa',
'北美洲': 'NorthAmerica',
'南美洲': 'SouthAmerica'}
def __country_from_node(countries: List[Dict[str, str]], node_name: str) -> Optional[Dict[str, str]]:
node_name_lower = node_name.lower()
for country in countries:
if country['chinese'] in node_name or country['english'].lower() in node_name.lower():
return continents_names[country['continent']]
if country['emoji'] and country['emoji'] in node_name:
return country
elif (
country['chinese'] in node_name
or country['english'].lower() in node_name_lower
):
return country
return None
def __add_notification_job(self, ruleset_names: List[str]):
@@ -1432,7 +1469,7 @@ class ClashRuleProvider(_PluginBase):
clash_config['proxy-groups'] = ClashRuleProvider.extend_with_name_checking(clash_config['proxy-groups'],
proxy_groups)
# 添加按大洲代理组
if self._group_by_region:
if self._group_by_region or self._group_by_country:
groups_by_region = self.proxy_groups_by_region()
if groups_by_region:
clash_config['proxy-groups'] = ClashRuleProvider.extend_with_name_checking(clash_config['proxy-groups'],

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +0,0 @@
.plugin-config[data-v-929102b8] {
margin: 0 auto;
}

View File

@@ -0,0 +1,4 @@
.plugin-config[data-v-c2db3dad] {
margin: 0 auto;
}

View File

@@ -1,13 +1,13 @@
.plugin-page[data-v-d6db167c] {
.plugin-page[data-v-6aba879d] {
margin: 0 auto;
}
/* 使卡片等宽并适应移动端 */
.d-flex.flex-wrap[data-v-d6db167c] {
.d-flex.flex-wrap[data-v-6aba879d] {
gap: 16px;
}
.url-display[data-v-d6db167c] {
.url-display[data-v-6aba879d] {
word-break: break-all;
padding: 8px;
background: rgba(0, 0, 0, 0.05);
@@ -16,19 +16,19 @@
/* 移动端堆叠布局 */
@media (max-width: 768px) {
.d-flex.flex-wrap[data-v-d6db167c] {
.d-flex.flex-wrap[data-v-6aba879d] {
flex-direction: column;
}
}
/* Add visual distinction between sections */
.ruleset-section[data-v-d6db167c] {
.ruleset-section[data-v-6aba879d] {
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 16px;
background-color: #f5f5f5;
}
.top-section[data-v-d6db167c] {
.top-section[data-v-6aba879d] {
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 16px;
@@ -36,15 +36,15 @@
}
/* Optional: Add different border colors to further distinguish */
.ruleset-section[data-v-d6db167c] {
.ruleset-section[data-v-6aba879d] {
border-left: 4px solid #2196F3; /* Blue accent */
}
.top-section[data-v-d6db167c] {
.top-section[data-v-6aba879d] {
border-left: 4px solid #4CAF50; /* Green accent */
}
.drag-handle[data-v-d6db167c] {
.drag-handle[data-v-6aba879d] {
cursor: move;
}
.gap-2[data-v-d6db167c] {
.gap-2[data-v-6aba879d] {
gap: 8px;
}

View File

@@ -4733,7 +4733,7 @@ const importRuleTypes = ['YAML'];
const importProxiesTypes = ['YAML', 'LINK'];
const ruleProviderTypes = ['http', 'file', 'inline'];
const ruleProviderBehaviorTypes = ['domain', 'ipcidr', 'classical'];
const ruleProviderFormatTypes = ['yaml', 'text'];
const ruleProviderFormatTypes = ['yaml', 'text', 'mrs'];
// 修改actions为计算属性合并内置动作和自定义出站
const actions = computed(() => [
'DIRECT', 'REJECT', 'REJECT-DROP', 'PASS', 'COMPATIBLE',
@@ -5601,39 +5601,75 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_tab, null, {
default: _withCtx(() => _cache[92] || (_cache[92] = [
_createTextVNode("规则集规则")
])),
default: _withCtx(() => [
_createVNode(_component_v_icon, { start: "" }, {
default: _withCtx(() => _cache[92] || (_cache[92] = [
_createTextVNode("mdi-format-list-bulleted")
])),
_: 1
}),
_cache[93] || (_cache[93] = _createTextVNode(" 规则集规则 "))
]),
_: 1
}),
_createVNode(_component_v_tab, null, {
default: _withCtx(() => _cache[93] || (_cache[93] = [
_createTextVNode("置顶规则")
])),
default: _withCtx(() => [
_createVNode(_component_v_icon, { start: "" }, {
default: _withCtx(() => _cache[94] || (_cache[94] = [
_createTextVNode("mdi-pin")
])),
_: 1
}),
_cache[95] || (_cache[95] = _createTextVNode(" 置顶规则 "))
]),
_: 1
}),
_createVNode(_component_v_tab, null, {
default: _withCtx(() => _cache[94] || (_cache[94] = [
_createTextVNode("代理组")
])),
default: _withCtx(() => [
_createVNode(_component_v_icon, { start: "" }, {
default: _withCtx(() => _cache[96] || (_cache[96] = [
_createTextVNode("mdi-source-branch")
])),
_: 1
}),
_cache[97] || (_cache[97] = _createTextVNode(" 代理组 "))
]),
_: 1
}),
_createVNode(_component_v_tab, null, {
default: _withCtx(() => _cache[95] || (_cache[95] = [
_createTextVNode("出站代理")
])),
default: _withCtx(() => [
_createVNode(_component_v_icon, { start: "" }, {
default: _withCtx(() => _cache[98] || (_cache[98] = [
_createTextVNode("mdi-rocket-launch")
])),
_: 1
}),
_cache[99] || (_cache[99] = _createTextVNode(" 出站代理 "))
]),
_: 1
}),
_createVNode(_component_v_tab, null, {
default: _withCtx(() => _cache[96] || (_cache[96] = [
_createTextVNode("规则集合")
])),
default: _withCtx(() => [
_createVNode(_component_v_icon, { start: "" }, {
default: _withCtx(() => _cache[100] || (_cache[100] = [
_createTextVNode("mdi-folder-multiple")
])),
_: 1
}),
_cache[101] || (_cache[101] = _createTextVNode(" 规则集合 "))
]),
_: 1
}),
_createVNode(_component_v_tab, null, {
default: _withCtx(() => _cache[97] || (_cache[97] = [
_createTextVNode("Hosts")
])),
default: _withCtx(() => [
_createVNode(_component_v_icon, { start: "" }, {
default: _withCtx(() => _cache[102] || (_cache[102] = [
_createTextVNode("mdi-lan")
])),
_: 1
}),
_cache[103] || (_cache[103] = _createTextVNode(" Hosts "))
]),
_: 1
})
]),
@@ -5649,7 +5685,7 @@ return (_ctx, _cache) => {
_createElementVNode("div", _hoisted_3, [
_createElementVNode("div", _hoisted_4, [
_createElementVNode("div", _hoisted_5, [
_cache[100] || (_cache[100] = _createElementVNode("div", { class: "text-h6" }, "规则集规则", -1)),
_cache[106] || (_cache[106] = _createElementVNode("div", { class: "text-h6" }, "规则集规则", -1)),
_createElementVNode("div", _hoisted_6, [
_createVNode(_component_v_text_field, {
modelValue: searchRulesetRule.value,
@@ -5668,12 +5704,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[98] || (_cache[98] = [
default: _withCtx(() => _cache[104] || (_cache[104] = [
_createTextVNode("mdi-plus")
])),
_: 1
}),
_cache[99] || (_cache[99] = _createTextVNode(" 添加规则 "))
_cache[105] || (_cache[105] = _createTextVNode(" 添加规则 "))
]),
_: 1
})
@@ -5702,7 +5738,7 @@ return (_ctx, _cache) => {
}, [
_createElementVNode("td", null, [
_createVNode(_component_v_icon, { class: "drag-handle" }, {
default: _withCtx(() => _cache[101] || (_cache[101] = [
default: _withCtx(() => _cache[107] || (_cache[107] = [
_createTextVNode("mdi-drag")
])),
_: 1
@@ -5733,7 +5769,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[102] || (_cache[102] = [
default: _withCtx(() => _cache[108] || (_cache[108] = [
_createTextVNode("mdi-pencil")
])),
_: 1
@@ -5750,7 +5786,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[103] || (_cache[103] = [
default: _withCtx(() => _cache[109] || (_cache[109] = [
_createTextVNode("mdi-delete")
])),
_: 1
@@ -5790,7 +5826,7 @@ return (_ctx, _cache) => {
]),
_: 1
}, 8, ["headers", "items", "search", "page", "items-per-page", "items-per-page-options"]),
_cache[104] || (_cache[104] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对规则集中规则的修改可以在Clash中立即生效。 ", -1))
_cache[110] || (_cache[110] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对规则集中规则的修改可以在Clash中立即生效。 ", -1))
])
]),
_: 1
@@ -5800,7 +5836,7 @@ return (_ctx, _cache) => {
_createElementVNode("div", _hoisted_11, [
_createElementVNode("div", _hoisted_12, [
_createElementVNode("div", _hoisted_13, [
_cache[109] || (_cache[109] = _createElementVNode("div", { class: "text-h6" }, "置顶规则", -1)),
_cache[115] || (_cache[115] = _createElementVNode("div", { class: "text-h6" }, "置顶规则", -1)),
_createElementVNode("div", _hoisted_14, [
_createVNode(_component_v_text_field, {
modelValue: searchTopRule.value,
@@ -5820,12 +5856,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[105] || (_cache[105] = [
default: _withCtx(() => _cache[111] || (_cache[111] = [
_createTextVNode("mdi-import")
])),
_: 1
}),
_cache[106] || (_cache[106] = _createTextVNode(" 导入规则 "))
_cache[112] || (_cache[112] = _createTextVNode(" 导入规则 "))
]),
_: 1
}),
@@ -5835,12 +5871,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[107] || (_cache[107] = [
default: _withCtx(() => _cache[113] || (_cache[113] = [
_createTextVNode("mdi-plus")
])),
_: 1
}),
_cache[108] || (_cache[108] = _createTextVNode(" 添加规则 "))
_cache[114] || (_cache[114] = _createTextVNode(" 添加规则 "))
]),
_: 1
})
@@ -5869,7 +5905,7 @@ return (_ctx, _cache) => {
}, [
_createElementVNode("td", null, [
_createVNode(_component_v_icon, { class: "drag-handle" }, {
default: _withCtx(() => _cache[110] || (_cache[110] = [
default: _withCtx(() => _cache[116] || (_cache[116] = [
_createTextVNode("mdi-drag")
])),
_: 1
@@ -5900,7 +5936,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[111] || (_cache[111] = [
default: _withCtx(() => _cache[117] || (_cache[117] = [
_createTextVNode("mdi-pencil")
])),
_: 1
@@ -5918,7 +5954,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[112] || (_cache[112] = [
default: _withCtx(() => _cache[118] || (_cache[118] = [
_createTextVNode("mdi-delete")
])),
_: 1
@@ -5932,7 +5968,7 @@ return (_ctx, _cache) => {
activator: "parent",
location: "top"
}, {
default: _withCtx(() => _cache[113] || (_cache[113] = [
default: _withCtx(() => _cache[119] || (_cache[119] = [
_createTextVNode(" 根据规则集自动添加 ")
])),
_: 1
@@ -5970,8 +6006,8 @@ return (_ctx, _cache) => {
]),
_: 1
}, 8, ["headers", "search", "items", "page", "items-per-page"]),
_cache[114] || (_cache[114] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *置顶规则用于管理来自规则集的匹配规则,这些规则会动态更新。 ", -1)),
_cache[115] || (_cache[115] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对置顶规则的修改只有Clash更新配置后才会生效。 ", -1))
_cache[120] || (_cache[120] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *置顶规则用于管理来自规则集的匹配规则,这些规则会动态更新。 ", -1)),
_cache[121] || (_cache[121] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对置顶规则的修改只有Clash更新配置后才会生效。 ", -1))
])
]),
_: 1
@@ -5981,7 +6017,7 @@ return (_ctx, _cache) => {
_createElementVNode("div", _hoisted_19, [
_createElementVNode("div", _hoisted_20, [
_createElementVNode("div", _hoisted_21, [
_cache[118] || (_cache[118] = _createElementVNode("div", { class: "text-h6" }, "代理组", -1)),
_cache[124] || (_cache[124] = _createElementVNode("div", { class: "text-h6" }, "代理组", -1)),
_createElementVNode("div", _hoisted_22, [
_createVNode(_component_v_btn, {
color: "primary",
@@ -5989,12 +6025,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[116] || (_cache[116] = [
default: _withCtx(() => _cache[122] || (_cache[122] = [
_createTextVNode("mdi-plus")
])),
_: 1
}),
_cache[117] || (_cache[117] = _createTextVNode(" 添加代理组 "))
_cache[123] || (_cache[123] = _createTextVNode(" 添加代理组 "))
]),
_: 1
})
@@ -6026,7 +6062,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[119] || (_cache[119] = [
default: _withCtx(() => _cache[125] || (_cache[125] = [
_createTextVNode("mdi-code-json")
])),
_: 1
@@ -6044,7 +6080,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[120] || (_cache[120] = [
default: _withCtx(() => _cache[126] || (_cache[126] = [
_createTextVNode("mdi-pencil")
])),
_: 1
@@ -6062,7 +6098,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[121] || (_cache[121] = [
default: _withCtx(() => _cache[127] || (_cache[127] = [
_createTextVNode("mdi-delete")
])),
_: 1
@@ -6076,7 +6112,7 @@ return (_ctx, _cache) => {
activator: "parent",
location: "top"
}, {
default: _withCtx(() => _cache[122] || (_cache[122] = [
default: _withCtx(() => _cache[128] || (_cache[128] = [
_createTextVNode(" 非手动添加 ")
])),
_: 1
@@ -6114,7 +6150,7 @@ return (_ctx, _cache) => {
]),
_: 1
}, 8, ["headers", "items", "page", "items-per-page"]),
_cache[123] || (_cache[123] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1))
_cache[129] || (_cache[129] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1))
])
]),
_: 1
@@ -6124,7 +6160,7 @@ return (_ctx, _cache) => {
_createElementVNode("div", _hoisted_26, [
_createElementVNode("div", _hoisted_27, [
_createElementVNode("div", _hoisted_28, [
_cache[126] || (_cache[126] = _createElementVNode("div", { class: "text-h6" }, "出站代理", -1)),
_cache[132] || (_cache[132] = _createElementVNode("div", { class: "text-h6" }, "出站代理", -1)),
_createElementVNode("div", _hoisted_29, [
_createVNode(_component_v_btn, {
color: "primary",
@@ -6132,12 +6168,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[124] || (_cache[124] = [
default: _withCtx(() => _cache[130] || (_cache[130] = [
_createTextVNode("mdi-plus")
])),
_: 1
}),
_cache[125] || (_cache[125] = _createTextVNode(" 导入节点 "))
_cache[131] || (_cache[131] = _createTextVNode(" 导入节点 "))
]),
_: 1
})
@@ -6171,7 +6207,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[127] || (_cache[127] = [
default: _withCtx(() => _cache[133] || (_cache[133] = [
_createTextVNode("mdi-code-json")
])),
_: 1
@@ -6189,7 +6225,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[128] || (_cache[128] = [
default: _withCtx(() => _cache[134] || (_cache[134] = [
_createTextVNode("mdi-delete")
])),
_: 1
@@ -6203,7 +6239,7 @@ return (_ctx, _cache) => {
activator: "parent",
location: "top"
}, {
default: _withCtx(() => _cache[129] || (_cache[129] = [
default: _withCtx(() => _cache[135] || (_cache[135] = [
_createTextVNode(" 非手动添加 ")
])),
_: 1
@@ -6241,7 +6277,7 @@ return (_ctx, _cache) => {
]),
_: 1
}, 8, ["headers", "items", "page", "items-per-page"]),
_cache[130] || (_cache[130] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1))
_cache[136] || (_cache[136] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1))
])
]),
_: 1
@@ -6251,7 +6287,7 @@ return (_ctx, _cache) => {
_createElementVNode("div", _hoisted_33, [
_createElementVNode("div", _hoisted_34, [
_createElementVNode("div", _hoisted_35, [
_cache[133] || (_cache[133] = _createElementVNode("div", { class: "text-h6" }, "规则集合", -1)),
_cache[139] || (_cache[139] = _createElementVNode("div", { class: "text-h6" }, "规则集合", -1)),
_createElementVNode("div", _hoisted_36, [
_createVNode(_component_v_text_field, {
modelValue: searchRuleProviders.value,
@@ -6270,12 +6306,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[131] || (_cache[131] = [
default: _withCtx(() => _cache[137] || (_cache[137] = [
_createTextVNode("mdi-plus")
])),
_: 1
}),
_cache[132] || (_cache[132] = _createTextVNode(" 添加规则集合 "))
_cache[138] || (_cache[138] = _createTextVNode(" 添加规则集合 "))
]),
_: 1
})
@@ -6311,7 +6347,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[134] || (_cache[134] = [
default: _withCtx(() => _cache[140] || (_cache[140] = [
_createTextVNode("mdi-pencil")
])),
_: 1
@@ -6329,7 +6365,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[135] || (_cache[135] = [
default: _withCtx(() => _cache[141] || (_cache[141] = [
_createTextVNode("mdi-delete")
])),
_: 1
@@ -6343,7 +6379,7 @@ return (_ctx, _cache) => {
activator: "parent",
location: "top"
}, {
default: _withCtx(() => _cache[136] || (_cache[136] = [
default: _withCtx(() => _cache[142] || (_cache[142] = [
_createTextVNode(" 非手动添加 ")
])),
_: 1
@@ -6390,7 +6426,7 @@ return (_ctx, _cache) => {
_createElementVNode("div", _hoisted_40, [
_createElementVNode("div", _hoisted_41, [
_createElementVNode("div", _hoisted_42, [
_cache[139] || (_cache[139] = _createElementVNode("div", { class: "text-h6" }, "Hosts", -1)),
_cache[145] || (_cache[145] = _createElementVNode("div", { class: "text-h6" }, "Hosts", -1)),
_createElementVNode("div", _hoisted_43, [
_createVNode(_component_v_text_field, {
modelValue: searchHosts.value,
@@ -6409,12 +6445,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[137] || (_cache[137] = [
default: _withCtx(() => _cache[143] || (_cache[143] = [
_createTextVNode("mdi-plus")
])),
_: 1
}),
_cache[138] || (_cache[138] = _createTextVNode(" 添加Hosts "))
_cache[144] || (_cache[144] = _createTextVNode(" 添加Hosts "))
]),
_: 1
})
@@ -6470,7 +6506,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[140] || (_cache[140] = [
default: _withCtx(() => _cache[146] || (_cache[146] = [
_createTextVNode("mdi-pencil")
])),
_: 1
@@ -6487,7 +6523,7 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[141] || (_cache[141] = [
default: _withCtx(() => _cache[147] || (_cache[147] = [
_createTextVNode("mdi-delete")
])),
_: 1
@@ -6548,7 +6584,7 @@ return (_ctx, _cache) => {
_createVNode(_component_v_card, { class: "h-100" }, {
default: _withCtx(() => [
_createVNode(_component_v_card_title, { class: "text-h6 font-weight-medium" }, {
default: _withCtx(() => _cache[142] || (_cache[142] = [
default: _withCtx(() => _cache[148] || (_cache[148] = [
_createTextVNode("状态信息")
])),
_: 1
@@ -6557,7 +6593,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createElementVNode("div", _hoisted_47, [
_createElementVNode("div", _hoisted_48, [
_cache[143] || (_cache[143] = _createElementVNode("span", null, "状态", -1)),
_cache[149] || (_cache[149] = _createElementVNode("span", null, "状态", -1)),
_createVNode(_component_v_chip, {
size: "small",
color: status.value === 'running' ? 'success' : 'warning'
@@ -6569,23 +6605,23 @@ return (_ctx, _cache) => {
}, 8, ["color"])
]),
_createElementVNode("div", _hoisted_49, [
_cache[144] || (_cache[144] = _createElementVNode("span", null, "订阅配置规则数", -1)),
_cache[150] || (_cache[150] = _createElementVNode("span", null, "订阅配置规则数", -1)),
_createElementVNode("span", null, _toDisplayString(subscriptionInfo.value.rule_size), 1)
]),
_createElementVNode("div", _hoisted_50, [
_cache[145] || (_cache[145] = _createElementVNode("span", null, "置顶规则数", -1)),
_cache[151] || (_cache[151] = _createElementVNode("span", null, "置顶规则数", -1)),
_createElementVNode("span", null, _toDisplayString(sortedRules.value.length), 1)
]),
_createElementVNode("div", _hoisted_51, [
_cache[146] || (_cache[146] = _createElementVNode("span", null, "规则集规则数", -1)),
_cache[152] || (_cache[152] = _createElementVNode("span", null, "规则集规则数", -1)),
_createElementVNode("span", null, _toDisplayString(sortedRulesetRules.value.length), 1)
]),
_createElementVNode("div", _hoisted_52, [
_cache[147] || (_cache[147] = _createElementVNode("span", null, "代理组数", -1)),
_cache[153] || (_cache[153] = _createElementVNode("span", null, "代理组数", -1)),
_createElementVNode("span", null, _toDisplayString(proxyGroups.value.length), 1)
]),
_createElementVNode("div", _hoisted_53, [
_cache[148] || (_cache[148] = _createElementVNode("span", null, "最后更新", -1)),
_cache[154] || (_cache[154] = _createElementVNode("span", null, "最后更新", -1)),
_createElementVNode("span", null, _toDisplayString(lastUpdated.value), 1)
])
])
@@ -6607,7 +6643,7 @@ return (_ctx, _cache) => {
default: _withCtx(() => [
_createVNode(_component_v_card_title, { class: "d-flex justify-space-between align-center" }, {
default: _withCtx(() => [
_cache[150] || (_cache[150] = _createElementVNode("span", { class: "text-h6 font-weight-medium" }, "订阅链接", -1)),
_cache[156] || (_cache[156] = _createElementVNode("span", { class: "text-h6 font-weight-medium" }, "订阅链接", -1)),
_createVNode(_component_v_tooltip, {
location: "top",
text: "复制链接"
@@ -6623,7 +6659,7 @@ return (_ctx, _cache) => {
}), {
default: _withCtx(() => [
_createVNode(_component_v_icon, null, {
default: _withCtx(() => _cache[149] || (_cache[149] = [
default: _withCtx(() => _cache[155] || (_cache[155] = [
_createTextVNode("mdi-content-copy")
])),
_: 1
@@ -6647,12 +6683,12 @@ return (_ctx, _cache) => {
color: "grey",
class: "mr-2"
}, {
default: _withCtx(() => _cache[151] || (_cache[151] = [
default: _withCtx(() => _cache[157] || (_cache[157] = [
_createTextVNode("mdi-link")
])),
_: 1
}),
_cache[152] || (_cache[152] = _createElementVNode("span", { class: "text-grey-darken-1" }, "原始链接:", -1))
_cache[158] || (_cache[158] = _createElementVNode("span", { class: "text-grey-darken-1" }, "原始链接:", -1))
]),
_createElementVNode("div", _hoisted_56, [
(Object.keys(subscriptionsInfo.value).length > 0)
@@ -6673,7 +6709,7 @@ return (_ctx, _cache) => {
]),
_createElementVNode("div", _hoisted_58, [
_createVNode(_component_v_icon, { color: "blue" }, {
default: _withCtx(() => _cache[153] || (_cache[153] = [
default: _withCtx(() => _cache[159] || (_cache[159] = [
_createTextVNode("mdi-arrow-down-bold")
])),
_: 1
@@ -6684,12 +6720,12 @@ return (_ctx, _cache) => {
color: "primary",
class: "mr-2"
}, {
default: _withCtx(() => _cache[154] || (_cache[154] = [
default: _withCtx(() => _cache[160] || (_cache[160] = [
_createTextVNode("mdi-link-variant")
])),
_: 1
}),
_cache[155] || (_cache[155] = _createElementVNode("span", { class: "text-grey-darken-1" }, "生成链接:", -1))
_cache[161] || (_cache[161] = _createElementVNode("span", { class: "text-grey-darken-1" }, "生成链接:", -1))
]),
_createElementVNode("div", _hoisted_60, [
_createElementVNode("a", {
@@ -6723,12 +6759,12 @@ return (_ctx, _cache) => {
_createVNode(_component_v_expansion_panel_title, null, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[156] || (_cache[156] = [
default: _withCtx(() => _cache[162] || (_cache[162] = [
_createTextVNode("mdi-cloud-download")
])),
_: 1
}),
_cache[157] || (_cache[157] = _createElementVNode("span", { class: "text-subtitle-1 font-weight-medium" }, "订阅管理", -1))
_cache[163] || (_cache[163] = _createElementVNode("span", { class: "text-subtitle-1 font-weight-medium" }, "订阅管理", -1))
]),
_: 1
}),
@@ -6741,7 +6777,7 @@ return (_ctx, _cache) => {
variant: "tonal",
class: "mb-4"
}, {
default: _withCtx(() => _cache[158] || (_cache[158] = [
default: _withCtx(() => _cache[164] || (_cache[164] = [
_createTextVNode(" 暂无订阅信息,请先添加订阅链接 ")
])),
_: 1
@@ -6827,11 +6863,11 @@ return (_ctx, _cache) => {
: _createCommentVNode("", true)
]),
_createElementVNode("div", _hoisted_64, [
_cache[159] || (_cache[159] = _createElementVNode("span", null, "已用流量:", -1)),
_cache[165] || (_cache[165] = _createElementVNode("span", null, "已用流量:", -1)),
_createElementVNode("strong", null, _toDisplayString(formatBytes(info.download + info.upload)), 1)
]),
_createElementVNode("div", _hoisted_65, [
_cache[160] || (_cache[160] = _createElementVNode("span", null, "剩余流量:", -1)),
_cache[166] || (_cache[166] = _createElementVNode("span", null, "剩余流量:", -1)),
_createElementVNode("strong", null, _toDisplayString(formatBytes(info.total - info.download)), 1)
]),
_createVNode(_component_v_progress_linear, {
@@ -6860,12 +6896,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[161] || (_cache[161] = [
default: _withCtx(() => _cache[167] || (_cache[167] = [
_createTextVNode("mdi-cloud-sync")
])),
_: 1
}),
_cache[162] || (_cache[162] = _createTextVNode(" 更新订阅 "))
_cache[168] || (_cache[168] = _createTextVNode(" 更新订阅 "))
]),
_: 2
}, 1032, ["onClick", "loading"])
@@ -6899,12 +6935,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[163] || (_cache[163] = [
default: _withCtx(() => _cache[169] || (_cache[169] = [
_createTextVNode("mdi-refresh")
])),
_: 1
}),
_cache[164] || (_cache[164] = _createTextVNode(" 刷新数据 "))
_cache[170] || (_cache[170] = _createTextVNode(" 刷新数据 "))
]),
_: 1
}, 8, ["loading"]),
@@ -6915,12 +6951,12 @@ return (_ctx, _cache) => {
}, {
default: _withCtx(() => [
_createVNode(_component_v_icon, { left: "" }, {
default: _withCtx(() => _cache[165] || (_cache[165] = [
default: _withCtx(() => _cache[171] || (_cache[171] = [
_createTextVNode("mdi-cog")
])),
_: 1
}),
_cache[166] || (_cache[166] = _createTextVNode(" 配置 "))
_cache[172] || (_cache[172] = _createTextVNode(" 配置 "))
]),
_: 1
})
@@ -7075,7 +7111,7 @@ return (_ctx, _cache) => {
color: "secondary",
onClick: closeRuleDialog
}, {
default: _withCtx(() => _cache[167] || (_cache[167] = [
default: _withCtx(() => _cache[173] || (_cache[173] = [
_createTextVNode("取消")
])),
_: 1
@@ -7084,7 +7120,7 @@ return (_ctx, _cache) => {
color: "primary",
type: "submit"
}, {
default: _withCtx(() => _cache[168] || (_cache[168] = [
default: _withCtx(() => _cache[174] || (_cache[174] = [
_createTextVNode("保存")
])),
_: 1
@@ -7300,7 +7336,7 @@ return (_ctx, _cache) => {
}, {
"prepend-inner": _withCtx(() => [
_createVNode(_component_v_icon, { color: "warning" }, {
default: _withCtx(() => _cache[169] || (_cache[169] = [
default: _withCtx(() => _cache[175] || (_cache[175] = [
_createTextVNode("mdi-timer")
])),
_: 1
@@ -7330,7 +7366,7 @@ return (_ctx, _cache) => {
}, {
"prepend-inner": _withCtx(() => [
_createVNode(_component_v_icon, { color: "warning" }, {
default: _withCtx(() => _cache[170] || (_cache[170] = [
default: _withCtx(() => _cache[176] || (_cache[176] = [
_createTextVNode("mdi-timer")
])),
_: 1
@@ -7461,7 +7497,7 @@ return (_ctx, _cache) => {
text: "",
variant: "tonal"
}, {
default: _withCtx(() => _cache[171] || (_cache[171] = [
default: _withCtx(() => _cache[177] || (_cache[177] = [
_createTextVNode(" 参考"),
_createElementVNode("a", {
href: "https://wiki.metacubex.one/config/proxy-groups/",
@@ -7477,7 +7513,7 @@ return (_ctx, _cache) => {
color: "secondary",
onClick: _cache[59] || (_cache[59] = $event => (proxyGroupDialog.value = false))
}, {
default: _withCtx(() => _cache[172] || (_cache[172] = [
default: _withCtx(() => _cache[178] || (_cache[178] = [
_createTextVNode("取消")
])),
_: 1
@@ -7486,7 +7522,7 @@ return (_ctx, _cache) => {
color: "primary",
type: "submit"
}, {
default: _withCtx(() => _cache[173] || (_cache[173] = [
default: _withCtx(() => _cache[179] || (_cache[179] = [
_createTextVNode("保存")
])),
_: 1
@@ -7512,7 +7548,7 @@ return (_ctx, _cache) => {
_createVNode(_component_v_card, null, {
default: _withCtx(() => [
_createVNode(_component_v_card_title, { class: "headline" }, {
default: _withCtx(() => _cache[174] || (_cache[174] = [
default: _withCtx(() => _cache[180] || (_cache[180] = [
_createTextVNode("YAML 配置")
])),
_: 1
@@ -7538,7 +7574,7 @@ return (_ctx, _cache) => {
color: "primary",
onClick: _cache[62] || (_cache[62] = $event => (copyToClipboard(displayedYaml.value)))
}, {
default: _withCtx(() => _cache[175] || (_cache[175] = [
default: _withCtx(() => _cache[181] || (_cache[181] = [
_createTextVNode("复制")
])),
_: 1
@@ -7547,7 +7583,7 @@ return (_ctx, _cache) => {
color: "primary",
onClick: _cache[63] || (_cache[63] = $event => (yamlDialog.value = false))
}, {
default: _withCtx(() => _cache[176] || (_cache[176] = [
default: _withCtx(() => _cache[182] || (_cache[182] = [
_createTextVNode("关闭")
])),
_: 1
@@ -7570,7 +7606,7 @@ return (_ctx, _cache) => {
_createVNode(_component_v_card, null, {
default: _withCtx(() => [
_createVNode(_component_v_card_title, null, {
default: _withCtx(() => _cache[177] || (_cache[177] = [
default: _withCtx(() => _cache[183] || (_cache[183] = [
_createTextVNode("导入规则")
])),
_: 1
@@ -7601,7 +7637,7 @@ return (_ctx, _cache) => {
class: "mb-4",
variant: "tonal"
}, {
default: _withCtx(() => _cache[178] || (_cache[178] = [
default: _withCtx(() => _cache[184] || (_cache[184] = [
_createTextVNode(" 请输入 Clash 规则中的 "),
_createElementVNode("strong", null, "rules", -1),
_createTextVNode(" 字段,例如:"),
@@ -7624,7 +7660,7 @@ return (_ctx, _cache) => {
color: "secondary",
onClick: _cache[67] || (_cache[67] = $event => (importRuleDialog.value = false, error.value=null))
}, {
default: _withCtx(() => _cache[179] || (_cache[179] = [
default: _withCtx(() => _cache[185] || (_cache[185] = [
_createTextVNode("取消")
])),
_: 1
@@ -7633,7 +7669,7 @@ return (_ctx, _cache) => {
color: "primary",
onClick: importRule
}, {
default: _withCtx(() => _cache[180] || (_cache[180] = [
default: _withCtx(() => _cache[186] || (_cache[186] = [
_createTextVNode("导入")
])),
_: 1
@@ -7656,7 +7692,7 @@ return (_ctx, _cache) => {
_createVNode(_component_v_card, null, {
default: _withCtx(() => [
_createVNode(_component_v_card_title, null, {
default: _withCtx(() => _cache[181] || (_cache[181] = [
default: _withCtx(() => _cache[187] || (_cache[187] = [
_createTextVNode("导入节点")
])),
_: 1
@@ -7702,7 +7738,7 @@ return (_ctx, _cache) => {
class: "mb-4",
variant: "tonal"
}, {
default: _withCtx(() => _cache[182] || (_cache[182] = [
default: _withCtx(() => _cache[188] || (_cache[188] = [
_createTextVNode(" 请输入 Clash 规则中的 "),
_createElementVNode("strong", null, "proxies", -1),
_createTextVNode(" 字段,例如:"),
@@ -7724,7 +7760,7 @@ return (_ctx, _cache) => {
class: "mb-4",
variant: "tonal"
}, {
default: _withCtx(() => _cache[183] || (_cache[183] = [
default: _withCtx(() => _cache[189] || (_cache[189] = [
_createTextVNode(" 请输入 V2RayN 格式的分享链接,例如:"),
_createElementVNode("br", null, null, -1),
_createElementVNode("code", null, "vmess://xxxx", -1),
@@ -7744,7 +7780,7 @@ return (_ctx, _cache) => {
color: "secondary",
onClick: _cache[72] || (_cache[72] = $event => (importExtraProxiesDialog.value=false, error.value=null))
}, {
default: _withCtx(() => _cache[184] || (_cache[184] = [
default: _withCtx(() => _cache[190] || (_cache[190] = [
_createTextVNode("取消")
])),
_: 1
@@ -7754,7 +7790,7 @@ return (_ctx, _cache) => {
onClick: importExtraProxiesFun,
loading: importProxiesLoading.value
}, {
default: _withCtx(() => _cache[185] || (_cache[185] = [
default: _withCtx(() => _cache[191] || (_cache[191] = [
_createTextVNode(" 导入 ")
])),
_: 1
@@ -7828,7 +7864,7 @@ return (_ctx, _cache) => {
required: "",
rules: [v => !!v || '当类型为文件时,路径不能为空'],
class: "mb-4",
hint: "文件路径,必须是唯一的"
hint: "文件路径,不填写时会使用 url 的 MD5 作为文件名"
}, null, 8, ["modelValue", "rules"]))
: _createCommentVNode("", true),
_createVNode(_component_v_text_field, {
@@ -7911,7 +7947,7 @@ return (_ctx, _cache) => {
color: "secondary",
onClick: _cache[83] || (_cache[83] = $event => (ruleProviderDialog.value = false, error.value=null))
}, {
default: _withCtx(() => _cache[186] || (_cache[186] = [
default: _withCtx(() => _cache[192] || (_cache[192] = [
_createTextVNode("取消")
])),
_: 1
@@ -7920,7 +7956,7 @@ return (_ctx, _cache) => {
color: "primary",
type: "submit"
}, {
default: _withCtx(() => _cache[187] || (_cache[187] = [
default: _withCtx(() => _cache[193] || (_cache[193] = [
_createTextVNode("保存")
])),
_: 1
@@ -8028,7 +8064,7 @@ return (_ctx, _cache) => {
variant: "outlined",
class: "mb-2"
}, {
default: _withCtx(() => _cache[188] || (_cache[188] = [
default: _withCtx(() => _cache[194] || (_cache[194] = [
_createTextVNode(" 请在「高级选项」配置 Cloudflare CDN 优选 IPs ")
])),
_: 1
@@ -8039,7 +8075,7 @@ return (_ctx, _cache) => {
text: "",
variant: "tonal"
}, {
default: _withCtx(() => _cache[189] || (_cache[189] = [
default: _withCtx(() => _cache[195] || (_cache[195] = [
_createTextVNode(" 支持"),
_createElementVNode("a", {
href: "https://wiki.metacubex.one/handbook/syntax/#_8",
@@ -8055,7 +8091,7 @@ return (_ctx, _cache) => {
color: "secondary",
onClick: _cache[88] || (_cache[88] = $event => (hostDialog.value = false, error.value=null))
}, {
default: _withCtx(() => _cache[190] || (_cache[190] = [
default: _withCtx(() => _cache[196] || (_cache[196] = [
_createTextVNode("取消")
])),
_: 1
@@ -8064,7 +8100,7 @@ return (_ctx, _cache) => {
color: "primary",
type: "submit"
}, {
default: _withCtx(() => _cache[191] || (_cache[191] = [
default: _withCtx(() => _cache[197] || (_cache[197] = [
_createTextVNode("保存")
])),
_: 1
@@ -8086,6 +8122,6 @@ return (_ctx, _cache) => {
}
};
const PageComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-d6db167c"]]);
const PageComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-6aba879d"]]);
export { PageComponent as default };

View File

@@ -2,11 +2,11 @@ const currentImports = {};
const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']);
let moduleMap = {
"./Page":()=>{
dynamicLoadingCss(["__federation_expose_Page-BOym_1fV.css"], false, './Page');
return __federation_import('./__federation_expose_Page-D5l2MyNA.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
dynamicLoadingCss(["__federation_expose_Page-D1PURE3U.css"], false, './Page');
return __federation_import('./__federation_expose_Page-DfeoSpny.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
"./Config":()=>{
dynamicLoadingCss(["__federation_expose_Config-BrXQaadr.css"], false, './Config');
return __federation_import('./__federation_expose_Config-NH09p1Am.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
dynamicLoadingCss(["__federation_expose_Config-rtAetK-O.css"], false, './Config');
return __federation_import('./__federation_expose_Config-DcObTkjA.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
"./Dashboard":()=>{
dynamicLoadingCss(["__federation_expose_Dashboard-vS9Qm2ZB.css"], false, './Dashboard');
return __federation_import('./__federation_expose_Dashboard-BDSt5WaH.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},};

View File

@@ -29,7 +29,7 @@ class ImdbSource(_PluginBase):
# 插件图标
plugin_icon = "IMDb_IOS-OSX_App.png"
# 插件版本
plugin_version = "1.5.6"
plugin_version = "1.5.8"
# 插件作者
plugin_author = "wumode"
# 作者主页
@@ -60,7 +60,7 @@ class ImdbSource(_PluginBase):
def init_plugin(self, config: dict = None):
plugin_instance = self
plugin_instance: ImdbSource = self
def patched_recognize_media(chain_self, meta: MetaBase = None,
mtype: Optional[MediaType] = None,
@@ -822,6 +822,14 @@ class ImdbSource(_PluginBase):
ChainBase.async_recognize_media._patched_by == id(self) and
self._original_async_method):
ChainBase.async_recognize_media = self._original_async_method
if self._scheduler:
try:
self._scheduler.remove_all_jobs()
if self._scheduler.running:
self._scheduler.shutdown()
self._scheduler = None
except Exception as e:
logger.error(f"退出插件失败:{e}")
def get_module(self) -> Dict[str, Any]:
"""

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,7 @@
![](https://images2.imgbox.com/d6/b6/kZu6EH2a_o.png)
![](https://images2.imgbox.com/c8/3a/rEJBWu5v_o.png)
![](https://images2.imgbox.com/97/b7/d6RXFtwD_o.png)
![](https://images2.imgbox.com/8a/d4/AtgOe265_o.jpg)
# Gemini
@@ -38,7 +39,7 @@ CEFR全称是Common European Framework of Reference for Languages。
# 计划
- 双语字幕支持
- 考试词汇标注
- ~~考试词汇标注~~
# FAQ

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,5 @@
pysubs2~=1.8.0
langdetect~=1.0.9
pymediainfo~=7.0.1
pymediainfo~=7.0.1
thinc==8.3.4
spacy==3.8.7

View File

@@ -0,0 +1,326 @@
from pathlib import Path
from typing import Any, List, Dict, Tuple
from app.core.context import MediaInfo
from app.core.event import eventmanager, Event
from app.log import logger
from app.plugins import _PluginBase
from app.schemas.types import ChainEventType, MediaType, NotificationType
class MultiClass(_PluginBase):
# 插件名称
plugin_name = "视频多级分类"
# 插件描述
plugin_desc = "支持电影按照评分,年代和系列分类"
# 插件图标
plugin_icon = "Calibreweb_B.png"
# 插件版本
plugin_version = "0.1"
# 插件作者
plugin_author = "liuhangbin"
# 作者主页
author_url = "https://github.com/liuhangbin"
# 插件配置项ID前缀
plugin_config_prefix = "multiclass_"
# 加载顺序
plugin_order = 1
# 可使用的用户级别
auth_level = 1
_enabled = False
_notify = False
_year_class = False
_vote_class = False
_collection_class = False
def init_plugin(self, config: dict = None):
if config:
self._enabled = config.get("enabled", False)
self._notify = config.get("notify", False)
self._year_class = config.get("year_class", False)
self._vote_class = config.get("vote_class", False)
self._collection_class = config.get("collection_class", False)
def get_state(self) -> bool:
return self._enabled
def get_api(self) -> List[Dict[str, Any]]:
pass
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
"""
return [
{
'component': 'VForm',
'content': [
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'enabled',
'label': '启用插件',
}
}
]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'year_class',
'label': '按照年代分类',
}
}
]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'vote_class',
'label': '按照评分分类',
}
}
]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'collection_class',
'label': '按照系列分类',
}
}
]
},
{
'component': 'VCol',
'props': {
'cols': 12,
'md': 6
},
'content': [
{
'component': 'VSwitch',
'props': {
'model': 'notify',
'label': '发送消息',
}
}
]
}
]
},
{
'component': 'VRow',
'content': [
{
'component': 'VCol',
'props': {
'cols': 12,
},
'content': [
{
'component': 'VAlert',
'props': {
'type': 'info',
'variant': 'tonal',
'text': '插件目前仅支持电影(需要开启智能重命名)。如果按评分分类7-9 高分4-6 一般1-3 垃圾。 系列电影不参与评分, 不按年代分类。'
}
}
]
}
]
}
]
}
], {
"enabled": False,
"notify": False,
"year_class": False,
"vote_class": False,
"collection_class": False
}
def get_page(self) -> List[dict]:
pass
@eventmanager.register(ChainEventType.TransferRename)
def category_handler(self, event: Event):
"""
根据多级分类规则重新分类组装地址
"""
logger.debug(f"多级分类插件触发!")
# 基础验证
if not self.get_state():
logger.debug(f"多级分类插件未启用!")
return
if not event:
logger.warning(f"多级分类异常:事件对象为空")
return
if not hasattr(event, 'event_data'):
logger.warning(f"多级分类异常:事件数据为空")
return
try:
data = event.event_data
# 验证必要的数据字段
if not hasattr(data, 'render_str') or not data.render_str:
logger.warning(f"多级分类异常render_str为空")
return
else:
render_str = data.render_str
# 暂时只支持电影分类
if not hasattr(data, 'rename_dict') or not data.rename_dict:
logger.warning(f"多级分类异常rename_dict为空")
return
else:
rename_dict = data.rename_dict
video_type = rename_dict.get("type", "")
if video_type != "电影":
logger.debug(f"多级分类异常:不支持的媒体类型: {video_type}, 只支持电影分类")
return
# 安全获取数据字段
title = rename_dict.get("title", "")
en_title = rename_dict.get("en_title", "")
year = rename_dict.get("year")
vote_average = rename_dict.get("vote_average")
media_info = rename_dict.get("__mediainfo__")
# 初始化默认值
vote_count = 0
c_name = None
vote_path = "未知评分"
decade = 0
# 安全处理媒体信息
if media_info and hasattr(media_info, 'vote_count'):
try:
vote_count = int(media_info.vote_count) if media_info.vote_count else 0
except (ValueError, TypeError):
vote_count = 0
if hasattr(media_info, 'tmdb_info') and media_info.tmdb_info:
collection = media_info.tmdb_info.get("belongs_to_collection")
if collection and isinstance(collection, dict):
c_name = collection.get("name")
# 安全处理评分数据
try:
if vote_average is not None:
vote_average = float(vote_average)
else:
vote_average = 0
except (ValueError, TypeError):
vote_average = 0
# 评分分类逻辑
if vote_count < 10:
vote_average = 0
vote_path = "评分不足"
elif vote_average >= 7:
vote_path = "高分电影"
elif vote_average >= 4:
vote_path = "一般电影"
else:
vote_path = "垃圾电影"
# 安全处理年份数据
try:
if year and str(year).isdigit():
year_int = int(year)
if 1900 <= year_int <= 2100: # 合理的年份范围
decade = (year_int // 10) * 10
else:
decade = 0
logger.warning(f"年份超出合理范围: {year}")
else:
decade = 0
except (ValueError, TypeError):
decade = 0
logger.warning(f"年份转换失败: {year}")
# 构建分类路径
path_parts = []
if self._collection_class and c_name:
# 当collection为true时只添加collection name
# 清理collection名称移除特殊字符
clean_c_name = str(c_name).strip()
if clean_c_name:
path_parts.append("系列电影")
path_parts.append(clean_c_name)
else:
# 当collection不为true时根据其他配置添加路径
if self._vote_class and vote_path:
path_parts.append(vote_path)
if self._year_class and decade > 0:
path_parts.append(f"{decade}s")
# 构建最终的路径
if path_parts:
# 确保render_str不为空
safe_render_str = str(render_str).strip() if render_str else ""
event.event_data.updated_str = f"{'/'.join(path_parts)}/{safe_render_str}"
# 更新事件数据
event.event_data.updated = True
event.event_data.source = "MultiClass"
# 发送消息
if self._notify:
self.post_message(
mtype=NotificationType.Organize,
title="多级分类完成",
text=f"已重新分类: {event.event_data.updated_str}",
)
else:
event.event_data.updated = False
logger.warning(f"多级分类失败: 未找到分类路径,请检查配置是否已开启")
except Exception as e:
logger.error(f"多级分类异常: {str(e)}", exc_info=True)
# 确保即使出错也不会影响原始数据
if hasattr(event, 'event_data') and event.event_data:
event.event_data.updated = False
event.event_data.updated_str = getattr(data, 'render_str', '') if data else ''
def stop_service(self):
"""
停止服务
"""
pass

View File

@@ -29,7 +29,7 @@ class ToBypassTrackers(_PluginBase):
# 插件图标
plugin_icon = "Clash_A.png"
# 插件版本
plugin_version = "1.4.2"
plugin_version = "1.4.3"
# 插件作者
plugin_author = "wumode"
# 作者主页
@@ -68,7 +68,8 @@ class ToBypassTrackers(_PluginBase):
self.ipv6_txt = self.get_data("ipv6_txt") if self.get_data("ipv6_txt") else ""
self.ipv4_txt = self.get_data("ipv4_txt") if self.get_data("ipv4_txt") else ""
try:
with open(f"{settings.ROOT_PATH}/app/plugins/tobypasstrackers/sites/trackers", "r", encoding="utf-8") as f:
site_file = settings.ROOT_PATH/'app'/'plugins'/'tobypasstrackers'/'sites'/'trackers'
with open(site_file, "r", encoding="utf-8") as f:
base64_str = f.read()
self.trackers = json.loads(base64.b64decode(base64_str).decode("utf-8"))
except Exception as e:
@@ -101,7 +102,6 @@ class ToBypassTrackers(_PluginBase):
)
self._onlyonce = False
self.__update_config()
# self._scheduler.print_jobs()
self._scheduler.start()
def get_state(self) -> bool:
@@ -619,16 +619,14 @@ class ToBypassTrackers(_PluginBase):
# Load Chnroute6 Lists
res = RequestUtils().get_res(url=chnroute6_lists_url)
if res is not None and res.status_code == 200:
chnroute6_lists = res.text[:-1].split('\n')
for ipr in chnroute6_lists:
ipv6_list.append(ipr)
chnroute6_lists = res.text.strip().split('\n')
ipv6_list = [*chnroute6_lists]
if self._china_ip_route:
# Load Chnroute Lists
res = RequestUtils().get_res(url=chnroute_lists_url)
if res is not None and res.status_code == 200:
chnroute_lists = res.text[:-1].split('\n')
for ipr in chnroute_lists:
ip_list.append(ipr)
chnroute_lists = res.text.strip().split('\n')
ip_list = [*chnroute_lists]
do_sites = {site.domain: site.name for site in SiteOper().list_order_by_pri() if
site.id in self._bypassed_sites}
domain_name_map = {}

View File

@@ -1 +1 @@
eyJoZGRvbGJ5LmNvbSI6IFsidC5oZGRvbGJ5LmNvbSJdLCAidGp1cHQub3JnIjogWyJ0cmFja2VyLXB1YmxpYy50anVwdC5vcmciXSwgIm5pY2VwdC5uZXQiOiBbInd3dy5uaWNlcHQubmV0Il0sICJyb3VzaS56aXAiOiBbImhpdHB0LmNvbSJdLCAicHRob21lLm5ldCI6IFsicHRob21lLm5ldCJdLCAiaGR0aW1lLm9yZyI6IFsiaGR0aW1lLm9yZyJdLCAiZWFzdGdhbWUub3JnIjogWyJwdC5lYXN0Z2FtZS5vcmciXSwgInB0dGltZS5vcmciOiBbInd3dy5wdHRpbWUub3JnIl0sICJtLXRlYW0uY2MiOiBbInRyYWNrZXIubS10ZWFtLmNjIiwgInRyYWNrZXIubS10ZWFtLmlvIl0sICI1MnB0LnNpdGUiOiBbIjUycHQuc2l0ZSJdLCAicWluZ3dhcHQuY29tIjogWyJ0cmFja2VyLnFpbmd3YS5wcm8iLCAidHJhY2tlci5xaW5nd2FwdC5jb20iXSwgImhka3lsLmluIjogWyJ0cmFja2VyLmhka3lsLmluIl0sICJyYWluZ2ZoLnRvcCI6IFsicmFpbmdmaC50b3AiXSwgImhkZmFucy5vcmciOiBbImhkZmFucy5vcmciXSwgInB0bGdzLm9yZyI6IFsicHRsLmdzIiwgInJlbGF5MDEucHRsLmdzIl0sICJtb25pa2FkZXNpZ24udWsiOiBbInRyYWNrZXIubW9uaWthZGVzaWduLnVrIiwgImRhaWtpcmFpLm1vbmlrYWRlc2lnbi51ayIsICJhbmltZS1uby1pbmRleC5jb20iXSwgInB0c2Jhby5jbHViIjogWyJwdHNiYW8uY2x1YiJdLCAidG90aGVnbG9yeS5pbSI6IFsidHJhY2tlci50b3RoZWdsb3J5LmltIl0sICJ1Mi5kbWh5Lm9yZyI6IFsiZGF5ZHJlYW0uZG1oeS5iZXN0Il0sICJieXIucHQiOiBbInRyYWNrZXIuYnlyLnB0Il0sICJodWRidC5odXN0LmVkdS5jbiI6IFsiaHVkYnQuaHVzdC5lZHUuY24iXSwgImlsb2xpY29uLmNvbSI6IFsidHJhY2tlci5pbG9saWNvbi5jYyJdLCAiaGl0cHQuY29tIjogWyJoaXRwdC5jb20iXSwgImJ0c2Nob29sLmNsdWIiOiBbInB0LmJ0c2Nob29sLmNsdWIiXSwgImhkYXJlYS5jbHViIjogWyJ0cmFja2VyLmhkYXJlYS5jbHViIl0sICJzcHJpbmdzdW5kYXkubmV0IjogWyJvbjYuc3ByaW5nc3VuZGF5Lm5ldCIsICJvbi5zcHJpbmdzdW5kYXkubmV0Il0sICJ6bXB0LmNjIjogWyJ6bXB0LmNjIl0sICJjYXJwdC5uZXQiOiBbInRyYWNrZXIuY2FycHQubmV0Il0sICJpY2MyMDIyLmNvbSI6IFsidHJhY2tlci5pY2MyMDIyLnh5eiJdLCAia2VlcGZyZHMuY29tIjogWyJ0cmFja2VyLmtlZXBmcmRzLmNvbSJdLCAicHR6b25lLnh5eiI6IFsicHR6b25lLnh5eiJdLCAiY3NwdC50b3AiOiBbInRyYWNrZXIuY3NwdC50b3AiLCAidHJhY2tlci5jc3B0LmNjIiwgInRyYWNrZXIuY3NwdC5kYXRlIl0sICJjcmFicHQudmlwIjogWyJjcmFicHQudmlwIl0sICJva3B0Lm5ldCI6IFsid3d3Lm9rcHQubmV0Il0sICJnYW1lZ2FtZXB0LmNvbSI6IFsid3d3LmdhbWVnYW1lcHQuY29tIl0sICJhdWRpZW5jZXMubWUiOiBbInQuYXVkaWVuY2VzLm1lIiwgInRyYWNrZXIuY2luZWZpbGVzLmluZm8iXSwgInhpbmd5dW5nZS50b3AiOiBbInRyYWNrZXIueGluZ3l1bmdlLnRvcCIsICJ0cmFja2VyLnhpbmd5dW5nZS5zYnMiXSwgImV0OC5vcmciOiBbImV0OC5vcmciLCAidC5ldDgub3JnIl0sICJkaXNjZmFuLm5ldCI6IFsiZGlzY2Zhbi54eXoiXX0=
eyI1MnB0LnNpdGUiOiBbIjUycHQuc2l0ZSJdLCAiYXVkaWVuY2VzLm1lIjogWyJ0LmF1ZGllbmNlcy5tZSIsICJ0cmFja2VyLmNpbmVmaWxlcy5pbmZvIl0sICJidHNjaG9vbC5jbHViIjogWyJwdC5idHNjaG9vbC5jbHViIl0sICJieXIucHQiOiBbInRyYWNrZXIuYnlyLnB0Il0sICJjYXJwdC5uZXQiOiBbInRyYWNrZXIuY2FycHQubmV0Il0sICJjcmFicHQudmlwIjogWyJjcmFicHQudmlwIl0sICJjc3B0LnRvcCI6IFsidHJhY2tlci5jc3B0LnRvcCIsICJ0cmFja2VyLmNzcHQuY2MiLCAidHJhY2tlci5jc3B0LmRhdGUiXSwgImRpc2NmYW4ubmV0IjogWyJkaXNjZmFuLnh5eiJdLCAiZWFzdGdhbWUub3JnIjogWyJwdC5lYXN0Z2FtZS5vcmciXSwgImV0OC5vcmciOiBbImV0OC5vcmciLCAidC5ldDgub3JnIl0sICJnYW1lZ2FtZXB0LmNvbSI6IFsid3d3LmdhbWVnYW1lcHQuY29tIl0sICJoZGFyZWEuY2x1YiI6IFsidHJhY2tlci5oZGFyZWEuY2x1YiJdLCAiaGRkb2xieS5jb20iOiBbInQuaGRkb2xieS5jb20iXSwgImhkZmFucy5vcmciOiBbImhkZmFucy5vcmciXSwgImhka3lsLmluIjogWyJ0cmFja2VyLmhka3lsLmluIl0sICJoZHRpbWUub3JnIjogWyJoZHRpbWUub3JnIl0sICJoaXRwdC5jb20iOiBbImhpdHB0LmNvbSJdLCAiaHVkYnQuaHVzdC5lZHUuY24iOiBbImh1ZGJ0Lmh1c3QuZWR1LmNuIl0sICJpY2MyMDIyLmNvbSI6IFsidHJhY2tlci5pY2MyMDIyLnh5eiJdLCAiaWxvbGljb24uY29tIjogWyJ0cmFja2VyLmlsb2xpY29uLmNjIl0sICJrZWVwZnJkcy5jb20iOiBbInRyYWNrZXIua2VlcGZyZHMuY29tIl0sICJtLXRlYW0uY2MiOiBbInRyYWNrZXIubS10ZWFtLmNjIiwgInRyYWNrZXIubS10ZWFtLmlvIl0sICJtb25pa2FkZXNpZ24udWsiOiBbInRyYWNrZXIubW9uaWthZGVzaWduLnVrIiwgImRhaWtpcmFpLm1vbmlrYWRlc2lnbi51ayIsICJhbmltZS1uby1pbmRleC5jb20iXSwgIm5pY2VwdC5uZXQiOiBbInd3dy5uaWNlcHQubmV0Il0sICJva3B0Lm5ldCI6IFsid3d3Lm9rcHQubmV0Il0sICJwdGhvbWUubmV0IjogWyJwdGhvbWUubmV0Il0sICJwdGxncy5vcmciOiBbInB0bC5ncyIsICJyZWxheTAxLnB0bC5ncyJdLCAicHRzYmFvLmNsdWIiOiBbInB0c2Jhby5jbHViIl0sICJwdHRpbWUub3JnIjogWyJ3d3cucHR0aW1lLm9yZyJdLCAicHR6b25lLnh5eiI6IFsicHR6b25lLnh5eiJdLCAicWluZ3dhcHQuY29tIjogWyJ0cmFja2VyLnFpbmd3YS5wcm8iLCAidHJhY2tlci5xaW5nd2FwdC5jb20iXSwgInJhaW5nZmgudG9wIjogWyJyYWluZ2ZoLnRvcCJdLCAicm91c2kuemlwIjogWyJoaXRwdC5jb20iXSwgInNwcmluZ3N1bmRheS5uZXQiOiBbIm9uNi5zcHJpbmdzdW5kYXkubmV0IiwgIm9uLnNwcmluZ3N1bmRheS5uZXQiXSwgInRqdXB0Lm9yZyI6IFsidHJhY2tlci1wdWJsaWMudGp1cHQub3JnIl0sICJ0b3RoZWdsb3J5LmltIjogWyJ0cmFja2VyLnRvdGhlZ2xvcnkuaW0iXSwgInUyLmRtaHkub3JnIjogWyJkYXlkcmVhbS5kbWh5LmJlc3QiXSwgInhpbmd5dW5nZS50b3AiOiBbInRyYWNrZXIueGluZ3l1bmdlLnRvcCIsICJ0cmFja2VyLnhpbmd5dW5nZS5zYnMiXSwgInptcHQuY2MiOiBbInptcHQuY2MiXSwgImhoYW5jbHViLnRvcCI6IFsidHJhY2tlci5oaGFuY2x1Yi50b3AiXSwgImhkY2l0eS5jaXR5IjogWyJzeW5jLmxlbml0ZXIub3JnIl19

View File

@@ -4,7 +4,6 @@ from abc import ABCMeta, abstractmethod
from typing import Tuple
import chardet
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.helper.browser import PlaywrightHelper
@@ -33,7 +32,7 @@ class _ISiteSigninHandler(metaclass=ABCMeta):
return False
@abstractmethod
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,7 +1,5 @@
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.string import StringUtils
@@ -26,7 +24,7 @@ class BTSchool(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -3,7 +3,6 @@ import re
from typing import Tuple
from lxml import etree
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
@@ -36,7 +35,7 @@ class CHDBits(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,7 +1,5 @@
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.string import StringUtils
@@ -26,7 +24,7 @@ class HaiDan(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,8 +1,6 @@
import json
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
@@ -29,7 +27,7 @@ class Hares(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,7 +1,5 @@
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
@@ -30,7 +28,7 @@ class HDArea(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -2,7 +2,6 @@ import json
from typing import Tuple
from lxml import etree
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
@@ -30,7 +29,7 @@ class HDChina(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,7 +1,5 @@
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.string import StringUtils
@@ -28,7 +26,7 @@ class HDCity(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -2,8 +2,6 @@ import json
import time
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.helper.ocr import OcrHelper
from app.log import logger
@@ -31,7 +29,7 @@ class HDSky(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,8 +1,6 @@
import re
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.string import StringUtils
@@ -30,7 +28,7 @@ class HDUpt(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,7 +1,4 @@
from typing import Tuple
from urllib.parse import urljoin
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.plugins.autosignin.sites import _ISiteSigninHandler
@@ -25,7 +22,7 @@ class MTorrent(_ISiteSigninHandler):
"""
return True if cls.site_url in url.split(".") else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作,馒头实际没有签到,非仿真模式下需要更新访问时间
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息
@@ -52,7 +49,7 @@ class MTorrent(_ISiteSigninHandler):
else:
return False, "模拟登录失败,无法打开网站"
def login(self, site_info: CommentedMap) -> Tuple[bool, str]:
def login(self, site_info: dict) -> Tuple[bool, str]:
"""
执行登录操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,7 +1,5 @@
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
@@ -30,7 +28,7 @@ class NexusHD(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -3,7 +3,6 @@ import time
from typing import Tuple
from lxml import etree
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.helper.ocr import OcrHelper
@@ -32,7 +31,7 @@ class Opencd(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,8 +1,6 @@
import json
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.string import StringUtils
@@ -24,7 +22,7 @@ class PTerClub(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,7 +1,5 @@
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.string import StringUtils
@@ -26,7 +24,7 @@ class PTTime(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -6,7 +6,6 @@ from typing import Tuple
from PIL import Image
from lxml import etree
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
@@ -46,7 +45,7 @@ class Tjupt(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,8 +1,6 @@
import re
from typing import Tuple
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
from app.plugins.autosignin.sites import _ISiteSigninHandler
@@ -33,7 +31,7 @@ class TTG(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -4,7 +4,6 @@ import re
from typing import Tuple
from lxml import etree
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
@@ -39,7 +38,7 @@ class U2(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -1,8 +1,6 @@
from typing import Tuple
from urllib.parse import urljoin
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.plugins.autosignin.sites import _ISiteSigninHandler
from app.utils.http import RequestUtils
@@ -24,7 +22,7 @@ class YemaPT(_ISiteSigninHandler):
"""
return True if cls.site_url in url else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息
@@ -50,7 +48,7 @@ class YemaPT(_ISiteSigninHandler):
else:
return False, "签到失败,无法打开网站"
def login(self, site_info: CommentedMap) -> Tuple[bool, str]:
def login(self, site_info: dict) -> Tuple[bool, str]:
"""
执行登录操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -2,7 +2,6 @@ import json
from typing import Tuple
from lxml import etree
from ruamel.yaml import CommentedMap
from app.core.config import settings
from app.log import logger
@@ -27,7 +26,7 @@ class ZhuQue(_ISiteSigninHandler):
"""
return True if StringUtils.url_equal(url, cls.site_url) else False
def signin(self, site_info: CommentedMap) -> Tuple[bool, str]:
def signin(self, site_info: dict) -> Tuple[bool, str]:
"""
执行签到操作
:param site_info: 站点信息含有站点Url、站点Cookie、UA等信息

View File

@@ -21,7 +21,7 @@ class DingdingMsg(_PluginBase):
# 插件图标
plugin_icon = "Dingding_A.png"
# 插件版本
plugin_version = "1.12"
plugin_version = "1.13"
# 插件作者
plugin_author = "nnlegenda"
# 作者主页
@@ -209,6 +209,8 @@ class DingdingMsg(_PluginBase):
if text:
# 对text进行Markdown特殊字符转义
text = re.sub(r"([_`])", r"\\\1", text)
# 钉钉中需要在换行前有两个空格,才能够正常换行
text = re.sub(r"\n", r" \n", text)
else:
text = ""