mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 12:23:03 +00:00
feat: 新增客户端协议生成系统,支持8种主流下载工具
🚀 核心功能 - 新增完整的客户端下载链接生成器系统 - 支持ARIA2、Motrix、比特彗星、迅雷、wget、cURL、IDM、FDM、PowerShell等8种客户端 - 自动处理防盗链参数(User-Agent、Referer、Cookie等) - 提供可扩展的生成器架构,支持自定义客户端 🔧 技术实现 - ClientLinkGeneratorFactory: 工厂模式管理生成器 - DownloadLinkMeta: 元数据存储下载信息 - ClientLinkUtils: 便捷工具类 - 线程安全的ConcurrentHashMap设计 🌐 前端集成 - 新增ClientLinks.vue界面,支持客户端链接展示 - Element Plus图标系统,混合图标显示 - 客户端检测逻辑优化,避免自动打开外部应用 - 移动端和PC端环境判断 📚 文档完善 - 完整的CLIENT_LINK_GENERATOR_GUIDE.md使用指南 - API文档和测试用例 - 输出示例和最佳实践 从单纯的网盘解析工具升级为完整的下载解决方案生态
This commit is contained in:
@@ -2,11 +2,13 @@ import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '@/views/Home.vue'
|
||||
import ShowFile from '@/views/ShowFile.vue'
|
||||
import ShowList from '@/views/ShowList.vue'
|
||||
import ClientLinks from '@/views/ClientLinks.vue'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/showFile', component: ShowFile },
|
||||
{ path: '/showList', component: ShowList }
|
||||
{ path: '/showList', component: ShowList },
|
||||
{ path: '/clientLinks', component: ClientLinks }
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
125
web-front/src/utils/api.js
Normal file
125
web-front/src/utils/api.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import axios from 'axios'
|
||||
|
||||
// 创建 axios 实例
|
||||
const api = axios.create({
|
||||
baseURL: process.env.VUE_APP_API_BASE_URL || 'http://localhost:6400',
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
api.interceptors.request.use(
|
||||
config => {
|
||||
// 可以在这里添加认证token等
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
api.interceptors.response.use(
|
||||
response => {
|
||||
return response.data
|
||||
},
|
||||
error => {
|
||||
console.error('API请求错误:', error)
|
||||
|
||||
if (error.response) {
|
||||
// 服务器返回错误状态码
|
||||
const message = error.response.data?.message || error.response.data?.error || '服务器错误'
|
||||
return Promise.reject(new Error(message))
|
||||
} else if (error.request) {
|
||||
// 网络错误
|
||||
return Promise.reject(new Error('网络连接失败,请检查网络设置'))
|
||||
} else {
|
||||
// 其他错误
|
||||
return Promise.reject(new Error(error.message || '请求失败'))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 客户端链接 API
|
||||
export const clientLinksApi = {
|
||||
/**
|
||||
* 获取所有客户端下载链接
|
||||
* @param {string} shareUrl - 分享链接
|
||||
* @param {string} password - 提取码(可选)
|
||||
* @returns {Promise} 客户端链接响应
|
||||
*/
|
||||
async getClientLinks(shareUrl, password = '') {
|
||||
const params = new URLSearchParams()
|
||||
params.append('url', shareUrl)
|
||||
if (password) {
|
||||
params.append('pwd', password)
|
||||
}
|
||||
|
||||
return await api.get(`/v2/clientLinks?${params.toString()}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取指定类型的客户端下载链接
|
||||
* @param {string} shareUrl - 分享链接
|
||||
* @param {string} password - 提取码(可选)
|
||||
* @param {string} clientType - 客户端类型
|
||||
* @returns {Promise} 指定类型的客户端链接
|
||||
*/
|
||||
async getClientLink(shareUrl, password = '', clientType) {
|
||||
const params = new URLSearchParams()
|
||||
params.append('url', shareUrl)
|
||||
if (password) {
|
||||
params.append('pwd', password)
|
||||
}
|
||||
params.append('clientType', clientType)
|
||||
|
||||
return await api.get(`/v2/clientLink?${params.toString()}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 其他 API(如果需要的话)
|
||||
export const parserApi = {
|
||||
/**
|
||||
* 解析分享链接
|
||||
* @param {string} shareUrl - 分享链接
|
||||
* @param {string} password - 提取码(可选)
|
||||
* @returns {Promise} 解析结果
|
||||
*/
|
||||
async parseLink(shareUrl, password = '') {
|
||||
const params = new URLSearchParams()
|
||||
params.append('url', shareUrl)
|
||||
if (password) {
|
||||
params.append('pwd', password)
|
||||
}
|
||||
|
||||
return await api.get(`/v2/linkInfo?${params.toString()}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取文件列表
|
||||
* @param {string} shareUrl - 分享链接
|
||||
* @param {string} password - 提取码(可选)
|
||||
* @param {string} dirId - 目录ID(可选)
|
||||
* @param {string} uuid - UUID(可选)
|
||||
* @returns {Promise} 文件列表
|
||||
*/
|
||||
async getFileList(shareUrl, password = '', dirId = '', uuid = '') {
|
||||
const params = new URLSearchParams()
|
||||
params.append('url', shareUrl)
|
||||
if (password) {
|
||||
params.append('pwd', password)
|
||||
}
|
||||
if (dirId) {
|
||||
params.append('dirId', dirId)
|
||||
}
|
||||
if (uuid) {
|
||||
params.append('uuid', uuid)
|
||||
}
|
||||
|
||||
return await api.get(`/v2/getFileList?${params.toString()}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default api
|
||||
720
web-front/src/views/ClientLinks.vue
Normal file
720
web-front/src/views/ClientLinks.vue
Normal file
File diff suppressed because one or more lines are too long
@@ -91,6 +91,7 @@
|
||||
<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="getStatistics">分享统计</el-button>
|
||||
<el-button style="margin-left: 20px" @click="goToClientLinks" type="primary">客户端链接(实验)</el-button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -589,6 +590,55 @@ export default {
|
||||
}).catch(() => {
|
||||
this.$message.error('复制失败');
|
||||
});
|
||||
},
|
||||
|
||||
// 跳转到客户端链接页面
|
||||
async goToClientLinks() {
|
||||
// 验证输入
|
||||
if (!this.link.trim()) {
|
||||
this.$message.warning('请先输入分享链接')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.link.startsWith("https://") && !this.link.startsWith("http://")) {
|
||||
this.$message.error("请输入有效链接!")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 显示加载状态
|
||||
this.isLoading = true
|
||||
|
||||
// 直接使用 axios 请求客户端链接 API,因为它的响应格式与其他 API 不同
|
||||
const params = { url: this.link }
|
||||
if (this.password) params.pwd = this.password
|
||||
|
||||
const response = await axios.get(`${this.baseAPI}/v2/clientLinks`, { params })
|
||||
const result = response.data
|
||||
|
||||
// 处理包装格式的响应
|
||||
const clientData = result.data || result
|
||||
|
||||
if (clientData.success) {
|
||||
// 将数据存储到 sessionStorage,供客户端链接页面使用
|
||||
sessionStorage.setItem('clientLinksData', JSON.stringify(clientData))
|
||||
sessionStorage.setItem('clientLinksForm', JSON.stringify({
|
||||
shareUrl: this.link,
|
||||
password: this.password
|
||||
}))
|
||||
|
||||
// 跳转到客户端链接页面
|
||||
this.$router.push('/clientLinks')
|
||||
this.$message.success('客户端链接生成成功,正在跳转...')
|
||||
} else {
|
||||
this.$message.error(clientData.error || '生成客户端链接失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成客户端链接失败:', error)
|
||||
this.$message.error('生成客户端链接失败')
|
||||
} finally {
|
||||
this.isLoading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user