mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-05-27 08:47:28 +00:00
Compare commits
8 Commits
b967c7a1bb
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8582290db3 | ||
|
|
5ff33d7c58 | ||
|
|
0cfb69a240 | ||
|
|
110a9beda4 | ||
|
|
fd6a3f5929 | ||
|
|
82ad6ec427 | ||
|
|
1bfc7c960d | ||
|
|
332f49f483 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -41,7 +41,9 @@ gradlew.bat
|
|||||||
unused.txt
|
unused.txt
|
||||||
/web-service/src/main/generated/
|
/web-service/src/main/generated/
|
||||||
/db
|
/db
|
||||||
|
/netdisk-fast-download/
|
||||||
/webroot/nfd-front/
|
/webroot/nfd-front/
|
||||||
|
/netdisk-fast-download/webroot/nfd-front/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
# Maven generated files
|
# Maven generated files
|
||||||
|
|||||||
48
README.md
48
README.md
@@ -29,7 +29,8 @@ QQ交流群:1017480890
|
|||||||
[API接入](https://nfdparser.apifox.cn/)
|
[API接入](https://nfdparser.apifox.cn/)
|
||||||
[公益解析,lz站](https://lz.qaiu.top)
|
[公益解析,lz站](https://lz.qaiu.top)
|
||||||
[公益解析,lz0站](https://lz0.qaiu.top)
|
[公益解析,lz0站](https://lz0.qaiu.top)
|
||||||
[专业版189站,注册体验](https://189.qaiu.top)
|
[专业版](https://189.qaiu.top)
|
||||||
|
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
命令行下载分享文件:
|
命令行下载分享文件:
|
||||||
@@ -58,7 +59,7 @@ https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.
|
|||||||
|
|
||||||
**注意⚠️小飞机解析有IP限制,多数云服务商的大陆IP会被拦截(可以自行配置代理),和本程序无关**
|
**注意⚠️小飞机解析有IP限制,多数云服务商的大陆IP会被拦截(可以自行配置代理),和本程序无关**
|
||||||
**注意⚠️收到很多用户反馈,小飞机近期封号频繁,请尽可能选择其他网盘分享**
|
**注意⚠️收到很多用户反馈,小飞机近期封号频繁,请尽可能选择其他网盘分享**
|
||||||
**注意⚠️请不要过度依赖 lz.qaiu.top,建议本地搭建或者云服务器自行搭建。请求量过多的话服务器可能会被云盘厂商限制,遇到解析失败的分享链接不要着急提issues,请先检查分享是否有效,** [lz站](https://lz.qaiu.top) 和 [lz0](https://lz0.qaiu.top) 不支持大文件,请使用 [189站](https://189.qaiu.top) 注册体验。
|
**注意⚠️请不要过度依赖 lz.qaiu.top,建议本地搭建或者云服务器自行搭建。请求量过多的话服务器可能会被云盘厂商限制,遇到解析失败的分享链接不要着急提issues,请先检查分享是否有效。**
|
||||||
|
|
||||||
## 网盘支持情况:
|
## 网盘支持情况:
|
||||||
> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/
|
> 20230905 奶牛云直链做了防盗链,需加入请求头:Referer: https://cowtransfer.com/
|
||||||
@@ -90,17 +91,10 @@ https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.
|
|||||||
- [WPS云文档-pwps](https://www.kdocs.cn/)
|
- [WPS云文档-pwps](https://www.kdocs.cn/)
|
||||||
- [汽水音乐-qishui_music](https://music.douyin.com/qishui/)
|
- [汽水音乐-qishui_music](https://music.douyin.com/qishui/)
|
||||||
- [咪咕音乐-migu](https://music.migu.cn/)
|
- [咪咕音乐-migu](https://music.migu.cn/)
|
||||||
- [一刻相册-baidu_photo](https://photo.baidu.com/)
|
|
||||||
- Google云盘-pgd
|
- Google云盘-pgd
|
||||||
- Onedrive-pod
|
- Onedrive-pod
|
||||||
- Dropbox-pdp
|
- Dropbox-pdp
|
||||||
- iCloud-pic
|
- iCloud-pic
|
||||||
### 专属版提供
|
|
||||||
- [夸克云盘-qk](https://pan.quark.cn/)
|
|
||||||
- [UC云盘-uc](https://fast.uc.cn/)
|
|
||||||
- [移动云盘-p139](https://yun.139.com/)
|
|
||||||
- [联通云盘-pwo](https://pan.wo.cn/)
|
|
||||||
- [天翼云盘-p189](https://cloud.189.cn/)
|
|
||||||
|
|
||||||
## API接口
|
## API接口
|
||||||
|
|
||||||
@@ -332,10 +326,9 @@ json返回数据格式示例:
|
|||||||
| 网盘名称 | 免登陆下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 |
|
| 网盘名称 | 免登陆下载分享 | 加密分享 | 初始网盘空间 | 单文件大小限制 |
|
||||||
|-------------|---------|----------|-----------|-----------------|
|
|-------------|---------|----------|-----------|-----------------|
|
||||||
| 蓝奏云 | √ | √ | 不限空间 | 100M |
|
| 蓝奏云 | √ | √ | 不限空间 | 100M |
|
||||||
| 奶牛快传 | √ | X | 10G | 不限大小 |
|
|
||||||
| 移动云云空间(个人版) | √ | √(密码可忽略) | 5G(个人) | 不限大小 |
|
| 移动云云空间(个人版) | √ | √(密码可忽略) | 5G(个人) | 不限大小 |
|
||||||
| 小飞机网盘 | √ | √(密码可忽略) | 10G | 不限大小 |
|
| 小飞机网盘 | √ | √ | 10G | 不限大小 |
|
||||||
| 360亿方云 | √ | √(密码可忽略) | 100G(须实名) | 不限大小 |
|
| 360亿方云 | √ | √ | 100G(须实名) | 不限大小 |
|
||||||
| 123云盘 | √ | √ | 2T | 100G(>100M需要登录) |
|
| 123云盘 | √ | √ | 2T | 100G(>100M需要登录) |
|
||||||
| 文叔叔 | √ | √ | 10G | 5GB |
|
| 文叔叔 | √ | √ | 10G | 5GB |
|
||||||
| WPS云文档 | √ | X | 5G(免费) | 10M(免费)/2G(会员) |
|
| WPS云文档 | √ | X | 5G(免费) | 10M(免费)/2G(会员) |
|
||||||
@@ -494,23 +487,6 @@ auths:
|
|||||||
|
|
||||||
**注意:** 目前仅支持 123(ye)的认证配置。
|
**注意:** 目前仅支持 123(ye)的认证配置。
|
||||||
|
|
||||||
## 开发计划
|
|
||||||
### v0.1.8~v0.1.9 ✓
|
|
||||||
- API添加文件信息(专属版/开源版)
|
|
||||||
- 目录解析(专属版/开源版)
|
|
||||||
- 文件预览功能(专属版/开源版)
|
|
||||||
- 文件夹预览功能(开源版)
|
|
||||||
- 友好的错误提示和一键反馈功能(开源版)
|
|
||||||
- 带cookie/token/username/pwd参数解析大文件(专属版)
|
|
||||||
### v0.2.x
|
|
||||||
- web后台管理--认证配置/分享链接管理(开源版/专属版)
|
|
||||||
- 123/小飞机/蓝奏优享等大文件解析(开源版)
|
|
||||||
- 直链分享(开源版/专属版)
|
|
||||||
- aria2/idm+/curl/wget链接生成(开源版/专属版)
|
|
||||||
- IP限流配置(开源版/专属版)
|
|
||||||
- refere防盗链,API鉴权防盗链(专属版)
|
|
||||||
- 123/小飞机/蓝奏优享/蓝奏文件夹解析API,天翼云盘/移动云盘文件夹解析API(专属版)
|
|
||||||
- 用户管理面板--营销推广系统(专属版)
|
|
||||||
|
|
||||||
**技术栈:**
|
**技术栈:**
|
||||||
Jdk17+Vert.x4
|
Jdk17+Vert.x4
|
||||||
@@ -535,20 +511,6 @@ Core模块集成Vert.x实现类似spring的注解式路由API
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
### 关于赞助定制专属版
|
|
||||||
1. 专属版提供对小飞机,蓝奏优享大文件解析的支持, 提供天翼云盘/移动云盘/联通云盘的解析支持。
|
|
||||||
2. 可提供托管服务:包含部署服务和云服务器环境。
|
|
||||||
3. 可提供功能定制开发。
|
|
||||||
您可能需要提供一定的资金赞助支持定制专属版, 请添加以下任意一个联系方式详谈赞助模式:
|
|
||||||
<p>qq: 197575894</p>
|
|
||||||
<p>wechat: imcoding_</p>
|
|
||||||
|
|
||||||
<!--
|
|
||||||

|
|
||||||
|
|
||||||
[手机端支付宝打赏跳转链接](https://qr.alipay.com/fkx01882dnoxxtjenhlxt53)
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,25 +78,13 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
* @param proxyConf 代理配置
|
* @param proxyConf 代理配置
|
||||||
*/
|
*/
|
||||||
private void handleProxyConf(JsonObject proxyConf) {
|
private void handleProxyConf(JsonObject proxyConf) {
|
||||||
// page404 path
|
// page404 path: 兼容不同启动目录(根目录或子模块目录)
|
||||||
if (proxyConf.containsKey(
|
String configured404 = proxyConf.getString("page404");
|
||||||
|
String resolved404 = resolveExistingPath(configured404, false);
|
||||||
"page404")) {
|
if (resolved404 == null) {
|
||||||
System.getProperty("user.dir");
|
resolved404 = resolveExistingPath(DEFAULT_PATH_404, false);
|
||||||
String path = proxyConf.getString("page404");
|
|
||||||
if (StringUtils.isEmpty(path)) {
|
|
||||||
proxyConf.put("page404", DEFAULT_PATH_404);
|
|
||||||
} else {
|
|
||||||
if (!path.startsWith("/")) {
|
|
||||||
path = "/" + path;
|
|
||||||
}
|
|
||||||
if (!new File(System.getProperty("user.dir") + path).exists()) {
|
|
||||||
proxyConf.put("page404", DEFAULT_PATH_404);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
proxyConf.put("page404", DEFAULT_PATH_404);
|
|
||||||
}
|
}
|
||||||
|
proxyConf.put("page404", resolved404 == null ? DEFAULT_PATH_404 : resolved404);
|
||||||
|
|
||||||
final HttpClient httpClient = VertxHolder.getVertxInstance().createHttpClient();
|
final HttpClient httpClient = VertxHolder.getVertxInstance().createHttpClient();
|
||||||
Router proxyRouter = Router.router(vertx);
|
Router proxyRouter = Router.router(vertx);
|
||||||
@@ -180,7 +169,14 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
|
|
||||||
StaticHandler staticHandler;
|
StaticHandler staticHandler;
|
||||||
if (staticConf.containsKey("root")) {
|
if (staticConf.containsKey("root")) {
|
||||||
staticHandler = StaticHandler.create(staticConf.getString("root"));
|
String configuredRoot = staticConf.getString("root");
|
||||||
|
String resolvedRoot = resolveStaticRoot(configuredRoot);
|
||||||
|
if (resolvedRoot != null) {
|
||||||
|
staticHandler = StaticHandler.create(resolvedRoot);
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("static root not found, fallback to configured path: {}", configuredRoot);
|
||||||
|
staticHandler = StaticHandler.create(configuredRoot);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
staticHandler = StaticHandler.create();
|
staticHandler = StaticHandler.create();
|
||||||
}
|
}
|
||||||
@@ -253,4 +249,77 @@ public class ReverseProxyVerticle extends AbstractVerticle {
|
|||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析配置路径: 优先绝对路径, 否则尝试 user.dir 和 user.dir/..。
|
||||||
|
*/
|
||||||
|
private String resolveExistingPath(String path, boolean directory) {
|
||||||
|
if (StringUtils.isBlank(path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
File directFile = new File(path);
|
||||||
|
if (existsByType(directFile, directory)) {
|
||||||
|
return directFile.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
String userDir = System.getProperty("user.dir");
|
||||||
|
File inUserDir = new File(userDir, path);
|
||||||
|
if (existsByType(inUserDir, directory)) {
|
||||||
|
return inUserDir.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
File inParentDir = new File(new File(userDir).getParentFile(), path);
|
||||||
|
if (existsByType(inParentDir, directory)) {
|
||||||
|
return inParentDir.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StaticHandler 只接受相对 web root,不接受以 / 开头的绝对路径。
|
||||||
|
*/
|
||||||
|
private String resolveStaticRoot(String path) {
|
||||||
|
if (StringUtils.isBlank(path)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
File directFile = new File(path);
|
||||||
|
if (existsByType(directFile, true)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String userDir = System.getProperty("user.dir");
|
||||||
|
File inUserDir = new File(userDir, path);
|
||||||
|
if (existsByType(inUserDir, true)) {
|
||||||
|
return relativizePath(new File(userDir), inUserDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
File userDirFile = new File(userDir);
|
||||||
|
File parentDir = userDirFile.getParentFile();
|
||||||
|
File inParentDir = parentDir == null ? null : new File(parentDir, path);
|
||||||
|
if (existsByType(inParentDir, true)) {
|
||||||
|
return relativizePath(userDirFile, inParentDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String relativizePath(File baseDir, File target) {
|
||||||
|
try {
|
||||||
|
Path basePath = baseDir.toPath().toAbsolutePath().normalize();
|
||||||
|
Path targetPath = target.toPath().toAbsolutePath().normalize();
|
||||||
|
return basePath.relativize(targetPath).toString().replace(File.separatorChar, '/');
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
return target.getPath().replace(File.separatorChar, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean existsByType(File file, boolean directory) {
|
||||||
|
if (file == null || !file.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return directory ? file.isDirectory() : file.isFile();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,12 +59,12 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<vertx.version>4.5.22</vertx.version>
|
<vertx.version>4.5.24</vertx.version>
|
||||||
<org.reflections.version>0.10.2</org.reflections.version>
|
<org.reflections.version>0.10.2</org.reflections.version>
|
||||||
<lombok.version>1.18.38</lombok.version>
|
<lombok.version>1.18.38</lombok.version>
|
||||||
<slf4j.version>2.0.5</slf4j.version>
|
<slf4j.version>2.0.16</slf4j.version>
|
||||||
<commons-lang3.version>3.18.0</commons-lang3.version>
|
<commons-lang3.version>3.18.0</commons-lang3.version>
|
||||||
<jackson.version>2.14.2</jackson.version>
|
<jackson.version>2.18.6</jackson.version>
|
||||||
<logback.version>1.5.19</logback.version>
|
<logback.version>1.5.19</logback.version>
|
||||||
<junit.version>4.13.2</junit.version>
|
<junit.version>4.13.2</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ public class PodTool extends PanBase {
|
|||||||
Matcher matcher1 =
|
Matcher matcher1 =
|
||||||
Pattern.compile("\"downloadUrl\":\"(?<url>https?://[^\s\"]+)").matcher(body);
|
Pattern.compile("\"downloadUrl\":\"(?<url>https?://[^\s\"]+)").matcher(body);
|
||||||
if (matcher1.find()) {
|
if (matcher1.find()) {
|
||||||
complete(matcher1.group("url"));
|
// 响应体是 JSON 文本,URL 中的 '&' 被转义为 \u0026,需要反转义
|
||||||
|
complete(unescapeJsonUnicode(matcher1.group("url")));
|
||||||
} else {
|
} else {
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
@@ -134,6 +135,34 @@ public class PodTool extends PanBase {
|
|||||||
throw new RuntimeException("URL匹配失败");
|
throw new RuntimeException("URL匹配失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反转义 JSON 响应文本中残留的 Unicode 转义序列(主要是 \u0026 -> &)。
|
||||||
|
* 主分支通过正则直接从 JSON 原文抠 URL,未经过 JSON 解析器,需要手动还原。
|
||||||
|
*/
|
||||||
|
private String unescapeJsonUnicode(String s) {
|
||||||
|
if (s == null || s.indexOf("\\u") < 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder(s.length());
|
||||||
|
int i = 0;
|
||||||
|
while (i < s.length()) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if (c == '\\' && i + 5 < s.length() && s.charAt(i + 1) == 'u') {
|
||||||
|
try {
|
||||||
|
int cp = Integer.parseInt(s.substring(i + 2, i + 6), 16);
|
||||||
|
sb.append((char) cp);
|
||||||
|
i += 6;
|
||||||
|
continue;
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
// 非法转义按原样保留
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(c);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private String matcherToken(String html) {
|
private String matcherToken(String html) {
|
||||||
// 正则表达式来匹配 inputElem.value 中的 Token
|
// 正则表达式来匹配 inputElem.value 中的 Token
|
||||||
@@ -228,4 +257,4 @@ public class PodTool extends PanBase {
|
|||||||
|
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
4
pom.xml
4
pom.xml
@@ -26,13 +26,13 @@
|
|||||||
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
||||||
|
|
||||||
<!-- Vert.x 4.5.24 已包含安全修复,无需单独指定 Netty 版本 -->
|
<!-- Vert.x 4.5.24 已包含安全修复,无需单独指定 Netty 版本 -->
|
||||||
<vertx.version>4.5.14</vertx.version>
|
<vertx.version>4.5.24</vertx.version>
|
||||||
<org.reflections.version>0.10.2</org.reflections.version>
|
<org.reflections.version>0.10.2</org.reflections.version>
|
||||||
<lombok.version>1.18.38</lombok.version>
|
<lombok.version>1.18.38</lombok.version>
|
||||||
<slf4j.version>2.0.16</slf4j.version>
|
<slf4j.version>2.0.16</slf4j.version>
|
||||||
<commons-lang3.version>3.18.0</commons-lang3.version>
|
<commons-lang3.version>3.18.0</commons-lang3.version>
|
||||||
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
||||||
<jackson.version>2.18.2</jackson.version>
|
<jackson.version>2.18.6</jackson.version>
|
||||||
<!-- Logback 最新稳定版 -->
|
<!-- Logback 最新稳定版 -->
|
||||||
<logback.version>1.5.18</logback.version>
|
<logback.version>1.5.18</logback.version>
|
||||||
<junit.version>4.13.2</junit.version>
|
<junit.version>4.13.2</junit.version>
|
||||||
|
|||||||
@@ -344,7 +344,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { ElTree } from 'element-plus'
|
import { ElTree, ElMessageBox } from 'element-plus'
|
||||||
import { Splitpanes, Pane } from 'splitpanes'
|
import { Splitpanes, Pane } from 'splitpanes'
|
||||||
import 'splitpanes/dist/splitpanes.css'
|
import 'splitpanes/dist/splitpanes.css'
|
||||||
import fileTypeUtils from '@/utils/fileTypeUtils'
|
import fileTypeUtils from '@/utils/fileTypeUtils'
|
||||||
@@ -677,7 +677,31 @@ export default {
|
|||||||
this.$message.success(`自动检测到 ${detected.type} ${detected.version}`)
|
this.$message.success(`自动检测到 ${detected.type} ${detected.version}`)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
this.$message.error('下载器连接失败,请先在首页设置中配置下载器')
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
'未检测到本地下载器,是否切换为迅雷下载?',
|
||||||
|
'下载器未检测到',
|
||||||
|
{
|
||||||
|
confirmButtonText: '使用迅雷',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const thunderConfig = {
|
||||||
|
...config,
|
||||||
|
downloaderType: 'thunder',
|
||||||
|
rpcUrl: ''
|
||||||
|
}
|
||||||
|
saveConfig(thunderConfig)
|
||||||
|
const thunderResult = await testConnection()
|
||||||
|
if (thunderResult.connected) {
|
||||||
|
this.$message.success('已切换并保存为迅雷下载器配置')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
this.$message.error('已保存为迅雷配置,但未检测到迅雷客户端,请先启动迅雷')
|
||||||
|
} catch {
|
||||||
|
this.$message.error('下载器连接失败,请先在首页设置中配置下载器')
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
async sendSingleToDownloader(file) {
|
async sendSingleToDownloader(file) {
|
||||||
|
|||||||
@@ -73,9 +73,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 项目简介移到卡片内 -->
|
<!-- 项目简介移到卡片内 -->
|
||||||
<div class="project-intro">
|
<div class="project-intro">
|
||||||
<div class="intro-title">NFD网盘直链解析0.2.1b3</div>
|
<div class="intro-title">NFD网盘直链解析0.3.0</div>
|
||||||
<div class="intro-desc">
|
<div class="intro-desc">
|
||||||
<div>支持网盘:蓝奏云、蓝奏云优享、小飞机盘、123云盘、奶牛快传、移动云空间、QQ邮箱云盘、QQ闪传等 <el-link style="color:#606cf5" href="https://github.com/qaiu/netdisk-fast-download?tab=readme-ov-file#%E7%BD%91%E7%9B%98%E6%94%AF%E6%8C%81%E6%83%85%E5%86%B5" target="_blank"> >> </el-link></div>
|
<div>支持网盘:蓝奏云、蓝奏云优享、小飞机盘、123云盘、iCloud、移动云空间、联想乐云、QQ闪传等 <el-link style="color:#606cf5" href="https://github.com/qaiu/netdisk-fast-download?tab=readme-ov-file#%E7%BD%91%E7%9B%98%E6%94%AF%E6%8C%81%E6%83%85%E5%86%B5" target="_blank"> >> </el-link></div>
|
||||||
<div>文件夹解析支持:蓝奏云、蓝奏云优享、小飞机盘、123云盘</div>
|
<div>文件夹解析支持:蓝奏云、蓝奏云优享、小飞机盘、123云盘</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,8 +111,8 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
|
|
||||||
<p style="text-align: center">
|
<p style="text-align: center">
|
||||||
<el-button style="margin-left: 40px" @click="parseFile">解析文件</el-button>
|
<el-button class="parse-action-btn" type="success" style="margin-left: 40px" @click="parseFile">解析文件</el-button>
|
||||||
<el-button style="margin-left: 20px" @click="parseDirectory">解析目录</el-button>
|
<el-button class="parse-action-btn" type="success" style="margin-left: 20px" @click="parseDirectory">解析目录</el-button>
|
||||||
<el-button style="margin-left: 20px" @click="generateMarkdown">生成Markdown</el-button>
|
<el-button style="margin-left: 20px" @click="generateMarkdown">生成Markdown</el-button>
|
||||||
<el-button style="margin-left: 20px" @click="generateQRCode">扫码下载</el-button>
|
<el-button style="margin-left: 20px" @click="generateQRCode">扫码下载</el-button>
|
||||||
<el-button style="margin-left: 20px" @click="getStatistics">分享统计</el-button>
|
<el-button style="margin-left: 20px" @click="getStatistics">分享统计</el-button>
|
||||||
@@ -592,7 +592,7 @@ import DirectoryTree from '@/components/DirectoryTree'
|
|||||||
import DownloadDialog from '@/components/DownloadDialog'
|
import DownloadDialog from '@/components/DownloadDialog'
|
||||||
import parserUrl from '../parserUrl1'
|
import parserUrl from '../parserUrl1'
|
||||||
import fileTypeUtils from '@/utils/fileTypeUtils'
|
import fileTypeUtils from '@/utils/fileTypeUtils'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { playgroundApi } from '@/utils/playgroundApi'
|
import { playgroundApi } from '@/utils/playgroundApi'
|
||||||
import { testConnection, autoDetect, addDownload, getConfig, saveConfig } from '@/utils/downloaderService'
|
import { testConnection, autoDetect, addDownload, getConfig, saveConfig } from '@/utils/downloaderService'
|
||||||
|
|
||||||
@@ -708,7 +708,9 @@ export default {
|
|||||||
downloadDialogVisible: false,
|
downloadDialogVisible: false,
|
||||||
downloadDialogInfo: null,
|
downloadDialogInfo: null,
|
||||||
// 目录解析支持的网盘列表
|
// 目录解析支持的网盘列表
|
||||||
directoryParseSupportedPans: []
|
directoryParseSupportedPans: [],
|
||||||
|
// 后端支持网盘列表(用于短格式 type:key@pwd 展开)
|
||||||
|
panList: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -1041,6 +1043,7 @@ export default {
|
|||||||
|
|
||||||
// 验证输入
|
// 验证输入
|
||||||
validateInput() {
|
validateInput() {
|
||||||
|
this.normalizeShortcutInput()
|
||||||
this.clearResults()
|
this.clearResults()
|
||||||
|
|
||||||
if (!this.link.startsWith("https://") && !this.link.startsWith("http://")) {
|
if (!this.link.startsWith("https://") && !this.link.startsWith("http://")) {
|
||||||
@@ -1049,6 +1052,58 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 获取后端支持网盘列表
|
||||||
|
async getPanList() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${this.baseAPI}/v2/getPanList`)
|
||||||
|
const payload = response?.data
|
||||||
|
const list = Array.isArray(payload)
|
||||||
|
? payload
|
||||||
|
: (Array.isArray(payload?.data) ? payload.data : [])
|
||||||
|
if (list.length > 0) {
|
||||||
|
this.panList = list
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 静默失败:短格式解析会自动回退
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 按后端网盘列表展开短格式(type:key@pwd)
|
||||||
|
expandShortFormat(text) {
|
||||||
|
const raw = (text || '').trim()
|
||||||
|
if (!raw) return null
|
||||||
|
|
||||||
|
const shortMatch = raw.match(/^([a-zA-Z][a-zA-Z0-9]{1,10}):([^@]+?)(?:@(.+))?$/)
|
||||||
|
if (!shortMatch) return null
|
||||||
|
|
||||||
|
const [, shortType, shortKey, shortPwd] = shortMatch
|
||||||
|
const pan = this.panList.find(p => (p.type || '').toLowerCase() === shortType.toLowerCase())
|
||||||
|
if (!pan || !pan.shareUrlFormat) return null
|
||||||
|
|
||||||
|
const link = pan.shareUrlFormat
|
||||||
|
.replace('{shareKey}', shortKey)
|
||||||
|
.replace(/\{pwd}/g, shortPwd || '')
|
||||||
|
|
||||||
|
return {
|
||||||
|
link,
|
||||||
|
pwd: shortPwd || '',
|
||||||
|
name: pan.name || pan.type || shortType
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 识别并转换短链输入(如 lz:shareKey@pwd)
|
||||||
|
normalizeShortcutInput() {
|
||||||
|
const shortInfo = this.expandShortFormat(this.link)
|
||||||
|
if (!shortInfo) return
|
||||||
|
|
||||||
|
this.link = shortInfo.link
|
||||||
|
if (!this.password && shortInfo.pwd) {
|
||||||
|
this.password = shortInfo.pwd
|
||||||
|
}
|
||||||
|
this.$message.success(`已识别短格式并自动转换,网盘类型: ${shortInfo.name}`)
|
||||||
|
this.updateDirectLink()
|
||||||
|
},
|
||||||
|
|
||||||
// 清除结果
|
// 清除结果
|
||||||
clearResults() {
|
clearResults() {
|
||||||
this.parseResult = {}
|
this.parseResult = {}
|
||||||
@@ -1265,6 +1320,23 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const text = await navigator.clipboard.readText()
|
const text = await navigator.clipboard.readText()
|
||||||
console.log('获取到的文本内容是:', text)
|
console.log('获取到的文本内容是:', text)
|
||||||
|
|
||||||
|
const shortInfo = this.expandShortFormat(text)
|
||||||
|
if (shortInfo) {
|
||||||
|
if (shortInfo.link !== this.link || shortInfo.pwd !== this.password) {
|
||||||
|
this.password = shortInfo.pwd
|
||||||
|
this.link = shortInfo.link
|
||||||
|
this.updateDirectLink()
|
||||||
|
if (!this.hasClipboardSuccessTip) {
|
||||||
|
this.$message.success(`自动识别分享成功, 网盘类型: ${shortInfo.name}; 分享URL ${this.link}; 分享密码: ${this.password || '空'}`)
|
||||||
|
this.hasClipboardSuccessTip = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message.warning(`[${shortInfo.name}]分享信息无变化`)
|
||||||
|
}
|
||||||
|
this.hasWarnedNoLink = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const linkInfo = parserUrl.parseLink(text)
|
const linkInfo = parserUrl.parseLink(text)
|
||||||
const pwd = parserUrl.parsePwd(text) || ''
|
const pwd = parserUrl.parsePwd(text) || ''
|
||||||
@@ -1375,6 +1447,7 @@ export default {
|
|||||||
|
|
||||||
// 跳转到客户端链接页面
|
// 跳转到客户端链接页面
|
||||||
async goToClientLinks() {
|
async goToClientLinks() {
|
||||||
|
this.normalizeShortcutInput()
|
||||||
// 验证输入
|
// 验证输入
|
||||||
if (!this.link.trim()) {
|
if (!this.link.trim()) {
|
||||||
this.$message.warning('请先输入分享链接')
|
this.$message.warning('请先输入分享链接')
|
||||||
@@ -1550,9 +1623,19 @@ export default {
|
|||||||
aria2: 'http://localhost:6800/jsonrpc',
|
aria2: 'http://localhost:6800/jsonrpc',
|
||||||
thunder: ''
|
thunder: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 切换类型时先清空旧连接状态,避免显示残留版本信息
|
||||||
|
this.aria2Connected = false
|
||||||
|
this.aria2Version = ''
|
||||||
|
|
||||||
if (defaults[this.aria2ConfigForm.downloaderType] !== undefined) {
|
if (defaults[this.aria2ConfigForm.downloaderType] !== undefined) {
|
||||||
this.aria2ConfigForm.rpcUrl = defaults[this.aria2ConfigForm.downloaderType]
|
this.aria2ConfigForm.rpcUrl = defaults[this.aria2ConfigForm.downloaderType]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 非迅雷类型在切换后自动静默重测,刷新连接状态
|
||||||
|
if (this.aria2ConfigForm.downloaderType !== 'thunder') {
|
||||||
|
this.$nextTick(() => this.testAria2Connection(true))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async testAria2Connection(silent = false) {
|
async testAria2Connection(silent = false) {
|
||||||
this.aria2Testing = true
|
this.aria2Testing = true
|
||||||
@@ -1599,7 +1682,25 @@ export default {
|
|||||||
this.aria2Version = result.version || ''
|
this.aria2Version = result.version || ''
|
||||||
this.$message.success(`检测到 ${this.downloaderTypeName} ${this.aria2Version}`)
|
this.$message.success(`检测到 ${this.downloaderTypeName} ${this.aria2Version}`)
|
||||||
} else {
|
} else {
|
||||||
this.$message.warning('未检测到本地下载器,请确认 Motrix/Gopeed/Aria2 正在运行')
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
'未检测到本地下载器,是否切换为迅雷下载?',
|
||||||
|
'下载器未检测到',
|
||||||
|
{
|
||||||
|
confirmButtonText: '使用迅雷',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.aria2ConfigForm.downloaderType = 'thunder'
|
||||||
|
this.aria2ConfigForm.rpcUrl = ''
|
||||||
|
saveConfig(this.aria2ConfigForm)
|
||||||
|
this.$message.success('已切换并保存为迅雷下载器配置')
|
||||||
|
this.aria2DialogVisible = true
|
||||||
|
await this.testAria2Connection(true)
|
||||||
|
} catch {
|
||||||
|
this.$message.warning('未检测到本地下载器,请确认 Motrix/Gopeed/Aria2 正在运行')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$message.error('自动检测失败:' + e.message)
|
this.$message.error('自动检测失败:' + e.message)
|
||||||
@@ -1676,6 +1777,9 @@ export default {
|
|||||||
// 初始化下载器配置
|
// 初始化下载器配置
|
||||||
this.getAria2Config()
|
this.getAria2Config()
|
||||||
|
|
||||||
|
// 拉取后端网盘支持列表(用于 type:key@pwd 短格式)
|
||||||
|
this.getPanList()
|
||||||
|
|
||||||
// 自动读取剪切板
|
// 自动读取剪切板
|
||||||
if (this.autoReadClipboard) {
|
if (this.autoReadClipboard) {
|
||||||
this.getPaste()
|
this.getPaste()
|
||||||
@@ -2094,4 +2198,26 @@ hr {
|
|||||||
#app.dark-theme .el-form-item__label {
|
#app.dark-theme .el-form-item__label {
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 解析按钮专用配色:亮色浅绿,暗色深绿 */
|
||||||
|
.parse-action-btn.el-button--success {
|
||||||
|
--el-button-bg-color: #7fcb96;
|
||||||
|
--el-button-border-color: #7fcb96;
|
||||||
|
--el-button-text-color: #f7fff9;
|
||||||
|
--el-button-hover-bg-color: #93d8a8;
|
||||||
|
--el-button-hover-border-color: #93d8a8;
|
||||||
|
--el-button-active-bg-color: #69b884;
|
||||||
|
--el-button-active-border-color: #69b884;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app.dark-theme .parse-action-btn.el-button--success,
|
||||||
|
body.dark-theme .parse-action-btn.el-button--success {
|
||||||
|
--el-button-bg-color: #1f6b3a;
|
||||||
|
--el-button-border-color: #1f6b3a;
|
||||||
|
--el-button-text-color: #ecf9f0;
|
||||||
|
--el-button-hover-bg-color: #2b7d49;
|
||||||
|
--el-button-hover-border-color: #2b7d49;
|
||||||
|
--el-button-active-bg-color: #185731;
|
||||||
|
--el-button-active-border-color: #185731;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
source: './node_modules/monaco-editor/min/vs',
|
source: './node_modules/monaco-editor/min/vs',
|
||||||
destination: './nfd-front/js/vs'
|
destination: './nfd-front/js/vs'
|
||||||
}
|
},
|
||||||
|
{ source: './nfd-front', destination: '../webroot/nfd-front' }
|
||||||
],
|
],
|
||||||
archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
|
archive: [ //然后我们选择dist文件夹将之打包成dist.zip并放在根目录
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user