mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-18 02:40:44 +08:00
Merge 5bf8dc1655 into 46ec89d201
This commit is contained in:
commit
2847dab505
257
app/run.py
257
app/run.py
@ -25,11 +25,22 @@ import base64
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import io
|
||||||
|
import threading
|
||||||
|
|
||||||
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
sys.path.insert(0, parent_dir)
|
sys.path.insert(0, parent_dir)
|
||||||
from quark_auto_save import Quark, Config, MagicRename
|
from quark_auto_save import Quark, Config, MagicRename
|
||||||
|
|
||||||
|
# 用于存储当前运行的日志
|
||||||
|
current_log = io.StringIO()
|
||||||
|
log_lock = threading.Lock()
|
||||||
|
# 添加一个标志来跟踪任务是否正在运行
|
||||||
|
task_running = False
|
||||||
|
# 存储当前运行的进程
|
||||||
|
current_process = None
|
||||||
|
process_lock = threading.Lock()
|
||||||
|
|
||||||
|
|
||||||
def get_app_ver():
|
def get_app_ver():
|
||||||
BUILD_SHA = os.environ.get("BUILD_SHA", "")
|
BUILD_SHA = os.environ.get("BUILD_SHA", "")
|
||||||
@ -188,6 +199,27 @@ def run_script_now():
|
|||||||
f">>> 手动运行任务 [{tasklist[0].get('taskname') if len(tasklist)>0 else 'ALL'}] 开始执行..."
|
f">>> 手动运行任务 [{tasklist[0].get('taskname') if len(tasklist)>0 else 'ALL'}] 开始执行..."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 清空当前日志
|
||||||
|
with log_lock:
|
||||||
|
current_log.seek(0)
|
||||||
|
current_log.truncate(0)
|
||||||
|
current_log.write("任务开始执行...\n")
|
||||||
|
|
||||||
|
# 设置任务运行状态
|
||||||
|
global task_running, current_process
|
||||||
|
task_running = True
|
||||||
|
|
||||||
|
# 确保之前的进程已经终止
|
||||||
|
with process_lock:
|
||||||
|
if current_process is not None:
|
||||||
|
try:
|
||||||
|
if current_process.poll() is None:
|
||||||
|
current_process.terminate()
|
||||||
|
current_process.wait(timeout=1)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"终止旧进程失败: {str(e)}")
|
||||||
|
current_process = None
|
||||||
|
|
||||||
def generate_output():
|
def generate_output():
|
||||||
# 设置环境变量
|
# 设置环境变量
|
||||||
process_env = os.environ.copy()
|
process_env = os.environ.copy()
|
||||||
@ -202,7 +234,11 @@ def run_script_now():
|
|||||||
)
|
)
|
||||||
if tasklist:
|
if tasklist:
|
||||||
process_env["TASKLIST"] = json.dumps(tasklist, ensure_ascii=False)
|
process_env["TASKLIST"] = json.dumps(tasklist, ensure_ascii=False)
|
||||||
process = subprocess.Popen(
|
|
||||||
|
# 创建进程并保存引用
|
||||||
|
global current_process
|
||||||
|
with process_lock:
|
||||||
|
current_process = subprocess.Popen(
|
||||||
command,
|
command,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
@ -212,14 +248,32 @@ def run_script_now():
|
|||||||
bufsize=1,
|
bufsize=1,
|
||||||
env=process_env,
|
env=process_env,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for line in iter(process.stdout.readline, ""):
|
for line in iter(current_process.stdout.readline, ""):
|
||||||
logging.info(line.strip())
|
logging.info(line.strip())
|
||||||
|
# 将日志存储到全局变量中
|
||||||
|
with log_lock:
|
||||||
|
current_log.write(line)
|
||||||
yield f"data: {line}\n\n"
|
yield f"data: {line}\n\n"
|
||||||
|
|
||||||
|
# 检查进程退出状态
|
||||||
|
exit_code = current_process.wait()
|
||||||
|
if exit_code != 0:
|
||||||
|
error_msg = f"任务异常结束,退出码: {exit_code}\n"
|
||||||
|
logging.error(error_msg)
|
||||||
|
with log_lock:
|
||||||
|
current_log.write(error_msg)
|
||||||
|
yield f"data: {error_msg}\n\n"
|
||||||
|
|
||||||
yield "data: [DONE]\n\n"
|
yield "data: [DONE]\n\n"
|
||||||
finally:
|
finally:
|
||||||
process.stdout.close()
|
if current_process:
|
||||||
process.wait()
|
if current_process.poll() is None:
|
||||||
|
current_process.stdout.close()
|
||||||
|
current_process.wait()
|
||||||
|
# 检查进程状态
|
||||||
|
check_process_status()
|
||||||
|
|
||||||
return Response(
|
return Response(
|
||||||
stream_with_context(generate_output()),
|
stream_with_context(generate_output()),
|
||||||
@ -227,6 +281,54 @@ def run_script_now():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 添加一个函数来检查进程状态
|
||||||
|
def check_process_status():
|
||||||
|
global task_running, current_process
|
||||||
|
with process_lock:
|
||||||
|
if current_process is not None:
|
||||||
|
# 检查进程是否仍在运行
|
||||||
|
poll_result = current_process.poll()
|
||||||
|
if poll_result is None:
|
||||||
|
# 进程仍在运行
|
||||||
|
logging.debug(">>> 检查进程状态:进程仍在运行")
|
||||||
|
task_running = True
|
||||||
|
else:
|
||||||
|
# 进程已结束,记录退出码
|
||||||
|
if poll_result != 0:
|
||||||
|
error_msg = f">>> 检查进程状态:进程异常结束,退出码 {poll_result}"
|
||||||
|
logging.error(error_msg)
|
||||||
|
# 将错误信息添加到日志中
|
||||||
|
with log_lock:
|
||||||
|
if not current_log.getvalue().endswith(
|
||||||
|
f"任务异常结束,退出码: {poll_result}\n"
|
||||||
|
):
|
||||||
|
current_log.write(f"任务异常结束,退出码: {poll_result}\n")
|
||||||
|
else:
|
||||||
|
logging.info(f">>> 检查进程状态:进程正常结束,退出码 {poll_result}")
|
||||||
|
task_running = False
|
||||||
|
current_process = None
|
||||||
|
else:
|
||||||
|
if task_running:
|
||||||
|
logging.info(">>> 检查进程状态:无进程引用但状态为运行中,重置状态")
|
||||||
|
task_running = False
|
||||||
|
else:
|
||||||
|
logging.debug(">>> 检查进程状态:无进程运行")
|
||||||
|
|
||||||
|
|
||||||
|
# 获取当前运行的日志
|
||||||
|
@app.route("/get_current_log")
|
||||||
|
def get_current_log():
|
||||||
|
if not is_login():
|
||||||
|
return jsonify({"success": False, "message": "未登录"})
|
||||||
|
|
||||||
|
# 检查进程状态
|
||||||
|
check_process_status()
|
||||||
|
|
||||||
|
with log_lock:
|
||||||
|
log_content = current_log.getvalue()
|
||||||
|
return jsonify({"success": True, "log": log_content, "running": task_running})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/task_suggestions")
|
@app.route("/task_suggestions")
|
||||||
def get_task_suggestions():
|
def get_task_suggestions():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
@ -299,6 +401,7 @@ def get_share_detail():
|
|||||||
def preview_regex(data):
|
def preview_regex(data):
|
||||||
task = request.json.get("task", {})
|
task = request.json.get("task", {})
|
||||||
magic_regex = request.json.get("magic_regex", {})
|
magic_regex = request.json.get("magic_regex", {})
|
||||||
|
global_regex = request.json.get("global_regex", {})
|
||||||
mr = MagicRename(magic_regex)
|
mr = MagicRename(magic_regex)
|
||||||
mr.set_taskname(task.get("taskname", ""))
|
mr.set_taskname(task.get("taskname", ""))
|
||||||
account = Quark(config_data["cookie"][0])
|
account = Quark(config_data["cookie"][0])
|
||||||
@ -310,34 +413,64 @@ def get_share_detail():
|
|||||||
dir_file_list = []
|
dir_file_list = []
|
||||||
dir_filename_list = []
|
dir_filename_list = []
|
||||||
|
|
||||||
pattern, replace = mr.magic_regex_conv(
|
# 获取全局正则和任务正则
|
||||||
|
global_regex_enabled = global_regex.get("enabled", False)
|
||||||
|
global_pattern = global_regex.get("pattern", "")
|
||||||
|
global_replace = global_regex.get("replace", "")
|
||||||
|
|
||||||
|
task_pattern, task_replace = mr.magic_regex_conv(
|
||||||
task.get("pattern", ""), task.get("replace", "")
|
task.get("pattern", ""), task.get("replace", "")
|
||||||
)
|
)
|
||||||
|
|
||||||
for share_file in data["list"]:
|
for share_file in data["list"]:
|
||||||
search_pattern = (
|
if share_file["dir"]:
|
||||||
task["update_subdir"]
|
search_pattern = task.get("update_subdir", "")
|
||||||
if share_file["dir"] and task.get("update_subdir")
|
|
||||||
else pattern
|
|
||||||
)
|
|
||||||
if re.search(search_pattern, share_file["file_name"]):
|
if re.search(search_pattern, share_file["file_name"]):
|
||||||
# 文件名重命名,目录不重命名
|
# 目录不进行重命名
|
||||||
file_name_re = (
|
share_file["file_name_re"] = share_file["file_name"]
|
||||||
share_file["file_name"]
|
continue
|
||||||
if share_file["dir"]
|
|
||||||
else mr.sub(pattern, replace, share_file["file_name"])
|
# 初始化重命名后的文件名为原始文件名
|
||||||
|
renamed_name = share_file["file_name"]
|
||||||
|
applied_global_regex = False
|
||||||
|
applied_task_regex = False
|
||||||
|
|
||||||
|
# 应用全局正则(如果启用)
|
||||||
|
if global_regex_enabled and global_pattern:
|
||||||
|
if re.search(global_pattern, renamed_name):
|
||||||
|
global_renamed_name = re.sub(
|
||||||
|
global_pattern, global_replace, renamed_name
|
||||||
)
|
)
|
||||||
|
renamed_name = global_renamed_name
|
||||||
|
applied_global_regex = True
|
||||||
|
|
||||||
|
# 应用任务正则(如果存在)
|
||||||
|
if task_pattern:
|
||||||
|
if re.search(task_pattern, renamed_name):
|
||||||
|
task_renamed_name = mr.sub(task_pattern, task_replace, renamed_name)
|
||||||
|
renamed_name = task_renamed_name
|
||||||
|
applied_task_regex = True
|
||||||
|
|
||||||
|
# 检查是否应用了任何正则
|
||||||
|
if applied_global_regex or applied_task_regex:
|
||||||
if file_name_saved := mr.is_exists(
|
if file_name_saved := mr.is_exists(
|
||||||
file_name_re,
|
renamed_name,
|
||||||
dir_filename_list,
|
dir_filename_list,
|
||||||
(task.get("ignore_extension") and not share_file["dir"]),
|
(task.get("ignore_extension") and not share_file["dir"]),
|
||||||
):
|
):
|
||||||
share_file["file_name_saved"] = file_name_saved
|
share_file["file_name_saved"] = file_name_saved
|
||||||
else:
|
else:
|
||||||
share_file["file_name_re"] = file_name_re
|
share_file["file_name_re"] = renamed_name
|
||||||
|
|
||||||
# 文件列表排序
|
# 文件列表排序
|
||||||
if re.search(r"\{I+\}", replace):
|
replace_for_i = ""
|
||||||
mr.set_dir_file_list(dir_file_list, replace)
|
if task.get("replace"):
|
||||||
|
replace_for_i = task_replace
|
||||||
|
elif global_regex_enabled and global_replace:
|
||||||
|
replace_for_i = global_replace
|
||||||
|
|
||||||
|
if replace_for_i and re.search(r"\{I+\}", replace_for_i):
|
||||||
|
mr.set_dir_file_list(dir_file_list, replace_for_i)
|
||||||
mr.sort_file_list(data["list"])
|
mr.sort_file_list(data["list"])
|
||||||
|
|
||||||
if request.json.get("task"):
|
if request.json.get("task"):
|
||||||
@ -375,8 +508,9 @@ def get_savepath_detail():
|
|||||||
return jsonify({"success": False, "data": {"error": "获取fid失败"}})
|
return jsonify({"success": False, "data": {"error": "获取fid失败"}})
|
||||||
else:
|
else:
|
||||||
fid = request.args.get("fid", "0")
|
fid = request.args.get("fid", "0")
|
||||||
|
|
||||||
file_list = {
|
file_list = {
|
||||||
"list": account.ls_dir(fid)["data"]["list"],
|
"list": account.ls_dir(fid),
|
||||||
"paths": paths,
|
"paths": paths,
|
||||||
}
|
}
|
||||||
return jsonify({"success": True, "data": file_list})
|
return jsonify({"success": True, "data": file_list})
|
||||||
@ -471,8 +605,57 @@ def init():
|
|||||||
# 读取配置
|
# 读取配置
|
||||||
config_data = Config.read_json(CONFIG_PATH)
|
config_data = Config.read_json(CONFIG_PATH)
|
||||||
Config.breaking_change_update(config_data)
|
Config.breaking_change_update(config_data)
|
||||||
|
|
||||||
|
# --- 开始进行配置项向后兼容更新 ---
|
||||||
|
|
||||||
|
# 1. 确保 magic_regex 存在
|
||||||
if not config_data.get("magic_regex"):
|
if not config_data.get("magic_regex"):
|
||||||
config_data["magic_regex"] = MagicRename().magic_regex
|
config_data["magic_regex"] = MagicRename().magic_regex
|
||||||
|
logging.info("配置升级:已补全 'magic_regex' 默认配置。")
|
||||||
|
|
||||||
|
# 2. 确保 global_regex 存在
|
||||||
|
if not config_data.get("global_regex"):
|
||||||
|
config_data["global_regex"] = {"enabled": False, "pattern": "", "replace": ""}
|
||||||
|
logging.info("配置升级:已补全 'global_regex' 默认配置。")
|
||||||
|
|
||||||
|
# 3. 确保 file_blacklist 存在
|
||||||
|
if "file_blacklist" not in config_data:
|
||||||
|
config_data["file_blacklist"] = []
|
||||||
|
logging.info("配置升级:已补全 'file_blacklist' 默认配置。")
|
||||||
|
|
||||||
|
# 4. 确保 source 存在
|
||||||
|
if "source" not in config_data:
|
||||||
|
config_data["source"] = {
|
||||||
|
"cloudsaver": {"server": "", "username": "", "password": "", "token": ""}
|
||||||
|
}
|
||||||
|
logging.info("配置升级:已补全 'source' 默认配置。")
|
||||||
|
|
||||||
|
# 5. 确保 shortcuts 及其所有子项存在
|
||||||
|
if "shortcuts" not in config_data:
|
||||||
|
config_data["shortcuts"] = {
|
||||||
|
"saveEnabled": True,
|
||||||
|
"runEnabled": False,
|
||||||
|
"autoSaveEnabled": True,
|
||||||
|
}
|
||||||
|
logging.info("配置升级:已补全 'shortcuts' 默认配置。")
|
||||||
|
else:
|
||||||
|
if "saveEnabled" not in config_data["shortcuts"]:
|
||||||
|
config_data["shortcuts"]["saveEnabled"] = True
|
||||||
|
if "runEnabled" not in config_data["shortcuts"]:
|
||||||
|
config_data["shortcuts"]["runEnabled"] = False
|
||||||
|
if "autoSaveEnabled" not in config_data["shortcuts"]:
|
||||||
|
config_data["shortcuts"]["autoSaveEnabled"] = True
|
||||||
|
logging.info("配置升级:为 'shortcuts' 已补全 'autoSaveEnabled' 子项。")
|
||||||
|
|
||||||
|
# 6. 确保 tasklist 中每个任务都包含新参数 (例如 ignore_extension)
|
||||||
|
if config_data.get("tasklist") and isinstance(config_data["tasklist"], list):
|
||||||
|
for task in config_data["tasklist"]:
|
||||||
|
if "ignore_extension" not in task:
|
||||||
|
task["ignore_extension"] = False
|
||||||
|
if "addition" not in task:
|
||||||
|
task["addition"] = {}
|
||||||
|
|
||||||
|
# --- 配置项向后兼容更新结束 ---
|
||||||
|
|
||||||
# 默认管理账号
|
# 默认管理账号
|
||||||
config_data["webui"] = {
|
config_data["webui"] = {
|
||||||
@ -491,10 +674,42 @@ def init():
|
|||||||
plugins_config_default.update(config_data.get("plugins", {}))
|
plugins_config_default.update(config_data.get("plugins", {}))
|
||||||
config_data["plugins"] = plugins_config_default
|
config_data["plugins"] = plugins_config_default
|
||||||
|
|
||||||
# 更新配置
|
# 更新配置,将所有补全的默认值写回文件
|
||||||
Config.write_json(CONFIG_PATH, config_data)
|
Config.write_json(CONFIG_PATH, config_data)
|
||||||
|
|
||||||
|
|
||||||
|
# 添加一个重置任务状态的路由
|
||||||
|
@app.route("/reset_task_status", methods=["POST"])
|
||||||
|
def reset_task_status():
|
||||||
|
if not is_login():
|
||||||
|
return jsonify({"success": False, "message": "未登录"})
|
||||||
|
|
||||||
|
global task_running, current_process
|
||||||
|
|
||||||
|
# 如果有正在运行的进程,尝试终止它
|
||||||
|
with process_lock:
|
||||||
|
if current_process is not None:
|
||||||
|
try:
|
||||||
|
if current_process.poll() is None: # 进程仍在运行
|
||||||
|
current_process.terminate()
|
||||||
|
current_process.wait(timeout=5) # 等待最多5秒
|
||||||
|
current_process = None
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"终止进程失败: {str(e)}")
|
||||||
|
|
||||||
|
# 重置任务状态
|
||||||
|
task_running = False
|
||||||
|
|
||||||
|
# 清空日志
|
||||||
|
with log_lock:
|
||||||
|
current_log.seek(0)
|
||||||
|
current_log.truncate(0)
|
||||||
|
current_log.write("任务状态已重置\n")
|
||||||
|
|
||||||
|
logging.info(">>> 任务状态已手动重置")
|
||||||
|
return jsonify({"success": True, "message": "任务状态已重置"})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
init()
|
init()
|
||||||
reload_tasks()
|
reload_tasks()
|
||||||
|
|||||||
@ -2,14 +2,15 @@
|
|||||||
// @name QAS一键推送助手
|
// @name QAS一键推送助手
|
||||||
// @namespace https://github.com/Cp0204/quark-auto-save
|
// @namespace https://github.com/Cp0204/quark-auto-save
|
||||||
// @license AGPL
|
// @license AGPL
|
||||||
// @version 0.4
|
// @version 0.7-custom
|
||||||
// @description 在夸克网盘分享页面添加推送到 QAS 的按钮
|
// @description 在夸克网盘分享页面添加推送到 QAS 的按钮。修改为直接获取文件名作为任务名,且不将任务名附加到保存路径。
|
||||||
// @icon https://pan.quark.cn/favicon.ico
|
// @icon https://pan.quark.cn/favicon.ico
|
||||||
// @author Cp0204
|
// @author Cp0204 (Modified by Gemini)
|
||||||
// @match https://pan.quark.cn/s/*
|
// @match https://pan.quark.cn/s/*
|
||||||
// @grant GM_getValue
|
// @grant GM_getValue
|
||||||
// @grant GM_setValue
|
// @grant GM_setValue
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
|
// @grant GM_registerMenuCommand
|
||||||
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
|
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
|
||||||
// @downloadURL https://cdn.jsdelivr.net/gh/Cp0204/quark-auto-save@refs/heads/main/app/static/js/qas.addtask.user.js
|
// @downloadURL https://cdn.jsdelivr.net/gh/Cp0204/quark-auto-save@refs/heads/main/app/static/js/qas.addtask.user.js
|
||||||
// @updateURL https://cdn.jsdelivr.net/gh/Cp0204/quark-auto-save@refs/heads/main/app/static/js/qas.addtask.user.js
|
// @updateURL https://cdn.jsdelivr.net/gh/Cp0204/quark-auto-save@refs/heads/main/app/static/js/qas.addtask.user.js
|
||||||
@ -30,23 +31,24 @@
|
|||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
html: `
|
html: `
|
||||||
<label for="qas_base">QAS 地址</label>
|
<label for="qas_base">QAS 地址</label>
|
||||||
<input id="qas_base" class="swal2-input" placeholder="如: http://192.168.1.8:5005" value="${qas_base}"><br>
|
<input id="qas_base" class="swal2-input" placeholder="如: http://192.168.1.8:5005" value="${qas_base}">
|
||||||
<label for="qas_token">QAS Token</label>
|
<label for="qas_token">QAS Token</label>
|
||||||
<input id="qas_token" class="swal2-input" placeholder="v0.5+ 系统配置中查找" value="${qas_token}"><br>
|
<input id="qas_token" class="swal2-input" placeholder="v0.5+ 系统配置中查找" value="${qas_token}">
|
||||||
<label for="qas_token">默认正则</label>
|
<label for="default_pattern">默认正则</label>
|
||||||
<input id="default_pattern" class="swal2-input" placeholder="如 $TV" value="${default_pattern}"><br>
|
<input id="default_pattern" class="swal2-input" placeholder="如 $TV" value="${default_pattern}">
|
||||||
<label for="qas_token">默认替换</label><input id="default_replace" class="swal2-input" value="${default_replace}">
|
<label for="default_replace">默认替换</label>
|
||||||
|
<input id="default_replace" class="swal2-input" value="${default_replace}">
|
||||||
`,
|
`,
|
||||||
focusConfirm: false,
|
focusConfirm: false,
|
||||||
preConfirm: () => {
|
preConfirm: () => {
|
||||||
qas_base = document.getElementById('qas_base').value;
|
const base = document.getElementById('qas_base').value;
|
||||||
qas_token = document.getElementById('qas_token').value;
|
const token = document.getElementById('qas_token').value;
|
||||||
default_pattern = document.getElementById('default_pattern').value;
|
const pattern = document.getElementById('default_pattern').value;
|
||||||
default_replace = document.getElementById('default_replace').value;
|
const replace = document.getElementById('default_replace').value;
|
||||||
if (!qas_base || !qas_token) {
|
if (!base || !token) {
|
||||||
Swal.showValidationMessage('请填写 QAS 地址和 Token');
|
Swal.showValidationMessage('请填写 QAS 地址和 Token');
|
||||||
}
|
}
|
||||||
return { qas_base: qas_base, qas_token: qas_token, default_pattern: default_pattern, default_replace: default_replace }
|
return { qas_base: base, qas_token: token, default_pattern: pattern, default_replace: replace }
|
||||||
}
|
}
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
@ -54,39 +56,25 @@
|
|||||||
GM_setValue('qas_token', result.value.qas_token);
|
GM_setValue('qas_token', result.value.qas_token);
|
||||||
GM_setValue('default_pattern', result.value.default_pattern);
|
GM_setValue('default_pattern', result.value.default_pattern);
|
||||||
GM_setValue('default_replace', result.value.default_replace);
|
GM_setValue('default_replace', result.value.default_replace);
|
||||||
|
|
||||||
|
// Update live variables
|
||||||
qas_base = result.value.qas_base;
|
qas_base = result.value.qas_base;
|
||||||
qas_token = result.value.qas_token;
|
qas_token = result.value.qas_token;
|
||||||
default_pattern = result.value.default_pattern;
|
default_pattern = result.value.default_pattern;
|
||||||
default_replace = result.value.default_replace;
|
default_replace = result.value.default_replace;
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(); // 执行回调函数
|
callback(); // Execute the callback function if it exists
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加 QAS 设置按钮
|
// 注册油猴菜单命令
|
||||||
function addQASSettingButton() {
|
function registerMenuCommands() {
|
||||||
function waitForElement(selector, callback) {
|
GM_registerMenuCommand('QAS 设置', () => {
|
||||||
const element = document.querySelector(selector);
|
|
||||||
if (element) {
|
|
||||||
callback(element);
|
|
||||||
} else {
|
|
||||||
setTimeout(() => waitForElement(selector, callback), 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForElement('.DetailLayout--client-download--FpyCkdW.ant-dropdown-trigger', (clientDownloadButton) => {
|
|
||||||
const qasSettingButton = document.createElement('div');
|
|
||||||
qasSettingButton.className = 'DetailLayout--client-download--FpyCkdW ant-dropdown-trigger';
|
|
||||||
qasSettingButton.innerHTML = 'QAS设置';
|
|
||||||
|
|
||||||
qasSettingButton.addEventListener('click', () => {
|
|
||||||
showQASSettingDialog();
|
showQASSettingDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
clientDownloadButton.parentNode.insertBefore(qasSettingButton, clientDownloadButton.nextSibling);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 推送到 QAS 按钮
|
// 推送到 QAS 按钮
|
||||||
@ -107,31 +95,36 @@
|
|||||||
qasButton.style.marginLeft = '10px';
|
qasButton.style.marginLeft = '10px';
|
||||||
qasButton.innerHTML = '<span class="share-save-ico"></span><span>创建QAS任务</span>';
|
qasButton.innerHTML = '<span class="share-save-ico"></span><span>创建QAS任务</span>';
|
||||||
|
|
||||||
let taskname, shareurl, savepath; // 声明变量
|
let taskname, shareurl, savepath; // Declare variables
|
||||||
|
|
||||||
// 获取数据函数
|
|
||||||
function getData() {
|
function getData() {
|
||||||
const currentUrl = window.location.href;
|
const currentUrl = window.location.href;
|
||||||
taskname = currentUrl.lastIndexOf('-') > 0 ? decodeURIComponent(currentUrl.match(/.*\/[^-]+-(.+)$/)[1]).replace('*101', '-') : document.querySelector('.author-name').textContent;
|
|
||||||
|
// ==================== 修改点 1: taskname 获取方式 ====================
|
||||||
|
// 直接从页面文件列表的第一个文件名中获取任务名
|
||||||
|
const fileNameElement = document.querySelector('.filename-text');
|
||||||
|
taskname = fileNameElement ? fileNameElement.title : '未知任务'; // 使用 .title 以获取完整文件名
|
||||||
|
|
||||||
shareurl = currentUrl;
|
shareurl = currentUrl;
|
||||||
let pathElement = document.querySelector('.path-name');
|
|
||||||
|
// ==================== 修改点 2: savepath 获取方式 ====================
|
||||||
|
const pathElement = document.querySelector('.path-name');
|
||||||
|
// 只获取路径,不附加任务名
|
||||||
savepath = pathElement ? pathElement.title.replace('全部文件', '').trim() : "";
|
savepath = pathElement ? pathElement.title.replace('全部文件', '').trim() : "";
|
||||||
savepath += "/" + taskname;
|
|
||||||
|
// 如果 savepath 为空(即根目录),则设置为 "/"
|
||||||
|
if (savepath === "") {
|
||||||
|
savepath = "/";
|
||||||
|
}
|
||||||
|
|
||||||
qasButton.title = `任务名称: ${taskname}\n分享链接: ${shareurl}\n保存路径: ${savepath}`;
|
qasButton.title = `任务名称: ${taskname}\n分享链接: ${shareurl}\n保存路径: ${savepath}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qasButton.addEventListener('mouseover', getData);
|
||||||
|
|
||||||
// 添加鼠标悬停事件
|
function createQASTask() {
|
||||||
qasButton.addEventListener('mouseover', () => {
|
getData();
|
||||||
getData(); // 鼠标悬停时获取数据
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// 添加点击事件
|
|
||||||
qasButton.addEventListener('click', () => {
|
|
||||||
getData(); // 点击时重新获取数据,确保最新
|
|
||||||
|
|
||||||
// 检查 qas_base 是否包含 http 或 https,如果没有则添加 http://
|
|
||||||
let qasApiBase = qas_base;
|
let qasApiBase = qas_base;
|
||||||
if (!qasApiBase.startsWith('http')) {
|
if (!qasApiBase.startsWith('http')) {
|
||||||
qasApiBase = 'http://' + qasApiBase;
|
qasApiBase = 'http://' + qasApiBase;
|
||||||
@ -163,7 +156,7 @@
|
|||||||
<b>任务名称:</b> ${taskname}<br><br>
|
<b>任务名称:</b> ${taskname}<br><br>
|
||||||
<b>保存路径:</b> ${savepath}<br><br>
|
<b>保存路径:</b> ${savepath}<br><br>
|
||||||
<a href="${qasApiBase}" target="_blank">去 QAS 查看</a>
|
<a href="${qasApiBase}" target="_blank">去 QAS 查看</a>
|
||||||
<small>`,
|
</small>`,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -182,29 +175,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onerror: function (error) {
|
onerror: function (error) {
|
||||||
|
console.error("QAS Connection Error Details:", error);
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '任务创建失败',
|
title: '任务创建失败',
|
||||||
text: error,
|
html: '无法连接到您的 QAS 服务器。<br>这很可能是一个网络或配置问题。',
|
||||||
icon: 'error'
|
icon: 'error',
|
||||||
|
footer: '<div style="text-align: left; font-size: 0.9em; line-height: 1.5;"><b>请检查以下几点:</b><br>' +
|
||||||
|
'1. QAS 地址 (<code>' + qas_base + '</code>) 是否填写正确?<br>' +
|
||||||
|
'2. 运行 QAS 的设备(如NAS、电脑)是否已开机并在同一网络下?<br>' +
|
||||||
|
'3. QAS 服务程序是否已正常启动?<br>' +
|
||||||
|
'4. 您能否在浏览器新标签页中直接访问您的 QAS 地址?</div>'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
qasButton.addEventListener('click', () => {
|
||||||
|
if (!qas_base || !qas_token) {
|
||||||
|
showQASSettingDialog(createQASTask);
|
||||||
|
} else {
|
||||||
|
createQASTask();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
saveButton.parentNode.insertBefore(qasButton, saveButton.nextSibling);
|
saveButton.parentNode.insertBefore(qasButton, saveButton.nextSibling);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
|
||||||
(function init() {
|
(function init() {
|
||||||
addQASSettingButton();
|
registerMenuCommands();
|
||||||
|
addQASButton();
|
||||||
if (!qas_base || !qas_token) {
|
})();
|
||||||
showQASSettingDialog(() => {
|
|
||||||
addQASButton(); // 在设置后添加 QAS 按钮
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
addQASButton(); // 如果配置存在,则直接添加 QAS 按钮
|
|
||||||
}
|
|
||||||
})(); // 立即执行初始化
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1462
quark_auto_save.py
1462
quark_auto_save.py
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,51 @@
|
|||||||
{
|
{
|
||||||
"cookie": [
|
"cookie": [
|
||||||
"Your pan.quark.cn Cookie1, Only this one will do save task."
|
"在此处填写你的夸克网盘Cookie。第一个Cookie将用于执行转存任务。",
|
||||||
|
"可以填写多个Cookie作为备用,以防第一个Cookie失效。"
|
||||||
],
|
],
|
||||||
"push_config": {
|
"push_config": {
|
||||||
"QUARK_SIGN_NOTIFY": true,
|
"QUARK_SIGN_NOTIFY": true,
|
||||||
|
"PUSH_PLUS_TOKEN": "",
|
||||||
"其他推送渠道//此项可删": "配置方法同青龙"
|
"其他推送渠道//此项可删": "配置方法同青龙"
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
|
"alist": {
|
||||||
|
"url": "",
|
||||||
|
"token": "",
|
||||||
|
"storage_id": ""
|
||||||
|
},
|
||||||
|
"alist_strm": {
|
||||||
|
"url": "",
|
||||||
|
"cookie": "",
|
||||||
|
"config_id": ""
|
||||||
|
},
|
||||||
|
"alist_strm_gen": {
|
||||||
|
"url": "",
|
||||||
|
"token": "",
|
||||||
|
"storage_id": "",
|
||||||
|
"strm_save_dir": "/media",
|
||||||
|
"strm_replace_host": ""
|
||||||
|
},
|
||||||
|
"alist_sync": {
|
||||||
|
"url": "",
|
||||||
|
"token": "",
|
||||||
|
"quark_storage_id": "",
|
||||||
|
"save_storage_id": "",
|
||||||
|
"tv_mode": ""
|
||||||
|
},
|
||||||
|
"aria2": {
|
||||||
|
"host_port": "http://127.0.0.1:6800/jsonrpc",
|
||||||
|
"secret": "",
|
||||||
|
"dir": "/Downloads"
|
||||||
|
},
|
||||||
"emby": {
|
"emby": {
|
||||||
"url": "",
|
"url": "",
|
||||||
"token": ""
|
"token": ""
|
||||||
|
},
|
||||||
|
"plex": {
|
||||||
|
"url": "",
|
||||||
|
"token": "",
|
||||||
|
"quark_root_path": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"magic_regex": {
|
"magic_regex": {
|
||||||
@ -30,43 +66,81 @@
|
|||||||
"replace": "{TASKNAME}.{SXX}E{E}.{EXT}"
|
"replace": "{TASKNAME}.{SXX}E{E}.{EXT}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"global_regex": {
|
||||||
|
"enabled": false,
|
||||||
|
"pattern": "^(.*?)(广告内容1|广告内容2)?(\\.\\w+)?$",
|
||||||
|
"replace": "\\1\\3"
|
||||||
|
},
|
||||||
|
"file_blacklist": [
|
||||||
|
"要全局忽略的文件名或关键词1",
|
||||||
|
"要全局忽略的文件名或关键词2.txt"
|
||||||
|
],
|
||||||
"tasklist": [
|
"tasklist": [
|
||||||
{
|
{
|
||||||
|
"id": "task_example_1",
|
||||||
"taskname": "测试-魔法匹配剧集(这是一组有效分享,配置CK后可测试任务是否正常)",
|
"taskname": "测试-魔法匹配剧集(这是一组有效分享,配置CK后可测试任务是否正常)",
|
||||||
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-夸克自动转存测试",
|
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-夸克自动转存测试",
|
||||||
"savepath": "/夸克自动转存测试/剧集",
|
"savepath": "/夸克自动转存测试/剧集",
|
||||||
"pattern": "$TV_REGEX",
|
"pattern": "$TV_REGEX",
|
||||||
"replace": "",
|
"replace": "",
|
||||||
"enddate": "2099-01-30",
|
"enddate": "2099-01-30",
|
||||||
"update_subdir": "4k|1080p"
|
"update_subdir": "4k|1080p",
|
||||||
|
"runweek": [1, 2, 3, 4, 5, 6, 7],
|
||||||
|
"addition": {
|
||||||
|
"aria2": {
|
||||||
|
"auto_download": false
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "task_example_2",
|
||||||
"taskname": "测试-综艺命名",
|
"taskname": "测试-综艺命名",
|
||||||
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-%E5%A4%B8%E5%85%8B%E8%87%AA%E5%8A%A8%E8%BD%AC%E5%AD%98%E6%B5%8B%E8%AF%95/71df3902f42d4270a58c0eb12aa2b014-%E7%BB%BC%E8%89%BA%E5%91%BD%E5%90%8D",
|
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-%E5%A4%B8%E5%85%8B%E8%87%AA%E5%8A%A8%E8%BD%AC%E5%AD%98%E6%B5%8B%E8%AF%95/71df3902f42d4270a58c0eb12aa2b014-%E7%BB%BC%E8%89%BA%E5%91%BD%E5%90%8D",
|
||||||
"savepath": "/夸克自动转存测试/综艺命名",
|
"savepath": "/夸克自动转存测试/综艺命名",
|
||||||
"pattern": "^(?!.*纯享)(?!.*加更)(?!.*抢先)(?!.*预告).*?第\\d+期.*",
|
"pattern": "^(?!.*纯享)(?!.*加更)(?!.*抢先)(?!.*预告).*?第\\d+期.*",
|
||||||
"replace": "{II}.{TASKNAME}.{DATE}.第{E}期{PART}.{EXT}"
|
"replace": "{II}.{TASKNAME}.{DATE}.第{E}期{PART}.{EXT}",
|
||||||
|
"runweek": [1, 2, 3, 4, 5, 6, 7],
|
||||||
|
"addition": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "task_example_3",
|
||||||
"taskname": "测试-去广告字符",
|
"taskname": "测试-去广告字符",
|
||||||
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-夸克自动转存测试/680d91e490814da0927c38b432f88edc-带广告文件夹",
|
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-夸克自动转存测试/680d91e490814da0927c38b432f88edc-带广告文件夹",
|
||||||
"savepath": "/夸克自动转存测试/去广告字符",
|
"savepath": "/夸克自动转存测试/去广告字符",
|
||||||
"pattern": "【XX电影网】(.*)\\.(mp4|mkv)",
|
"pattern": "【XX电影网】(.*)\\.(mp4|mkv)",
|
||||||
"replace": "\\1.\\2",
|
"replace": "\\1.\\2",
|
||||||
"enddate": "2099-01-30"
|
"enddate": "2099-01-30",
|
||||||
|
"runweek": [1, 2, 3, 4, 5, 6, 7],
|
||||||
|
"addition": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"id": "task_example_4",
|
||||||
"taskname": "测试-超期任务",
|
"taskname": "测试-超期任务",
|
||||||
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-夸克自动转存测试",
|
"shareurl": "https://pan.quark.cn/s/d07a34a9c695#/list/share/7e25ddd87cf64443b637125478733295-夸克自动转存测试",
|
||||||
"savepath": "/夸克自动转存测试/不会运行",
|
"savepath": "/夸克自动转存测试/不会运行",
|
||||||
"pattern": "",
|
"pattern": "",
|
||||||
"replace": "",
|
"replace": "",
|
||||||
"enddate": "2000-01-30",
|
"enddate": "2000-01-30",
|
||||||
"runweek": [
|
"runweek": [2, 4, 6],
|
||||||
2,
|
"addition": {}
|
||||||
4,
|
}
|
||||||
6
|
],
|
||||||
]
|
"webui": {
|
||||||
|
"username": "admin",
|
||||||
|
"password": "admin123"
|
||||||
|
},
|
||||||
|
"crontab": "0 7-23/2 * * *",
|
||||||
|
"source": {
|
||||||
|
"cloudsaver": {
|
||||||
|
"server": "",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"token": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shortcuts": {
|
||||||
|
"saveEnabled": true,
|
||||||
|
"runEnabled": false,
|
||||||
|
"autoSaveEnabled": true
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user