mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-12 15:20:44 +08:00
为资源搜索功能新增了 PanSou 服务器支持,优化了搜索结果的展示与排序方式,调整了系统配置页面的模块顺序
This commit is contained in:
parent
a27c76637b
commit
7bf5e7423a
108
app/run.py
108
app/run.py
@ -16,6 +16,10 @@ from flask import (
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
from sdk.cloudsaver import CloudSaver
|
||||
try:
|
||||
from sdk.pansou import PanSou
|
||||
except Exception:
|
||||
PanSou = None
|
||||
from datetime import timedelta, datetime
|
||||
import subprocess
|
||||
import requests
|
||||
@ -511,6 +515,14 @@ def get_data():
|
||||
}
|
||||
}
|
||||
|
||||
# 初始化搜索来源默认结构
|
||||
if "source" not in data or not isinstance(data.get("source"), dict):
|
||||
data["source"] = {}
|
||||
# CloudSaver 默认字段
|
||||
data["source"].setdefault("cloudsaver", {"server": "", "username": "", "password": "", "token": ""})
|
||||
# PanSou 默认字段
|
||||
data["source"].setdefault("pansou", {"server": "https://so.252035.xyz"})
|
||||
|
||||
# 发送webui信息,但不发送密码原文
|
||||
data["webui"] = {
|
||||
"username": config_data["webui"]["username"],
|
||||
@ -929,7 +941,14 @@ def get_task_suggestions():
|
||||
search_query = extract_show_name(query)
|
||||
|
||||
try:
|
||||
cs_data = config_data.get("source", {}).get("cloudsaver", {})
|
||||
sources_cfg = config_data.get("source", {}) or {}
|
||||
cs_data = sources_cfg.get("cloudsaver", {})
|
||||
ps_data = sources_cfg.get("pansou", {})
|
||||
|
||||
merged = []
|
||||
providers = []
|
||||
|
||||
# CloudSaver
|
||||
if (
|
||||
cs_data.get("server")
|
||||
and cs_data.get("username")
|
||||
@ -941,35 +960,76 @@ def get_task_suggestions():
|
||||
cs_data.get("password", ""),
|
||||
cs_data.get("token", ""),
|
||||
)
|
||||
# 使用处理后的搜索关键词
|
||||
search = cs.auto_login_search(search_query)
|
||||
if search.get("success"):
|
||||
if search.get("new_token"):
|
||||
cs_data["token"] = search.get("new_token")
|
||||
Config.write_json(CONFIG_PATH, config_data)
|
||||
search_results = cs.clean_search_results(search.get("data"))
|
||||
# 在返回结果中添加实际使用的搜索关键词
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"source": "CloudSaver",
|
||||
"data": search_results
|
||||
}
|
||||
)
|
||||
else:
|
||||
return jsonify({"success": True, "message": search.get("message")})
|
||||
else:
|
||||
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
|
||||
# 使用处理后的搜索关键词
|
||||
url = f"{base_url}/task_suggestions?q={search_query}&d={deep}"
|
||||
response = requests.get(url)
|
||||
return jsonify(
|
||||
{
|
||||
"success": True,
|
||||
"source": "网络公开",
|
||||
"data": response.json()
|
||||
}
|
||||
)
|
||||
if isinstance(search_results, list):
|
||||
merged.extend(search_results)
|
||||
providers.append("CloudSaver")
|
||||
|
||||
# PanSou
|
||||
if ps_data and ps_data.get("server") and PanSou is not None:
|
||||
try:
|
||||
ps = PanSou(ps_data.get("server"))
|
||||
result = ps.search(search_query)
|
||||
if result.get("success") and isinstance(result.get("data"), list):
|
||||
merged.extend(result.get("data"))
|
||||
providers.append("PanSou")
|
||||
except Exception as e:
|
||||
logging.warning(f"PanSou 搜索失败: {str(e)}")
|
||||
|
||||
# 去重(按shareurl优先,其次taskname)
|
||||
dedup = []
|
||||
seen = set()
|
||||
for item in merged:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
key = item.get("shareurl") or item.get("taskname")
|
||||
if not key:
|
||||
continue
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
dedup.append(item)
|
||||
|
||||
# 全局时间排序:所有来源的结果混合排序,按时间倒序(最新的在前)
|
||||
if dedup:
|
||||
def parse_datetime_for_sort(item):
|
||||
"""解析时间字段,返回可比较的时间戳"""
|
||||
# 兼容两个字段名:publish_date 和 datetime
|
||||
datetime_str = item.get("publish_date") or item.get("datetime")
|
||||
if not datetime_str:
|
||||
return 0 # 没有时间的排在最后
|
||||
try:
|
||||
from datetime import datetime
|
||||
# 尝试解析格式: 2025-01-01 12:00:00
|
||||
dt = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
|
||||
return dt.timestamp()
|
||||
except:
|
||||
return 0 # 解析失败排在最后
|
||||
|
||||
# 按时间倒序排序(最新的在前)
|
||||
dedup.sort(key=parse_datetime_for_sort, reverse=True)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"source": ", ".join(providers) if providers else "聚合",
|
||||
"data": dedup
|
||||
})
|
||||
|
||||
# 若无本地可用来源,回退到公开网络
|
||||
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
|
||||
url = f"{base_url}/task_suggestions?q={search_query}&d={deep}"
|
||||
response = requests.get(url)
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"source": "网络公开",
|
||||
"data": response.json()
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"success": True, "message": f"error: {str(e)}"})
|
||||
|
||||
|
||||
@ -124,6 +124,10 @@ class CloudSaver:
|
||||
content = content.replace('<mark class="highlight">', "")
|
||||
content = content.replace("</mark>", "")
|
||||
content = content.strip()
|
||||
# 获取发布时间 - 采用与原始实现一致的方式
|
||||
pubdate = item.get("pubDate", "") # 使用 pubDate 字段
|
||||
if pubdate:
|
||||
pubdate = self._iso_to_cst(pubdate) # 转换为中国标准时间
|
||||
# 链接去重
|
||||
if link.get("link") not in link_array:
|
||||
link_array.append(link.get("link"))
|
||||
@ -132,12 +136,33 @@ class CloudSaver:
|
||||
"shareurl": link.get("link"),
|
||||
"taskname": title,
|
||||
"content": content,
|
||||
"datetime": pubdate, # 使用 datetime 字段名,与原始实现一致
|
||||
"tags": item.get("tags", []),
|
||||
"channel": item.get("channel", ""),
|
||||
"channel_id": item.get("channelId", ""),
|
||||
"channel": item.get("channelId", ""),
|
||||
"source": "CloudSaver"
|
||||
}
|
||||
)
|
||||
|
||||
# 注意:排序逻辑已移至全局,这里不再进行内部排序
|
||||
# 返回原始顺序的结果,由全局排序函数统一处理
|
||||
return clean_results
|
||||
|
||||
def _iso_to_cst(self, iso_time_str: str) -> str:
|
||||
"""将 ISO 格式的时间字符串转换为 CST(China Standard Time) 时间并格式化为 %Y-%m-%d %H:%M:%S 格式
|
||||
|
||||
Args:
|
||||
iso_time_str (str): ISO 格式时间字符串
|
||||
|
||||
Returns:
|
||||
str: CST(China Standard Time) 时间字符串
|
||||
"""
|
||||
try:
|
||||
from datetime import datetime, timezone, timedelta
|
||||
dt = datetime.fromisoformat(iso_time_str)
|
||||
dt_cst = dt.astimezone(timezone(timedelta(hours=8)))
|
||||
return dt_cst.strftime("%Y-%m-%d %H:%M:%S") if dt_cst.year >= 1970 else ""
|
||||
except:
|
||||
return iso_time_str # 转换失败时返回原始字符串
|
||||
|
||||
|
||||
# 测试示例
|
||||
|
||||
188
app/sdk/pansou.py
Normal file
188
app/sdk/pansou.py
Normal file
@ -0,0 +1,188 @@
|
||||
import requests
|
||||
import json
|
||||
from typing import List, Dict, Any
|
||||
|
||||
|
||||
class PanSou:
|
||||
"""PanSou 资源搜索客户端"""
|
||||
|
||||
def __init__(self, server: str):
|
||||
self.server = server.rstrip("/") if server else ""
|
||||
self.session = requests.Session()
|
||||
# 使用标准请求头
|
||||
self.session.headers.update({
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"User-Agent": "QASX-PanSouClient/1.0"
|
||||
})
|
||||
|
||||
def _request_json(self, url: str, params: dict):
|
||||
"""发送 GET 请求并解析 JSON 响应"""
|
||||
try:
|
||||
resp = self.session.get(url, params=params, timeout=15)
|
||||
return resp.json()
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def search(self, keyword: str):
|
||||
"""
|
||||
搜索资源(仅返回夸克网盘结果)
|
||||
返回:{"success": True, "data": [{taskname, content, shareurl, tags[]}]}
|
||||
"""
|
||||
if not self.server:
|
||||
return {"success": False, "message": "PanSou未配置服务器"}
|
||||
|
||||
# 使用已验证的参数:kw + cloud_types=quark + res=all
|
||||
params = {
|
||||
"kw": keyword,
|
||||
"cloud_types": "quark", # 单个类型用字符串,多个类型用逗号分隔
|
||||
"res": "all"
|
||||
}
|
||||
|
||||
# 优先使用 /api/search 路径
|
||||
url = f"{self.server}/api/search"
|
||||
result = self._request_json(url, params)
|
||||
|
||||
if not result:
|
||||
return {"success": False, "message": "PanSou请求失败"}
|
||||
|
||||
# 解析响应:兼容 {code, message, data: {results, merged_by_type}} 格式
|
||||
payload = result
|
||||
if isinstance(result.get("data"), dict):
|
||||
payload = result["data"]
|
||||
|
||||
# 检查错误码
|
||||
if "code" in result and result.get("code") != 0:
|
||||
return {"success": False, "message": result.get("message") or "PanSou搜索失败"}
|
||||
|
||||
# 解析结果:优先 results,然后 merged_by_type
|
||||
cleaned = []
|
||||
|
||||
try:
|
||||
# 1) results: 主要结果数组,每个结果包含 title 和 links
|
||||
results = payload.get("results", [])
|
||||
if isinstance(results, list):
|
||||
for result_item in results:
|
||||
if not isinstance(result_item, dict):
|
||||
continue
|
||||
|
||||
# 从 result_item 获取标题、内容和发布日期
|
||||
title = result_item.get("title", "")
|
||||
content = result_item.get("content", "")
|
||||
datetime_str = result_item.get("datetime", "") # 获取发布日期
|
||||
|
||||
# 从 links 获取具体链接
|
||||
links = result_item.get("links", [])
|
||||
if isinstance(links, list):
|
||||
for link in links:
|
||||
if isinstance(link, dict):
|
||||
url = link.get("url", "")
|
||||
link_type = link.get("type", "")
|
||||
if url: # 确保有有效链接
|
||||
cleaned.append({
|
||||
"taskname": title,
|
||||
"content": content,
|
||||
"shareurl": url,
|
||||
"tags": [link_type] if link_type else (result_item.get("tags", []) or []),
|
||||
"publish_date": datetime_str, # 添加发布日期字段
|
||||
"source": "PanSou" # 添加来源标识
|
||||
})
|
||||
|
||||
# 2) merged_by_type: 兜底解析,使用 note 字段作为标题
|
||||
if not cleaned:
|
||||
merged = payload.get("merged_by_type")
|
||||
if isinstance(merged, dict):
|
||||
for cloud_type, links in merged.items():
|
||||
if isinstance(links, list):
|
||||
for link in links:
|
||||
if isinstance(link, dict):
|
||||
# 从 merged_by_type 获取链接信息
|
||||
url = link.get("url", "")
|
||||
note = link.get("note", "") # 使用 note 字段作为标题
|
||||
datetime_str = link.get("datetime", "") # 获取发布日期
|
||||
if url:
|
||||
cleaned.append({
|
||||
"taskname": note,
|
||||
"content": note, # 如果没有 content,使用 note
|
||||
"shareurl": url,
|
||||
"tags": [cloud_type] if cloud_type else [],
|
||||
"publish_date": datetime_str, # 添加发布日期字段
|
||||
"source": "PanSou" # 添加来源标识
|
||||
})
|
||||
|
||||
# 3) 直接 data 数组兜底
|
||||
if not cleaned and isinstance(payload, list):
|
||||
for item in payload:
|
||||
if isinstance(item, dict):
|
||||
cleaned.append({
|
||||
"taskname": item.get("title", ""),
|
||||
"content": item.get("content", ""),
|
||||
"shareurl": item.get("url", ""),
|
||||
"tags": item.get("tags", []) or [],
|
||||
"publish_date": item.get("datetime", ""), # 添加发布日期字段
|
||||
"source": "PanSou" # 添加来源标识
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return {"success": False, "message": f"解析PanSou结果失败: {str(e)}"}
|
||||
|
||||
# 二次过滤:确保只返回夸克网盘链接
|
||||
if cleaned:
|
||||
filtered = []
|
||||
for item in cleaned:
|
||||
try:
|
||||
url = item.get("shareurl", "")
|
||||
tags = item.get("tags", []) or []
|
||||
# 检查是否为夸克网盘
|
||||
is_quark = ("quark" in tags) or ("pan.quark.cn" in url)
|
||||
if is_quark:
|
||||
filtered.append(item)
|
||||
except Exception:
|
||||
continue
|
||||
cleaned = filtered
|
||||
|
||||
if not cleaned:
|
||||
return {"success": False, "message": "PanSou搜索无夸克网盘结果"}
|
||||
|
||||
# 去重:按 shareurl 去重
|
||||
seen_urls = set()
|
||||
unique_results = []
|
||||
for item in cleaned:
|
||||
url = item.get("shareurl", "")
|
||||
if url and url not in seen_urls:
|
||||
seen_urls.add(url)
|
||||
unique_results.append(item)
|
||||
|
||||
# 按发布日期排序:最新的在前
|
||||
def parse_datetime(datetime_str):
|
||||
"""解析日期时间字符串,返回可比较的时间戳"""
|
||||
if not datetime_str:
|
||||
return 0 # 没有日期的排在最后
|
||||
try:
|
||||
from datetime import datetime, timezone, timedelta
|
||||
# 尝试解析 ISO 8601 格式: 2025-07-28T20:43:27Z
|
||||
dt = datetime.fromisoformat(datetime_str.replace('Z', '+00:00'))
|
||||
return dt.timestamp()
|
||||
except:
|
||||
return 0 # 解析失败排在最后
|
||||
|
||||
def convert_to_cst(datetime_str):
|
||||
"""将 ISO 时间转换为中国标准时间 (CST)"""
|
||||
if not datetime_str:
|
||||
return ""
|
||||
try:
|
||||
from datetime import datetime, timezone, timedelta
|
||||
dt = datetime.fromisoformat(datetime_str.replace('Z', '+00:00'))
|
||||
dt_cst = dt.astimezone(timezone(timedelta(hours=8)))
|
||||
return dt_cst.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except:
|
||||
return datetime_str # 转换失败时返回原始字符串
|
||||
|
||||
# 转换时间为中国标准时间格式
|
||||
for item in unique_results:
|
||||
if item.get("publish_date"):
|
||||
item["publish_date"] = convert_to_cst(item["publish_date"])
|
||||
|
||||
# 注意:排序逻辑已移至全局,这里不再进行内部排序
|
||||
# 返回原始顺序的结果,由全局排序函数统一处理
|
||||
return {"success": True, "data": unique_results}
|
||||
@ -3218,7 +3218,7 @@ div[data-toggle="collapse"] .btn.text-left i.bi-caret-right-fill {
|
||||
color: inherit;
|
||||
transition: transform 0.2s;
|
||||
position: relative;
|
||||
top: 0.5px; /* 调整箭头垂直对齐,使其与文本居中 */
|
||||
top: 0; /* 调整箭头垂直对齐,使其与文本居中 */
|
||||
font-size: 0.95rem; /* 调整箭头大小与文本比例协调 */
|
||||
margin-right: 4px; /* 添加右侧间距使与文字有适当间距 */
|
||||
}
|
||||
@ -6399,3 +6399,42 @@ body .selectable-files tr.selected-file:has([style*="white-space: normal"]) .fil
|
||||
font-family: inherit;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
/* 仅在“搜索来源”前的最后一个插件折叠时,将间距减少 2px */
|
||||
div:has(> .collapse:not(.show)):has(+ .row.title[title^="资源搜索"]) {
|
||||
margin-bottom: -10px !important; /* override inline -8px only for collapsed state */
|
||||
}
|
||||
|
||||
/* 修复系统配置页面性能设置与API接口模块间距问题 */
|
||||
.row.mb-2.performance-setting-row + .row.title[title^="API接口"] {
|
||||
margin-top: 0 !important; /* prevent unexpected collapse stacking */
|
||||
padding-top: 4px !important; /* adds effective +4px spacing */
|
||||
}
|
||||
|
||||
/* --------------- 来源标识样式 --------------- */
|
||||
.source-badge {
|
||||
display: inline-block;
|
||||
margin-left: 1px;
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
color: var(--light-text-color);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.source-badge::before {
|
||||
content: "· ";
|
||||
margin-right: 0px;
|
||||
color: var(--light-text-color);
|
||||
}
|
||||
|
||||
.source-badge::after {
|
||||
content: attr(data-publish-date);
|
||||
margin-left: 2px;
|
||||
color: var(--light-text-color);
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
@ -509,7 +509,7 @@
|
||||
|
||||
<div class="row title" title="通知推送,支持多个渠道,查阅Wiki了解详情">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">通知</h2>
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">通知设置</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/通知推送服务配置" target="_blank"><i class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
@ -531,7 +531,7 @@
|
||||
|
||||
<div class="row title" v-if="Object.keys(getAvailablePlugins(formData.plugins)).length" title="插件配置,具体键值由插件定义,查阅Wiki了解详情">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">插件</h2>
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">插件设置</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/插件配置" target="_blank"><i class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
@ -541,7 +541,7 @@
|
||||
<div class="form-group row mb-0" style="display:flex; align-items:center;">
|
||||
<div data-toggle="collapse" :data-target="'#collapse_'+pluginName" aria-expanded="true" :aria-controls="'collapse_'+pluginName">
|
||||
<div class="btn btn-block text-left">
|
||||
<i class="bi bi-caret-right-fill"></i> <span v-html="`${pluginName}`"></span>
|
||||
<i class="bi bi-caret-right-fill"></i> <span v-html="getPluginDisplayName(pluginName)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -582,6 +582,72 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row title" title="资源搜索服务配置,用于任务名称智能搜索,查阅Wiki了解详情">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">搜索来源</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/CloudSaver搜索源" target="_blank"><i class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 按插件风格显示为可展开项 -->
|
||||
<div style="margin-bottom: -8px;">
|
||||
<!-- CloudSaver -->
|
||||
<div style="margin-bottom: -8px; margin-top: -8px;">
|
||||
<div class="form-group row mb-0" style="display:flex; align-items:center;">
|
||||
<div data-toggle="collapse" data-target="#collapse_source_cloudsaver" aria-expanded="true" aria-controls="collapse_source_cloudsaver">
|
||||
<div class="btn btn-block text-left">
|
||||
<i class="bi bi-caret-right-fill"></i> CloudSaver
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse" id="collapse_source_cloudsaver" style="margin-left: 26px;">
|
||||
<div class="input-group mb-2">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">服务器</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.source.cloudsaver.server" class="form-control" placeholder="资源搜索服务器地址,如 http://172.17.0.1:8008">
|
||||
</div>
|
||||
<div class="input-group mb-2">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">用户名</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.source.cloudsaver.username" class="form-control" placeholder="用户名">
|
||||
</div>
|
||||
<div class="input-group mb-2">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">密码</span>
|
||||
</div>
|
||||
<input :type="showCloudSaverPassword ? 'text' : 'password'" v-model="formData.source.cloudsaver.password" class="form-control" placeholder="密码">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary" @click="toggleCloudSaverPassword">
|
||||
<i :class="['bi', showCloudSaverPassword ? 'bi-eye' : 'bi-eye-slash']"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PanSou -->
|
||||
<div style="margin-bottom: -9.5px;">
|
||||
<div class="form-group row mb-0" style="display:flex; align-items:center;">
|
||||
<div data-toggle="collapse" data-target="#collapse_source_pansou" aria-expanded="false" aria-controls="collapse_source_pansou">
|
||||
<div class="btn btn-block text-left">
|
||||
<i class="bi bi-caret-right-fill"></i> PanSou
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse" id="collapse_source_pansou" style="margin-left: 26px;">
|
||||
<div class="input-group mb-2" style="margin-bottom: 10.5px !important;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">服务器</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.source.pansou.server" class="form-control" placeholder="PanSou 服务器,如 https://so.252035.xyz">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row title" title="预定义的正则匹配规则,在任务列表中可直接点击使用,查阅Wiki了解详情">
|
||||
<div class="col">
|
||||
@ -718,59 +784,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row title" title="API接口,用于第三方添加任务等操作,查阅Wiki了解详情">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">API</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/API接口" target="_blank"><i class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Token</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.api_token" class="form-control" style="background-color:white;" disabled>
|
||||
</div>
|
||||
|
||||
<div class="row title" title="资源搜索服务配置,用于任务名称智能搜索,查阅Wiki了解详情">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">CloudSaver</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/CloudSaver搜索源" target="_blank"><i class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group mb-2">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">服务器</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.source.cloudsaver.server" class="form-control" placeholder="资源搜索服务器地址,如 http://172.17.0.1:8008">
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col cloudsaver-username-col">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">用户名</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.source.cloudsaver.username" class="form-control" placeholder="用户名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col cloudsaver-password-col">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">密码</span>
|
||||
</div>
|
||||
<input :type="showCloudSaverPassword ? 'text' : 'password'" v-model="formData.source.cloudsaver.password" class="form-control" placeholder="密码">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary" @click="toggleCloudSaverPassword">
|
||||
<i :class="['bi', showCloudSaverPassword ? 'bi-eye' : 'bi-eye-slash']"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row title" title="设置任务列表页面的任务按钮的显示方式,刷新Plex媒体库和刷新AList目录按钮仅在配置了对应插件的前提下才支持显示">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">显示设置</h2>
|
||||
@ -910,6 +923,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row title" title="API接口,用于第三方添加任务等操作,查阅Wiki了解详情">
|
||||
<div class="col">
|
||||
<h2 style="display: inline-block; font-size: 1.5rem;">API</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/x1ao4/quark-auto-save-x/wiki/API接口" target="_blank"><i class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Token</span>
|
||||
</div>
|
||||
<input type="text" v-model="formData.api_token" class="form-control" style="background-color:white;" disabled>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@ -999,6 +1026,7 @@
|
||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||
<small class="text-muted">
|
||||
<a :href="suggestion.shareurl" target="_blank" @click.stop>{{ suggestion.shareurl }}</a>
|
||||
<span v-if="suggestion.source" class="source-badge" :class="suggestion.source.toLowerCase()" :data-publish-date="(suggestion.publish_date || suggestion.datetime) ? ' · ' + (suggestion.publish_date || suggestion.datetime) : ''">{{ suggestion.source }}</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -1926,6 +1954,7 @@
|
||||
<span v-html="suggestion.verify ? '✅': ''"></span> {{ suggestion.taskname }}
|
||||
<small class="text-muted">
|
||||
<a :href="suggestion.shareurl" target="_blank" @click.stop>{{ suggestion.shareurl }}</a>
|
||||
<span v-if="suggestion.source" class="source-badge" :class="suggestion.source.toLowerCase()" :data-publish-date="(suggestion.publish_date || suggestion.datetime) ? ' · ' + (suggestion.publish_date || suggestion.datetime) : ''">{{ suggestion.source }}</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@ -2094,6 +2123,14 @@
|
||||
showCloudSaverPassword: false,
|
||||
showWebuiPassword: false,
|
||||
pageWidthMode: 'medium', // 页面宽度模式:narrow, medium, wide
|
||||
pluginDisplayAliases: {
|
||||
alist: 'AList',
|
||||
alist_strm: 'AList Strm',
|
||||
alist_strm_gen: 'AList Strm Gen',
|
||||
aria2: 'Aria2',
|
||||
emby: 'Emby',
|
||||
plex: 'Plex'
|
||||
},
|
||||
formData: {
|
||||
cookie: [],
|
||||
push_config: {},
|
||||
@ -2118,6 +2155,9 @@
|
||||
username: "",
|
||||
password: "",
|
||||
token: ""
|
||||
},
|
||||
pansou: {
|
||||
server: "https://so.252035.xyz"
|
||||
}
|
||||
},
|
||||
webui: {
|
||||
@ -2718,6 +2758,10 @@
|
||||
document.removeEventListener('click', this.handleOutsideClick);
|
||||
},
|
||||
methods: {
|
||||
// 获取插件展示名称(支持别名,仅用于WebUI显示)
|
||||
getPluginDisplayName(pluginName) {
|
||||
return this.pluginDisplayAliases[pluginName] || pluginName;
|
||||
},
|
||||
// 设置移动端任务列表展开/收起状态监听
|
||||
setupMobileTaskListToggle() {
|
||||
// 监听所有collapse事件
|
||||
@ -7632,10 +7676,10 @@
|
||||
// 打开创建任务模态框
|
||||
$('#createTaskModal').modal('show');
|
||||
|
||||
// 如果启用了自动搜索资源且配置了有效的CloudSaver信息,自动触发资源搜索
|
||||
// 如果启用了自动搜索资源且配置了有效的搜索来源,自动触发资源搜索
|
||||
this.$nextTick(() => {
|
||||
if (this.formData.task_settings.auto_search_resources === 'enabled' &&
|
||||
this.isCloudSaverConfigValid() &&
|
||||
this.hasAnyValidSearchSource() &&
|
||||
this.createTask.taskData.taskname) {
|
||||
this.searchSuggestions(-1, this.createTask.taskData.taskname);
|
||||
}
|
||||
@ -7644,13 +7688,13 @@
|
||||
console.error('创建任务时出错:', error);
|
||||
}
|
||||
},
|
||||
isCloudSaverConfigValid() {
|
||||
// 检查CloudSaver配置是否有效
|
||||
const csData = this.formData.source && this.formData.source.cloudsaver;
|
||||
return csData &&
|
||||
csData.server &&
|
||||
csData.username &&
|
||||
csData.password;
|
||||
hasAnyValidSearchSource() {
|
||||
const src = this.formData.source || {};
|
||||
const cs = src.cloudsaver || {};
|
||||
const ps = src.pansou || {};
|
||||
const csValid = cs.server && cs.username && cs.password;
|
||||
const psValid = ps.server;
|
||||
return !!(csValid || psValid);
|
||||
},
|
||||
smartFillTaskData(item, movieData) {
|
||||
// 智能填充任务数据
|
||||
|
||||
Loading…
Reference in New Issue
Block a user