mirror of
https://github.com/d0zingcat/MoviePilot-Plugins.git
synced 2026-05-13 15:09:12 +00:00
update(ClashRuleProvider): 支持配置 Hosts
This commit is contained in:
@@ -461,11 +461,12 @@
|
||||
"name": "Clash Rule Provider",
|
||||
"description": "随时为Clash添加一些额外的规则。",
|
||||
"labels": "工具",
|
||||
"version": "1.2.8",
|
||||
"version": "1.3.1",
|
||||
"icon": "Mihomo_Meta_A.png",
|
||||
"author": "wumode",
|
||||
"level": 1,
|
||||
"history": {
|
||||
"v1.3.1": "支持配置 Hosts",
|
||||
"v1.2.8": "改进导入界面",
|
||||
"v1.2.7": "修复分享链接解析错误",
|
||||
"v1.2.6": "修复代理组修改丢失问题",
|
||||
|
||||
@@ -4,7 +4,6 @@ import urllib
|
||||
from typing import Any, Optional, List, Dict, Tuple, Union
|
||||
import time
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import yaml
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta
|
||||
@@ -14,7 +13,6 @@ import math
|
||||
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
import httpx
|
||||
import asyncio
|
||||
from fastapi import HTTPException, Request, status, Body, Response
|
||||
import websockets
|
||||
@@ -23,9 +21,10 @@ from sse_starlette.sse import EventSourceResponse
|
||||
from app import schemas
|
||||
from app.core.config import settings
|
||||
from app.log import logger
|
||||
from app.plugins import _PluginBase
|
||||
from app.schemas.types import NotificationType
|
||||
from app.utils.http import RequestUtils
|
||||
from app.utils.ip import IpUtils
|
||||
from app.utils.http import RequestUtils, AsyncRequestUtils
|
||||
from app.plugins import _PluginBase
|
||||
from app.plugins.clashruleprovider.clashruleparser import ClashRuleParser, Converter
|
||||
from app.plugins.clashruleprovider.clashruleparser import Action, RuleType, ClashRule, MatchRule, LogicRule
|
||||
from app.plugins.clashruleprovider.clashruleparser import ProxyGroup, RuleProvider
|
||||
@@ -39,7 +38,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
# 插件图标
|
||||
plugin_icon = "Mihomo_Meta_A.png"
|
||||
# 插件版本
|
||||
plugin_version = "1.2.8"
|
||||
plugin_version = "1.3.1"
|
||||
# 插件作者
|
||||
plugin_author = "wumode"
|
||||
# 作者主页
|
||||
@@ -77,6 +76,8 @@ class ClashRuleProvider(_PluginBase):
|
||||
_dashboard_components: List[str] = []
|
||||
_clash_template_yaml: str = ''
|
||||
_hint_geo_dat: bool = False
|
||||
# Cloudflare 优选 IPs 可通过外部设置
|
||||
_best_cf_ip: List[str] = []
|
||||
|
||||
# 插件数据
|
||||
_top_rules: List[str] = []
|
||||
@@ -91,6 +92,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
_acl4ssr_prefix: str = '🗂️=>'
|
||||
# 保存每个订阅文件的原始内容
|
||||
_clash_configs: Dict[str, Any] = {}
|
||||
_hosts: List[Dict[str, Any]] = []
|
||||
|
||||
# protected variables
|
||||
_clash_rule_parser = None
|
||||
@@ -111,6 +113,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
self._ruleset_names = self.get_data("ruleset_names") or {}
|
||||
self._acl4ssr_providers = self.get_data("acl4ssr_providers") or {}
|
||||
self._clash_configs = self.get_data("clash_configs") or {}
|
||||
self._hosts = self.get_data("hosts") or []
|
||||
if config:
|
||||
self._enabled = config.get("enabled")
|
||||
self._proxy = config.get("proxy")
|
||||
@@ -126,7 +129,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
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]
|
||||
self._cron = config.get("cron_string")
|
||||
self._cron = config.get("cron_string") or '30 12 * * *'
|
||||
self._timeout = config.get("timeout")
|
||||
self._retry_times = config.get("retry_times") or 3
|
||||
self._filter_keywords = config.get("filter_keywords")
|
||||
@@ -140,6 +143,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
self._dashboard_components = config.get("dashboard_components") or []
|
||||
self._clash_template_yaml = config.get("clash_template") or ''
|
||||
self._hint_geo_dat = config.get("hint_geo_dat", False)
|
||||
self._best_cf_ip = config.get("best_cf_ip") or []
|
||||
self._clash_rule_parser = ClashRuleParser()
|
||||
self._ruleset_rule_parser = ClashRuleParser()
|
||||
self._clash_template = {}
|
||||
@@ -367,6 +371,30 @@ class ClashRuleProvider(_PluginBase):
|
||||
"summary": "导入规则",
|
||||
"description": "导入规则"
|
||||
},
|
||||
{
|
||||
"path": "/hosts",
|
||||
"endpoint": self.get_hosts,
|
||||
"methods": ["GET"],
|
||||
"auth": "bear",
|
||||
"summary": "获取 Hosts",
|
||||
"description": "获取 Hosts"
|
||||
},
|
||||
{
|
||||
"path": "/host",
|
||||
"endpoint": self.update_hosts,
|
||||
"methods": ["POST"],
|
||||
"auth": "bear",
|
||||
"summary": "更新 Host",
|
||||
"description": "更新 Host"
|
||||
},
|
||||
{
|
||||
"path": "/host",
|
||||
"endpoint": self.delete_host,
|
||||
"methods": ["DELETE"],
|
||||
"auth": "bear",
|
||||
"summary": "删除一条 Host",
|
||||
"description": "删除一条 Host"
|
||||
},
|
||||
{
|
||||
"path": "/config",
|
||||
"endpoint": self.get_clash_config,
|
||||
@@ -471,6 +499,16 @@ class ClashRuleProvider(_PluginBase):
|
||||
}]
|
||||
return []
|
||||
|
||||
def update_best_cf_ip(self, ips: List[str]):
|
||||
"""
|
||||
通过深拷贝更新 Cloudflare 优选 IPs
|
||||
:param ips: Best Cloudflare IPs
|
||||
"""
|
||||
self._best_cf_ip = [*ips]
|
||||
config = self.get_config()
|
||||
config['best_cf_ip'] = self._best_cf_ip
|
||||
self.update_config(config)
|
||||
|
||||
def __save_data(self):
|
||||
self.__insert_ruleset()
|
||||
self._top_rules = self._clash_rule_parser.to_list()
|
||||
@@ -485,6 +523,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
self.save_data('extra_rule_providers', self._extra_rule_providers)
|
||||
self.save_data('acl4ssr_providers', self._acl4ssr_providers)
|
||||
self.save_data('clash_configs', self._clash_configs)
|
||||
self.save_data('hosts', self._hosts)
|
||||
|
||||
def __parse_config(self):
|
||||
if self._top_rules is None:
|
||||
@@ -505,6 +544,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
queue = asyncio.Queue()
|
||||
ws_base = self._clash_dashboard_url.replace('http://', 'ws://').replace('https://', 'wss://')
|
||||
url = f"{ws_base}/{endpoint}?token={self._clash_dashboard_secret}"
|
||||
|
||||
async def clash_ws_listener():
|
||||
try:
|
||||
async with websockets.connect(url, ping_interval=None) as ws:
|
||||
@@ -536,34 +576,32 @@ class ClashRuleProvider(_PluginBase):
|
||||
async def fetch_clash_data(self, endpoint: str) -> Dict:
|
||||
clash_headers = {"Authorization": f"Bearer {self._clash_dashboard_secret}"}
|
||||
url = f"{self._clash_dashboard_url}/{endpoint}"
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
response = await client.get(url, headers=clash_headers, timeout=5.0)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except httpx.HTTPError as e:
|
||||
raise HTTPException(status_code=502, detail=f"Failed to fetch {endpoint}: {str(e)}")
|
||||
response = await AsyncRequestUtils().get_res(url, headers=clash_headers, timeout=10)
|
||||
if response is None:
|
||||
raise HTTPException(status_code=502, detail=f"Failed to fetch {endpoint}")
|
||||
return response.json()
|
||||
|
||||
async def clash_proxy(self, path: str) -> Dict:
|
||||
return await self.fetch_clash_data(path)
|
||||
|
||||
def test_connectivity(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
async def test_connectivity(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return schemas.Response(success=False, message="")
|
||||
if not params.get('clash_dashboard_url') or not params.get('clash_dashboard_secret') \
|
||||
or not params.get('sub_link'):
|
||||
or not params.get('sub_links'):
|
||||
return schemas.Response(success=True, message="missing params")
|
||||
clash_version_url = f"{params.get('clash_dashboard_url')}/version"
|
||||
ret = RequestUtils(accept_type="application/json",
|
||||
ret = await AsyncRequestUtils(accept_type="application/json",
|
||||
headers={"authorization": f"Bearer {params.get('clash_dashboard_secret')}"}
|
||||
).get(clash_version_url)
|
||||
if not ret:
|
||||
if ret is None:
|
||||
return schemas.Response(success=False, message="无法连接到Clash")
|
||||
ret = RequestUtils(accept_type="text/html",
|
||||
proxies=settings.PROXY if self._proxy else None
|
||||
).get(params.get('sub_link'))
|
||||
if not ret:
|
||||
return schemas.Response(success=False, message=f"Unable to get {params.get('sub_link')}")
|
||||
for sub_link in (params.get('sub_links') or []):
|
||||
ret = await AsyncRequestUtils(accept_type="text/html",
|
||||
proxies=settings.PROXY if self._proxy else None
|
||||
).get(sub_link)
|
||||
if ret is None:
|
||||
return schemas.Response(success=False, message=f"Unable to fetch {sub_link}")
|
||||
return schemas.Response(success=True, message="测试连接成功")
|
||||
|
||||
def get_ruleset(self, name):
|
||||
@@ -585,6 +623,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
"data": {"state": self._enabled,
|
||||
"ruleset_prefix": self._ruleset_prefix,
|
||||
"clash": {"rule_size": rule_size},
|
||||
"best_cf_ip": self._best_cf_ip,
|
||||
"geoRules": self._geo_rules,
|
||||
"subscription_info": self._subscription_info,
|
||||
"sub_url": f"{self._movie_pilot_url}/api/v1/plugin/ClashRuleProvider/config?"
|
||||
@@ -608,6 +647,47 @@ class ClashRuleProvider(_PluginBase):
|
||||
f'expire={sub_info.get("expire", 0)}'}
|
||||
return Response(headers=headers, content=res, media_type="text/yaml")
|
||||
|
||||
def get_hosts(self) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
schemas.Response(success=True, message='', data={'hosts': []})
|
||||
return schemas.Response(success=True, message='', data={'hosts': self._hosts})
|
||||
|
||||
def update_hosts(self, params: dict = Body(...)) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return schemas.Response(success=False, message='')
|
||||
domain = params.get('domain')
|
||||
if not domain:
|
||||
return schemas.Response(success=False, message=f"Invalid param: domain={domain}")
|
||||
# Search for the host with the same domain
|
||||
for i, host in enumerate(self._hosts):
|
||||
if host['domain'] == domain:
|
||||
# Update the existing host
|
||||
self._hosts[i] = {**host, **params.get('value', {})}
|
||||
self.save_data('hosts', self._hosts)
|
||||
return schemas.Response(success=True, message=f'Host for domain {domain} updated successfully.')
|
||||
|
||||
self._hosts.append(params.get('value', {}))
|
||||
self.save_data('hosts', self._hosts)
|
||||
|
||||
return schemas.Response(success=True, message=f"New host for domain {domain} added successfully.")
|
||||
|
||||
def delete_host(self, params: dict = Body(...)) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return schemas.Response(success=False, message='Host deletion is disabled.')
|
||||
|
||||
domain = params.get('domain')
|
||||
if not domain:
|
||||
return schemas.Response(success=False, message=f"Invalid param: domain={domain}")
|
||||
|
||||
original_hosts_length = len(self._hosts)
|
||||
self._hosts = [host for host in self._hosts if host.get('domain') != domain]
|
||||
self.save_data('hosts', self._hosts)
|
||||
|
||||
if len(self._hosts) < original_hosts_length:
|
||||
return schemas.Response(success=True, message=f'Host for domain {domain} deleted successfully.')
|
||||
else:
|
||||
return schemas.Response(success=False, message=f'Host for domain {domain} not found.')
|
||||
|
||||
def get_rules(self, rule_type: str) -> schemas.Response:
|
||||
if rule_type == 'ruleset':
|
||||
return schemas.Response(success=True, message='', data={'rules': self._ruleset_rule_parser.to_dict()})
|
||||
@@ -664,9 +744,9 @@ class ClashRuleProvider(_PluginBase):
|
||||
self.__update_rules(params.get('rules'), self._clash_rule_parser)
|
||||
return schemas.Response(success=True)
|
||||
|
||||
def update_rule(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def update_rule(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
return {"success": False, "message": ""}
|
||||
return schemas.Response(success=False, message='')
|
||||
if params.get('type') == 'ruleset':
|
||||
original_rule = self._ruleset_rule_parser.get_rule_at_priority(params.get('priority'))
|
||||
res = self.update_rule_by_priority(params.get('rule_data'),
|
||||
@@ -679,7 +759,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
self.__add_notification_job(ruleset_to_notify)
|
||||
else:
|
||||
res = self.update_rule_by_priority(params.get('rule_data'), params.get('priority'), self._clash_rule_parser)
|
||||
return {"success": bool(res), "message": None}
|
||||
return schemas.Response(success=bool(res), message='')
|
||||
|
||||
def add_rule(self, params: Dict[str, Any]) -> schemas.Response:
|
||||
if not self._enabled:
|
||||
@@ -697,7 +777,7 @@ class ClashRuleProvider(_PluginBase):
|
||||
return schemas.Response(success=False, message="")
|
||||
url = params.get('url')
|
||||
if not url:
|
||||
return schemas.Response(success=False, message="missing params")
|
||||
return schemas.Response(success=False, message="Missing params")
|
||||
config, info = self.__get_subscription(url)
|
||||
if not config:
|
||||
return schemas.Response(success=False, message=f"订阅链接 {url} 更新失败")
|
||||
@@ -1051,12 +1131,14 @@ class ClashRuleProvider(_PluginBase):
|
||||
def refresh_subscription_service(self):
|
||||
res = self.refresh_subscriptions()
|
||||
messages = []
|
||||
index = 1
|
||||
for url, result in res.items():
|
||||
try:
|
||||
host_name = urlparse(url).hostname
|
||||
except ValueError:
|
||||
host_name = url
|
||||
message = f"1. 「 {host_name} 」\n"
|
||||
message = f"{index}. 「 {host_name} 」\n"
|
||||
index += 1
|
||||
if result:
|
||||
sub_info = self._subscription_info.get(url, {})
|
||||
if sub_info.get('total') is not None:
|
||||
@@ -1389,6 +1471,16 @@ class ClashRuleProvider(_PluginBase):
|
||||
continue
|
||||
top_rules.append(rule.raw_rule)
|
||||
clash_config["rules"] = top_rules
|
||||
|
||||
# 添加 Hosts
|
||||
if self._hosts:
|
||||
clash_config.setdefault('hosts', {})
|
||||
new_hosts = {
|
||||
item['domain']: item.get('value', []) if not item.get('using_cloudflare') else self._best_cf_ip
|
||||
for item in self._hosts if item.get('domain')
|
||||
}
|
||||
clash_config["hosts"] = {**clash_config["hosts"], **new_hosts}
|
||||
|
||||
if self._rule_provider:
|
||||
clash_config['rule-providers'] = clash_config.get('rule-providers') or {}
|
||||
clash_config['rule-providers'].update(self._rule_provider)
|
||||
@@ -1404,3 +1496,13 @@ class ClashRuleProvider(_PluginBase):
|
||||
self.save_data('ruleset_names', self._ruleset_names)
|
||||
self.save_data('rule_provider', self._rule_provider)
|
||||
return clash_config
|
||||
|
||||
@property
|
||||
def best_cf_ipv4(self) -> List[str]:
|
||||
v4 = [ip for ip in self._best_cf_ip if IpUtils.is_ipv4(ip)]
|
||||
return v4
|
||||
|
||||
@property
|
||||
def best_cf_ipv6(self) -> List[str]:
|
||||
v6 = [ip for ip in self._best_cf_ip if IpUtils.is_ipv6(ip)]
|
||||
return v6
|
||||
|
||||
4
plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BrXQaadr.css
vendored
Normal file
4
plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BrXQaadr.css
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
.plugin-config[data-v-929102b8] {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
.plugin-config[data-v-c67dff26] {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -89,6 +89,7 @@ const defaultConfig = {
|
||||
dashboard_components: [],
|
||||
clash_template: '',
|
||||
hint_geo_dat: false,
|
||||
best_cf_ip: []
|
||||
};
|
||||
|
||||
// 响应式配置对象
|
||||
@@ -128,6 +129,25 @@ function validateSubLinks(links) {
|
||||
return true
|
||||
}
|
||||
|
||||
function isValidIP(ip) {
|
||||
// IPv4 正则:四段数字(0–255),用点隔开
|
||||
const ipv4Regex = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
|
||||
|
||||
// IPv6 正则:八组 1–4 位 16 进制数,用冒号隔开,支持简写 ::(不严格支持所有极端情况,但能覆盖大多数合法 IPv6)
|
||||
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(([0-9a-fA-F]{1,4}:){1,7}|:):([0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4})$/;
|
||||
|
||||
return ipv4Regex.test(ip) || ipv6Regex.test(ip);
|
||||
}
|
||||
|
||||
function validateIPs(ips) {
|
||||
for (const ip of ips) {
|
||||
if (!isValidIP(ip)) {
|
||||
return `无效的 IP 地址: ${ip}`
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
async function testConnection() {
|
||||
testing.value = true;
|
||||
@@ -152,7 +172,7 @@ async function testConnection() {
|
||||
const testParams = {
|
||||
clash_dashboard_url: config.clash_dashboard_url,
|
||||
clash_dashboard_secret: config.clash_dashboard_secret,
|
||||
sub_link: config.sub_links[0] // 使用第一个订阅链接进行测试
|
||||
sub_links: config.sub_links // 使用第一个订阅链接进行测试
|
||||
};
|
||||
|
||||
// 调用API进行连接测试
|
||||
@@ -284,7 +304,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { left: "" }, {
|
||||
default: _withCtx(() => _cache[28] || (_cache[28] = [
|
||||
default: _withCtx(() => _cache[29] || (_cache[29] = [
|
||||
_createTextVNode("mdi-close")
|
||||
])),
|
||||
_: 1
|
||||
@@ -295,7 +315,7 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_card_title, null, {
|
||||
default: _withCtx(() => _cache[27] || (_cache[27] = [
|
||||
default: _withCtx(() => _cache[28] || (_cache[28] = [
|
||||
_createTextVNode("Clash Rule Provider 插件配置")
|
||||
])),
|
||||
_: 1
|
||||
@@ -321,11 +341,11 @@ return (_ctx, _cache) => {
|
||||
ref_key: "form",
|
||||
ref: form,
|
||||
modelValue: isFormValid.value,
|
||||
"onUpdate:modelValue": _cache[21] || (_cache[21] = $event => ((isFormValid).value = $event)),
|
||||
"onUpdate:modelValue": _cache[22] || (_cache[22] = $event => ((isFormValid).value = $event)),
|
||||
onSubmit: _withModifiers(saveConfig, ["prevent"])
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_cache[42] || (_cache[42] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "基本设置", -1)),
|
||||
_cache[43] || (_cache[43] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mb-2" }, "基本设置", -1)),
|
||||
_createVNode(_component_v_row, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, {
|
||||
@@ -399,7 +419,7 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[43] || (_cache[43] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "订阅配置", -1)),
|
||||
_cache[44] || (_cache[44] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "订阅配置", -1)),
|
||||
_createVNode(_component_v_row, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, { cols: "12" }, {
|
||||
@@ -441,6 +461,7 @@ return (_ctx, _cache) => {
|
||||
multiple: "",
|
||||
chips: "",
|
||||
"closable-chips": "",
|
||||
clearable: "",
|
||||
hint: "添加用于过滤节点的关键词"
|
||||
}, {
|
||||
chip: _withCtx(({ props, item }) => [
|
||||
@@ -463,7 +484,7 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[44] || (_cache[44] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "Clash 面板设置", -1)),
|
||||
_cache[45] || (_cache[45] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "Clash 面板设置", -1)),
|
||||
_createVNode(_component_v_row, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, { cols: "12" }, {
|
||||
@@ -479,7 +500,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "primary" }, {
|
||||
default: _withCtx(() => _cache[29] || (_cache[29] = [
|
||||
default: _withCtx(() => _cache[30] || (_cache[30] = [
|
||||
_createTextVNode("mdi-web")
|
||||
])),
|
||||
_: 1
|
||||
@@ -508,7 +529,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "warning" }, {
|
||||
default: _withCtx(() => _cache[30] || (_cache[30] = [
|
||||
default: _withCtx(() => _cache[31] || (_cache[31] = [
|
||||
_createTextVNode("mdi-key")
|
||||
])),
|
||||
_: 1
|
||||
@@ -538,7 +559,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[31] || (_cache[31] = [
|
||||
default: _withCtx(() => _cache[32] || (_cache[32] = [
|
||||
_createTextVNode("mdi-view-dashboard")
|
||||
])),
|
||||
_: 1
|
||||
@@ -552,7 +573,7 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[45] || (_cache[45] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "MoviePilot 设置", -1)),
|
||||
_cache[46] || (_cache[46] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "MoviePilot 设置", -1)),
|
||||
_createVNode(_component_v_row, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, { cols: "12" }, {
|
||||
@@ -568,7 +589,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "success" }, {
|
||||
default: _withCtx(() => _cache[32] || (_cache[32] = [
|
||||
default: _withCtx(() => _cache[33] || (_cache[33] = [
|
||||
_createTextVNode("mdi-movie")
|
||||
])),
|
||||
_: 1
|
||||
@@ -582,10 +603,13 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_cache[46] || (_cache[46] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "执行设置", -1)),
|
||||
_cache[47] || (_cache[47] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "执行设置", -1)),
|
||||
_createVNode(_component_v_row, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, { cols: "12" }, {
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "4"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_cron_field, {
|
||||
modelValue: config.cron_string,
|
||||
@@ -596,7 +620,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[33] || (_cache[33] = [
|
||||
default: _withCtx(() => _cache[34] || (_cache[34] = [
|
||||
_createTextVNode("mdi-clock-time-four-outline")
|
||||
])),
|
||||
_: 1
|
||||
@@ -609,7 +633,7 @@ return (_ctx, _cache) => {
|
||||
}),
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "6"
|
||||
md: "4"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_text_field, {
|
||||
@@ -630,7 +654,7 @@ return (_ctx, _cache) => {
|
||||
}),
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "6"
|
||||
md: "4"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_text_field, {
|
||||
@@ -647,7 +671,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[34] || (_cache[34] = [
|
||||
default: _withCtx(() => _cache[35] || (_cache[35] = [
|
||||
_createTextVNode("mdi-refresh")
|
||||
])),
|
||||
_: 1
|
||||
@@ -671,12 +695,12 @@ return (_ctx, _cache) => {
|
||||
_createVNode(_component_v_expansion_panel_title, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { class: "mr-2" }, {
|
||||
default: _withCtx(() => _cache[35] || (_cache[35] = [
|
||||
default: _withCtx(() => _cache[36] || (_cache[36] = [
|
||||
_createTextVNode("mdi-cog")
|
||||
])),
|
||||
_: 1
|
||||
}),
|
||||
_cache[36] || (_cache[36] = _createTextVNode(" 高级选项 "))
|
||||
_cache[37] || (_cache[37] = _createTextVNode(" 高级选项 "))
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
@@ -769,7 +793,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[37] || (_cache[37] = [
|
||||
default: _withCtx(() => _cache[38] || (_cache[38] = [
|
||||
_createTextVNode("mdi-palette")
|
||||
])),
|
||||
_: 1
|
||||
@@ -796,7 +820,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "primary" }, {
|
||||
default: _withCtx(() => _cache[38] || (_cache[38] = [
|
||||
default: _withCtx(() => _cache[39] || (_cache[39] = [
|
||||
_createTextVNode("mdi-palette")
|
||||
])),
|
||||
_: 1
|
||||
@@ -827,7 +851,7 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
"prepend-inner": _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { color: "info" }, {
|
||||
default: _withCtx(() => _cache[39] || (_cache[39] = [
|
||||
default: _withCtx(() => _cache[40] || (_cache[40] = [
|
||||
_createTextVNode("mdi-clock-outline")
|
||||
])),
|
||||
_: 1
|
||||
@@ -851,12 +875,12 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { left: "" }, {
|
||||
default: _withCtx(() => _cache[40] || (_cache[40] = [
|
||||
default: _withCtx(() => _cache[41] || (_cache[41] = [
|
||||
_createTextVNode("mdi-import")
|
||||
])),
|
||||
_: 1
|
||||
}),
|
||||
_cache[41] || (_cache[41] = _createTextVNode(" Clash 配置模板 "))
|
||||
_cache[42] || (_cache[42] = _createTextVNode(" Clash 配置模板 "))
|
||||
]),
|
||||
_: 1
|
||||
})
|
||||
@@ -865,6 +889,44 @@ return (_ctx, _cache) => {
|
||||
})
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
_createVNode(_component_v_row, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_col, {
|
||||
cols: "12",
|
||||
md: "12"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_combobox, {
|
||||
modelValue: config.best_cf_ip,
|
||||
"onUpdate:modelValue": _cache[21] || (_cache[21] = $event => ((config.best_cf_ip) = $event)),
|
||||
label: "Cloudflare CDN 优选 IPs",
|
||||
variant: "outlined",
|
||||
multiple: "",
|
||||
chips: "",
|
||||
"closable-chips": "",
|
||||
clearable: "",
|
||||
hint: "用于设置 Hosts 中的 Cloudflare 域名",
|
||||
rules: [validateIPs]
|
||||
}, {
|
||||
chip: _withCtx(({ props, item }) => [
|
||||
_createVNode(_component_v_chip, _mergeProps(props, {
|
||||
closable: "",
|
||||
size: "small"
|
||||
}), {
|
||||
default: _withCtx(() => [
|
||||
_createTextVNode(_toDisplayString(item.value), 1)
|
||||
]),
|
||||
_: 2
|
||||
}, 1040)
|
||||
]),
|
||||
_: 1
|
||||
}, 8, ["modelValue", "rules"])
|
||||
]),
|
||||
_: 1
|
||||
})
|
||||
]),
|
||||
_: 1
|
||||
})
|
||||
]),
|
||||
_: 1
|
||||
@@ -884,10 +946,9 @@ return (_ctx, _cache) => {
|
||||
_createVNode(_component_v_alert, {
|
||||
type: "info",
|
||||
text: "",
|
||||
class: "mb-6",
|
||||
variant: "tonal"
|
||||
}, {
|
||||
default: _withCtx(() => _cache[47] || (_cache[47] = [
|
||||
default: _withCtx(() => _cache[48] || (_cache[48] = [
|
||||
_createTextVNode(" 配置说明参考: "),
|
||||
_createElementVNode("a", {
|
||||
href: "https://github.com/wumode/MoviePilot-Plugins/tree/main/plugins.v2/clashruleprovider/README.md",
|
||||
@@ -904,12 +965,12 @@ return (_ctx, _cache) => {
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_icon, { left: "" }, {
|
||||
default: _withCtx(() => _cache[48] || (_cache[48] = [
|
||||
default: _withCtx(() => _cache[49] || (_cache[49] = [
|
||||
_createTextVNode("mdi-view-dashboard-edit")
|
||||
])),
|
||||
_: 1
|
||||
}),
|
||||
_cache[49] || (_cache[49] = _createTextVNode(" 规则 "))
|
||||
_cache[50] || (_cache[50] = _createTextVNode(" 规则 "))
|
||||
]),
|
||||
_: 1
|
||||
}),
|
||||
@@ -917,7 +978,7 @@ return (_ctx, _cache) => {
|
||||
color: "secondary",
|
||||
onClick: resetForm
|
||||
}, {
|
||||
default: _withCtx(() => _cache[50] || (_cache[50] = [
|
||||
default: _withCtx(() => _cache[51] || (_cache[51] = [
|
||||
_createTextVNode("重置")
|
||||
])),
|
||||
_: 1
|
||||
@@ -927,7 +988,7 @@ return (_ctx, _cache) => {
|
||||
onClick: testConnection,
|
||||
loading: testing.value
|
||||
}, {
|
||||
default: _withCtx(() => _cache[51] || (_cache[51] = [
|
||||
default: _withCtx(() => _cache[52] || (_cache[52] = [
|
||||
_createTextVNode("测试连接")
|
||||
])),
|
||||
_: 1
|
||||
@@ -939,7 +1000,7 @@ return (_ctx, _cache) => {
|
||||
onClick: saveConfig,
|
||||
loading: saving.value
|
||||
}, {
|
||||
default: _withCtx(() => _cache[52] || (_cache[52] = [
|
||||
default: _withCtx(() => _cache[53] || (_cache[53] = [
|
||||
_createTextVNode(" 保存配置 ")
|
||||
])),
|
||||
_: 1
|
||||
@@ -954,7 +1015,7 @@ return (_ctx, _cache) => {
|
||||
variant: "tonal",
|
||||
closable: "",
|
||||
class: "ma-4 mt-0",
|
||||
"onClick:close": _cache[22] || (_cache[22] = $event => (testResult.show = false))
|
||||
"onClick:close": _cache[23] || (_cache[23] = $event => (testResult.show = false))
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createElementVNode("div", _hoisted_2, [
|
||||
@@ -979,14 +1040,14 @@ return (_ctx, _cache) => {
|
||||
]),
|
||||
_createVNode(_component_v_dialog, {
|
||||
modelValue: clashTemplateDialog.value,
|
||||
"onUpdate:modelValue": _cache[26] || (_cache[26] = $event => ((clashTemplateDialog).value = $event)),
|
||||
"onUpdate:modelValue": _cache[27] || (_cache[27] = $event => ((clashTemplateDialog).value = $event)),
|
||||
"max-width": "600"
|
||||
}, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_card, null, {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_card_title, null, {
|
||||
default: _withCtx(() => _cache[53] || (_cache[53] = [
|
||||
default: _withCtx(() => _cache[54] || (_cache[54] = [
|
||||
_createTextVNode("Clash 配置模板")
|
||||
])),
|
||||
_: 1
|
||||
@@ -995,14 +1056,14 @@ return (_ctx, _cache) => {
|
||||
default: _withCtx(() => [
|
||||
_createVNode(_component_v_select, {
|
||||
modelValue: clashTemplateType.value,
|
||||
"onUpdate:modelValue": _cache[23] || (_cache[23] = $event => ((clashTemplateType).value = $event)),
|
||||
"onUpdate:modelValue": _cache[24] || (_cache[24] = $event => ((clashTemplateType).value = $event)),
|
||||
items: ['YAML'],
|
||||
label: "配置类型",
|
||||
class: "mb-4"
|
||||
}, null, 8, ["modelValue"]),
|
||||
_createVNode(_unref(VAceEditor), {
|
||||
value: clashTemplateContent.value,
|
||||
"onUpdate:value": _cache[24] || (_cache[24] = $event => ((clashTemplateContent).value = $event)),
|
||||
"onUpdate:value": _cache[25] || (_cache[25] = $event => ((clashTemplateContent).value = $event)),
|
||||
lang: "yaml",
|
||||
theme: "monokai",
|
||||
hint: "",
|
||||
@@ -1017,7 +1078,7 @@ return (_ctx, _cache) => {
|
||||
class: "mb-4",
|
||||
variant: "tonal"
|
||||
}, {
|
||||
default: _withCtx(() => _cache[54] || (_cache[54] = [
|
||||
default: _withCtx(() => _cache[55] || (_cache[55] = [
|
||||
_createTextVNode("规则和出站代理会被添加在配置模板上 ")
|
||||
])),
|
||||
_: 1
|
||||
@@ -1030,9 +1091,9 @@ return (_ctx, _cache) => {
|
||||
_createVNode(_component_v_spacer),
|
||||
_createVNode(_component_v_btn, {
|
||||
text: "",
|
||||
onClick: _cache[25] || (_cache[25] = $event => (clashTemplateDialog.value = false))
|
||||
onClick: _cache[26] || (_cache[26] = $event => (clashTemplateDialog.value = false))
|
||||
}, {
|
||||
default: _withCtx(() => _cache[55] || (_cache[55] = [
|
||||
default: _withCtx(() => _cache[56] || (_cache[56] = [
|
||||
_createTextVNode("取消")
|
||||
])),
|
||||
_: 1
|
||||
@@ -1041,7 +1102,7 @@ return (_ctx, _cache) => {
|
||||
color: "primary",
|
||||
onClick: saveClashTemplate
|
||||
}, {
|
||||
default: _withCtx(() => _cache[56] || (_cache[56] = [
|
||||
default: _withCtx(() => _cache[57] || (_cache[57] = [
|
||||
_createTextVNode("确定")
|
||||
])),
|
||||
_: 1
|
||||
@@ -1060,6 +1121,6 @@ return (_ctx, _cache) => {
|
||||
}
|
||||
|
||||
};
|
||||
const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-c67dff26"]]);
|
||||
const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-929102b8"]]);
|
||||
|
||||
export { ConfigComponent as default };
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
.plugin-page[data-v-c476bdad] {
|
||||
.plugin-page[data-v-d6db167c] {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 使卡片等宽并适应移动端 */
|
||||
.d-flex.flex-wrap[data-v-c476bdad] {
|
||||
.d-flex.flex-wrap[data-v-d6db167c] {
|
||||
gap: 16px;
|
||||
}
|
||||
.url-display[data-v-c476bdad] {
|
||||
.url-display[data-v-d6db167c] {
|
||||
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-c476bdad] {
|
||||
.d-flex.flex-wrap[data-v-d6db167c] {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add visual distinction between sections */
|
||||
.ruleset-section[data-v-c476bdad] {
|
||||
.ruleset-section[data-v-d6db167c] {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.top-section[data-v-c476bdad] {
|
||||
.top-section[data-v-d6db167c] {
|
||||
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-c476bdad] {
|
||||
.ruleset-section[data-v-d6db167c] {
|
||||
border-left: 4px solid #2196F3; /* Blue accent */
|
||||
}
|
||||
.top-section[data-v-c476bdad] {
|
||||
.top-section[data-v-d6db167c] {
|
||||
border-left: 4px solid #4CAF50; /* Green accent */
|
||||
}
|
||||
.drag-handle[data-v-c476bdad] {
|
||||
.drag-handle[data-v-d6db167c] {
|
||||
cursor: move;
|
||||
}
|
||||
.gap-2[data-v-c476bdad] {
|
||||
.gap-2[data-v-d6db167c] {
|
||||
gap: 8px;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,11 @@ const currentImports = {};
|
||||
const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']);
|
||||
let moduleMap = {
|
||||
"./Page":()=>{
|
||||
dynamicLoadingCss(["__federation_expose_Page-BYQOdJxf.css"], false, './Page');
|
||||
return __federation_import('./__federation_expose_Page-D07z4AMB.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
|
||||
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)},
|
||||
"./Config":()=>{
|
||||
dynamicLoadingCss(["__federation_expose_Config-CibZbOMJ.css"], false, './Config');
|
||||
return __federation_import('./__federation_expose_Config-D6fcnbk5.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},
|
||||
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)},
|
||||
"./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)},};
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
websockets
|
||||
httpx~=0.28.1
|
||||
sse_starlette~=2.3.6
|
||||
Reference in New Issue
Block a user