Compare commits
6 Commits
a3216891ff
...
2bff5239ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bff5239ce | ||
|
|
71b4fca6c2 | ||
|
|
b6aca2e2e9 | ||
|
|
996210f8c7 | ||
|
|
de6e4356a4 | ||
|
|
da7a5e93c8 |
20
README.md
@ -29,13 +29,13 @@
|
|||||||
> ⛔️⛔️⛔️ 注意!资源不会每时每刻更新,**严禁设定过高的定时运行频率!** 以免账号风控和给夸克服务器造成不必要的压力。雪山崩塌,每一片雪花都有责任!
|
> ⛔️⛔️⛔️ 注意!资源不会每时每刻更新,**严禁设定过高的定时运行频率!** 以免账号风控和给夸克服务器造成不必要的压力。雪山崩塌,每一片雪花都有责任!
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> 因不想当客服处理各种使用咨询,即日起 Issues 关闭,如果你发现了 bug 、有好的想法或功能建议,欢迎通过 PR 和我对话,谢谢!
|
> 开发者≠客服,开源免费≠帮你解决使用问题;本项目Wiki和已经相对完善,遇到问题请先翻阅 Issues 和 Wiki ,请勿盲目发问。
|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
- 部署方式
|
- 部署方式
|
||||||
- [x] 兼容青龙
|
- [x] 可能~~兼容青龙~~
|
||||||
- [x] 支持 Docker 独立部署,WebUI 配置
|
- [x] Docker 部署,WebUI 配置
|
||||||
|
|
||||||
- 分享链接
|
- 分享链接
|
||||||
- [x] 支持分享链接的子目录
|
- [x] 支持分享链接的子目录
|
||||||
@ -58,7 +58,7 @@
|
|||||||
- 媒体库整合
|
- 媒体库整合
|
||||||
- [x] 根据任务名搜索 Emby 媒体库
|
- [x] 根据任务名搜索 Emby 媒体库
|
||||||
- [x] 追更或整理后自动刷新 Emby 媒体库
|
- [x] 追更或整理后自动刷新 Emby 媒体库
|
||||||
- [x] **媒体库模块化,用户可很方便地[开发自己的媒体库hook模块](./plugins)**
|
- [x] 媒体库模块化,用户可很方便地[开发自己的媒体库hook模块](./plugins)
|
||||||
|
|
||||||
- 其它
|
- 其它
|
||||||
- [x] 每日签到领空间 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/使用技巧集锦#每日签到领空间)</sup>
|
- [x] 每日签到领空间 <sup>[?](https://github.com/Cp0204/quark-auto-save/wiki/使用技巧集锦#每日签到领空间)</sup>
|
||||||
@ -74,10 +74,10 @@ Docker 部署提供 WebUI 管理配置,图形化配置已能满足绝大多数
|
|||||||
```shell
|
```shell
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name quark-auto-save \
|
--name quark-auto-save \
|
||||||
-p 5005:5005 \
|
-p 5005:5005 \ # 映射端口,:前的可以改,即部署后访问的端口,:后的不可改
|
||||||
-e WEBUI_USERNAME=admin \
|
-e WEBUI_USERNAME=admin \
|
||||||
-e WEBUI_PASSWORD=admin123 \
|
-e WEBUI_PASSWORD=admin123 \
|
||||||
-v ./quark-auto-save/config:/app/config \
|
-v ./quark-auto-save/config:/app/config \ # 必须,配置持久化
|
||||||
-v ./quark-auto-save/media:/media \ # 可选,模块alist_strm_gen生成strm使用
|
-v ./quark-auto-save/media:/media \ # 可选,模块alist_strm_gen生成strm使用
|
||||||
--network bridge \
|
--network bridge \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
@ -167,12 +167,14 @@ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtow
|
|||||||
|
|
||||||
## 打赏
|
## 打赏
|
||||||
|
|
||||||
如果这个项目让你受益,你可以打赏我1块钱,让我知道开源有价值。谢谢!
|
如果这个项目让你受益,你可以无偿赠与我1块钱,让我知道开源有价值。谢谢!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 声明
|
## 声明
|
||||||
|
|
||||||
本程序为个人兴趣开发,开源仅供学习与交流使用。
|
本项目为个人兴趣开发,旨在通过程序自动化提高网盘使用效率。
|
||||||
|
|
||||||
程序没有任何破解行为,只是对于夸克已有的API进行封装,所有数据来自于夸克官方API,本人不对网盘内容负责、不对夸克官方API未来可能的改动导致的后果负责。
|
程序没有任何破解行为,只是对于夸克已有的API进行封装,所有数据来自于夸克官方API;本人不对网盘内容负责、不对夸克官方API未来可能的变动导致的影响负责,请自行斟酌使用。
|
||||||
|
|
||||||
|
开源仅供学习与交流使用,未盈利也未授权商业使用,严禁用于非法用途。
|
||||||
31
app/run.py
@ -268,15 +268,26 @@ def get_share_detail():
|
|||||||
account = Quark("", 0)
|
account = Quark("", 0)
|
||||||
pwd_id, passcode, pdir_fid, paths = account.extract_url(shareurl)
|
pwd_id, passcode, pdir_fid, paths = account.extract_url(shareurl)
|
||||||
if not stoken:
|
if not stoken:
|
||||||
is_sharing, stoken = account.get_stoken(pwd_id, passcode)
|
get_stoken = account.get_stoken(pwd_id, passcode)
|
||||||
if not is_sharing:
|
if get_stoken.get("status") == 200:
|
||||||
return jsonify({"success": False, "data": {"error": stoken}})
|
stoken = get_stoken["data"]["stoken"]
|
||||||
|
else:
|
||||||
|
return jsonify(
|
||||||
|
{"success": False, "data": {"error": get_stoken.get("message")}}
|
||||||
|
)
|
||||||
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, _fetch_share=1)
|
share_detail = account.get_detail(pwd_id, stoken, pdir_fid, _fetch_share=1)
|
||||||
share_detail["paths"] = paths
|
|
||||||
share_detail["stoken"] = stoken
|
if share_detail.get("code") != 0:
|
||||||
|
return jsonify(
|
||||||
|
{"success": False, "data": {"error": share_detail.get("message")}}
|
||||||
|
)
|
||||||
|
|
||||||
|
data = share_detail["data"]
|
||||||
|
data["paths"] = paths
|
||||||
|
data["stoken"] = stoken
|
||||||
|
|
||||||
# 正则处理预览
|
# 正则处理预览
|
||||||
def preview_regex(share_detail):
|
def preview_regex(data):
|
||||||
regex = request.json.get("regex", {})
|
regex = request.json.get("regex", {})
|
||||||
pattern, replace = account.magic_regex_func(
|
pattern, replace = account.magic_regex_func(
|
||||||
regex.get("pattern", ""),
|
regex.get("pattern", ""),
|
||||||
@ -284,7 +295,7 @@ def get_share_detail():
|
|||||||
regex.get("taskname", ""),
|
regex.get("taskname", ""),
|
||||||
regex.get("magic_regex", {}),
|
regex.get("magic_regex", {}),
|
||||||
)
|
)
|
||||||
for item in share_detail["list"]:
|
for item in data["list"]:
|
||||||
file_name = item["file_name"]
|
file_name = item["file_name"]
|
||||||
if re.search(pattern, item["file_name"]):
|
if re.search(pattern, item["file_name"]):
|
||||||
item["file_name_re"] = (
|
item["file_name_re"] = (
|
||||||
@ -293,9 +304,9 @@ def get_share_detail():
|
|||||||
return share_detail
|
return share_detail
|
||||||
|
|
||||||
if request.json.get("regex"):
|
if request.json.get("regex"):
|
||||||
share_detail = preview_regex(share_detail)
|
share_detail = preview_regex(data)
|
||||||
|
|
||||||
return jsonify({"success": True, "data": share_detail})
|
return jsonify({"success": True, "data": data})
|
||||||
|
|
||||||
|
|
||||||
@app.route("/get_savepath_detail")
|
@app.route("/get_savepath_detail")
|
||||||
@ -328,7 +339,7 @@ def get_savepath_detail():
|
|||||||
else:
|
else:
|
||||||
fid = request.args.get("fid", "0")
|
fid = request.args.get("fid", "0")
|
||||||
file_list = {
|
file_list = {
|
||||||
"list": account.ls_dir(fid),
|
"list": account.ls_dir(fid)["data"]["list"],
|
||||||
"paths": paths,
|
"paths": paths,
|
||||||
}
|
}
|
||||||
return jsonify({"success": True, "data": file_list})
|
return jsonify({"success": True, "data": file_list})
|
||||||
|
|||||||
@ -45,7 +45,7 @@ body {
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
table.jsoneditor-tree > tbody > tr.jsoneditor-expandable:first-child {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,14 +82,14 @@ table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
|||||||
/* Behind the navbar */
|
/* Behind the navbar */
|
||||||
padding: 54px 0 0;
|
padding: 54px 0 0;
|
||||||
/* Height of navbar */
|
/* Height of navbar */
|
||||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-sticky {
|
.sidebar-sticky {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: calc(100vh - 54px);
|
height: calc(100vh - 54px);
|
||||||
padding-top: .5rem;
|
padding-top: 0.5rem;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
/* Scrollable contents if viewport is shorter than content. */
|
/* Scrollable contents if viewport is shorter than content. */
|
||||||
@ -125,9 +125,8 @@ table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
|||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.sidebar-heading {
|
.sidebar-heading {
|
||||||
font-size: .75rem;
|
font-size: 0.75rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,10 +135,10 @@ table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
padding-top: .75rem;
|
padding-top: 0.75rem;
|
||||||
padding-bottom: .75rem;
|
padding-bottom: 0.75rem;
|
||||||
background-color: rgba(0, 0, 0, .25);
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar .navbar-toggler {
|
.navbar .navbar-toggler {
|
||||||
@ -147,22 +146,53 @@ table.jsoneditor-tree>tbody>tr.jsoneditor-expandable:first-child {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar .form-control {
|
.navbar .form-control {
|
||||||
padding: .75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control-dark {
|
.form-control-dark {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: rgba(255, 255, 255, .1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
border-color: rgba(255, 255, 255, .1);
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.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, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursor-pointer {
|
.cursor-pointer {
|
||||||
cursor: pointer
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-bottom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 32px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-relative:hover .position-absolute {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-tutorial {
|
||||||
|
display: none;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
bottom: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
z-index: 1000;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
max-width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-tutorial img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
BIN
app/static/img/qrcode_tutorial.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
@ -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.3
|
// @version 0.4
|
||||||
// @description 在夸克网盘分享页面添加推送到 QAS 的按钮
|
// @description 在夸克网盘分享页面添加推送到 QAS 的按钮
|
||||||
// @icon https://pan.quark.cn/favicon.ico
|
// @icon https://pan.quark.cn/favicon.ico
|
||||||
// @author Cp0204
|
// @author Cp0204
|
||||||
@ -11,41 +11,53 @@
|
|||||||
// @grant GM_setValue
|
// @grant GM_setValue
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
|
// @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
|
// @downloadURL https://cdn.jsdelivr.net/gh/Cp0204/quark-auto-save@refs/heads/main/app/static/js/qas.addtask.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
|
// @updateURL https://cdn.jsdelivr.net/gh/Cp0204/quark-auto-save@refs/heads/main/app/static/js/qas.addtask.user.js
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
|
||||||
(function() {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
let qas_base = GM_getValue('qas_base', '');
|
let qas_base = GM_getValue('qas_base', '');
|
||||||
let qas_token = GM_getValue('qas_token', '');
|
let qas_token = GM_getValue('qas_token', '');
|
||||||
|
let default_pattern = GM_getValue('default_pattern', '');
|
||||||
|
let default_replace = GM_getValue('default_replace', '');
|
||||||
|
|
||||||
// QAS 设置弹窗函数
|
// QAS 设置弹窗函数
|
||||||
function showQASSettingDialog(callback) {
|
function showQASSettingDialog(callback) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'QAS 设置',
|
title: 'QAS 设置',
|
||||||
|
showCancelButton: true,
|
||||||
html: `
|
html: `
|
||||||
<label for="qas_base">QAS 服务器</label>
|
<label for="qas_base">QAS 地址</label>
|
||||||
<input id="qas_base" class="swal2-input" placeholder="例如: 192.168.1.8:5005" value="${qas_base}">
|
<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}">
|
<input id="qas_token" class="swal2-input" placeholder="v0.5+ 系统配置中查找" value="${qas_token}">
|
||||||
|
<label for="qas_token">默认正则</label>
|
||||||
|
<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}">
|
||||||
`,
|
`,
|
||||||
focusConfirm: false,
|
focusConfirm: false,
|
||||||
preConfirm: () => {
|
preConfirm: () => {
|
||||||
qas_base = document.getElementById('qas_base').value;
|
qas_base = document.getElementById('qas_base').value;
|
||||||
qas_token = document.getElementById('qas_token').value;
|
qas_token = document.getElementById('qas_token').value;
|
||||||
|
default_pattern = document.getElementById('default_pattern').value;
|
||||||
|
default_replace = document.getElementById('default_replace').value;
|
||||||
if (!qas_base || !qas_token) {
|
if (!qas_base || !qas_token) {
|
||||||
Swal.showValidationMessage('请填写 QAS 服务器和 Token');
|
Swal.showValidationMessage('请填写 QAS 地址和 Token');
|
||||||
}
|
}
|
||||||
return { qas_base: qas_base, qas_token: qas_token }
|
return { qas_base: qas_base, qas_token: qas_token, default_pattern: default_pattern, default_replace: default_replace }
|
||||||
}
|
}
|
||||||
}).then((result) => {
|
}).then((result) => {
|
||||||
if (result.isConfirmed) {
|
if (result.isConfirmed) {
|
||||||
GM_setValue('qas_base', result.value.qas_base);
|
GM_setValue('qas_base', result.value.qas_base);
|
||||||
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_replace', result.value.default_replace);
|
||||||
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_replace = result.value.default_replace;
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(); // 执行回调函数
|
callback(); // 执行回调函数
|
||||||
}
|
}
|
||||||
@ -93,18 +105,18 @@
|
|||||||
qasButton.type = 'button';
|
qasButton.type = 'button';
|
||||||
qasButton.className = 'ant-btn share-save';
|
qasButton.className = 'ant-btn share-save';
|
||||||
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; // 声明变量
|
||||||
|
|
||||||
// 获取数据函数
|
// 获取数据函数
|
||||||
function getData() {
|
function getData() {
|
||||||
const currentUrl = window.location.href;
|
const currentUrl = window.location.href;
|
||||||
taskname = currentUrl.lastIndexOf('-') > 0 ? decodeURIComponent(currentUrl.match(/.*\/[^-]+-(.+)$/)[1]) : document.querySelector('.author-name').textContent;
|
taskname = currentUrl.lastIndexOf('-') > 0 ? decodeURIComponent(currentUrl.match(/.*\/[^-]+-(.+)$/)[1]).replace('*101', '-') : document.querySelector('.author-name').textContent;
|
||||||
shareurl = currentUrl;
|
shareurl = currentUrl;
|
||||||
let pathElement = document.querySelector('.path-name')
|
let pathElement = document.querySelector('.path-name');
|
||||||
savepath = pathElement ? pathElement.title.replace('全部文件', '').trim() : "";
|
savepath = pathElement ? pathElement.title.replace('全部文件', '').trim() : "";
|
||||||
savepath += "/" + taskname
|
savepath += "/" + taskname;
|
||||||
qasButton.title = `任务名称: ${taskname}\n分享链接: ${shareurl}\n保存路径: ${savepath}`;
|
qasButton.title = `任务名称: ${taskname}\n分享链接: ${shareurl}\n保存路径: ${savepath}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,11 +131,19 @@
|
|||||||
qasButton.addEventListener('click', () => {
|
qasButton.addEventListener('click', () => {
|
||||||
getData(); // 点击时重新获取数据,确保最新
|
getData(); // 点击时重新获取数据,确保最新
|
||||||
|
|
||||||
const apiUrl = `http://${qas_base}/api/add_task?token=${qas_token}`;
|
// 检查 qas_base 是否包含 http 或 https,如果没有则添加 http://
|
||||||
|
let qasApiBase = qas_base;
|
||||||
|
if (!qasApiBase.startsWith('http')) {
|
||||||
|
qasApiBase = 'http://' + qasApiBase;
|
||||||
|
}
|
||||||
|
const apiUrl = `${qasApiBase}/api/add_task?token=${qas_token}`;
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
"taskname": taskname,
|
"taskname": taskname,
|
||||||
"shareurl": shareurl,
|
"shareurl": shareurl,
|
||||||
"savepath": savepath,
|
"savepath": savepath,
|
||||||
|
"pattern": default_pattern,
|
||||||
|
"replace": default_replace,
|
||||||
};
|
};
|
||||||
|
|
||||||
GM_xmlhttpRequest({
|
GM_xmlhttpRequest({
|
||||||
@ -133,7 +153,7 @@
|
|||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
data: JSON.stringify(data),
|
data: JSON.stringify(data),
|
||||||
onload: function(response) {
|
onload: function (response) {
|
||||||
try {
|
try {
|
||||||
const jsonResponse = JSON.parse(response.responseText);
|
const jsonResponse = JSON.parse(response.responseText);
|
||||||
if (jsonResponse.success) {
|
if (jsonResponse.success) {
|
||||||
@ -142,7 +162,7 @@
|
|||||||
html: `<small>
|
html: `<small>
|
||||||
<b>任务名称:</b> ${taskname}<br><br>
|
<b>任务名称:</b> ${taskname}<br><br>
|
||||||
<b>保存路径:</b> ${savepath}<br><br>
|
<b>保存路径:</b> ${savepath}<br><br>
|
||||||
<a href="http://${qas_base}" target="_blank">去 QAS 查看</a>
|
<a href="${qasApiBase}" target="_blank">去 QAS 查看</a>
|
||||||
<small>`,
|
<small>`,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
});
|
});
|
||||||
@ -161,7 +181,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onerror: function(error) {
|
onerror: function (error) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '任务创建失败',
|
title: '任务创建失败',
|
||||||
text: error,
|
text: error,
|
||||||
|
|||||||
@ -49,9 +49,17 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="text-center" style="position: absolute; bottom: 32px; width: 100%; font-size: small;">
|
<div class="nav-bottom text-center">
|
||||||
<p><a class="" target="_blank" href="https://github.com/Cp0204/quark-auto-save/wiki"><i class="bi bi-wechat"></i> 使用交流</a></p>
|
<p class="position-relative" hidden>
|
||||||
<p><a target="_blank" href="https://github.com/Cp0204/quark-auto-save"><i class="bi bi-github"></i> quark-auto-save</a></p>
|
<b class="text-success"><i class="bi bi-record-circle mr-1"></i>视频教程</b>
|
||||||
|
<span class="position-absolute qrcode-tutorial">
|
||||||
|
使用夸克扫码查看<br>
|
||||||
|
<img src="./static/img/qrcode_tutorial.png">
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p><a target="_blank" href="https://github.com/Cp0204/quark-auto-save/wiki"><i class="bi bi-wechat mr-1"></i>使用交流</a></p>
|
||||||
|
<p><a href="./static/js/qas.addtask.user.js"><i class="bi bi-cloud-plus-fill mr-1"></i>推送任务油猴脚本</a></p>
|
||||||
|
<p><a target="_blank" href="https://github.com/Cp0204/quark-auto-save"><i class="bi bi-github mr-1"></i>quark-auto-save</a></p>
|
||||||
<p><span v-html="versionTips"></span></p>
|
<p><span v-html="versionTips"></span></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -391,9 +399,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-buttons">
|
<div class="bottom-buttons">
|
||||||
<button class="btn btn-success" title="保存 CTRL+S"><i class="bi bi-floppy2-fill"></i></button>
|
<button class="btn btn-success" data-toggle="tooltip" data-placement="top" title="保存 CTRL+S"><i class="bi bi-floppy2-fill"></i></button>
|
||||||
<button type="button" class="btn btn-primary" title="运行 CTRL+R" @click="runScriptNow()"><i class="bi bi-play-fill"></i></button>
|
<button type="button" class="btn btn-primary" data-toggle="tooltip" data-placement="top" title="运行 CTRL+R" @click="runScriptNow()"><i class="bi bi-play-fill"></i></button>
|
||||||
<button type="button" class="btn btn-info" @click="scrollToX(0)" @dblclick="scrollToX()" data-toggle="tooltip" data-placement="top" title="单击回顶,双击到底"><i class="bi bi-chevron-bar-up"></i></button>
|
<button type="button" class="btn btn-info" data-toggle="tooltip" data-placement="top" title="单击回顶,双击到底" @click="scrollToX(0)" @dblclick="scrollToX()"><i class="bi bi-chevron-bar-up"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
@ -605,7 +613,7 @@
|
|||||||
latestVersion = response.data[0].name;
|
latestVersion = response.data[0].name;
|
||||||
console.log(`检查版本:当前 ${this.version} 最新 ${latestVersion}`);
|
console.log(`检查版本:当前 ${this.version} 最新 ${latestVersion}`);
|
||||||
if (latestVersion != this.version) {
|
if (latestVersion != this.version) {
|
||||||
this.versionTips += ` <sup><span class="badge badge-pill badge-danger">${latestVersion}</span></sup>`;
|
this.versionTips += ` <sup><span class="position-absolute badge badge-pill badge-danger">${latestVersion}</span></sup>`;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -922,6 +930,8 @@
|
|||||||
},
|
},
|
||||||
selectSuggestion(index, suggestion) {
|
selectSuggestion(index, suggestion) {
|
||||||
this.smart_param.showSuggestions = false;
|
this.smart_param.showSuggestions = false;
|
||||||
|
this.fileSelect.selectDir = true;
|
||||||
|
this.fileSelect.previewRegex = false;
|
||||||
this.showShareSelect(index, suggestion.shareurl);
|
this.showShareSelect(index, suggestion.shareurl);
|
||||||
},
|
},
|
||||||
addMagicRegex() {
|
addMagicRegex() {
|
||||||
@ -1021,10 +1031,12 @@
|
|||||||
this.fileSelect.fileList = [];
|
this.fileSelect.fileList = [];
|
||||||
this.fileSelect.paths = [];
|
this.fileSelect.paths = [];
|
||||||
this.fileSelect.error = undefined;
|
this.fileSelect.error = undefined;
|
||||||
if (this.getShareurl(this.fileSelect.shareurl) != this.getShareurl(this.formData.tasklist[index].shareurl)) {
|
// 如果分享链接发生变化,则重置 stoken
|
||||||
|
const newShareurl = shareurl || this.formData.tasklist[index].shareurl
|
||||||
|
if (this.getShareurl(this.fileSelect.shareurl) != this.getShareurl(newShareurl)) {
|
||||||
this.fileSelect.stoken = "";
|
this.fileSelect.stoken = "";
|
||||||
}
|
}
|
||||||
this.fileSelect.shareurl = shareurl || this.formData.tasklist[index].shareurl;
|
this.fileSelect.shareurl = newShareurl;
|
||||||
this.fileSelect.index = index;
|
this.fileSelect.index = index;
|
||||||
$('#fileSelectModal').modal('toggle');
|
$('#fileSelectModal').modal('toggle');
|
||||||
this.getShareDetail();
|
this.getShareDetail();
|
||||||
|
|||||||
BIN
img/run_log.png
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 241 KiB |
|
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 313 KiB |
@ -240,7 +240,9 @@ class Quark:
|
|||||||
print(f"_send_request error:\n{e}")
|
print(f"_send_request error:\n{e}")
|
||||||
fake_response = requests.Response()
|
fake_response = requests.Response()
|
||||||
fake_response.status_code = 500
|
fake_response.status_code = 500
|
||||||
fake_response._content = b'{"status": 500, "message": "request error"}'
|
fake_response._content = (
|
||||||
|
b'{"status": 500, "code": 1, "message": "request error"}'
|
||||||
|
)
|
||||||
return fake_response
|
return fake_response
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
@ -312,10 +314,7 @@ class Quark:
|
|||||||
response = self._send_request(
|
response = self._send_request(
|
||||||
"POST", url, json=payload, params=querystring
|
"POST", url, json=payload, params=querystring
|
||||||
).json()
|
).json()
|
||||||
if response.get("status") == 200:
|
return response
|
||||||
return True, response["data"]["stoken"]
|
|
||||||
else:
|
|
||||||
return False, response["message"]
|
|
||||||
|
|
||||||
def get_detail(self, pwd_id, stoken, pdir_fid, _fetch_share=0):
|
def get_detail(self, pwd_id, stoken, pdir_fid, _fetch_share=0):
|
||||||
list_merge = []
|
list_merge = []
|
||||||
@ -338,7 +337,7 @@ class Quark:
|
|||||||
}
|
}
|
||||||
response = self._send_request("GET", url, params=querystring).json()
|
response = self._send_request("GET", url, params=querystring).json()
|
||||||
if response["code"] != 0:
|
if response["code"] != 0:
|
||||||
return {"error": response["message"]}
|
return response
|
||||||
if response["data"]["list"]:
|
if response["data"]["list"]:
|
||||||
list_merge += response["data"]["list"]
|
list_merge += response["data"]["list"]
|
||||||
page += 1
|
page += 1
|
||||||
@ -347,7 +346,7 @@ class Quark:
|
|||||||
if len(list_merge) >= response["metadata"]["_total"]:
|
if len(list_merge) >= response["metadata"]["_total"]:
|
||||||
break
|
break
|
||||||
response["data"]["list"] = list_merge
|
response["data"]["list"] = list_merge
|
||||||
return response["data"]
|
return response
|
||||||
|
|
||||||
def get_fids(self, file_paths):
|
def get_fids(self, file_paths):
|
||||||
fids = []
|
fids = []
|
||||||
@ -369,7 +368,7 @@ class Quark:
|
|||||||
return fids
|
return fids
|
||||||
|
|
||||||
def ls_dir(self, pdir_fid, **kwargs):
|
def ls_dir(self, pdir_fid, **kwargs):
|
||||||
file_list = []
|
list_merge = []
|
||||||
page = 1
|
page = 1
|
||||||
while True:
|
while True:
|
||||||
url = f"{self.BASE_URL}/1/clouddrive/file/sort"
|
url = f"{self.BASE_URL}/1/clouddrive/file/sort"
|
||||||
@ -387,15 +386,16 @@ class Quark:
|
|||||||
}
|
}
|
||||||
response = self._send_request("GET", url, params=querystring).json()
|
response = self._send_request("GET", url, params=querystring).json()
|
||||||
if response["code"] != 0:
|
if response["code"] != 0:
|
||||||
return {"error": response["message"]}
|
return response
|
||||||
if response["data"]["list"]:
|
if response["data"]["list"]:
|
||||||
file_list += response["data"]["list"]
|
list_merge += response["data"]["list"]
|
||||||
page += 1
|
page += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if len(file_list) >= response["metadata"]["_total"]:
|
if len(list_merge) >= response["metadata"]["_total"]:
|
||||||
break
|
break
|
||||||
return file_list
|
response["data"]["list"] = list_merge
|
||||||
|
return response
|
||||||
|
|
||||||
def save_file(self, fid_list, fid_token_list, to_pdir_fid, pwd_id, stoken):
|
def save_file(self, fid_list, fid_token_list, to_pdir_fid, pwd_id, stoken):
|
||||||
url = f"{self.BASE_URL}/1/clouddrive/share/sharepage/save"
|
url = f"{self.BASE_URL}/1/clouddrive/share/sharepage/save"
|
||||||
@ -595,8 +595,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.extract_url(shareurl)
|
pwd_id, passcode, pdir_fid, _ = self.extract_url(shareurl)
|
||||||
_, stoken = self.get_stoken(pwd_id, passcode)
|
stoken = self.get_stoken(pwd_id, passcode)["data"]["stoken"]
|
||||||
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["list"]
|
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["data"]["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]
|
||||||
file_name_list = [item["file_name"] for item in share_file_list]
|
file_name_list = [item["file_name"] for item in share_file_list]
|
||||||
@ -612,7 +612,7 @@ class Quark:
|
|||||||
if save_file["code"] == 41017:
|
if save_file["code"] == 41017:
|
||||||
return
|
return
|
||||||
elif save_file["code"] == 0:
|
elif save_file["code"] == 0:
|
||||||
dir_file_list = self.ls_dir(to_pdir_fid)
|
dir_file_list = self.ls_dir(to_pdir_fid)["data"]["list"]
|
||||||
del_list = [
|
del_list = [
|
||||||
item["fid"]
|
item["fid"]
|
||||||
for item in dir_file_list
|
for item in dir_file_list
|
||||||
@ -644,10 +644,16 @@ class Quark:
|
|||||||
pwd_id, passcode, pdir_fid, _ = self.extract_url(task["shareurl"])
|
pwd_id, passcode, pdir_fid, _ = self.extract_url(task["shareurl"])
|
||||||
|
|
||||||
# 获取stoken,同时可验证资源是否失效
|
# 获取stoken,同时可验证资源是否失效
|
||||||
is_sharing, stoken = self.get_stoken(pwd_id, passcode)
|
get_stoken = self.get_stoken(pwd_id, passcode)
|
||||||
if not is_sharing:
|
if get_stoken.get("status") == 200:
|
||||||
add_notify(f"❌《{task['taskname']}》:{stoken}\n")
|
stoken = get_stoken["data"]["stoken"]
|
||||||
task["shareurl_ban"] = stoken
|
elif get_stoken.get("status") == 500:
|
||||||
|
print(f"跳过任务:网络异常 {get_stoken.get("message")}")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
message = get_stoken.get("message")
|
||||||
|
add_notify(f"❌《{task['taskname']}》:{message}\n")
|
||||||
|
task["shareurl_ban"] = message
|
||||||
return
|
return
|
||||||
# print("stoken: ", stoken)
|
# print("stoken: ", stoken)
|
||||||
|
|
||||||
@ -662,7 +668,7 @@ class Quark:
|
|||||||
def dir_check_and_save(self, task, pwd_id, stoken, pdir_fid="", subdir_path=""):
|
def dir_check_and_save(self, task, pwd_id, stoken, pdir_fid="", subdir_path=""):
|
||||||
tree = Tree()
|
tree = Tree()
|
||||||
# 获取分享文件列表
|
# 获取分享文件列表
|
||||||
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["list"]
|
share_file_list = self.get_detail(pwd_id, stoken, pdir_fid)["data"]["list"]
|
||||||
# print("share_file_list: ", share_file_list)
|
# print("share_file_list: ", share_file_list)
|
||||||
|
|
||||||
if not share_file_list:
|
if not share_file_list:
|
||||||
@ -678,7 +684,7 @@ class Quark:
|
|||||||
print("🧠 该分享是一个文件夹,读取文件夹内列表")
|
print("🧠 该分享是一个文件夹,读取文件夹内列表")
|
||||||
share_file_list = self.get_detail(
|
share_file_list = self.get_detail(
|
||||||
pwd_id, stoken, share_file_list[0]["fid"]
|
pwd_id, stoken, share_file_list[0]["fid"]
|
||||||
)["list"]
|
)["data"]["list"]
|
||||||
|
|
||||||
# 获取目标目录文件列表
|
# 获取目标目录文件列表
|
||||||
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}{subdir_path}")
|
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}{subdir_path}")
|
||||||
@ -689,7 +695,7 @@ class Quark:
|
|||||||
print(f"❌ 目录 {savepath} fid获取失败,跳过转存")
|
print(f"❌ 目录 {savepath} fid获取失败,跳过转存")
|
||||||
return tree
|
return tree
|
||||||
to_pdir_fid = self.savepath_fid[savepath]
|
to_pdir_fid = self.savepath_fid[savepath]
|
||||||
dir_file_list = self.ls_dir(to_pdir_fid)
|
dir_file_list = self.ls_dir(to_pdir_fid)["data"]["list"]
|
||||||
# print("dir_file_list: ", dir_file_list)
|
# print("dir_file_list: ", dir_file_list)
|
||||||
# 清空目标文件夹
|
# 清空目标文件夹
|
||||||
fid_list = [item["fid"] for item in dir_file_list]
|
fid_list = [item["fid"] for item in dir_file_list]
|
||||||
@ -819,7 +825,7 @@ class Quark:
|
|||||||
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}{subdir_path}")
|
savepath = re.sub(r"/{2,}", "/", f"/{task['savepath']}{subdir_path}")
|
||||||
if not self.savepath_fid.get(savepath):
|
if not self.savepath_fid.get(savepath):
|
||||||
self.savepath_fid[savepath] = self.get_fids([savepath])[0]["fid"]
|
self.savepath_fid[savepath] = self.get_fids([savepath])[0]["fid"]
|
||||||
dir_file_list = self.ls_dir(self.savepath_fid[savepath])
|
dir_file_list = self.ls_dir(self.savepath_fid[savepath])["data"]["list"]
|
||||||
dir_file_name_list = [item["file_name"] for item in dir_file_list]
|
dir_file_name_list = [item["file_name"] for item in dir_file_list]
|
||||||
is_rename_count = 0
|
is_rename_count = 0
|
||||||
for dir_file in dir_file_list:
|
for dir_file in dir_file_list:
|
||||||
|
|||||||