1 Commits
1.0.6 ... 2.0.0

Author SHA1 Message Date
putyy
511111f874 V2.0重磅更新,所见即所得! 2024-07-18 17:28:26 +08:00
9 changed files with 294 additions and 314 deletions

View File

@@ -1,56 +1,45 @@
# res-downloader
#### 爱享素材下载器
💪 支持视频、音频、图片、m3u8等网络资源下载
📦 支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源下载
🍊 支持设置代理以获取特殊网络下的资源
# V2.0重磅更新,所见即所得!
## res-downloader(爱享素材下载器)
🖥️ 支持Win10、Win11、Mac
🌐 支持视频、音频、图片、m3u8等网络资源下载
💪 支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源下载
👼 支持设置代理以获取特殊网络下的资源
## 软件下载
🆕 [github下载](https://github.com/putyy/res-downloader/releases)
🆕 [蓝奏云下载 密码:9vs5](https://wwjv.lanzoum.com/b04wgtfyb)
## 二次开发
> ps 打包慢的问题可以参考 https://www.putyy.com/articles/87
```sh
git clone https://github.com/putyy/res-downloader
cd res-downloader
yarn install
yarn run dev
# 打包mac
yarn run build --universal --mac
# 打包win
yarn run build --win
```
## 使用方法
> 0. 安装一定要同意安装证书文件,安装一定要同意安装证书文件,安装一定要同意安装证书文件!
> 1. 打开本软件
> 2. 软件首页选择要获取的资源类型(默认选中的视频)
> 3. 打开要捕获的源, 如:视频号、网页、小程序等等
> 4. 返回软件首页即可看到要下载的资源
## 常见问题
> 1. 无法拦截获取
> > 手动检测系统代理是否设置正确 本软件代理地址: 127.0.0.1:8899
> 2. 关闭软件后无法正常上网
> > 手动关闭系统代理设置
> 3. 视频号抓取流程
> > 将需要下载的视频发给好友或者文件助手 再打开即可拦截,通常会出现解密下载按钮
> >
> > 大视频可以复制链接通过其他工具加速下载,然后再通过对应的视频操作项进行"视频解密"
> 4. 返回软件首页即可看到资源列表
## 软件截图
![](public/show.webp)
## 实现原理
> 通过代理网络抓包拦截响应筛选出有用的资源同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的只不过这些软件需要手动进行筛选对于小白用户上手还是有点难度所以就有了本项目这样的软件。
## 常见问题
下载慢、大视频下载失败
> 推荐使用如下工具加速下载,视频号可以下载完成后再到对应视频操作项选择 “视频解密(视频号)” 按钮
>> [Neat Download Manager](https://www.neatdownloadmanager.com/index.php/en/)、[Motrix](https://motrix.app/download)等软件进行下载
## 参考项目
Win7无法使用
> 软件不支持,也无计划支持
- [WeChatVideoDownloader](https://github.com/lecepin/WeChatVideoDownloader) 原项目是react写的本项目参考原项目用vue3重写了一下核心逻辑没什么变化主要是增加了一些新的功能再次感谢
打开本软件,无法正常拦截获取
> 检查系统代理是否正确设置 代理地址127.0.0.1 端口8899
关闭软件后无法正常上网
> 手动关闭系统代理设置
打开本软件后无法上网
> 手动删除安装标识锁文件,之后再打开软件会进行检查证书是否正确安装
>> MAC: /Users/你的用户名称/.res-downloader@putyy/res-downloader-installed.lock
>> Win: C:\Users\Admin\.res-downloader@putyy/res-downloader-installed.lock
#### 更多问题见: [issues](https://github.com/putyy/res-downloader/issues)、[爱享论坛](https://s.gowas.cn/d/4089-quan-ping-tai-zi-yuan-xia-zai-ruan-jian-zui-xin-ban-v106/171)
## 免责声明
本软件用于学习研究使用,若因使用本软件造成的一切法律责任均与本人无关!
```

View File

@@ -5,16 +5,12 @@ import {downloadFile, decodeWxFile, suffix} from './utils'
// @ts-ignore
import {hexMD5} from '../../src/common/md5'
import fs from "fs"
import CryptoJS from 'crypto-js'
import {floor} from "lodash"
let getMac = require("getmac").default
let win: BrowserWindow
let previewWin: BrowserWindow
let isStartProxy = false
let aesKey = "as5d45as4d6qe6wqfar6gt4749q6y7w6h34v64tv7t37ty5qwtv6t6qv"
export default function initIPC() {
ipcMain.handle('invoke_app_is_init', async (event, arg) => {
@@ -67,16 +63,15 @@ export default function initIPC() {
return {is_file: res, fileName: `${save_path}/${fileName}.mp4`}
})
ipcMain.handle('invoke_down_file', async (event, {data, save_path, description}) => {
let down_url = data.down_url
ipcMain.handle('invoke_down_file', async (event, {data, save_path}) => {
let down_url = data.url
if (!down_url) {
return false
}
let fileName = description ? description.replace(/[^a-zA-Z\u4e00-\u9fa5]/g, '') : hexMD5(down_url);
let fileName = data?.description ? data.description.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '') : hexMD5(down_url);
let save_path_file = `${save_path}/${fileName}` + suffix(data.type)
if (process.platform === 'win32'){
if (process.platform === 'win32') {
save_path_file = `${save_path}\\${fileName}` + suffix(data.type)
}
@@ -92,20 +87,10 @@ export default function initIPC() {
win?.webContents.send('on_down_file_schedule', {schedule: floor(res * 100)})
}
).catch(err => {
// console.log("err:", err)
return false
})
})
ipcMain.handle('invoke_get_mac', async (event) => {
let mac = getMac()
if (mac === "") {
return ""
}
return CryptoJS.AES.encrypt(mac, CryptoJS.enc.Hex.parse(aesKey), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).ciphertext.toString()
})
});
ipcMain.handle('invoke_resources_preview', async (event, {url}) => {
if (!url) {
@@ -128,6 +113,17 @@ export default function initIPC() {
shell.showItemInFolder(save_path)
})
ipcMain.handle('invoke_file_del', (event, {url_sign}) => {
if (url_sign === "all"){
global.videoList = {}
return
}
if (url_sign) {
delete global.videoList[url_sign]
return
}
})
ipcMain.handle('invoke_window_restart', (event) => {
app.relaunch()
app.exit()

View File

@@ -7,78 +7,35 @@ import * as urlTool from "url"
import {toSize} from "./utils"
// @ts-ignore
import {hexMD5} from '../../src/common/md5'
import pkg from '../../package.json'
const hoXy = require('hoxy')
const port = 8899
let videoList = {}
global.videoList = {}
if (process.platform === 'win32') {
process.env.OPENSSL_BIN = CONFIG.OPEN_SSL_BIN_PATH
process.env.OPENSSL_CONF = CONFIG.OPEN_SSL_CNF_PATH
}
// setTimeout to allow working in macOS
// in windows: H5ExtTransfer:ok
// in macOS: finderH5ExtTransfer:ok
const resObject = {
url: "",
url_sign: "",
platform: "",
size: "",
type: "video/mp4",
type_str: 'video',
progress_bar: "",
save_path: "",
decode_key: "",
description: ""
}
const injection_script1 = `
setTimeout(() => {
let receiver_url = "https://res-downloader.666666.com";
const vv = hexMD5(pkg.version) + (CONFIG.IS_DEV ? Math.random() :"")
function send_response_if_is_video(response) {
if (response == undefined) return;
if (!response["err_msg"].includes("H5ExtTransfer:ok")) return;
let value = JSON.parse(response["jsapi_resp"]["resp_json"]);
if (value["object"] == undefined || value["object"]["object_desc"] == undefined || value["object"]["object_desc"]["media"].length == 0) {
return;
}
let media = value["object"]["object_desc"]["media"][0];
let description = value["object"]["object_desc"]["description"].trim();
let video_data = {
"decode_key": media["decode_key"],
"url": media["url"]+media["url_token"],
"size": media["file_size"],
"description": description,
"uploader": value["object"]["nickname"]
};
fetch(receiver_url, {
method: "POST",
mode: "no-cors",
body: JSON.stringify(video_data),
}).then((resp) => {
// alert(\`video data for \${video_data["description"]} sent!\`);
});
}
function wrapper(name,origin) {
return function() {
let cmdName = arguments[0];
if (arguments.length == 3) {
let original_callback = arguments[2];
arguments[2] = async function () {
if (arguments.length == 1) {
send_response_if_is_video(arguments[0]);
}
return await original_callback.apply(this, arguments);
}
} else {
}
let result = origin.apply(this,arguments);
return result;
}
}
window.WeixinJSBridge.invoke = wrapper("WeixinJSBridge.invoke", window.WeixinJSBridge.invoke);
window.wvds = true;
}, 200);`;
export async function startServer({
win,
upstreamProxy,
setProxyErrorCallback = f => f,
}) {
export async function startServer({win, upstreamProxy, setProxyErrorCallback = f => f,}) {
return new Promise(async (resolve: any, reject) => {
try {
const proxy = hoXy.createServer({
@@ -94,14 +51,14 @@ export async function startServer({
resolve()
})
.catch((err) => {
setProxyErrorCallback(err);
reject('setting proxy err: '+ err.toString());
setProxyErrorCallback(err)
reject('setting proxy err: ' + err.toString())
});
})
.on('error', err => {
setProxyErrorCallback(err);
reject('proxy service err: ' + err.toString());
});
setProxyErrorCallback(err)
reject('proxy service err: ' + err.toString())
})
proxy.intercept(
@@ -113,26 +70,32 @@ export async function startServer({
(req, res) => {
res.string = 'ok'
res.statusCode = 200
let url_sign: string = hexMD5(req.json.url)
let urlInfo = urlTool.parse(req.json.url, true)
win?.webContents?.send?.('on_get_queue', {
url_sign: url_sign,
url: req.json.url,
down_url: req.json.url,
high_url: '',
platform: urlInfo.hostname,
size: toSize(req.json.size ?? 0),
type: "video/mp4",
type_str: 'video',
progress_bar: '',
save_path: '',
downing: false,
decode_key: req.json.decode_key,
description: req.json.description,
uploader: '',
})
try {
if (!req.json?.description || req.json?.media?.length <= 0) {
return
}
const media = req.json?.media[0]
const url_sign: string = hexMD5(media.url)
if (global.videoList.hasOwnProperty(url_sign) === true) {
return
}
const urlInfo = urlTool.parse(media.url, true)
global.videoList[url_sign] = media.url
win.webContents.send('on_get_queue', Object.assign({}, resObject, {
url_sign: url_sign,
url: media.url + media.urlToken,
platform: urlInfo.hostname,
size: media?.fileSize ? toSize(media.fileSize) : 0,
type: "video/mp4",
type_str: 'video',
decode_key: media?.decodeKey ? media?.decodeKey : '',
description: req.json.description,
}))
} catch (e) {
log.log(e.toString())
}
},
);
)
proxy.intercept(
{
@@ -142,10 +105,42 @@ export async function startServer({
},
async (req, res) => {
if (req.url.includes('/web/pages/feed') || req.url.includes('/web/pages/home')) {
res.string = res.string.replace('</body>', '\n<script>' + injection_script1 + '</script>\n</body>');
res.statusCode = 200;
res.string = res.string.replaceAll('.js"', '.js?v=' + vv + '"')
res.statusCode = 200
}
},
)
proxy.intercept(
{
phase: 'response',
as: 'string',
},
async (req, res) => {
if (req.url.endsWith('.js?v=' + vv)) {
res.string = res.string.replaceAll('.js"', '.js?v=' + vv + '"');
}
if (req.url.includes("web/web-finder/res/js/virtual_svg-icons-register.publish")) {
// console.log(res.string.match(/return\s*\{\s*width:([\s\S]*?)scalingInfo:([\s\S]*?)\}/))
// res.string = res.string.replace(
// /return\s*{\s*width:(.*?)scalingInfo:(.*?)\s*}/,
// `var mediaInfo = {width:$1scalingInfo:$2};
// console.log("mediaInfo", mediaInfo);
// console.log("this.objectDesc", this.objectDesc);
// return mediaInfo;`
// )
res.string = res.string.replace(/get\s*media\s*\(\)\s*\{/, `
get media(){
if(this.objectDesc){
fetch("https://res-downloader.666666.com", {
method: "POST",
mode: "no-cors",
body: JSON.stringify(this.objectDesc),
});
};
`)
}
}
);
proxy.intercept(
@@ -153,125 +148,94 @@ export async function startServer({
phase: 'response',
},
async (req, res) => {
// 拦截响应
let ctype = res?._data?.headers?.['content-type']
let url_sign: string = hexMD5(req.fullUrl())
let res_url = req.fullUrl()
let urlInfo = urlTool.parse(res_url, true)
switch (ctype) {
case "video/mp4":
case "video/webm":
case "video/ogg":
case "video/x-msvideo":
case "video/mpeg":
case "video/quicktime":
case "video/x-ms-wmv":
case "video/x-flv":
case "video/3gpp":
case "video/x-matroska":
if (videoList.hasOwnProperty(url_sign) === false) {
videoList[url_sign] = req.fullUrl()
let high_url = ''
let down_url = res_url
win?.webContents?.send?.('on_get_queue', {
try {
// 拦截响应
const ctype = res?._data?.headers?.['content-type']
const url_sign: string = hexMD5(req.fullUrl())
const res_url = req.fullUrl()
const urlInfo = urlTool.parse(res_url, true)
switch (ctype) {
case "video/mp4":
case "video/webm":
case "video/ogg":
case "video/x-msvideo":
case "video/mpeg":
case "video/quicktime":
case "video/x-ms-wmv":
case "video/x-flv":
case "video/3gpp":
case "video/x-matroska":
if (global.videoList.hasOwnProperty(url_sign) === false) {
global.videoList[url_sign] = res_url
win.webContents.send('on_get_queue', Object.assign({}, resObject, {
url: res_url,
url_sign: url_sign,
platform: urlInfo.hostname,
size: toSize(res?._data?.headers?.['content-length'] ?? 0),
type: ctype,
type_str: 'video',
}))
}
break;
case "image/png":
case "image/webp":
case "image/jpeg":
case "image/jpg":
case "image/svg+xml":
case "image/gif":
case "image/avif":
case "image/bmp":
case "image/tiff":
case "image/x-icon":
case "image/heic":
case "image/vnd.adobe.photoshop":
win.webContents.send('on_get_queue', Object.assign({}, resObject, {
url: res_url,
url_sign: url_sign,
url: down_url,
down_url: down_url,
high_url: high_url,
platform: urlInfo.hostname,
size: toSize(res?._data?.headers?.['content-length'] ?? 0),
size: res?._data?.headers?.['content-length'] ? toSize(res?._data?.headers?.['content-length']) : 0,
type: ctype,
type_str: 'video',
progress_bar: '',
save_path: '',
downing: false,
decode_key: '',
description: '',
uploader: '',
})
}
break;
case "image/png":
case "image/webp":
case "image/jpeg":
case "image/jpg":
case "image/svg+xml":
case "image/gif":
case "image/avif":
case "image/bmp":
case "image/tiff":
case "image/x-icon":
case "image/heic":
case "image/vnd.adobe.photoshop":
win?.webContents?.send?.('on_get_queue', {
url_sign: url_sign,
url: res_url,
down_url: res_url,
high_url: '',
platform: urlInfo.hostname,
size: toSize(res?._data?.headers?.['content-length'] ?? 0),
type: ctype,
type_str: 'image',
progress_bar: '',
save_path: '',
downing: false,
decode_key: '',
description: '',
uploader: '',
})
break;
case "audio/mpeg":
case "audio/wav":
case "audio/aiff":
case "audio/x-aiff":
case "audio/aac":
case "audio/ogg":
case "audio/flac":
case "audio/midi":
case "audio/x-midi":
case "audio/x-ms-wma":
case "audio/opus":
case "audio/webm":
case "audio/mp4":
win?.webContents?.send?.('on_get_queue', {
url_sign: url_sign,
url: res_url,
down_url: res_url,
high_url: '',
platform: urlInfo.hostname,
size: toSize(res?._data?.headers?.['content-length'] ?? 0),
type: ctype,
type_str: 'audio',
progress_bar: '',
save_path: '',
downing: false,
decode_key: '',
description: '',
uploader: '',
})
break;
case "application/vnd.apple.mpegurl":
case "application/x-mpegURL":
win.webContents?.send?.('on_get_queue', {
url_sign: url_sign,
url: res_url,
down_url: res_url,
high_url: '',
platform: urlInfo.hostname,
size: toSize(res?._data?.headers?.['content-length'] ?? 0),
type: ctype,
type_str: 'm3u8',
progress_bar: '',
save_path: '',
downing: false,
decode_key: '',
description: '',
uploader: '',
})
break;
type_str: 'image',
}))
break
case "audio/mpeg":
case "audio/wav":
case "audio/aiff":
case "audio/x-aiff":
case "audio/aac":
case "audio/ogg":
case "audio/flac":
case "audio/midi":
case "audio/x-midi":
case "audio/x-ms-wma":
case "audio/opus":
case "audio/webm":
case "audio/mp4":
win.webContents.send('on_get_queue', Object.assign({}, resObject, {
url: res_url,
url_sign: url_sign,
platform: urlInfo.hostname,
size: res?._data?.headers?.['content-length'] ? toSize(res?._data?.headers?.['content-length']) : 0,
type: ctype,
type_str: 'audio',
}))
break
case "application/vnd.apple.mpegurl":
case "application/x-mpegURL":
win.webContents.send('on_get_queue', Object.assign({}, resObject, {
url: res_url,
url_sign: url_sign,
platform: urlInfo.hostname,
size: res?._data?.headers?.['content-length'] ? toSize(res?._data?.headers?.['content-length']) : 0,
type: ctype,
type_str: 'm3u8',
}))
break
}
} catch (e) {
log.log(e.toString())
}
},
)
} catch (e) {

View File

@@ -25,13 +25,18 @@ function downloadFile(url, decodeKey, fullFileName, progressCallback) {
if (decodeKey) {
xorStream = xorTransform(getDecryptionArray(decodeKey));
}
return axios.get(url, {
let config = {
responseType: 'stream',
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36',
},
}).then(({data, headers}) => {
}
if (url.includes("douyin")){
config.headers['Referer'] = url
}
return axios.get(url, config).then(({data, headers}) => {
let currentLen = 0
const totalLen = headers['content-length']
@@ -95,25 +100,46 @@ function toSize(size: number) {
function suffix(type: string) {
switch (type) {
case "video/mp4":
case "video/webm":
case "video/ogg":
case "video/x-msvideo":
case "video/mpeg":
case "video/quicktime":
case "video/x-ms-wmv":
case "video/x-flv":
case "video/3gpp":
case "video/x-matroska":
return ".mp4";
case "image/png":
return ".png";
case "image/webp":
return ".webp";
case "image/jpeg":
case "image/jpg":
case "image/svg+xml":
return ".svg";
case "image/gif":
return ".gif";
case "image/avif":
case "image/bmp":
case "image/tiff":
case "image/x-icon":
case "image/heic":
case "image/vnd.adobe.photoshop":
return ".png";
case "audio/mpeg":
case "audio/wav":
case "audio/aiff":
case "audio/x-aiff":
case "audio/aac":
case "audio/ogg":
case "audio/flac":
case "audio/midi":
case "audio/x-midi":
case "audio/x-ms-wma":
case "audio/opus":
case "audio/webm":
case "audio/mp4":
return ".mp3";
case "application/vnd.apple.mpegurl":
case "application/x-mpegURL":
return ".m3u8";
case "image/jpeg":
return ".jpeg";
case "image/jpg":
return ".jpg";
case "image/avif":
return ".avif";
}
return ""
}

View File

@@ -1,8 +1,8 @@
{
"name": "res-downloader",
"version": "1.0.6",
"version": "2.0.0",
"main": "dist-electron/main/index.js",
"description": "Electron + Vue + Vite 实现的资源下载软件,支持微信视频号下载、抖音视频下载、快手视频下载、酷狗音乐下载等",
"description": "res-downloader(爱享素材下载器),支持视频号、小程序、抖音、快手、小红书、酷狗音乐、qq音乐下载等",
"author": "putyy@qq.com",
"license": "MIT",
"private": true,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -24,6 +24,11 @@ const jump = (scene: number)=>{
url: "https://s.gowas.cn/d/4089-quan-ping-tai-zi-yuan-xia-zai-ruan-jian"
})
break;
case 5:
ipcRenderer.invoke('invoke_open_default_browser', {
url: "https://www.ais.do/ivi/rr2GaZ"
})
break;
case 6:
ipcRenderer.invoke('invoke_open_default_browser', {
url: "https://github.com/putyy/res-downloader"
@@ -40,7 +45,8 @@ div.line
a.item(@click="jump(4)") 问题反馈
div.line
a.item 推荐:
a.item(@click="jump(2)") 云盘资源
a.item(@click="jump(5)") Ai助手(免费)
a.item(@click="jump(2)") 网盘资源
a.item(@click="jump(3)") 图片无损压缩
a.item(@click="jump(6)") 软件源码
</template>

View File

@@ -47,7 +47,7 @@ const str = "使用方法\n" +
</script>
<template lang="pug">
div.about
div 1. 本软件免费代码已开源不会以任何形式收取费用&nbsp;
div 1. 本软件免费代码已开源不会以任何形式收取费用&nbsp;
el-button(@click="jump(3)") 查看源码
div 2. m3u8复制的链接如何使用? &nbsp;
el-button(@click="jump(1)") 在线下载
@@ -77,4 +77,4 @@ div.about
}
}
</style>
</style>

View File

@@ -6,20 +6,16 @@ import localStorageCache from "../common/localStorage"
import {Delete, Promotion} from "@element-plus/icons-vue"
interface resData {
url_sign: string,
url: string,
down_url: string,
high_url: string,
url_sign: string,
size: any,
platform: string,
type: string,
type_str: string,
progress_bar: any,
save_path: string,
downing: boolean,
decode_key: string,
description: string,
uploader: string,
}
const tableData = ref<resData[]>([])
@@ -95,7 +91,7 @@ onUnmounted(() => {
})
})
watch(resType, (res, res1)=>{
watch(resType, (res, res1) => {
localStorageCache.set("res-type", resType.value, -1)
}, {deep: true})
@@ -137,10 +133,8 @@ const handleBatchDown = async () => {
}
let downRes = await ipcRenderer.invoke('invoke_down_file', {
index: 0,
data: Object.assign({}, item),
save_path: save_dir,
high: false
})
if (downRes !== false) {
@@ -173,7 +167,7 @@ const handleDown = async (index: number, row: any) => {
let result = await ipcRenderer.invoke('invoke_file_exists', {
save_path: save_dir,
url: row.high_url ? row.high_url : row.url,
url: row.url,
description: row.description
})
@@ -188,17 +182,15 @@ const handleDown = async (index: number, row: any) => {
localStorageCache.set("res-table-data", tableData.value, -1)
return
}
ipcRenderer.invoke('invoke_down_file', {
data: Object.assign({}, tableData.value[index]),
save_path: save_dir,
description: row.description
save_path: save_dir
}).then((res) => {
if (res !== false) {
tableData.value[index].progress_bar = "100%"
tableData.value[index].save_path = res.fullFileName
localStorageCache.set("res-table-data", tableData.value, -1)
}else{
} else {
ElMessage({
message: "下载失败",
type: 'warning',
@@ -233,7 +225,7 @@ const decodeWxFile = (index: number) => {
tableData.value[index].progress_bar = "100%"
tableData.value[index].save_path = res.fullFileName
localStorageCache.set("res-table-data", tableData.value, -1)
}else{
} else {
ElMessage({
message: "解密失败",
type: 'warning',
@@ -250,36 +242,42 @@ const decodeWxFile = (index: number) => {
}
const handlePreview = (index: number, row: any) => {
ipcRenderer.invoke('invoke_resources_preview', {url: row.down_url}).catch(() => {
ipcRenderer.invoke('invoke_resources_preview', {url: row.url}).catch(() => {
})
}
const handleClear = () => {
tableData.value = []
localStorageCache.del("res-table-data")
ipcRenderer.invoke('invoke_file_del', {
url_sign: "all"
})
}
const handleCopy = (text: string) => {
let el = document.createElement('input')
el.setAttribute('value', text)
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
ElMessage({
message: "复制成功",
type: 'success',
})
let el = document.createElement('input')
el.setAttribute('value', text)
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
ElMessage({
message: "复制成功",
type: 'success',
})
}
const handleDel = (index: number)=>{
const handleDel = (index: number) => {
let arr = tableData.value
arr.splice(index, 1);
tableData.value = arr
localStorageCache.set("res-table-data", tableData.value, -1)
ipcRenderer.invoke('invoke_file_del', {
url_sign: tableData.value[index].url_sign
})
}
const openFileDir = (index: number)=>{
const openFileDir = (index: number) => {
ipcRenderer.invoke('invoke_open_file_dir', {
save_path: tableData.value[index].save_path
})
@@ -322,10 +320,10 @@ el-container.container
el-table-column(label="预览" show-overflow-tooltip width="300px")
template(#default="scope")
div.show_res
video.video(v-if="scope.row.type_str === 'video'" :src="scope.row.down_url" controls preload="none") 您的浏览器不支持 video 标签
img.img(v-if="scope.row.type_str === 'image'" :src="scope.row.down_url" crossorigin="anonymous")
video.video(v-if="scope.row.type_str === 'video'" :src="scope.row.url" controls preload="none") 您的浏览器不支持 video 标签
img.img(v-if="scope.row.type_str === 'image'" :src="scope.row.url" crossorigin="anonymous")
audio.audio(v-if="scope.row.type_str === 'audio'" controls preload="none")
source(:src="scope.row.down_url" :type="scope.row.type")
source(:src="scope.row.url" :type="scope.row.type")
div {{scope.row.description}}
el-table-column(prop="type_str" label="类型" show-overflow-tooltip)
el-table-column(prop="platform" label="主机地址")
@@ -336,10 +334,10 @@ el-container.container
template(#default="scope")
div.actions
template(v-if="scope.row.type_str !== 'm3u8'" )
el-button(v-if="!scope.row.save_path" link type="primary" @click="handleDown(scope.$index, scope.row)") {{scope.row.decode_key ? "解密下载(视频号)" : "下载"}}
el-button(v-if="scope.row.decode_key" link type="primary" @click="decodeWxFile(scope.$index)") 视频解密(视频号)
el-button(v-if="!scope.row.save_path" link type="primary" @click="handleDown(scope.$index, scope.row)") {{scope.row.decode_key || scope.row.decryptor_array ? "解密下载(视频号)" : "下载"}}
el-button(v-if="scope.row.decode_key || scope.row.decryptor_array" link type="primary" @click="decodeWxFile(scope.$index)") 视频解密(视频号)
el-button(link type="primary" @click="handlePreview(scope.$index, scope.row)") 窗口预览
el-button(link type="primary" @click="handleCopy(scope.row.down_url)") 复制链接
el-button(link type="primary" @click="handleCopy(scope.row.url)") 复制链接
el-button(link type="primary" @click="handleDel(scope.$index)") 删除
el-button(v-if="scope.row.save_path" link type="primary" @click="openFileDir(scope.$index)") 打开文件目录
</template>
@@ -375,15 +373,16 @@ el-container.container
}
}
.show_res{
.show_res {
width: 100%;
height: auto;
.img{
.img {
max-height: 200px;
}
}
.actions{
.actions {
display: flex;
flex-direction: column;
align-items: flex-start;