mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-19 19:49:36 +08:00
Compare commits
No commits in common. "7c20a03c5ff567bdb8d438accaad1f3c34a52405" and "cd435a5818d4720796aeaf98dcb4b7144b9f480b" have entirely different histories.
7c20a03c5f
...
cd435a5818
455
app/run.py
455
app/run.py
@ -1,7 +1,6 @@
|
|||||||
# !/usr/bin/env python3
|
# !/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from flask import (
|
from flask import (
|
||||||
json,
|
|
||||||
Flask,
|
Flask,
|
||||||
url_for,
|
url_for,
|
||||||
session,
|
session,
|
||||||
@ -15,16 +14,15 @@ from flask import (
|
|||||||
)
|
)
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
from sdk.cloudsaver import CloudSaver
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import subprocess
|
import subprocess
|
||||||
import requests
|
import requests
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
|
||||||
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)
|
||||||
@ -48,10 +46,9 @@ PYTHON_PATH = "python3" if os.path.exists("/usr/bin/python3") else "python"
|
|||||||
SCRIPT_PATH = os.environ.get("SCRIPT_PATH", "./quark_auto_save.py")
|
SCRIPT_PATH = os.environ.get("SCRIPT_PATH", "./quark_auto_save.py")
|
||||||
CONFIG_PATH = os.environ.get("CONFIG_PATH", "./config/quark_config.json")
|
CONFIG_PATH = os.environ.get("CONFIG_PATH", "./config/quark_config.json")
|
||||||
PLUGIN_FLAGS = os.environ.get("PLUGIN_FLAGS", "")
|
PLUGIN_FLAGS = os.environ.get("PLUGIN_FLAGS", "")
|
||||||
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
|
DEBUG = os.environ.get("DEBUG", False)
|
||||||
|
|
||||||
config_data = {}
|
task_plugins_config = {}
|
||||||
task_plugins_config_default = {}
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config["APP_VERSION"] = get_app_ver()
|
app.config["APP_VERSION"] = get_app_ver()
|
||||||
@ -80,15 +77,24 @@ def gen_md5(string):
|
|||||||
return md5.hexdigest()
|
return md5.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def get_login_token():
|
# 读取 JSON 文件内容
|
||||||
username = config_data["webui"]["username"]
|
def read_json():
|
||||||
password = config_data["webui"]["password"]
|
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
||||||
return gen_md5(f"token{username}{password}+-*/")[8:24]
|
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 is_login():
|
def is_login():
|
||||||
login_token = get_login_token()
|
data = read_json()
|
||||||
if session.get("token") == login_token or request.args.get("token") == login_token:
|
username = data["webui"]["username"]
|
||||||
|
password = data["webui"]["password"]
|
||||||
|
if session.get("login") == gen_md5(username + password):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@ -108,15 +114,16 @@ def favicon():
|
|||||||
@app.route("/login", methods=["GET", "POST"])
|
@app.route("/login", methods=["GET", "POST"])
|
||||||
def login():
|
def login():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
username = config_data["webui"]["username"]
|
data = read_json()
|
||||||
password = config_data["webui"]["password"]
|
username = data["webui"]["username"]
|
||||||
|
password = data["webui"]["password"]
|
||||||
# 验证用户名和密码
|
# 验证用户名和密码
|
||||||
if (username == request.form.get("username")) and (
|
if (username == request.form.get("username")) and (
|
||||||
password == request.form.get("password")
|
password == request.form.get("password")
|
||||||
):
|
):
|
||||||
logging.info(f">>> 用户 {username} 登录成功")
|
logging.info(f">>> 用户 {username} 登录成功")
|
||||||
|
session["login"] = gen_md5(username + password)
|
||||||
session.permanent = True
|
session.permanent = True
|
||||||
session["token"] = get_login_token()
|
|
||||||
return redirect(url_for("index"))
|
return redirect(url_for("index"))
|
||||||
else:
|
else:
|
||||||
logging.info(f">>> 用户 {username} 登录失败")
|
logging.info(f">>> 用户 {username} 登录失败")
|
||||||
@ -130,7 +137,7 @@ def login():
|
|||||||
# 退出登录
|
# 退出登录
|
||||||
@app.route("/logout")
|
@app.route("/logout")
|
||||||
def logout():
|
def logout():
|
||||||
session.pop("token", None)
|
session.pop("login", None)
|
||||||
return redirect(url_for("login"))
|
return redirect(url_for("login"))
|
||||||
|
|
||||||
|
|
||||||
@ -148,51 +155,47 @@ def index():
|
|||||||
@app.route("/data")
|
@app.route("/data")
|
||||||
def get_data():
|
def get_data():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return redirect(url_for("login"))
|
||||||
data = Config.read_json(CONFIG_PATH)
|
data = read_json()
|
||||||
del data["webui"]
|
del data["webui"]
|
||||||
data["api_token"] = get_login_token()
|
data["task_plugins_config"] = task_plugins_config
|
||||||
data["task_plugins_config_default"] = task_plugins_config_default
|
return jsonify(data)
|
||||||
return jsonify({"success": True, "data": data})
|
|
||||||
|
|
||||||
|
|
||||||
# 更新数据
|
# 更新数据
|
||||||
@app.route("/update", methods=["POST"])
|
@app.route("/update", methods=["POST"])
|
||||||
def update():
|
def update():
|
||||||
global config_data
|
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return "未登录"
|
||||||
dont_save_keys = ["task_plugins_config_default", "api_token"]
|
data = request.json
|
||||||
for key, value in request.json.items():
|
data["webui"] = read_json()["webui"]
|
||||||
if key not in dont_save_keys:
|
if "task_plugins_config" in data:
|
||||||
config_data.update({key: value})
|
del data["task_plugins_config"]
|
||||||
Config.write_json(CONFIG_PATH, config_data)
|
write_json(data)
|
||||||
# 重新加载任务
|
# 重新加载任务
|
||||||
if reload_tasks():
|
if reload_tasks():
|
||||||
logging.info(f">>> 配置更新成功")
|
logging.info(f">>> 配置更新成功")
|
||||||
return jsonify({"success": True, "message": "配置更新成功"})
|
return "配置更新成功"
|
||||||
else:
|
else:
|
||||||
logging.info(f">>> 配置更新失败")
|
logging.info(f">>> 配置更新失败")
|
||||||
return jsonify({"success": False, "message": "配置更新失败"})
|
return "配置更新失败"
|
||||||
|
|
||||||
|
|
||||||
# 处理运行脚本请求
|
# 处理运行脚本请求
|
||||||
@app.route("/run_script_now", methods=["POST"])
|
@app.route("/run_script_now", methods=["GET"])
|
||||||
def run_script_now():
|
def run_script_now():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return "未登录"
|
||||||
tasklist = request.json.get("tasklist", [])
|
task_index = request.args.get("task_index", "")
|
||||||
command = [PYTHON_PATH, "-u", SCRIPT_PATH, CONFIG_PATH]
|
command = [PYTHON_PATH, "-u", SCRIPT_PATH, CONFIG_PATH, task_index]
|
||||||
logging.info(
|
logging.info(
|
||||||
f">>> 手动运行任务 [{tasklist[0].get('taskname') if len(tasklist)>0 else 'ALL'}] 开始执行..."
|
f">>> 手动运行任务{int(task_index)+1 if task_index.isdigit() else 'all'}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_output():
|
def generate_output():
|
||||||
# 设置环境变量
|
# 设置环境变量
|
||||||
process_env = os.environ.copy()
|
process_env = os.environ.copy()
|
||||||
process_env["PYTHONIOENCODING"] = "utf-8"
|
process_env["PYTHONIOENCODING"] = "utf-8"
|
||||||
if tasklist:
|
|
||||||
process_env["TASKLIST"] = json.dumps(tasklist, ensure_ascii=False)
|
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
command,
|
command,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
@ -221,324 +224,64 @@ def run_script_now():
|
|||||||
@app.route("/task_suggestions")
|
@app.route("/task_suggestions")
|
||||||
def get_task_suggestions():
|
def get_task_suggestions():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return jsonify({"error": "未登录"})
|
||||||
|
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
|
||||||
query = request.args.get("q", "").lower()
|
query = request.args.get("q", "").lower()
|
||||||
deep = request.args.get("d", "").lower()
|
deep = request.args.get("d", "").lower()
|
||||||
|
url = f"{base_url}/task_suggestions?q={query}&d={deep}"
|
||||||
try:
|
try:
|
||||||
cs_data = config_data.get("source", {}).get("cloudsaver", {})
|
response = requests.get(url)
|
||||||
if (
|
return jsonify(response.json())
|
||||||
cs_data.get("server")
|
|
||||||
and cs_data.get("username")
|
|
||||||
and cs_data.get("password")
|
|
||||||
):
|
|
||||||
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, "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={query}&d={deep}"
|
|
||||||
response = requests.get(url)
|
|
||||||
return jsonify(
|
|
||||||
{"success": True, "source": "网络公开", "data": response.json()}
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"success": True, "message": f"error: {str(e)}"})
|
return jsonify({"error": str(e)})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/get_share_detail", methods=["POST"])
|
@app.route("/get_share_detail")
|
||||||
def get_share_detail():
|
def get_share_files():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return jsonify({"error": "未登录"})
|
||||||
shareurl = request.json.get("shareurl", "")
|
shareurl = request.args.get("shareurl", "")
|
||||||
stoken = request.json.get("stoken", "")
|
|
||||||
account = Quark("", 0)
|
account = Quark("", 0)
|
||||||
pwd_id, passcode, pdir_fid, paths = account.extract_url(shareurl)
|
pwd_id, passcode, pdir_fid = account.get_id_from_url(shareurl)
|
||||||
if not stoken:
|
is_sharing, stoken = account.get_stoken(pwd_id, passcode)
|
||||||
is_sharing, stoken = account.get_stoken(pwd_id, passcode)
|
if not is_sharing:
|
||||||
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)
|
||||||
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, _fetch_share=1)
|
return jsonify(share_detail)
|
||||||
share_detail["paths"] = paths
|
|
||||||
share_detail["stoken"] = stoken
|
|
||||||
|
|
||||||
# 正则命名预览
|
|
||||||
def preview_regex(share_detail):
|
|
||||||
regex = request.json.get("regex")
|
|
||||||
# 检查是否为顺序命名模式
|
|
||||||
if regex.get("use_sequence_naming") and regex.get("sequence_naming"):
|
|
||||||
# 顺序命名模式预览
|
|
||||||
sequence_pattern = regex.get("sequence_naming")
|
|
||||||
current_sequence = 1
|
|
||||||
|
|
||||||
# 构建顺序命名的正则表达式
|
|
||||||
regex_pattern = re.escape(sequence_pattern).replace('\\{\\}', '(\\d+)')
|
|
||||||
|
|
||||||
# 实现高级排序算法
|
|
||||||
def extract_sorting_value(file):
|
|
||||||
if file["dir"]: # 跳过文件夹
|
|
||||||
return float('inf')
|
|
||||||
|
|
||||||
filename = file["file_name"]
|
|
||||||
|
|
||||||
# 提取文件名,不含扩展名
|
|
||||||
file_name_without_ext = os.path.splitext(filename)[0]
|
|
||||||
|
|
||||||
# 1. "第X期/集/话" 格式
|
|
||||||
match_chinese = re.search(r'第(\d+)[期集话]', filename)
|
|
||||||
episode_num = int(match_chinese.group(1)) if match_chinese else 0
|
|
||||||
|
|
||||||
# 5. 文件名含"上中下"(优先处理,因为可能与其他格式同时存在)
|
|
||||||
if match_chinese:
|
|
||||||
# 如果同时存在集数和上中下,则按照集数*10+位置排序
|
|
||||||
if '上' in filename:
|
|
||||||
return episode_num * 10 + 1
|
|
||||||
elif '中' in filename:
|
|
||||||
return episode_num * 10 + 2
|
|
||||||
elif '下' in filename:
|
|
||||||
return episode_num * 10 + 3
|
|
||||||
elif '上' in filename:
|
|
||||||
return 1
|
|
||||||
elif '中' in filename:
|
|
||||||
return 2
|
|
||||||
elif '下' in filename:
|
|
||||||
return 3
|
|
||||||
|
|
||||||
# 如果已经匹配到"第X期/集/话"格式,直接返回
|
|
||||||
if episode_num > 0:
|
|
||||||
return episode_num * 10
|
|
||||||
|
|
||||||
# 2.1 S01E01 格式,提取季数和集数
|
|
||||||
match_s_e = re.search(r'[Ss](\d+)[Ee](\d+)', filename)
|
|
||||||
if match_s_e:
|
|
||||||
season = int(match_s_e.group(1))
|
|
||||||
episode = int(match_s_e.group(2))
|
|
||||||
return season * 1000 + episode
|
|
||||||
|
|
||||||
# 2.2 E01 格式,仅提取集数
|
|
||||||
match_e = re.search(r'[Ee][Pp]?(\d+)', filename)
|
|
||||||
if match_e:
|
|
||||||
return int(match_e.group(1))
|
|
||||||
|
|
||||||
# 2.3 1x01 格式,提取季数和集数
|
|
||||||
match_x = re.search(r'(\d+)[Xx](\d+)', filename)
|
|
||||||
if match_x:
|
|
||||||
season = int(match_x.group(1))
|
|
||||||
episode = int(match_x.group(2))
|
|
||||||
return season * 1000 + episode
|
|
||||||
|
|
||||||
# 3. 日期格式识别(支持多种格式)
|
|
||||||
|
|
||||||
# 3.1 完整的YYYYMMDD格式
|
|
||||||
match_date_compact = re.search(r'(20\d{2})(\d{2})(\d{2})', filename)
|
|
||||||
if match_date_compact:
|
|
||||||
year = int(match_date_compact.group(1))
|
|
||||||
month = int(match_date_compact.group(2))
|
|
||||||
day = int(match_date_compact.group(3))
|
|
||||||
return year * 10000 + month * 100 + day
|
|
||||||
|
|
||||||
# 3.2 YYYY-MM-DD 或 YYYY.MM.DD 或 YYYY/MM/DD 格式
|
|
||||||
match_date_full = re.search(r'(20\d{2})[-./](\d{1,2})[-./](\d{1,2})', filename)
|
|
||||||
if match_date_full:
|
|
||||||
year = int(match_date_full.group(1))
|
|
||||||
month = int(match_date_full.group(2))
|
|
||||||
day = int(match_date_full.group(3))
|
|
||||||
return year * 10000 + month * 100 + day
|
|
||||||
|
|
||||||
# 3.3 MM/DD/YYYY 或 DD/MM/YYYY 格式
|
|
||||||
match_date_alt = re.search(r'(\d{1,2})[-./](\d{1,2})[-./](20\d{2})', filename)
|
|
||||||
if match_date_alt:
|
|
||||||
# 假设第一个是月,第二个是日(美式日期)
|
|
||||||
month = int(match_date_alt.group(1))
|
|
||||||
day = int(match_date_alt.group(2))
|
|
||||||
year = int(match_date_alt.group(3))
|
|
||||||
# 检查月份值,如果大于12可能是欧式日期格式(DD/MM/YYYY)
|
|
||||||
if month > 12:
|
|
||||||
month, day = day, month
|
|
||||||
return year * 10000 + month * 100 + day
|
|
||||||
|
|
||||||
# 3.4 MM/DD 格式(无年份),假设为当前年
|
|
||||||
match_date_short = re.search(r'(\d{1,2})[-./](\d{1,2})', filename)
|
|
||||||
if match_date_short:
|
|
||||||
# 假设第一个是月,第二个是日
|
|
||||||
month = int(match_date_short.group(1))
|
|
||||||
day = int(match_date_short.group(2))
|
|
||||||
# 检查月份值,如果大于12可能是欧式日期格式(DD/MM)
|
|
||||||
if month > 12:
|
|
||||||
month, day = day, month
|
|
||||||
# 由于没有年份,使用一个较低的基数,确保任何有年份的日期都排在后面
|
|
||||||
return month * 100 + day
|
|
||||||
|
|
||||||
# 3.5 年期格式,如"2025年14期"
|
|
||||||
match_year_issue = re.search(r'(20\d{2})[年].*?(\d+)[期]', filename)
|
|
||||||
if match_year_issue:
|
|
||||||
year = int(match_year_issue.group(1))
|
|
||||||
issue = int(match_year_issue.group(2))
|
|
||||||
return year * 1000 + issue
|
|
||||||
|
|
||||||
# 4. 纯数字格式(文件名开头是纯数字)
|
|
||||||
match_num = re.match(r'^(\d+)', file_name_without_ext)
|
|
||||||
if match_num:
|
|
||||||
return int(match_num.group(1))
|
|
||||||
|
|
||||||
# 6. 默认使用更新时间
|
|
||||||
try:
|
|
||||||
return file.get("last_update_at", 0)
|
|
||||||
except:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# 过滤出非目录文件,并且排除已经符合命名规则的文件
|
|
||||||
files_to_process = [
|
|
||||||
f for f in share_detail["list"]
|
|
||||||
if not f["dir"] and not re.match(regex_pattern, f["file_name"])
|
|
||||||
]
|
|
||||||
|
|
||||||
# 根据提取的排序值进行排序
|
|
||||||
sorted_files = sorted(files_to_process, key=extract_sorting_value)
|
|
||||||
|
|
||||||
# 应用过滤词过滤
|
|
||||||
filterwords = regex.get("filterwords", "")
|
|
||||||
if filterwords:
|
|
||||||
# 同时支持中英文逗号分隔
|
|
||||||
filterwords = filterwords.replace(",", ",")
|
|
||||||
filterwords_list = [word.strip() for word in filterwords.split(',')]
|
|
||||||
for item in sorted_files:
|
|
||||||
# 被过滤的文件不会有file_name_re,与不匹配正则的文件显示一致
|
|
||||||
if any(word in item['file_name'] for word in filterwords_list):
|
|
||||||
item["filtered"] = True
|
|
||||||
|
|
||||||
# 为每个文件分配序号
|
|
||||||
for file in sorted_files:
|
|
||||||
if not file.get("filtered"):
|
|
||||||
# 获取文件扩展名
|
|
||||||
file_ext = os.path.splitext(file["file_name"])[1]
|
|
||||||
# 生成预览文件名
|
|
||||||
file["file_name_re"] = sequence_pattern.replace("{}", f"{current_sequence:02d}") + file_ext
|
|
||||||
current_sequence += 1
|
|
||||||
|
|
||||||
return share_detail
|
|
||||||
else:
|
|
||||||
# 普通正则命名预览
|
|
||||||
pattern, replace = account.magic_regex_func(
|
|
||||||
regex.get("pattern", ""),
|
|
||||||
regex.get("replace", ""),
|
|
||||||
regex.get("taskname", ""),
|
|
||||||
regex.get("magic_regex", {}),
|
|
||||||
)
|
|
||||||
|
|
||||||
# 应用过滤词过滤
|
|
||||||
filterwords = regex.get("filterwords", "")
|
|
||||||
if filterwords:
|
|
||||||
# 同时支持中英文逗号分隔
|
|
||||||
filterwords = filterwords.replace(",", ",")
|
|
||||||
filterwords_list = [word.strip() for word in filterwords.split(',')]
|
|
||||||
for item in share_detail["list"]:
|
|
||||||
# 被过滤的文件不会有file_name_re,与不匹配正则的文件显示一致
|
|
||||||
if any(word in item['file_name'] for word in filterwords_list):
|
|
||||||
item["filtered"] = True
|
|
||||||
|
|
||||||
# 应用正则命名
|
|
||||||
for item in share_detail["list"]:
|
|
||||||
# 只对未被过滤的文件应用正则命名
|
|
||||||
if not item.get("filtered") and re.search(pattern, item["file_name"]):
|
|
||||||
file_name = item["file_name"]
|
|
||||||
item["file_name_re"] = (
|
|
||||||
re.sub(pattern, replace, file_name) if replace != "" else file_name
|
|
||||||
)
|
|
||||||
return share_detail
|
|
||||||
|
|
||||||
share_detail = preview_regex(share_detail)
|
|
||||||
|
|
||||||
return jsonify({"success": True, "data": share_detail})
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/get_savepath_detail")
|
@app.route("/get_savepath")
|
||||||
def get_savepath_detail():
|
def get_savepath():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return jsonify({"error": "未登录"})
|
||||||
account = Quark(config_data["cookie"][0], 0)
|
data = read_json()
|
||||||
paths = []
|
account = Quark(data["cookie"][0], 0)
|
||||||
if path := request.args.get("path"):
|
if path := request.args.get("path"):
|
||||||
if path == "/":
|
if path == "/":
|
||||||
fid = 0
|
fid = 0
|
||||||
|
elif get_fids := account.get_fids([path]):
|
||||||
|
fid = get_fids[0]["fid"]
|
||||||
else:
|
else:
|
||||||
dir_names = path.split("/")
|
return jsonify([])
|
||||||
if dir_names[0] == "":
|
|
||||||
dir_names.pop(0)
|
|
||||||
path_fids = []
|
|
||||||
current_path = ""
|
|
||||||
for dir_name in dir_names:
|
|
||||||
current_path += "/" + dir_name
|
|
||||||
path_fids.append(current_path)
|
|
||||||
if get_fids := account.get_fids(path_fids):
|
|
||||||
fid = get_fids[-1]["fid"]
|
|
||||||
paths = [
|
|
||||||
{"fid": get_fid["fid"], "name": dir_name}
|
|
||||||
for get_fid, dir_name in zip(get_fids, dir_names)
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
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 = account.ls_dir(fid)
|
||||||
"list": account.ls_dir(fid),
|
return jsonify(file_list)
|
||||||
"paths": paths,
|
|
||||||
}
|
|
||||||
return jsonify({"success": True, "data": file_list})
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/delete_file", methods=["POST"])
|
@app.route("/delete_file", methods=["POST"])
|
||||||
def delete_file():
|
def delete_file():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return jsonify({"error": "未登录"})
|
||||||
account = Quark(config_data["cookie"][0], 0)
|
data = read_json()
|
||||||
|
account = Quark(data["cookie"][0], 0)
|
||||||
if fid := request.json.get("fid"):
|
if fid := request.json.get("fid"):
|
||||||
response = account.delete([fid])
|
response = account.delete([fid])
|
||||||
else:
|
else:
|
||||||
response = {"success": False, "message": "缺失必要字段: fid"}
|
response = {"error": "fid not found"}
|
||||||
return jsonify(response)
|
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):
|
def run_python(args):
|
||||||
logging.info(f">>> 定时运行任务")
|
logging.info(f">>> 定时运行任务")
|
||||||
@ -547,8 +290,11 @@ def run_python(args):
|
|||||||
|
|
||||||
# 重新加载任务
|
# 重新加载任务
|
||||||
def reload_tasks():
|
def reload_tasks():
|
||||||
# 读取定时规则
|
# 读取数据
|
||||||
if crontab := config_data.get("crontab"):
|
data = read_json()
|
||||||
|
# 添加新任务
|
||||||
|
crontab = data.get("crontab")
|
||||||
|
if crontab:
|
||||||
if scheduler.state == 1:
|
if scheduler.state == 1:
|
||||||
scheduler.pause() # 暂停调度器
|
scheduler.pause() # 暂停调度器
|
||||||
trigger = CronTrigger.from_crontab(crontab)
|
trigger = CronTrigger.from_crontab(crontab)
|
||||||
@ -575,7 +321,7 @@ def reload_tasks():
|
|||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
global config_data, task_plugins_config_default
|
global task_plugins_config
|
||||||
logging.info(f">>> 初始化配置")
|
logging.info(f">>> 初始化配置")
|
||||||
# 检查配置文件是否存在
|
# 检查配置文件是否存在
|
||||||
if not os.path.exists(CONFIG_PATH):
|
if not os.path.exists(CONFIG_PATH):
|
||||||
@ -583,30 +329,43 @@ def init():
|
|||||||
os.makedirs(os.path.dirname(CONFIG_PATH))
|
os.makedirs(os.path.dirname(CONFIG_PATH))
|
||||||
with open("quark_config.json", "rb") as src, open(CONFIG_PATH, "wb") as dest:
|
with open("quark_config.json", "rb") as src, open(CONFIG_PATH, "wb") as dest:
|
||||||
dest.write(src.read())
|
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)
|
|
||||||
|
|
||||||
# 默认管理账号
|
# 默认管理账号
|
||||||
config_data["webui"] = {
|
data["webui"] = {
|
||||||
"username": os.environ.get("WEBUI_USERNAME")
|
"username": os.environ.get("WEBUI_USERNAME")
|
||||||
or config_data.get("webui", {}).get("username", "admin"),
|
or data.get("webui", {}).get("username", "admin"),
|
||||||
"password": os.environ.get("WEBUI_PASSWORD")
|
"password": os.environ.get("WEBUI_PASSWORD")
|
||||||
or config_data.get("webui", {}).get("password", "admin123"),
|
or data.get("webui", {}).get("password", "admin123"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# 默认定时规则
|
# 默认定时规则
|
||||||
if not config_data.get("crontab"):
|
if not data.get("crontab"):
|
||||||
config_data["crontab"] = "0 8,18,20 * * *"
|
data["crontab"] = "0 8,18,20 * * *"
|
||||||
|
|
||||||
# 初始化插件配置
|
# 初始化插件配置
|
||||||
_, plugins_config_default, task_plugins_config_default = Config.load_plugins()
|
_, plugins_config_default, task_plugins_config = Config.load_plugins()
|
||||||
plugins_config_default.update(config_data.get("plugins", {}))
|
plugins_config_default.update(data.get("plugins", {}))
|
||||||
config_data["plugins"] = plugins_config_default
|
data["plugins"] = plugins_config_default
|
||||||
|
write_json(data)
|
||||||
|
|
||||||
# 更新配置
|
|
||||||
Config.write_json(CONFIG_PATH, config_data)
|
def filter_files(files, filterwords):
|
||||||
|
if not filterwords:
|
||||||
|
return files
|
||||||
|
filterwords_list = [word.strip() for word in filterwords.split(',')]
|
||||||
|
return [file for file in files if not any(word in file['file_name'] for word in filterwords_list)]
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/get_filtered_files")
|
||||||
|
def get_filtered_files():
|
||||||
|
if not is_login():
|
||||||
|
return jsonify({"error": "未登录"})
|
||||||
|
data = read_json()
|
||||||
|
filterwords = request.args.get("filterwords", "")
|
||||||
|
account = Quark(data["cookie"][0], 0)
|
||||||
|
fid = request.args.get("fid", 0)
|
||||||
|
files = account.ls_dir(fid)
|
||||||
|
filtered_files = filter_files(files, filterwords)
|
||||||
|
return jsonify(filtered_files)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user