mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-17 18:30:43 +08:00
Compare commits
12 Commits
b108d24981
...
4b2d78a0b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2d78a0b2 | ||
|
|
e1e0a6afc4 | ||
|
|
d5a802c218 | ||
|
|
70093a3f2c | ||
|
|
81d4098b6c | ||
|
|
6f976f242a | ||
|
|
50090db1f4 | ||
|
|
4225f1986b | ||
|
|
f398f3fa07 | ||
|
|
ffe95fcf66 | ||
|
|
83fd60f1a1 | ||
|
|
dda9ec0a01 |
@ -41,6 +41,7 @@
|
||||
- [x] 支持分享链接的子目录
|
||||
- [x] 记录失效分享并跳过任务
|
||||
- [x] 支持需提取码的分享链接 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/使用技巧集锦#支持需提取码的分享链接)</sup>
|
||||
- [x] 智能搜索资源并自动填充 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/CloudSaver搜索源)</sup>
|
||||
|
||||
- 文件管理
|
||||
- [x] 目标目录不存在时自动新建
|
||||
|
||||
182
app/run.py
182
app/run.py
@ -14,13 +14,13 @@ from flask import (
|
||||
)
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
from sdk.cloudsaver import CloudSaver
|
||||
from datetime import timedelta
|
||||
import subprocess
|
||||
import requests
|
||||
import hashlib
|
||||
import logging
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
@ -48,7 +48,8 @@ CONFIG_PATH = os.environ.get("CONFIG_PATH", "./config/quark_config.json")
|
||||
PLUGIN_FLAGS = os.environ.get("PLUGIN_FLAGS", "")
|
||||
DEBUG = os.environ.get("DEBUG", False)
|
||||
|
||||
task_plugins_config = {}
|
||||
config_data = {}
|
||||
task_plugins_config_default = {}
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config["APP_VERSION"] = get_app_ver()
|
||||
@ -77,24 +78,18 @@ def gen_md5(string):
|
||||
return md5.hexdigest()
|
||||
|
||||
|
||||
# 读取 JSON 文件内容
|
||||
def read_json():
|
||||
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
return data
|
||||
|
||||
|
||||
# 将数据写入 JSON 文件
|
||||
def write_json(data):
|
||||
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, sort_keys=False, indent=2)
|
||||
def get_login_token():
|
||||
username = config_data["webui"]["username"]
|
||||
password = config_data["webui"]["password"]
|
||||
return gen_md5(f"token{username}{password}+-*/")[8:24]
|
||||
|
||||
|
||||
def is_login():
|
||||
data = read_json()
|
||||
username = data["webui"]["username"]
|
||||
password = data["webui"]["password"]
|
||||
if session.get("login") == gen_md5(username + password):
|
||||
login_token = get_login_token()
|
||||
if (
|
||||
session.get("token") == login_token
|
||||
or request.args.get("token") == login_token
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@ -114,16 +109,15 @@ def favicon():
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
if request.method == "POST":
|
||||
data = read_json()
|
||||
username = data["webui"]["username"]
|
||||
password = data["webui"]["password"]
|
||||
username = config_data["webui"]["username"]
|
||||
password = config_data["webui"]["password"]
|
||||
# 验证用户名和密码
|
||||
if (username == request.form.get("username")) and (
|
||||
password == request.form.get("password")
|
||||
):
|
||||
logging.info(f">>> 用户 {username} 登录成功")
|
||||
session["login"] = gen_md5(username + password)
|
||||
session.permanent = True
|
||||
session["token"] = get_login_token()
|
||||
return redirect(url_for("index"))
|
||||
else:
|
||||
logging.info(f">>> 用户 {username} 登录失败")
|
||||
@ -137,7 +131,7 @@ def login():
|
||||
# 退出登录
|
||||
@app.route("/logout")
|
||||
def logout():
|
||||
session.pop("login", None)
|
||||
session.pop("token", None)
|
||||
return redirect(url_for("login"))
|
||||
|
||||
|
||||
@ -155,37 +149,39 @@ def index():
|
||||
@app.route("/data")
|
||||
def get_data():
|
||||
if not is_login():
|
||||
return redirect(url_for("login"))
|
||||
data = read_json()
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
data = Config.read_json(CONFIG_PATH)
|
||||
del data["webui"]
|
||||
data["task_plugins_config"] = task_plugins_config
|
||||
return jsonify(data)
|
||||
data["api_token"] = get_login_token()
|
||||
data["task_plugins_config_default"] = task_plugins_config_default
|
||||
return jsonify({"success": True, "data": data})
|
||||
|
||||
|
||||
# 更新数据
|
||||
@app.route("/update", methods=["POST"])
|
||||
def update():
|
||||
global config_data
|
||||
if not is_login():
|
||||
return "未登录"
|
||||
data = request.json
|
||||
data["webui"] = read_json()["webui"]
|
||||
if "task_plugins_config" in data:
|
||||
del data["task_plugins_config"]
|
||||
write_json(data)
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
dont_save_keys = ["task_plugins_config_default", "api_token"]
|
||||
for key, value in request.json.items():
|
||||
if key not in dont_save_keys:
|
||||
config_data.update({key: value})
|
||||
Config.write_json(CONFIG_PATH, config_data)
|
||||
# 重新加载任务
|
||||
if reload_tasks():
|
||||
logging.info(f">>> 配置更新成功")
|
||||
return "配置更新成功"
|
||||
return jsonify({"success": True, "message": "配置更新成功"})
|
||||
else:
|
||||
logging.info(f">>> 配置更新失败")
|
||||
return "配置更新失败"
|
||||
return jsonify({"success": False, "message": "配置更新失败"})
|
||||
|
||||
|
||||
# 处理运行脚本请求
|
||||
@app.route("/run_script_now", methods=["GET"])
|
||||
def run_script_now():
|
||||
if not is_login():
|
||||
return "未登录"
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
task_index = request.args.get("task_index", "")
|
||||
command = [PYTHON_PATH, "-u", SCRIPT_PATH, CONFIG_PATH, task_index]
|
||||
logging.info(
|
||||
@ -224,64 +220,106 @@ def run_script_now():
|
||||
@app.route("/task_suggestions")
|
||||
def get_task_suggestions():
|
||||
if not is_login():
|
||||
return jsonify({"error": "未登录"})
|
||||
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
query = request.args.get("q", "").lower()
|
||||
deep = request.args.get("d", "").lower()
|
||||
url = f"{base_url}/task_suggestions?q={query}&d={deep}"
|
||||
try:
|
||||
response = requests.get(url)
|
||||
return jsonify(response.json())
|
||||
if cs_data := config_data.get("source", {}).get("cloudsaver", {}):
|
||||
cs = CloudSaver(cs_data.get("server"))
|
||||
cs.set_auth(
|
||||
cs_data.get("username", ""),
|
||||
cs_data.get("password", ""),
|
||||
cs_data.get("token", ""),
|
||||
)
|
||||
search = cs.auto_login_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, "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={query}&d={deep}"
|
||||
response = requests.get(url)
|
||||
return jsonify({"success": True, "data": response.json()})
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)})
|
||||
return jsonify({"success": False, "message": str(e)})
|
||||
|
||||
|
||||
@app.route("/get_share_detail")
|
||||
def get_share_files():
|
||||
if not is_login():
|
||||
return jsonify({"error": "未登录"})
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
shareurl = request.args.get("shareurl", "")
|
||||
account = Quark("", 0)
|
||||
pwd_id, passcode, pdir_fid = account.get_id_from_url(shareurl)
|
||||
is_sharing, stoken = account.get_stoken(pwd_id, passcode)
|
||||
if not is_sharing:
|
||||
return jsonify({"error": stoken})
|
||||
return jsonify({"success": False, "data": {"error": stoken}})
|
||||
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, 1)
|
||||
return jsonify(share_detail)
|
||||
return jsonify({"success": True, "data": share_detail})
|
||||
|
||||
|
||||
@app.route("/get_savepath")
|
||||
def get_savepath():
|
||||
if not is_login():
|
||||
return jsonify({"error": "未登录"})
|
||||
data = read_json()
|
||||
account = Quark(data["cookie"][0], 0)
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
if path := request.args.get("path"):
|
||||
if path == "/":
|
||||
fid = 0
|
||||
elif get_fids := account.get_fids([path]):
|
||||
fid = get_fids[0]["fid"]
|
||||
else:
|
||||
return jsonify([])
|
||||
return jsonify({"success": False, "message": "获取fid失败"})
|
||||
else:
|
||||
fid = request.args.get("fid", 0)
|
||||
file_list = account.ls_dir(fid)
|
||||
return jsonify(file_list)
|
||||
return jsonify({"success": True, "data": file_list})
|
||||
|
||||
|
||||
@app.route("/delete_file", methods=["POST"])
|
||||
def delete_file():
|
||||
if not is_login():
|
||||
return jsonify({"error": "未登录"})
|
||||
data = read_json()
|
||||
account = Quark(data["cookie"][0], 0)
|
||||
return jsonify({"success": False, "message": "未登录"})
|
||||
account = Quark(config_data["cookie"][0], 0)
|
||||
if fid := request.json.get("fid"):
|
||||
response = account.delete([fid])
|
||||
else:
|
||||
response = {"error": "fid not found"}
|
||||
response = {"success": False, "message": "缺失必要字段: fid"}
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
# 添加任务接口
|
||||
@app.route("/api/add_task", methods=["POST"])
|
||||
def add_task():
|
||||
global config_data
|
||||
# 验证token
|
||||
if not is_login():
|
||||
return jsonify({"success": False, "code": 1, "message": "未登录"}), 401
|
||||
# 必选字段
|
||||
request_data = request.json
|
||||
required_fields = ["taskname", "shareurl", "savepath"]
|
||||
for field in required_fields:
|
||||
if field not in request_data or not request_data[field]:
|
||||
return (
|
||||
jsonify(
|
||||
{"success": False, "code": 2, "message": f"缺少必要字段: {field}"}
|
||||
),
|
||||
400,
|
||||
)
|
||||
# 添加任务
|
||||
config_data["tasklist"].append(request_data)
|
||||
Config.write_json(CONFIG_PATH, config_data)
|
||||
logging.info(f">>> 通过API添加任务: {request_data['taskname']}")
|
||||
return jsonify(
|
||||
{"success": True, "code": 0, "message": "任务添加成功", "data": request_data}
|
||||
)
|
||||
|
||||
|
||||
# 定时任务执行的函数
|
||||
def run_python(args):
|
||||
logging.info(f">>> 定时运行任务")
|
||||
@ -290,11 +328,8 @@ def run_python(args):
|
||||
|
||||
# 重新加载任务
|
||||
def reload_tasks():
|
||||
# 读取数据
|
||||
data = read_json()
|
||||
# 添加新任务
|
||||
crontab = data.get("crontab")
|
||||
if crontab:
|
||||
# 读取定时规则
|
||||
if crontab := config_data.get("crontab"):
|
||||
if scheduler.state == 1:
|
||||
scheduler.pause() # 暂停调度器
|
||||
trigger = CronTrigger.from_crontab(crontab)
|
||||
@ -321,7 +356,7 @@ def reload_tasks():
|
||||
|
||||
|
||||
def init():
|
||||
global task_plugins_config
|
||||
global config_data, task_plugins_config_default
|
||||
logging.info(f">>> 初始化配置")
|
||||
# 检查配置文件是否存在
|
||||
if not os.path.exists(CONFIG_PATH):
|
||||
@ -329,23 +364,30 @@ def init():
|
||||
os.makedirs(os.path.dirname(CONFIG_PATH))
|
||||
with open("quark_config.json", "rb") as src, open(CONFIG_PATH, "wb") as dest:
|
||||
dest.write(src.read())
|
||||
data = read_json()
|
||||
Config.breaking_change_update(data)
|
||||
|
||||
# 读取配置
|
||||
config_data = Config.read_json(CONFIG_PATH)
|
||||
Config.breaking_change_update(config_data)
|
||||
|
||||
# 默认管理账号
|
||||
data["webui"] = {
|
||||
config_data["webui"] = {
|
||||
"username": os.environ.get("WEBUI_USERNAME")
|
||||
or data.get("webui", {}).get("username", "admin"),
|
||||
or config_data.get("webui", {}).get("username", "admin"),
|
||||
"password": os.environ.get("WEBUI_PASSWORD")
|
||||
or data.get("webui", {}).get("password", "admin123"),
|
||||
or config_data.get("webui", {}).get("password", "admin123"),
|
||||
}
|
||||
|
||||
# 默认定时规则
|
||||
if not data.get("crontab"):
|
||||
data["crontab"] = "0 8,18,20 * * *"
|
||||
if not config_data.get("crontab"):
|
||||
config_data["crontab"] = "0 8,18,20 * * *"
|
||||
|
||||
# 初始化插件配置
|
||||
_, plugins_config_default, task_plugins_config = Config.load_plugins()
|
||||
plugins_config_default.update(data.get("plugins", {}))
|
||||
data["plugins"] = plugins_config_default
|
||||
write_json(data)
|
||||
_, plugins_config_default, task_plugins_config_default = Config.load_plugins()
|
||||
plugins_config_default.update(config_data.get("plugins", {}))
|
||||
config_data["plugins"] = plugins_config_default
|
||||
|
||||
# 更新配置
|
||||
Config.write_json(CONFIG_PATH, config_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
144
app/sdk/cloudsaver.py
Normal file
144
app/sdk/cloudsaver.py
Normal file
@ -0,0 +1,144 @@
|
||||
import requests
|
||||
|
||||
|
||||
class CloudSaver:
|
||||
"""
|
||||
CloudSaver 类,用于获取云盘资源
|
||||
"""
|
||||
|
||||
def __init__(self, server):
|
||||
self.server = server
|
||||
self.username = None
|
||||
self.password = None
|
||||
self.token = None
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({"Content-Type": "application/json"})
|
||||
|
||||
def set_auth(self, username, password, token=""):
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.token = token
|
||||
self.session.headers.update({"Authorization": f"Bearer {self.token}"})
|
||||
|
||||
def login(self):
|
||||
if not self.username or not self.password:
|
||||
return {"success": False, "message": "CloudSaver未设置用户名或密码"}
|
||||
try:
|
||||
url = f"{self.server}/api/user/login"
|
||||
data = {"username": self.username, "password": self.password}
|
||||
response = self.session.post(url, json=data)
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
self.token = result.get("data", {}).get("token")
|
||||
self.session.headers.update({"Authorization": f"Bearer {self.token}"})
|
||||
return {"success": True, "token": self.token}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": f"CloudSaver登录{result.get('message', '未知错误')}",
|
||||
}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def search(self, keyword, last_message_id=""):
|
||||
"""
|
||||
搜索资源
|
||||
|
||||
Args:
|
||||
keyword (str): 搜索关键词
|
||||
last_message_id (str): 上一条消息ID,用于分页
|
||||
|
||||
Returns:
|
||||
list: 搜索结果列表
|
||||
"""
|
||||
try:
|
||||
url = f"{self.server}/api/search"
|
||||
params = {"keyword": keyword, "lastMessageId": last_message_id}
|
||||
response = self.session.get(url, params=params)
|
||||
result = response.json()
|
||||
if result.get("success"):
|
||||
data = result.get("data", [])
|
||||
return {"success": True, "data": data}
|
||||
else:
|
||||
return {"success": False, "message": result.get("message", "未知错误")}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
|
||||
def auto_login_search(self, keyword, last_message_id=""):
|
||||
"""
|
||||
自动登录并搜索资源
|
||||
|
||||
Args:
|
||||
keyword (str): 搜索关键词
|
||||
last_message_id (str): 上一条消息ID,用于分页
|
||||
"""
|
||||
result = self.search(keyword, last_message_id)
|
||||
if result.get("success"):
|
||||
return result
|
||||
else:
|
||||
if result.get("message") == "无效的 token" or result.get("message") == "未提供 token":
|
||||
login_result = self.login()
|
||||
if login_result.get("success"):
|
||||
result = self.search(keyword, last_message_id)
|
||||
result["new_token"] = login_result.get("token")
|
||||
return result
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": login_result.get("message", "未知错误"),
|
||||
}
|
||||
return {"success": False, "message": result.get("message", "未知错误")}
|
||||
|
||||
def clean_search_results(self, search_results):
|
||||
"""
|
||||
清洗搜索结果
|
||||
|
||||
Args:
|
||||
search_results (list): 搜索结果列表
|
||||
|
||||
Returns:
|
||||
list: 夸克网盘链接列表
|
||||
"""
|
||||
clean_results = []
|
||||
for channel in search_results:
|
||||
for item in channel.get("list", []):
|
||||
cloud_links = item.get("cloudLinks", [])
|
||||
for link in cloud_links:
|
||||
if link.get("cloudType") == "quark":
|
||||
clean_results.append(
|
||||
{
|
||||
"shareurl": link.get("link"),
|
||||
"taskname": item.get("title", "")
|
||||
.strip("名称:")
|
||||
.replace("&", "&"),
|
||||
"content": item.get("content", "")
|
||||
.split("描述:")[1]
|
||||
.split("链接:")[0]
|
||||
.replace('<mark class="highlight">', "")
|
||||
.replace("</mark>", ""),
|
||||
"tags": item.get("tags", []),
|
||||
}
|
||||
)
|
||||
return clean_results
|
||||
|
||||
|
||||
# 测试示例
|
||||
if __name__ == "__main__":
|
||||
# 创建CloudSaver实例
|
||||
server = ""
|
||||
username = ""
|
||||
password = ""
|
||||
token = ""
|
||||
cloud_saver = CloudSaver(server)
|
||||
cloud_saver.set_auth(username, password, token)
|
||||
# 搜索资源
|
||||
results = cloud_saver.auto_login_search("黑镜")
|
||||
# 提取夸克网盘链接
|
||||
clean_results = cloud_saver.clean_search_results(results.get("data", []))
|
||||
# 打印结果
|
||||
for item in clean_results:
|
||||
print(f"标题: {item['taskname']}")
|
||||
print(f"描述: {item['content']}")
|
||||
print(f"链接: {item['shareurl']}")
|
||||
print(f"标签: {' '.join(item['tags'])}")
|
||||
print("-" * 50)
|
||||
@ -42,6 +42,7 @@ body {
|
||||
|
||||
.title {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
||||
@ -59,7 +60,7 @@ table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
||||
|
||||
.task-suggestions {
|
||||
width: 100%;
|
||||
max-height: 500px;
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
transform: translate(0, -100%);
|
||||
top: 0;
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
<h2><i class="bi bi-cookie"></i> Cookie</h2>
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<button type="button" class="btn btn-outline-primary mb-3" @click="addCookie()">+</button>
|
||||
<button type="button" class="btn btn-outline-primary" @click="addCookie()">+</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>1. 所有账号执行签到,纯签到只需移动端参数即可!</p>
|
||||
@ -86,7 +86,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group mt-2 mb-2">
|
||||
<div class="input-group mb-2">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Crontab</span>
|
||||
</div>
|
||||
@ -101,7 +101,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<button type="button" class="btn btn-outline-primary mb-3" @click="addPush()">+</button>
|
||||
<button type="button" class="btn btn-outline-primary" @click="addPush()">+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(value, key) in formData.push_config" :key="key" class="input-group mb-2">
|
||||
@ -151,7 +151,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<button type="button" class="btn btn-outline-primary mb-3" @click="addMagicRegex()">+</button>
|
||||
<button type="button" class="btn btn-outline-primary" @click="addMagicRegex()">+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(value, key) in formData.magic_regex" :key="key" class="form-group mb-2">
|
||||
@ -171,6 +171,49 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row title" title="API接口,用以第三方添加任务等操作,见Wiki">
|
||||
<div class="col-10">
|
||||
<h2 style="display: inline-block;"><i class="bi bi-link-45deg"></i> API</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/Cp0204/quark-auto-save/wiki/API接口" target="_blank">?</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="资源搜索服务配置,用于任务名称智能搜索">
|
||||
<div class="col-10">
|
||||
<h2 style="display: inline-block;"><i class="bi bi-search"></i> CloudSaver</h2>
|
||||
<span class="badge badge-pill badge-light">
|
||||
<a href="https://github.com/Cp0204/quark-auto-save/wiki/CloudSaver搜索源" target="_blank">?</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">服务器</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" v-model="formData.source.cloudsaver.server" class="form-control" placeholder="资源搜索服务器地址">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">用户名</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" v-model="formData.source.cloudsaver.username" class="form-control" placeholder="用户名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">密码</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" v-model="formData.source.cloudsaver.password" class="form-control" placeholder="密码">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'tasklist'">
|
||||
@ -228,9 +271,9 @@
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" name="taskname[]" class="form-control" v-model="task.taskname" placeholder="必填" @focus="smart_param.showSuggestions=true;focusTaskname(index, task)" @input="changeTaskname(index, task)">
|
||||
<div class="dropdown-menu show task-suggestions" v-if="smart_param.showSuggestions && smart_param.taskSuggestions.length && smart_param.index === index">
|
||||
<div class="text-muted text-center" style="font-size: small;">以下资源来自第三方,网络公开搜集,请自行辨识,如有侵权请联系夸克官方</div>
|
||||
<div v-for="suggestion in smart_param.taskSuggestions" :key="suggestion.taskname" class="dropdown-item" @click.prevent="selectSuggestion(task, suggestion)" style="cursor: pointer;">
|
||||
<div class="dropdown-menu show task-suggestions" v-if="smart_param.showSuggestions && smart_param.taskSuggestions.success && smart_param.index === index">
|
||||
<div class="text-muted text-center" style="font-size:12px;">{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data.length ? "以下资源来自网络公开搜索,请自行辨识,如有侵权请联系资源方" : "未搜索到资源" }}</div>
|
||||
<div v-for="suggestion in smart_param.taskSuggestions.data" :key="suggestion.taskname" class="dropdown-item" @click.prevent="selectSuggestion(task, suggestion)" style="cursor: pointer;font-size: 12px;" :title="suggestion.content">
|
||||
<span v-html="suggestion.verify ? '✅': '❔'"></span> {{ suggestion.taskname }}
|
||||
<small class="text-muted">
|
||||
<a :href="suggestion.shareurl" target="_blank" @click.stop>{{ suggestion.shareurl }}</a>
|
||||
@ -438,7 +481,15 @@
|
||||
push_config: {},
|
||||
media_servers: {},
|
||||
tasklist: [],
|
||||
magic_regex: {}
|
||||
magic_regex: {},
|
||||
source: {
|
||||
cloudsaver: {
|
||||
server: "",
|
||||
username: "",
|
||||
password: "",
|
||||
token: ""
|
||||
}
|
||||
},
|
||||
},
|
||||
newTask: {
|
||||
taskname: "",
|
||||
@ -463,12 +514,13 @@
|
||||
index: null,
|
||||
savepath: "",
|
||||
origin_savepath: "",
|
||||
taskSuggestions: [],
|
||||
taskSuggestions: {},
|
||||
showSuggestions: false,
|
||||
lastSuggestionsTime: 0,
|
||||
isSearching: false,
|
||||
},
|
||||
activeTab: 'tasklist',
|
||||
configModified: false,
|
||||
},
|
||||
filters: {
|
||||
ts2date: function (value) {
|
||||
@ -485,6 +537,12 @@
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
formData: {
|
||||
handler(newVal, oldVal) {
|
||||
this.configModified = true;
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.fetchData();
|
||||
@ -496,6 +554,10 @@
|
||||
this.smart_param.showSuggestions = false;
|
||||
}
|
||||
});
|
||||
window.addEventListener('beforeunload', this.handleBeforeUnload);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
||||
},
|
||||
methods: {
|
||||
changeTab(tab) {
|
||||
@ -521,24 +583,40 @@
|
||||
fetchData() {
|
||||
axios.get('/data')
|
||||
.then(response => {
|
||||
config_data = response.data.data
|
||||
// cookie兼容
|
||||
if (typeof response.data.cookie === 'string')
|
||||
response.data.cookie = [response.data.cookie];
|
||||
if (typeof config_data.cookie === 'string')
|
||||
config_data.cookie = [config_data.cookie];
|
||||
// 添加星期预设
|
||||
response.data.tasklist = response.data.tasklist.map(task => {
|
||||
config_data.tasklist = config_data.tasklist.map(task => {
|
||||
if (!task.hasOwnProperty('runweek')) {
|
||||
task.runweek = [1, 2, 3, 4, 5, 6, 7];
|
||||
}
|
||||
return task;
|
||||
});
|
||||
// 获取所有任务父目录
|
||||
response.data.tasklist.forEach(item => {
|
||||
config_data.tasklist.forEach(item => {
|
||||
parentDir = this.getParentDirectory(item.savepath)
|
||||
if (!this.taskDirs.includes(parentDir))
|
||||
this.taskDirs.push(parentDir);
|
||||
});
|
||||
this.newTask.addition = response.data.task_plugins_config;
|
||||
this.formData = response.data;
|
||||
this.newTask.addition = config_data.task_plugins_config_default;
|
||||
// 确保source配置存在
|
||||
if (!config_data.source) {
|
||||
config_data.source = {};
|
||||
}
|
||||
if (!config_data.source.cloudsaver) {
|
||||
config_data.source.cloudsaver = {
|
||||
server: "",
|
||||
username: "",
|
||||
password: "",
|
||||
token: ""
|
||||
};
|
||||
}
|
||||
this.formData = config_data;
|
||||
setTimeout(() => {
|
||||
this.configModified = false;
|
||||
}, 100);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
@ -555,11 +633,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
handleBeforeUnload(e) {
|
||||
if (this.configModified) {
|
||||
e.preventDefault();
|
||||
e.returnValue = '配置已修改但未保存,确定要离开吗?';
|
||||
return e.returnValue;
|
||||
}
|
||||
},
|
||||
saveConfig() {
|
||||
axios.post('/update', this.formData)
|
||||
.then(response => {
|
||||
alert(response.data);
|
||||
console.log('Config saved successfully:', response.data);
|
||||
if (response.data.success) {
|
||||
this.configModified = false;
|
||||
alert(response.data.message);
|
||||
} else {
|
||||
alert(response.data.message);
|
||||
}
|
||||
console.log('Config saved result:', response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error saving config:', error);
|
||||
@ -642,19 +732,20 @@
|
||||
// 从分享中提取任务名
|
||||
axios.get('/get_share_detail', { params: { shareurl: task.shareurl } })
|
||||
.then(response => {
|
||||
if (response.data.error) {
|
||||
if (response.data.error.includes("提取码")) {
|
||||
const passcode = prompt("检查失败[" + response.data.error + "],请输入提取码:");
|
||||
share_detail = response.data.data
|
||||
if (!response.data.success) {
|
||||
if (share_detail.error.includes("提取码")) {
|
||||
const passcode = prompt("检查失败[" + share_detail.error + "],请输入提取码:");
|
||||
if (passcode != null) {
|
||||
task.shareurl = task.shareurl.replace(/pan.quark.cn\/s\/(\w+)(\?pwd=\w*)*/, `pan.quark.cn/s/$1?pwd=${passcode}`);
|
||||
this.changeShareurl(task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.$set(task, "shareurl_ban", response.data.error);
|
||||
this.$set(task, "shareurl_ban", share_detail.error);
|
||||
} else {
|
||||
task.taskname = task.taskname == "" ? response.data.share.title : task.taskname;
|
||||
task.savepath = task.savepath.replace(/TASKNAME/g, response.data.share.title);
|
||||
task.taskname = task.taskname == "" ? share_detail.share.title : task.taskname;
|
||||
task.savepath = task.savepath.replace(/TASKNAME/g, share_detail.share.title);
|
||||
this.$set(task, "shareurl_ban", undefined);
|
||||
}
|
||||
})
|
||||
@ -679,7 +770,7 @@
|
||||
this.savepaths = [{ fid: 0, dir: true, file_name: "加载中..." }]
|
||||
axios.get('/get_savepath', { params: params })
|
||||
.then(response => {
|
||||
this.savepaths = response.data
|
||||
this.savepaths = response.data.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error get_savepath:', error);
|
||||
@ -710,7 +801,7 @@
|
||||
axios.get('/get_share_detail', { params: { shareurl: this.formData.tasklist[index].shareurl } })
|
||||
.then(response => {
|
||||
this.forceTaskIndex = index
|
||||
this.shareFiles = response.data.list;
|
||||
this.shareFiles = response.data.data.list;
|
||||
this.modalLoading = false
|
||||
})
|
||||
.catch(error => {
|
||||
@ -718,6 +809,11 @@
|
||||
});
|
||||
},
|
||||
runScriptNow(task_index = "") {
|
||||
if (this.configModified) {
|
||||
if (!confirm('配置已修改但未保存,是否继续运行?')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$('#logModal').modal('toggle')
|
||||
this.modalLoading = true
|
||||
this.run_log = ''
|
||||
@ -785,19 +881,25 @@
|
||||
}
|
||||
this.smart_param.isSearching = true
|
||||
this.smart_param.index = index;
|
||||
axios.get('/task_suggestions', {
|
||||
params: {
|
||||
q: taskname,
|
||||
d: limit_msec == 0 ? 1 : 0
|
||||
}
|
||||
}).then(response => {
|
||||
this.smart_param.taskSuggestions = response.data;
|
||||
this.smart_param.showSuggestions = true;
|
||||
}).catch(error => {
|
||||
console.error('Error fetching suggestions:', error);
|
||||
}).finally(() => {
|
||||
this.smart_param.isSearching = false;
|
||||
});
|
||||
try {
|
||||
axios.get('/task_suggestions', {
|
||||
params: {
|
||||
q: taskname,
|
||||
d: limit_msec == 0 ? 1 : 0
|
||||
}
|
||||
}).then(response => {
|
||||
this.smart_param.taskSuggestions = response.data;
|
||||
this.smart_param.showSuggestions = true;
|
||||
}).catch(error => {
|
||||
console.error('Error fetching suggestions:', error);
|
||||
}).finally(() => {
|
||||
this.smart_param.isSearching = false;
|
||||
});
|
||||
} catch (e) {
|
||||
this.smart_param.taskSuggestions = {
|
||||
error: "网络异常"
|
||||
};
|
||||
}
|
||||
},
|
||||
selectSuggestion(task, suggestion) {
|
||||
task.taskname = suggestion.taskname;
|
||||
|
||||
@ -78,6 +78,17 @@ class Config:
|
||||
else:
|
||||
return False
|
||||
|
||||
# 读取 JSON 文件内容
|
||||
def read_json(config_path):
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
return data
|
||||
|
||||
# 将数据写入 JSON 文件
|
||||
def write_json(config_path, data):
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, sort_keys=False, indent=2)
|
||||
|
||||
# 读取CK
|
||||
def get_cookies(cookie_val):
|
||||
if isinstance(cookie_val, list):
|
||||
@ -677,7 +688,7 @@ class Quark:
|
||||
pattern, replace = task["update_subdir"], ""
|
||||
else:
|
||||
pattern, replace = self.magic_regex_func(
|
||||
task["pattern"], task["replace"], task["taskname"]
|
||||
task.get("pattern", ""), task.get("replace", ""), task["taskname"]
|
||||
)
|
||||
# 正则文件名匹配
|
||||
if re.search(pattern, share_file["file_name"]):
|
||||
@ -770,7 +781,7 @@ class Quark:
|
||||
|
||||
def do_rename_task(self, task, subdir_path=""):
|
||||
pattern, replace = self.magic_regex_func(
|
||||
task["pattern"], task["replace"], task["taskname"]
|
||||
task.get("pattern", ""), task.get("replace", ""), task["taskname"]
|
||||
)
|
||||
if not pattern or not replace:
|
||||
return 0
|
||||
@ -895,8 +906,10 @@ def do_save(account, tasklist=[]):
|
||||
print(f"任务名称: {task['taskname']}")
|
||||
print(f"分享链接: {task['shareurl']}")
|
||||
print(f"保存路径: {task['savepath']}")
|
||||
print(f"正则匹配: {task['pattern']}")
|
||||
print(f"正则替换: {task['replace']}")
|
||||
if task.get("pattern"):
|
||||
print(f"正则匹配: {task['pattern']}")
|
||||
if task.get("replace"):
|
||||
print(f"正则替换: {task['replace']}")
|
||||
if task.get("enddate"):
|
||||
print(f"任务截止: {task['enddate']}")
|
||||
if task.get("ignore_extension"):
|
||||
@ -960,9 +973,8 @@ def main():
|
||||
return
|
||||
else:
|
||||
print(f"⚙️ 正从 {config_path} 文件中读取配置")
|
||||
with open(config_path, "r", encoding="utf-8") as file:
|
||||
CONFIG_DATA = json.load(file)
|
||||
Config.breaking_change_update(CONFIG_DATA)
|
||||
CONFIG_DATA = Config.read_json(config_path)
|
||||
Config.breaking_change_update(CONFIG_DATA)
|
||||
cookie_val = CONFIG_DATA.get("cookie")
|
||||
if not CONFIG_DATA.get("magic_regex"):
|
||||
CONFIG_DATA["magic_regex"] = MAGIC_REGEX
|
||||
@ -1000,8 +1012,7 @@ def main():
|
||||
print()
|
||||
if cookie_form_file:
|
||||
# 更新配置
|
||||
with open(config_path, "w", encoding="utf-8") as file:
|
||||
json.dump(CONFIG_DATA, file, ensure_ascii=False, sort_keys=False, indent=2)
|
||||
Config.write_json(config_path, CONFIG_DATA)
|
||||
|
||||
print(f"===============程序结束===============")
|
||||
duration = datetime.now() - start_time
|
||||
|
||||
Loading…
Reference in New Issue
Block a user