mirror of
https://github.com/Cp0204/quark-auto-save.git
synced 2026-01-18 10:50:43 +08:00
Compare commits
11 Commits
b9b80b33a6
...
56bb5a5b03
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56bb5a5b03 | ||
|
|
c910a986b1 | ||
|
|
f8d10236e3 | ||
|
|
2f0c51283c | ||
|
|
8611824b9a | ||
|
|
2b16246beb | ||
|
|
62464fec17 | ||
|
|
92a86ce8e0 | ||
|
|
13f89b32c6 | ||
|
|
e0cb7443fe | ||
|
|
fd46206024 |
56
app/run.py
56
app/run.py
@ -239,47 +239,73 @@ def get_task_suggestions():
|
|||||||
cs_data["token"] = search.get("new_token")
|
cs_data["token"] = search.get("new_token")
|
||||||
Config.write_json(CONFIG_PATH, config_data)
|
Config.write_json(CONFIG_PATH, config_data)
|
||||||
search_results = cs.clean_search_results(search.get("data"))
|
search_results = cs.clean_search_results(search.get("data"))
|
||||||
return jsonify({"success": True, "source": "CloudSaver", "data": search_results})
|
return jsonify(
|
||||||
|
{"success": True, "source": "CloudSaver", "data": search_results}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return jsonify({"success": True, "message": search.get("message")})
|
return jsonify({"success": True, "message": search.get("message")})
|
||||||
else:
|
else:
|
||||||
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
|
base_url = base64.b64decode("aHR0cHM6Ly9zLjkxNzc4OC54eXo=").decode()
|
||||||
url = f"{base_url}/task_suggestions?q={query}&d={deep}"
|
url = f"{base_url}/task_suggestions?q={query}&d={deep}"
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
return jsonify({"success": True, "source": "网络公开", "data": response.json()})
|
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({"success": True, "message": f"error: {str(e)}"})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/get_share_detail")
|
@app.route("/get_share_detail")
|
||||||
def get_share_files():
|
def get_share_detail():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return jsonify({"success": False, "message": "未登录"})
|
||||||
shareurl = request.args.get("shareurl", "")
|
shareurl = request.args.get("shareurl", "")
|
||||||
|
stoken = request.args.get("stoken", "")
|
||||||
account = Quark("", 0)
|
account = Quark("", 0)
|
||||||
pwd_id, passcode, pdir_fid = account.get_id_from_url(shareurl)
|
pwd_id, passcode, pdir_fid, paths = account.extract_url(shareurl)
|
||||||
is_sharing, stoken = account.get_stoken(pwd_id, passcode)
|
if not stoken:
|
||||||
if not is_sharing:
|
is_sharing, stoken = account.get_stoken(pwd_id, passcode)
|
||||||
return jsonify({"success": False, "data": {"error": stoken}})
|
if not is_sharing:
|
||||||
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, 1)
|
return jsonify({"success": False, "data": {"error": stoken}})
|
||||||
|
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, _fetch_share=1)
|
||||||
|
share_detail["paths"] = paths
|
||||||
|
share_detail["stoken"] = stoken
|
||||||
|
|
||||||
return jsonify({"success": True, "data": share_detail})
|
return jsonify({"success": True, "data": share_detail})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/get_savepath")
|
@app.route("/get_savepath_detail")
|
||||||
def get_savepath():
|
def get_savepath_detail():
|
||||||
if not is_login():
|
if not is_login():
|
||||||
return jsonify({"success": False, "message": "未登录"})
|
return jsonify({"success": False, "message": "未登录"})
|
||||||
account = Quark(config_data["cookie"][0], 0)
|
account = Quark(config_data["cookie"][0], 0)
|
||||||
|
paths = []
|
||||||
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:
|
||||||
return jsonify({"success": False, "message": "获取fid失败"})
|
dir_names = path.split("/")
|
||||||
|
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 = account.ls_dir(fid)
|
file_list = {
|
||||||
|
"list": account.ls_dir(fid),
|
||||||
|
"paths": paths,
|
||||||
|
}
|
||||||
return jsonify({"success": True, "data": file_list})
|
return jsonify({"success": True, "data": file_list})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -66,6 +66,7 @@ table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
|||||||
top: 0;
|
top: 0;
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
border: 1px solid #007bff;
|
border: 1px solid #007bff;
|
||||||
|
z-index: 1021;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -160,4 +161,8 @@ table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
|||||||
.form-control-dark:focus {
|
.form-control-dark:focus {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer
|
||||||
}
|
}
|
||||||
190
app/static/js/qas.addtask.user.js
Normal file
190
app/static/js/qas.addtask.user.js
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
// ==UserScript==
|
||||||
|
// @name QAS一键推送助手
|
||||||
|
// @namespace https://github.com/Cp0204/quark-auto-save
|
||||||
|
// @license AGPL
|
||||||
|
// @version 0.3
|
||||||
|
// @description 在夸克网盘分享页面添加推送到 QAS 的按钮
|
||||||
|
// @icon https://pan.quark.cn/favicon.ico
|
||||||
|
// @author Cp0204
|
||||||
|
// @match https://pan.quark.cn/s/*
|
||||||
|
// @grant GM_getValue
|
||||||
|
// @grant GM_setValue
|
||||||
|
// @grant GM_xmlhttpRequest
|
||||||
|
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
|
||||||
|
// @downloadURL https://update.greasyfork.org/scripts/533201/QAS%E4%B8%80%E9%94%AE%E6%8E%A8%E9%80%81%E5%8A%A9%E6%89%8B.user.js
|
||||||
|
// @updateURL https://update.greasyfork.org/scripts/533201/QAS%E4%B8%80%E9%94%AE%E6%8E%A8%E9%80%81%E5%8A%A9%E6%89%8B.meta.js
|
||||||
|
// ==/UserScript==
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let qas_base = GM_getValue('qas_base', '');
|
||||||
|
let qas_token = GM_getValue('qas_token', '');
|
||||||
|
|
||||||
|
// QAS 设置弹窗函数
|
||||||
|
function showQASSettingDialog(callback) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'QAS 设置',
|
||||||
|
html: `
|
||||||
|
<label for="qas_base">QAS 服务器</label>
|
||||||
|
<input id="qas_base" class="swal2-input" placeholder="例如: 192.168.1.8:5005" value="${qas_base}">
|
||||||
|
<label for="qas_token">QAS Token</label>
|
||||||
|
<input id="qas_token" class="swal2-input" placeholder="v0.5+ 系统配置中查找" value="${qas_token}">
|
||||||
|
`,
|
||||||
|
focusConfirm: false,
|
||||||
|
preConfirm: () => {
|
||||||
|
qas_base = document.getElementById('qas_base').value;
|
||||||
|
qas_token = document.getElementById('qas_token').value;
|
||||||
|
if (!qas_base || !qas_token) {
|
||||||
|
Swal.showValidationMessage('请填写 QAS 服务器和 Token');
|
||||||
|
}
|
||||||
|
return { qas_base: qas_base, qas_token: qas_token }
|
||||||
|
}
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
GM_setValue('qas_base', result.value.qas_base);
|
||||||
|
GM_setValue('qas_token', result.value.qas_token);
|
||||||
|
qas_base = result.value.qas_base;
|
||||||
|
qas_token = result.value.qas_token;
|
||||||
|
if (callback) {
|
||||||
|
callback(); // 执行回调函数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加 QAS 设置按钮
|
||||||
|
function addQASSettingButton() {
|
||||||
|
function waitForElement(selector, callback) {
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
clientDownloadButton.parentNode.insertBefore(qasSettingButton, clientDownloadButton.nextSibling);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 推送到 QAS 按钮
|
||||||
|
function addQASButton() {
|
||||||
|
function waitForElement(selector, callback) {
|
||||||
|
const element = document.querySelector(selector);
|
||||||
|
if (element) {
|
||||||
|
callback(element);
|
||||||
|
} else {
|
||||||
|
setTimeout(() => waitForElement(selector, callback), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForElement('.ant-btn.share-save', (saveButton) => {
|
||||||
|
const qasButton = document.createElement('button');
|
||||||
|
qasButton.type = 'button';
|
||||||
|
qasButton.className = 'ant-btn share-save';
|
||||||
|
qasButton.style.marginLeft = '10px';
|
||||||
|
qasButton.innerHTML = '<span class="share-save-ico"></span><span>推送到QAS</span>';
|
||||||
|
|
||||||
|
let taskname, shareurl, savepath; // 声明变量
|
||||||
|
|
||||||
|
// 获取数据函数
|
||||||
|
function getData() {
|
||||||
|
const currentUrl = window.location.href;
|
||||||
|
taskname = currentUrl.lastIndexOf('-') > 0 ? decodeURIComponent(currentUrl.match(/.*\/[^-]+-(.+)$/)[1]) : document.querySelector('.author-name').textContent;
|
||||||
|
shareurl = currentUrl;
|
||||||
|
let pathElement = document.querySelector('.path-name')
|
||||||
|
savepath = pathElement ? pathElement.title.replace('全部文件', '').trim() : "";
|
||||||
|
savepath += "/" + taskname
|
||||||
|
qasButton.title = `任务名称: ${taskname}\n分享链接: ${shareurl}\n保存路径: ${savepath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 添加鼠标悬停事件
|
||||||
|
qasButton.addEventListener('mouseover', () => {
|
||||||
|
getData(); // 鼠标悬停时获取数据
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 添加点击事件
|
||||||
|
qasButton.addEventListener('click', () => {
|
||||||
|
getData(); // 点击时重新获取数据,确保最新
|
||||||
|
|
||||||
|
const apiUrl = `http://${qas_base}/api/add_task?token=${qas_token}`;
|
||||||
|
const data = {
|
||||||
|
"taskname": taskname,
|
||||||
|
"shareurl": shareurl,
|
||||||
|
"savepath": savepath,
|
||||||
|
};
|
||||||
|
|
||||||
|
GM_xmlhttpRequest({
|
||||||
|
method: 'POST',
|
||||||
|
url: apiUrl,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
onload: function(response) {
|
||||||
|
try {
|
||||||
|
const jsonResponse = JSON.parse(response.responseText);
|
||||||
|
if (jsonResponse.success) {
|
||||||
|
Swal.fire({
|
||||||
|
title: '任务创建成功',
|
||||||
|
html: `<small>
|
||||||
|
<b>任务名称:</b> ${taskname}<br><br>
|
||||||
|
<b>保存路径:</b> ${savepath}<br><br>
|
||||||
|
<a href="http://${qas_base}" target="_blank">去 QAS 查看</a>
|
||||||
|
<small>`,
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: '任务创建失败',
|
||||||
|
text: jsonResponse.message,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Swal.fire({
|
||||||
|
title: '解析响应失败',
|
||||||
|
text: `无法解析 JSON 响应: ${response.responseText}`,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onerror: function(error) {
|
||||||
|
Swal.fire({
|
||||||
|
title: '任务创建失败',
|
||||||
|
text: error,
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
saveButton.parentNode.insertBefore(qasButton, saveButton.nextSibling);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
(function init() {
|
||||||
|
addQASSettingButton();
|
||||||
|
|
||||||
|
if (!qas_base || !qas_token) {
|
||||||
|
showQASSettingDialog(() => {
|
||||||
|
addQASButton(); // 在设置后添加 QAS 按钮
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
addQASButton(); // 如果配置存在,则直接添加 QAS 按钮
|
||||||
|
}
|
||||||
|
})(); // 立即执行初始化
|
||||||
|
})();
|
||||||
@ -58,7 +58,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main class="col-md-9 col-lg-10 ml-sm-auto">
|
<main class="col-md-9 col-lg-10 ml-sm-auto">
|
||||||
<form @submit.prevent="saveConfig">
|
<form @submit.prevent="saveConfig" @keydown.enter.prevent>
|
||||||
|
|
||||||
<div v-if="activeTab === 'config'">
|
<div v-if="activeTab === 'config'">
|
||||||
<div class="row title">
|
<div class="row title">
|
||||||
@ -272,8 +272,8 @@
|
|||||||
<div class="input-group">
|
<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)">
|
<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.success && smart_param.index === index">
|
<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 ? `以下资源来自 ${smart_param.taskSuggestions.source} 搜索,请自行辨识,如有侵权请联系资源方` : "未搜索到资源" }}</div>
|
<div class="dropdown-item text-muted text-center" style="font-size:12px;">{{ smart_param.taskSuggestions.message ? smart_param.taskSuggestions.message : smart_param.taskSuggestions.data.length ? `以下资源来自 ${smart_param.taskSuggestions.source} 搜索,请自行辨识,如有侵权请联系资源方` : "未搜索到资源" }}</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">
|
<div v-for="suggestion in smart_param.taskSuggestions.data" :key="suggestion.taskname" class="dropdown-item cursor-pointer" @click.prevent="selectSuggestion(index, suggestion)" style="font-size: 12px;" :title="suggestion.content">
|
||||||
<span v-html="suggestion.verify ? '✅': '❔'"></span> {{ suggestion.taskname }}
|
<span v-html="suggestion.verify ? '✅': '❔'"></span> {{ suggestion.taskname }}
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
<a :href="suggestion.shareurl" target="_blank" @click.stop>{{ suggestion.shareurl }}</a>
|
<a :href="suggestion.shareurl" target="_blank" @click.stop>{{ suggestion.shareurl }}</a>
|
||||||
@ -298,6 +298,7 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" name="shareurl[]" class="form-control" v-model="task.shareurl" placeholder="必填" @blur="changeShareurl(task)">
|
<input type="text" name="shareurl[]" class="form-control" v-model="task.shareurl" placeholder="必填" @blur="changeShareurl(task)">
|
||||||
<div class="input-group-append" v-if="task.shareurl">
|
<div class="input-group-append" v-if="task.shareurl">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" @click="fileSelect.selectDir=true;showFolderSelect(index)" title="选择文件夹"><i class="bi bi-folder"></i></button>
|
||||||
<div class="input-group-text">
|
<div class="input-group-text">
|
||||||
<a target="_blank" :href="task.shareurl"><i class="bi bi-box-arrow-up-right"></i></a>
|
<a target="_blank" :href="task.shareurl"><i class="bi bi-box-arrow-up-right"></i></a>
|
||||||
</div>
|
</div>
|
||||||
@ -312,15 +313,7 @@
|
|||||||
<input type="text" name="savepath[]" class="form-control" v-model="task.savepath" placeholder="必填" @focus="focusTaskname(index, task)">
|
<input type="text" name="savepath[]" class="form-control" v-model="task.savepath" placeholder="必填" @focus="focusTaskname(index, task)">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-secondary" type="button" v-if="smart_param.savepath && smart_param.index == index && task.savepath != smart_param.origin_savepath" @click="task.savepath = smart_param.origin_savepath"><i class="bi bi-reply"></i></button>
|
<button class="btn btn-secondary" type="button" v-if="smart_param.savepath && smart_param.index == index && task.savepath != smart_param.origin_savepath" @click="task.savepath = smart_param.origin_savepath"><i class="bi bi-reply"></i></button>
|
||||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" @click="getSavepathDirs(task.savepath)" data-toggle="dropdown" aria-expanded="false">选择</button>
|
<button class="btn btn-outline-secondary" type="button" @click="showSavepathSelect(index)">选择</button>
|
||||||
<div class="dropdown-menu" style="max-height: 300px; min-width: 200px; overflow-y: auto;">
|
|
||||||
<a class="dropdown-item" @click.stop.prevent="selectSavepath(index,getParentDirectory(task.savepath),'..')" href="#">..</a>
|
|
||||||
<span v-if="!savepaths.some(item => item.dir)" class="dropdown-item disabled">无子目录</span>
|
|
||||||
<a v-for="(item, key) in savepaths" :class="{'disabled': item.fid === 0 || !item.dir}" class="dropdown-item" @click.stop.prevent="selectSavepath(index,item.fid,item.file_name)" href="#">
|
|
||||||
<i class="bi" :class="item.dir ? 'bi-folder2' : 'bi-file-earmark'"></i> {{ item.file_name }}
|
|
||||||
<i class="bi bi-trash3-fill text-danger" @click.stop.prevent="deleteFile(item.fid,item.file_name,item.dir)" style="position: absolute; right: 10px; pointer-events: auto;"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -349,9 +342,9 @@
|
|||||||
<label class="col-sm-2 col-form-label">文件起始</label>
|
<label class="col-sm-2 col-form-label">文件起始</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" placeholder="可选" name="startfid[]" v-model="task.startfid">
|
<input type="text" class="form-control" placeholder="可选,只转存修改日期>此文件的文件" name="startfid[]" v-model="task.startfid">
|
||||||
<div class="input-group-append" v-if="task.shareurl">
|
<div class="input-group-append" v-if="task.shareurl">
|
||||||
<button class="btn btn-outline-secondary" type="button" @click="showShareFiles(index)">选择</button>
|
<button class="btn btn-outline-secondary" type="button" @click="fileSelect.selectDir=false; showFolderSelect(index)">选择</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -425,42 +418,58 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 模态框 分享文件列表 -->
|
<!-- 模态框 文件选择 -->
|
||||||
<div class="modal" tabindex="-1" id="shareDetailModal">
|
<div class="modal" tabindex="-1" id="fileSelectModal">
|
||||||
<div class="modal-dialog modal-xl">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title"><b>分享文件列表</b>
|
<h5 class="modal-title">
|
||||||
|
<b v-if="fileSelect.selectDir">选择{{fileSelect.selectShare ? '分享' : '保存'}}文件夹</b>
|
||||||
|
<b v-else>选择文件</b>
|
||||||
<div v-if="modalLoading" class="spinner-border spinner-border-sm m-1" role="status"></div>
|
<div v-if="modalLoading" class="spinner-border spinner-border-sm m-1" role="status"></div>
|
||||||
</h5>
|
</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body small">
|
||||||
<div class="alert alert-warning" v-if="shareFiles.error" v-html="shareFiles.error"></div>
|
<div class="alert alert-warning" v-if="fileSelect.error" v-html="fileSelect.error"></div>
|
||||||
<table class="table table-hover table-sm" v-else-if="!modalLoading" title="请选择转存起始文件,将只转存修改日期>选中文件的文件">
|
<div v-else>
|
||||||
<thead>
|
<!-- 面包屑导航 -->
|
||||||
<tr>
|
<nav aria-label="breadcrumb" v-if="fileSelect.selectDir">
|
||||||
<!-- <th scope="col">fid</th> -->
|
<ol class="breadcrumb">
|
||||||
<th scope="col">文件名</th>
|
<li class="breadcrumb-item cursor-pointer" @click="navigateTo('0','/')"><i class="bi bi-house-door"></i></li>
|
||||||
<th scope="col">大小</th>
|
<li v-for="(item, index) in fileSelect.paths" class="breadcrumb-item">
|
||||||
<th scope="col">修改日期 ↓</th>
|
<a v-if="index != fileSelect.paths.length - 1" href="#" @click="navigateTo(item.fid, item.name)">{{ item.name }}</a>
|
||||||
</tr>
|
<span v-else class="text-muted">{{ item.name }}</span>
|
||||||
</thead>
|
</li>
|
||||||
<tbody>
|
</ol>
|
||||||
<tr>
|
</nav>
|
||||||
<td colspan="4"><i class="bi bi-folder-plus"></i> 后续更新的文件...</td>
|
<!-- 文件列表 -->
|
||||||
</tr>
|
<table class="table table-hover table-sm">
|
||||||
<tr v-for="(file, key) in shareFiles" :key="key" @click="selectStartFid(file.fid)" style="cursor: pointer;">
|
<thead>
|
||||||
<!-- <td>{{file.fid}}</td> -->
|
<tr>
|
||||||
<td><i class="bi" :class="file.dir ? 'bi-folder2' : 'bi-file-earmark'"></i> {{file.file_name}}</td>
|
<th scope="col">文件名</th>
|
||||||
<td>{{file.size | size}}</td>
|
<th scope="col">大小</th>
|
||||||
<td>{{file.last_update_at | ts2date}}</td>
|
<th scope="col">修改日期 ↓</th>
|
||||||
</td>
|
<th scope="col" v-if="!fileSelect.selectShare">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</thead>
|
||||||
</table>
|
<tbody>
|
||||||
|
<tr v-for="(file, key) in fileSelect.fileList" :key="key" @click="fileSelect.selectDir ? (file.dir ? navigateTo(file.fid, file.file_name) : null) : selectStartFid(file.fid)" :class="{'cursor-pointer': (fileSelect.selectDir && file.dir)}">
|
||||||
|
<td><i class="bi" :class="file.dir ? 'bi-folder-fill text-warning' : 'bi-file-earmark'"></i> {{file.file_name}}</td>
|
||||||
|
<td v-if="file.dir">{{ file.include_items }}项</td>
|
||||||
|
<td v-else>{{file.size | size}}</td>
|
||||||
|
<td>{{file.updated_at | ts2date}}</td>
|
||||||
|
<td v-if="!fileSelect.selectShare"><a @click.stop.prevent="deleteFile(file.fid, file.file_name, file.dir)">删除</a></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" v-if="fileSelect.selectDir">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" @click="selectCurrentFolder()">选择当前文件夹</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" v-if="!fileSelect.selectShare" @click="selectCurrentFolder(true)">选择当前文件夹+/任务名称</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -506,10 +515,7 @@
|
|||||||
taskDirs: [""],
|
taskDirs: [""],
|
||||||
taskDirSelected: "",
|
taskDirSelected: "",
|
||||||
taskNameFilter: "",
|
taskNameFilter: "",
|
||||||
savepaths: [],
|
|
||||||
modalLoading: false,
|
modalLoading: false,
|
||||||
shareFiles: [],
|
|
||||||
forceTaskIndex: null,
|
|
||||||
smart_param: {
|
smart_param: {
|
||||||
index: null,
|
index: null,
|
||||||
savepath: "",
|
savepath: "",
|
||||||
@ -521,6 +527,15 @@
|
|||||||
},
|
},
|
||||||
activeTab: 'tasklist',
|
activeTab: 'tasklist',
|
||||||
configModified: false,
|
configModified: false,
|
||||||
|
fileSelect: {
|
||||||
|
index: null,
|
||||||
|
shareurl: "",
|
||||||
|
stoken: "",
|
||||||
|
fileList: [],
|
||||||
|
paths: [],
|
||||||
|
selectDir: true,
|
||||||
|
selectShare: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
ts2date: function (value) {
|
ts2date: function (value) {
|
||||||
@ -528,11 +543,11 @@
|
|||||||
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
|
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
|
||||||
},
|
},
|
||||||
size: function (value) {
|
size: function (value) {
|
||||||
if (!value) return "0B";
|
if (!value) return "";
|
||||||
const unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
const unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||||
const srcsize = parseFloat(value);
|
const srcsize = parseFloat(value);
|
||||||
const index = srcsize ? Math.floor(Math.log(srcsize) / Math.log(1024)) : 0;
|
const index = srcsize ? Math.floor(Math.log(srcsize) / Math.log(1024)) : 0;
|
||||||
const size = (srcsize / Math.pow(1024, index)).toFixed(2).replace(/\.?0+$/, "");
|
const size = (srcsize / Math.pow(1024, index)).toFixed(1).replace(/\.?0+$/, "");
|
||||||
return size + unitArr[index];
|
return size + unitArr[index];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -761,58 +776,6 @@
|
|||||||
clearData(target) {
|
clearData(target) {
|
||||||
this[target] = "";
|
this[target] = "";
|
||||||
},
|
},
|
||||||
selectSavepath(index, fid, name) {
|
|
||||||
const savepath = name == ".." ? this.getParentDirectory(this.formData.tasklist[index].savepath) : `/${this.formData.tasklist[index].savepath}/${name}`.replace(/\/{2,}/g, '/')
|
|
||||||
Vue.set(this.formData.tasklist[index], 'savepath', savepath);
|
|
||||||
this.getSavepathDirs(fid);
|
|
||||||
},
|
|
||||||
getSavepathDirs(fid = 0) {
|
|
||||||
if (fid.includes('/')) {
|
|
||||||
params = { path: fid }
|
|
||||||
} else {
|
|
||||||
params = { fid: fid }
|
|
||||||
}
|
|
||||||
this.savepaths = [{ fid: 0, dir: true, file_name: "加载中..." }]
|
|
||||||
axios.get('/get_savepath', { params: params })
|
|
||||||
.then(response => {
|
|
||||||
this.savepaths = response.data.data
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error get_savepath:', error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deleteFile(fid, fname, isDir) {
|
|
||||||
if (fid != "" && confirm(`确认删除${isDir ? '目录' : '文件'} [${fname}] 吗?`))
|
|
||||||
axios.post('/delete_file', { fid: fid })
|
|
||||||
.then(response => {
|
|
||||||
if (response.data.code == 0) {
|
|
||||||
this.savepaths = this.savepaths.filter(item => item.fid != fid);
|
|
||||||
} else {
|
|
||||||
alert('删除失败:' + response.data.message);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error delete_file:', error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
selectStartFid(fid) {
|
|
||||||
Vue.set(this.formData.tasklist[this.forceTaskIndex], 'startfid', fid);
|
|
||||||
$('#shareDetailModal').modal('toggle')
|
|
||||||
},
|
|
||||||
showShareFiles(index) {
|
|
||||||
this.shareFiles = []
|
|
||||||
$('#shareDetailModal').modal('toggle')
|
|
||||||
this.modalLoading = true
|
|
||||||
axios.get('/get_share_detail', { params: { shareurl: this.formData.tasklist[index].shareurl } })
|
|
||||||
.then(response => {
|
|
||||||
this.forceTaskIndex = index
|
|
||||||
this.shareFiles = response.data.data.list;
|
|
||||||
this.modalLoading = false
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error get_share_detail:', error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
runScriptNow(task_index = "") {
|
runScriptNow(task_index = "") {
|
||||||
if (this.configModified) {
|
if (this.configModified) {
|
||||||
if (!confirm('配置已修改但未保存,是否继续运行?')) {
|
if (!confirm('配置已修改但未保存,是否继续运行?')) {
|
||||||
@ -876,8 +839,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
searchSuggestions(index, taskname, deep = 1) {
|
searchSuggestions(index, taskname, deep = 1) {
|
||||||
if (taskname.length == 0)
|
if (taskname.length < 2) {
|
||||||
|
console.log(`任务名[${taskname}]过短${taskname.length} 不进行搜索`);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
this.smart_param.isSearching = true;
|
this.smart_param.isSearching = true;
|
||||||
this.smart_param.index = index;
|
this.smart_param.index = index;
|
||||||
try {
|
try {
|
||||||
@ -900,11 +865,9 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectSuggestion(task, suggestion) {
|
selectSuggestion(index, suggestion) {
|
||||||
task.taskname = suggestion.taskname;
|
|
||||||
task.shareurl = suggestion.shareurl;
|
|
||||||
this.changeShareurl(task);
|
|
||||||
this.smart_param.showSuggestions = false;
|
this.smart_param.showSuggestions = false;
|
||||||
|
this.showFolderSelect(index, suggestion.shareurl);
|
||||||
},
|
},
|
||||||
addMagicRegex() {
|
addMagicRegex() {
|
||||||
const newKey = `$MAGIC_${Object.keys(this.formData.magic_regex).length + 1}`;
|
const newKey = `$MAGIC_${Object.keys(this.formData.magic_regex).length + 1}`;
|
||||||
@ -925,6 +888,133 @@
|
|||||||
this.$delete(this.formData.magic_regex, key);
|
this.$delete(this.formData.magic_regex, key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
deleteFile(fid, fname, isDir) {
|
||||||
|
if (fid != "" && confirm(`确认删除${isDir ? '目录' : '文件'} [${fname}] 吗?`))
|
||||||
|
axios.post('/delete_file', {
|
||||||
|
fid: fid
|
||||||
|
}).then(response => {
|
||||||
|
if (response.data.code == 0) {
|
||||||
|
this.fileSelect.fileList = this.fileSelect.fileList.filter(item => item.fid != fid);
|
||||||
|
} else {
|
||||||
|
alert('删除失败:' + response.data.message);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Error /delete_file:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getSavepathDetail(params = 0) {
|
||||||
|
if (params.includes('/')) {
|
||||||
|
params = { path: params }
|
||||||
|
} else {
|
||||||
|
params = { fid: params }
|
||||||
|
}
|
||||||
|
this.modalLoading = true;
|
||||||
|
axios.get('/get_savepath_detail', {
|
||||||
|
params: params
|
||||||
|
}).then(response => {
|
||||||
|
this.fileSelect.fileList = response.data.data.list
|
||||||
|
if (response.data.data.paths.length > 0) {
|
||||||
|
this.fileSelect.paths = response.data.data.paths
|
||||||
|
}
|
||||||
|
this.modalLoading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Error /get_savepath_detail:', error);
|
||||||
|
this.fileSelect = { error: "获取文件夹列表失败" };
|
||||||
|
this.modalLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showSavepathSelect(index) {
|
||||||
|
this.fileSelect.selectShare = false;
|
||||||
|
this.fileSelect.selectDir = true;
|
||||||
|
this.fileSelect.error = undefined;
|
||||||
|
this.fileSelect.fileList = [];
|
||||||
|
this.fileSelect.paths = [];
|
||||||
|
this.fileSelect.index = index;
|
||||||
|
$('#fileSelectModal').modal('toggle');
|
||||||
|
this.getSavepathDetail(this.formData.tasklist[index].savepath);
|
||||||
|
},
|
||||||
|
getShareDetail() {
|
||||||
|
this.modalLoading = true;
|
||||||
|
axios.get('/get_share_detail', {
|
||||||
|
params: {
|
||||||
|
shareurl: this.fileSelect.shareurl,
|
||||||
|
stoken: this.fileSelect.stoken
|
||||||
|
}
|
||||||
|
}).then(response => {
|
||||||
|
if (response.data.success) {
|
||||||
|
this.fileSelect.fileList = response.data.data.list;
|
||||||
|
this.fileSelect.paths = response.data.data.paths;
|
||||||
|
this.fileSelect.stoken = response.data.data.stoken;
|
||||||
|
} else {
|
||||||
|
this.fileSelect.error = response.data.data.error
|
||||||
|
}
|
||||||
|
this.modalLoading = false;
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Error getting folders:', error);
|
||||||
|
this.fileSelect = { error: "获取文件夹列表失败" };
|
||||||
|
this.modalLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showFolderSelect(index, shareurl = "") {
|
||||||
|
this.fileSelect.selectShare = true;
|
||||||
|
this.fileSelect.fileList = [];
|
||||||
|
this.fileSelect.paths = [];
|
||||||
|
this.fileSelect.error = undefined;
|
||||||
|
if (this.getShareurl(this.fileSelect.shareurl) != this.getShareurl(this.formData.tasklist[index].shareurl)) {
|
||||||
|
this.fileSelect.stoken = "";
|
||||||
|
}
|
||||||
|
this.fileSelect.shareurl = shareurl || this.formData.tasklist[index].shareurl;
|
||||||
|
this.fileSelect.index = index;
|
||||||
|
$('#fileSelectModal').modal('toggle');
|
||||||
|
this.getShareDetail();
|
||||||
|
},
|
||||||
|
navigateTo(fid, name) {
|
||||||
|
path = { fid: fid, name: name }
|
||||||
|
if (this.fileSelect.selectShare) {
|
||||||
|
this.fileSelect.shareurl = this.getShareurl(this.fileSelect.shareurl, path);
|
||||||
|
this.getShareDetail();
|
||||||
|
} else {
|
||||||
|
if (fid == "0") {
|
||||||
|
this.fileSelect.paths = []
|
||||||
|
} else {
|
||||||
|
index = this.fileSelect.paths.findIndex(item => item.fid === fid);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.fileSelect.paths = this.fileSelect.paths.slice(0, index + 1)
|
||||||
|
} else {
|
||||||
|
this.fileSelect.paths.push({ fid: fid, name: name })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.getSavepathDetail(fid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectCurrentFolder(addTaskname = false) {
|
||||||
|
if (this.fileSelect.selectShare) {
|
||||||
|
this.formData.tasklist[this.fileSelect.index].shareurl_ban = undefined;
|
||||||
|
this.formData.tasklist[this.fileSelect.index].shareurl = this.fileSelect.shareurl;
|
||||||
|
} else {
|
||||||
|
this.formData.tasklist[this.fileSelect.index].savepath = "/" + this.fileSelect.paths.map(item => item.name).join("/");
|
||||||
|
if (addTaskname) {
|
||||||
|
this.formData.tasklist[this.fileSelect.index].savepath += "/" + this.formData.tasklist[this.fileSelect.index].taskname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('#fileSelectModal').modal('hide')
|
||||||
|
},
|
||||||
|
selectStartFid(fid) {
|
||||||
|
Vue.set(this.formData.tasklist[this.fileSelect.index], 'startfid', fid);
|
||||||
|
$('#fileSelectModal').modal('hide')
|
||||||
|
},
|
||||||
|
getShareurl(shareurl, path = {}) {
|
||||||
|
if (path == {} || path.fid == 0) {
|
||||||
|
shareurl = shareurl.match(`.*s/[a-z0-9]+`)[0]
|
||||||
|
} else if (shareurl.includes(path.fid)) {
|
||||||
|
shareurl = shareurl.match(`.*/${path.fid}[^\/]*`)[0]
|
||||||
|
} else if (shareurl.includes('#/list/share')) {
|
||||||
|
shareurl = `${shareurl}/${path.fid}-${path.name}`
|
||||||
|
} else {
|
||||||
|
shareurl = `${shareurl}#/list/share/${path.fid}-${path.name}`
|
||||||
|
}
|
||||||
|
return shareurl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,8 +15,13 @@ import time
|
|||||||
import random
|
import random
|
||||||
import requests
|
import requests
|
||||||
import importlib
|
import importlib
|
||||||
|
import urllib.parse
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
# ========== 修改内容开始 ==========
|
||||||
|
MAX_SAVE_FILES = 0 # 最大保存文件数量,0 为不限制保存数量
|
||||||
|
# ========== 修改内容结束 ==========
|
||||||
|
|
||||||
# 兼容青龙
|
# 兼容青龙
|
||||||
try:
|
try:
|
||||||
from treelib import Tree
|
from treelib import Tree
|
||||||
@ -336,6 +341,8 @@ class Quark:
|
|||||||
"_sort": "file_type:asc,updated_at:desc",
|
"_sort": "file_type:asc,updated_at:desc",
|
||||||
}
|
}
|
||||||
response = self._send_request("GET", url, params=querystring).json()
|
response = self._send_request("GET", url, params=querystring).json()
|
||||||
|
if response["code"] != 0:
|
||||||
|
return {"error": response["message"]}
|
||||||
if response["data"]["list"]:
|
if response["data"]["list"]:
|
||||||
list_merge += response["data"]["list"]
|
list_merge += response["data"]["list"]
|
||||||
page += 1
|
page += 1
|
||||||
@ -383,6 +390,8 @@ class Quark:
|
|||||||
"_fetch_full_path": kwargs.get("fetch_full_path", 0),
|
"_fetch_full_path": kwargs.get("fetch_full_path", 0),
|
||||||
}
|
}
|
||||||
response = self._send_request("GET", url, params=querystring).json()
|
response = self._send_request("GET", url, params=querystring).json()
|
||||||
|
if response["code"] != 0:
|
||||||
|
return {"error": response["message"]}
|
||||||
if response["data"]["list"]:
|
if response["data"]["list"]:
|
||||||
file_list += response["data"]["list"]
|
file_list += response["data"]["list"]
|
||||||
page += 1
|
page += 1
|
||||||
@ -402,9 +411,20 @@ class Quark:
|
|||||||
"__dt": int(random.uniform(1, 5) * 60 * 1000),
|
"__dt": int(random.uniform(1, 5) * 60 * 1000),
|
||||||
"__t": datetime.now().timestamp(),
|
"__t": datetime.now().timestamp(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ========== 修改内容开始 ==========
|
||||||
|
# 当 MAX_SAVE_FILES = 0 时,不限制保存文件数量
|
||||||
|
if MAX_SAVE_FILES > 0:
|
||||||
|
files_to_save = fid_list[:MAX_SAVE_FILES]
|
||||||
|
tokens_to_save = fid_token_list[:MAX_SAVE_FILES]
|
||||||
|
else:
|
||||||
|
files_to_save = fid_list
|
||||||
|
tokens_to_save = fid_token_list
|
||||||
|
# ========== 修改内容结束 ==========
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"fid_list": fid_list,
|
"fid_list": files_to_save, # 修改为 files_to_save
|
||||||
"fid_token_list": fid_token_list,
|
"fid_token_list": tokens_to_save, # 修改为 tokens_to_save
|
||||||
"to_pdir_fid": to_pdir_fid,
|
"to_pdir_fid": to_pdir_fid,
|
||||||
"pwd_id": pwd_id,
|
"pwd_id": pwd_id,
|
||||||
"stoken": stoken,
|
"stoken": stoken,
|
||||||
@ -527,17 +547,34 @@ class Quark:
|
|||||||
replace = replace.replace("$TASKNAME", taskname)
|
replace = replace.replace("$TASKNAME", taskname)
|
||||||
return pattern, replace
|
return pattern, replace
|
||||||
|
|
||||||
def get_id_from_url(self, url):
|
# def get_id_from_url(self, url):
|
||||||
url = url.replace("https://pan.quark.cn/s/", "")
|
# url = url.replace("https://pan.quark.cn/s/", "")
|
||||||
pattern = r"(\w+)(\?pwd=(\w+))?(#/list/share.*/(\w+))?"
|
# pattern = r"(\w+)(\?pwd=(\w+))?(#/list/share.*/(\w+))?"
|
||||||
match = re.search(pattern, url)
|
# match = re.search(pattern, url)
|
||||||
if match:
|
# if match:
|
||||||
pwd_id = match.group(1)
|
# pwd_id = match.group(1)
|
||||||
passcode = match.group(3) if match.group(3) else ""
|
# passcode = match.group(3) if match.group(3) else ""
|
||||||
pdir_fid = match.group(5) if match.group(5) else 0
|
# pdir_fid = match.group(5) if match.group(5) else 0
|
||||||
return pwd_id, passcode, pdir_fid
|
# return pwd_id, passcode, pdir_fid
|
||||||
else:
|
# else:
|
||||||
return None
|
# return None
|
||||||
|
|
||||||
|
def extract_url(self, url):
|
||||||
|
# pwd_id
|
||||||
|
match_id = re.search(r"/s/(\w+)", url)
|
||||||
|
pwd_id = match_id.group(1) if match_id else None
|
||||||
|
# passcode
|
||||||
|
match_pwd = re.search(r"pwd=(\w+)", url)
|
||||||
|
passcode = match_pwd.group(1) if match_pwd else ""
|
||||||
|
# path: fid-name
|
||||||
|
paths = []
|
||||||
|
matches = re.findall(r"/(\w{32})-?([^/]+)?", url)
|
||||||
|
for match in matches:
|
||||||
|
fid = match[0]
|
||||||
|
name = urllib.parse.unquote(match[1])
|
||||||
|
paths.append({"fid": fid, "name": name})
|
||||||
|
pdir_fid = paths[-1]["fid"] if matches else 0
|
||||||
|
return pwd_id, passcode, pdir_fid, paths
|
||||||
|
|
||||||
def update_savepath_fid(self, tasklist):
|
def update_savepath_fid(self, tasklist):
|
||||||
dir_paths = [
|
dir_paths = [
|
||||||
@ -572,8 +609,8 @@ class Quark:
|
|||||||
|
|
||||||
def do_save_check(self, shareurl, savepath):
|
def do_save_check(self, shareurl, savepath):
|
||||||
try:
|
try:
|
||||||
pwd_id, passcode, pdir_fid = self.get_id_from_url(shareurl)
|
pwd_id, passcode, pdir_fid, _ = self.extract_url(shareurl)
|
||||||
is_sharing, stoken = self.get_stoken(pwd_id, passcode)
|
_, stoken = self.get_stoken(pwd_id, passcode)
|
||||||
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["list"]
|
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["list"]
|
||||||
fid_list = [item["fid"] for item in share_file_list]
|
fid_list = [item["fid"] for item in share_file_list]
|
||||||
fid_token_list = [item["share_fid_token"] for item in share_file_list]
|
fid_token_list = [item["share_fid_token"] for item in share_file_list]
|
||||||
@ -620,8 +657,7 @@ class Quark:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 链接转换所需参数
|
# 链接转换所需参数
|
||||||
pwd_id, passcode, pdir_fid = self.get_id_from_url(task["shareurl"])
|
pwd_id, passcode, pdir_fid, _ = self.extract_url(task["shareurl"])
|
||||||
# print("match: ", pwd_id, pdir_fid)
|
|
||||||
|
|
||||||
# 获取stoken,同时可验证资源是否失效
|
# 获取stoken,同时可验证资源是否失效
|
||||||
is_sharing, stoken = self.get_stoken(pwd_id, passcode)
|
is_sharing, stoken = self.get_stoken(pwd_id, passcode)
|
||||||
@ -742,7 +778,13 @@ class Quark:
|
|||||||
# 指定文件开始订阅/到达指定文件(含)结束历遍
|
# 指定文件开始订阅/到达指定文件(含)结束历遍
|
||||||
if share_file["fid"] == task.get("startfid", ""):
|
if share_file["fid"] == task.get("startfid", ""):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# ========== 修改内容开始 ==========
|
||||||
|
# 当 MAX_SAVE_FILES = 0 时,不限制保存文件数量
|
||||||
|
if MAX_SAVE_FILES > 0:
|
||||||
|
need_save_list = need_save_list[:MAX_SAVE_FILES]
|
||||||
|
# ========== 修改内容结束 ==========
|
||||||
|
|
||||||
fid_list = [item["fid"] for item in need_save_list]
|
fid_list = [item["fid"] for item in need_save_list]
|
||||||
fid_token_list = [item["share_fid_token"] for item in need_save_list]
|
fid_token_list = [item["share_fid_token"] for item in need_save_list]
|
||||||
if fid_list:
|
if fid_list:
|
||||||
@ -884,6 +926,11 @@ def do_save(account, tasklist=[]):
|
|||||||
# 获取全部保存目录fid
|
# 获取全部保存目录fid
|
||||||
account.update_savepath_fid(tasklist)
|
account.update_savepath_fid(tasklist)
|
||||||
|
|
||||||
|
# ========== 修改内容开始 ==========
|
||||||
|
# 初始化计数器
|
||||||
|
total_files_transferred = 0 # 无论 MAX_SAVE_FILES 的值如何,都初始化计数器
|
||||||
|
# ========== 修改内容结束 ==========
|
||||||
|
|
||||||
def check_date(task):
|
def check_date(task):
|
||||||
return (
|
return (
|
||||||
not task.get("enddate")
|
not task.get("enddate")
|
||||||
@ -901,6 +948,12 @@ def do_save(account, tasklist=[]):
|
|||||||
for index, task in enumerate(tasklist):
|
for index, task in enumerate(tasklist):
|
||||||
# 判断任务期限
|
# 判断任务期限
|
||||||
if check_date(task):
|
if check_date(task):
|
||||||
|
# ========== 修改内容开始 ==========
|
||||||
|
# 当 MAX_SAVE_FILES > 0 时,检查是否已转存超过限制
|
||||||
|
if MAX_SAVE_FILES > 0 and total_files_transferred >= MAX_SAVE_FILES:
|
||||||
|
print("⚠️ 已达到转存文件总数上限,停止转存任务")
|
||||||
|
break
|
||||||
|
# ========== 修改内容结束 ==========
|
||||||
print()
|
print()
|
||||||
print(f"#{index+1}------------------")
|
print(f"#{index+1}------------------")
|
||||||
print(f"任务名称: {task['taskname']}")
|
print(f"任务名称: {task['taskname']}")
|
||||||
@ -919,6 +972,13 @@ def do_save(account, tasklist=[]):
|
|||||||
print()
|
print()
|
||||||
is_new_tree = account.do_save_task(task)
|
is_new_tree = account.do_save_task(task)
|
||||||
is_rename = account.do_rename_task(task)
|
is_rename = account.do_rename_task(task)
|
||||||
|
|
||||||
|
# ========== 修改内容开始 ==========
|
||||||
|
# 当 MAX_SAVE_FILES > 0 时,更新计数器
|
||||||
|
if MAX_SAVE_FILES > 0 and (is_new_tree or is_rename):
|
||||||
|
total_files_transferred += 1 # 每成功转存或重命名一个文件,计数器加1
|
||||||
|
# ========== 修改内容结束 ==========
|
||||||
|
|
||||||
|
|
||||||
# 补充任务的插件配置
|
# 补充任务的插件配置
|
||||||
def merge_dicts(a, b):
|
def merge_dicts(a, b):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user