mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-19 03:20:42 +08:00
Compare commits
5 Commits
bd9222c875
...
6ed5d310c6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ed5d310c6 | ||
|
|
98e53b38db | ||
|
|
846bf0345a | ||
|
|
95ddc95c79 | ||
|
|
78581b15a7 |
48
app/run.py
48
app/run.py
@ -33,6 +33,19 @@ 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
|
||||||
|
|
||||||
|
print(
|
||||||
|
r"""
|
||||||
|
____ ___ _____
|
||||||
|
/ __ \ / | / ___/
|
||||||
|
/ / / / / /| | \__ \
|
||||||
|
/ /_/ / / ___ |___/ /
|
||||||
|
\___\_\/_/ |_/____/
|
||||||
|
|
||||||
|
-- Quark-Auto-Save --
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
def get_app_ver():
|
def get_app_ver():
|
||||||
"""获取应用版本"""
|
"""获取应用版本"""
|
||||||
@ -60,6 +73,7 @@ PLUGIN_FLAGS = os.environ.get("PLUGIN_FLAGS", "")
|
|||||||
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
|
DEBUG = os.environ.get("DEBUG", "false").lower() == "true"
|
||||||
HOST = os.environ.get("HOST", "0.0.0.0")
|
HOST = os.environ.get("HOST", "0.0.0.0")
|
||||||
PORT = os.environ.get("PORT", 5005)
|
PORT = os.environ.get("PORT", 5005)
|
||||||
|
TASK_TIMEOUT = int(os.environ.get("TASK_TIMEOUT", 1800))
|
||||||
|
|
||||||
config_data = {}
|
config_data = {}
|
||||||
task_plugins_config_default = {}
|
task_plugins_config_default = {}
|
||||||
@ -83,6 +97,8 @@ logging.basicConfig(
|
|||||||
# 过滤werkzeug日志输出
|
# 过滤werkzeug日志输出
|
||||||
if not DEBUG:
|
if not DEBUG:
|
||||||
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
logging.getLogger("werkzeug").setLevel(logging.ERROR)
|
||||||
|
logging.getLogger("apscheduler").setLevel(logging.ERROR)
|
||||||
|
sys.modules["flask.cli"].show_server_banner = lambda *x: None
|
||||||
|
|
||||||
|
|
||||||
def gen_md5(string):
|
def gen_md5(string):
|
||||||
@ -472,34 +488,42 @@ def add_task():
|
|||||||
def run_python(args):
|
def run_python(args):
|
||||||
logging.info(f">>> 定时运行任务")
|
logging.info(f">>> 定时运行任务")
|
||||||
try:
|
try:
|
||||||
# 使用 subprocess 替代 os.system,并设置超时时间(默认30分钟)
|
|
||||||
timeout = int(os.environ.get("TASK_TIMEOUT", "1800")) # 秒
|
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
f"{PYTHON_PATH} {args}",
|
f"{PYTHON_PATH} {args}",
|
||||||
shell=True,
|
shell=True,
|
||||||
timeout=timeout,
|
timeout=TASK_TIMEOUT,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
errors="replace"
|
errors="replace",
|
||||||
)
|
)
|
||||||
# 输出执行日志
|
# 输出执行日志
|
||||||
if result.stdout:
|
if result.stdout:
|
||||||
for line in result.stdout.strip().split('\n'):
|
for line in result.stdout.strip().split("\n"):
|
||||||
if line.strip():
|
if line.strip():
|
||||||
logging.info(line)
|
logging.info(line)
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
logging.info(f">>> 任务执行成功")
|
logging.info(f">>> 任务执行成功")
|
||||||
else:
|
else:
|
||||||
logging.error(f">>> 任务执行失败,返回码: {result.returncode}")
|
logging.error(f">>> 任务执行失败,返回码: {result.returncode}")
|
||||||
if result.stderr:
|
if result.stderr:
|
||||||
logging.error(f"错误信息: {result.stderr[:500]}")
|
logging.error(f"错误信息: {result.stderr[:500]}")
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired as e:
|
||||||
logging.error(f">>> 任务执行超时(超过 {timeout} 秒),已强制终止")
|
logging.error(f">>> 任务执行超时(>{TASK_TIMEOUT}s),强制终止")
|
||||||
|
# 尝试终止进程
|
||||||
|
if e.process:
|
||||||
|
try:
|
||||||
|
e.process.kill()
|
||||||
|
logging.info(">>> 已终止超时进程")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f">>> 任务执行异常: {str(e)}")
|
logging.error(f">>> 任务执行异常: {str(e)}")
|
||||||
logging.error(traceback.format_exc())
|
logging.error(traceback.format_exc())
|
||||||
|
finally:
|
||||||
|
# 确保函数能够正常返回
|
||||||
|
logging.debug(f">>> run_python 函数执行完成")
|
||||||
|
|
||||||
|
|
||||||
# 重新加载任务
|
# 重新加载任务
|
||||||
@ -515,6 +539,10 @@ def reload_tasks():
|
|||||||
trigger=trigger,
|
trigger=trigger,
|
||||||
args=[f"{SCRIPT_PATH} {CONFIG_PATH}"],
|
args=[f"{SCRIPT_PATH} {CONFIG_PATH}"],
|
||||||
id=SCRIPT_PATH,
|
id=SCRIPT_PATH,
|
||||||
|
max_instances=1, # 最多允许1个实例运行
|
||||||
|
coalesce=True, # 合并错过的任务,避免堆积
|
||||||
|
misfire_grace_time=300, # 错过任务的宽限期(秒),超过则跳过
|
||||||
|
replace_existing=True, # 替换已存在的同ID任务
|
||||||
)
|
)
|
||||||
if scheduler.state == 0:
|
if scheduler.state == 0:
|
||||||
scheduler.start()
|
scheduler.start()
|
||||||
@ -533,7 +561,7 @@ def reload_tasks():
|
|||||||
|
|
||||||
def init():
|
def init():
|
||||||
global config_data, task_plugins_config_default
|
global config_data, task_plugins_config_default
|
||||||
logging.info(f">>> 初始化配置")
|
logging.info(">>> 初始化配置")
|
||||||
# 检查配置文件是否存在
|
# 检查配置文件是否存在
|
||||||
if not os.path.exists(CONFIG_PATH):
|
if not os.path.exists(CONFIG_PATH):
|
||||||
if not os.path.exists(os.path.dirname(CONFIG_PATH)):
|
if not os.path.exists(os.path.dirname(CONFIG_PATH)):
|
||||||
@ -571,6 +599,8 @@ def init():
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
init()
|
init()
|
||||||
reload_tasks()
|
reload_tasks()
|
||||||
|
logging.info(">>> 启动Web服务")
|
||||||
|
logging.info(f"运行在: http://{HOST}:{PORT}")
|
||||||
app.run(
|
app.run(
|
||||||
debug=DEBUG,
|
debug=DEBUG,
|
||||||
host=HOST,
|
host=HOST,
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
// @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.5
|
// @version 0.6
|
||||||
// @description 在夸克网盘分享页面添加推送到 QAS 的按钮
|
// @description 在夸克网盘分享页面添加推送到 QAS 的按钮
|
||||||
// @icon https://pan.quark.cn/favicon.ico
|
// @icon https://pan.quark.cn/favicon.ico
|
||||||
// @author Cp0204
|
// @author Cp0204
|
||||||
@ -76,16 +76,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForElement('.DetailLayout--client-download--FpyCkdW.ant-dropdown-trigger', (clientDownloadButton) => {
|
waitForElement('.pc-member-entrance', (PcMemberButton) => {
|
||||||
const qasSettingButton = document.createElement('div');
|
const qasSettingButton = document.createElement('div');
|
||||||
qasSettingButton.className = 'DetailLayout--client-download--FpyCkdW ant-dropdown-trigger';
|
qasSettingButton.className = 'pc-member-entrance';
|
||||||
qasSettingButton.innerHTML = 'QAS设置';
|
qasSettingButton.innerHTML = 'QAS设置';
|
||||||
|
|
||||||
qasSettingButton.addEventListener('click', () => {
|
qasSettingButton.addEventListener('click', () => {
|
||||||
showQASSettingDialog();
|
showQASSettingDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
clientDownloadButton.parentNode.insertBefore(qasSettingButton, clientDownloadButton.nextSibling);
|
PcMemberButton.parentNode.insertBefore(qasSettingButton, PcMemberButton.nextSibling);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +155,63 @@
|
|||||||
},
|
},
|
||||||
data: JSON.stringify(data),
|
data: JSON.stringify(data),
|
||||||
onload: function (response) {
|
onload: function (response) {
|
||||||
|
// 检查 HTTP 状态码
|
||||||
|
if (response.status === 401) {
|
||||||
|
Swal.fire({
|
||||||
|
title: '认证失败',
|
||||||
|
text: 'Token 无效或已过期,请重新配置 QAS Token',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: '重新配置',
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: '取消'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
showQASSettingDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 503) {
|
||||||
|
Swal.fire({
|
||||||
|
title: '服务器不可用',
|
||||||
|
html: `服务器暂时无法处理请求 (503)<br><br>
|
||||||
|
<small>可能原因:<br>
|
||||||
|
• QAS 服务未运行<br>
|
||||||
|
• 服务器过载<br>
|
||||||
|
• 网络连接问题</small>`,
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: '重新配置',
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: '取消'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
showQASSettingDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查响应内容类型
|
||||||
|
const contentType = response.responseHeaders.match(/content-type:\s*([^;\s]+)/i);
|
||||||
|
if (contentType && !contentType[1].includes('application/json')) {
|
||||||
|
Swal.fire({
|
||||||
|
title: '认证失败',
|
||||||
|
html: `服务器返回了非 JSON 响应,可能是 Token 错误<br><br>
|
||||||
|
<small>响应类型: ${contentType[1]}</small><br>
|
||||||
|
<small>响应状态: ${response.status}</small>`,
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: '重新配置',
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: '取消'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
showQASSettingDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const jsonResponse = JSON.parse(response.responseText);
|
const jsonResponse = JSON.parse(response.responseText);
|
||||||
if (jsonResponse.success) {
|
if (jsonResponse.success) {
|
||||||
@ -177,16 +234,34 @@
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '解析响应失败',
|
title: '解析响应失败',
|
||||||
text: `无法解析 JSON 响应: ${response.responseText}`,
|
html: `<small>
|
||||||
icon: 'error'
|
响应状态: ${response.status}<br>
|
||||||
|
响应内容: ${response.responseText.substring(0, 200)}...<br><br>
|
||||||
|
错误详情: ${e.message}
|
||||||
|
</small>`,
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: '重新配置',
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: '取消'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
showQASSettingDialog();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onerror: function (error) {
|
onerror: function (error) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '任务创建失败',
|
title: '网络请求失败',
|
||||||
text: error,
|
text: '无法连接到 QAS 服务器,请检查网络连接和服务器地址',
|
||||||
icon: 'error'
|
icon: 'error',
|
||||||
|
confirmButtonText: '重新配置',
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: '取消'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
showQASSettingDialog();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -128,7 +128,6 @@ class Config:
|
|||||||
task_plugins_config[module_name] = plugin.default_task_config
|
task_plugins_config[module_name] = plugin.default_task_config
|
||||||
except (ImportError, AttributeError) as e:
|
except (ImportError, AttributeError) as e:
|
||||||
print(f"载入模块 {module_name} 失败: {e}")
|
print(f"载入模块 {module_name} 失败: {e}")
|
||||||
print()
|
|
||||||
return plugins_available, plugins_config, task_plugins_config
|
return plugins_available, plugins_config, task_plugins_config
|
||||||
|
|
||||||
def breaking_change_update(config_data):
|
def breaking_change_update(config_data):
|
||||||
@ -164,7 +163,7 @@ class MagicRename:
|
|||||||
"{YEAR}": [r"(?<!\d)(18|19|20)\d{2}(?!\d)"],
|
"{YEAR}": [r"(?<!\d)(18|19|20)\d{2}(?!\d)"],
|
||||||
"{S}": [r"(?<=[Ss])\d{1,2}(?=[EeXx])", r"(?<=[Ss])\d{1,2}"],
|
"{S}": [r"(?<=[Ss])\d{1,2}(?=[EeXx])", r"(?<=[Ss])\d{1,2}"],
|
||||||
"{SXX}": [r"[Ss]\d{1,2}(?=[EeXx])", r"[Ss]\d{1,2}"],
|
"{SXX}": [r"[Ss]\d{1,2}(?=[EeXx])", r"[Ss]\d{1,2}"],
|
||||||
"{E}": [
|
"{E+}": [
|
||||||
r"(?<=[Ss]\d\d[Ee])\d{1,3}",
|
r"(?<=[Ss]\d\d[Ee])\d{1,3}",
|
||||||
r"(?<=[Ee])\d{1,3}",
|
r"(?<=[Ee])\d{1,3}",
|
||||||
r"(?<=[Ee][Pp])\d{1,3}",
|
r"(?<=[Ee][Pp])\d{1,3}",
|
||||||
@ -224,7 +223,7 @@ class MagicRename:
|
|||||||
return file_name
|
return file_name
|
||||||
# 预处理替换变量
|
# 预处理替换变量
|
||||||
for key, p_list in self.magic_variable.items():
|
for key, p_list in self.magic_variable.items():
|
||||||
if key in replace:
|
if match_key := re.search(key, replace):
|
||||||
# 正则类替换变量
|
# 正则类替换变量
|
||||||
if p_list and isinstance(p_list, list):
|
if p_list and isinstance(p_list, list):
|
||||||
for p in p_list:
|
for p in p_list:
|
||||||
@ -240,7 +239,10 @@ class MagicRename:
|
|||||||
value = (
|
value = (
|
||||||
str(datetime.now().year)[: (8 - len(value))] + value
|
str(datetime.now().year)[: (8 - len(value))] + value
|
||||||
)
|
)
|
||||||
replace = replace.replace(key, value)
|
# 集数零填充处理
|
||||||
|
elif key == "{E+}":
|
||||||
|
value = value.lstrip("0").zfill(match_key.group().count("E"))
|
||||||
|
replace = re.sub(key, value, replace)
|
||||||
break
|
break
|
||||||
# 非正则类替换变量
|
# 非正则类替换变量
|
||||||
if key == "{TASKNAME}":
|
if key == "{TASKNAME}":
|
||||||
@ -251,7 +253,7 @@ class MagicRename:
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# 清理未匹配的 magic_variable key
|
# 清理未匹配的 magic_variable key
|
||||||
replace = replace.replace(key, "")
|
replace = re.sub(key, "", replace)
|
||||||
if pattern and replace:
|
if pattern and replace:
|
||||||
file_name = re.sub(pattern, replace, file_name)
|
file_name = re.sub(pattern, replace, file_name)
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user