1 Commits
1.0.3 ... 1.0.4

14 changed files with 151 additions and 168 deletions

3
components.d.ts vendored
View File

@@ -14,13 +14,10 @@ declare module 'vue' {
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
Footer: typeof import('./src/components/layout/Footer.vue')['default']
Index: typeof import('./src/components/layout/Index.vue')['default']

View File

@@ -22,7 +22,7 @@ export default {
CERT_PUBLIC_PATH: path.join(EXECUTABLE_PATH, './keys/public.pem'),
INSTALL_CERT_FLAG: path.join(HOME_PATH, './installed.lock'),
WIN_CERT_INSTALL_HELPER: path.join(EXECUTABLE_PATH, './w_c.exe'),
APP_CN_NAME: '资源下载器',
APP_CN_NAME: '爱享素材下载器',
APP_EN_NAME: 'ResDownloader',
REGEDIT_VBS_PATH: path.join(EXECUTABLE_PATH, './regedit-vbs'),
OPEN_SSL_BIN_PATH: path.join(EXECUTABLE_PATH, './openssl/openssl.exe'),

View File

@@ -1,13 +1,14 @@
import {ipcMain, dialog, BrowserWindow, app, shell} from 'electron'
import {startServer} from './proxyServer'
import {installCert, checkCertInstalled} from './cert'
import {downloadFile} from './utils'
import {downloadFile, decodeWxFile} from './utils'
// @ts-ignore
import {hexMD5} from '../../src/common/md5'
import fs from "fs"
import CryptoJS from 'crypto-js'
import {closeProxy, setProxy} from "./setProxy"
import log from "electron-log"
import {floor} from "lodash";
let getMac = require("getmac").default
let win: BrowserWindow
@@ -17,16 +18,6 @@ let isOpenProxy = false
let aesKey = "as5d45as4d6qe6wqfar6gt4749q6y7w6h34v64tv7t37ty5qwtv6t6qv"
const toSize = (size: number) => {
if (size > 1048576) {
return (size / 1048576).toFixed(2) + "MB"
}
if (size > 1024) {
return (size / 1024).toFixed(2) + "KB"
}
return size + 'b'
}
const suffix = (type: string) => {
switch (type) {
case "video/mp4":
@@ -104,6 +95,16 @@ export default function initIPC() {
return result?.[0]
})
ipcMain.handle('invoke_select_wx_file', async (event, {index, data}) => {
// 选择下载位置
const result = dialog.showOpenDialogSync({title: '保存', properties: ['openFile']})
if (!result?.[0]) {
return false
}
return decodeWxFile(result?.[0], data.decode_key, result?.[0].replace(".mp4", "_解密.mp4"))
})
ipcMain.handle('invoke_file_exists', async (event, {save_path, url}) => {
let url_sign = hexMD5(url)
let res = fs.existsSync(`${save_path}/${url_sign}.mp4`)
@@ -131,7 +132,7 @@ export default function initIPC() {
data.decode_key,
save_path_file,
(res) => {
return save_path_file
win?.webContents.send('on_down_file_schedule', {schedule: floor(res * 100)})
}
).catch(err => {
// console.log('invoke_down_file:err', err)
@@ -167,8 +168,8 @@ export default function initIPC() {
shell.openExternal(url).then(r => {})
})
ipcMain.handle('invoke_open_dir', (event, {dir}) => {
shell.openPath(dir).then(r => {})
ipcMain.handle('invoke_open_file_dir', (event, {save_path}) => {
shell.showItemInFolder(save_path)
})
}

View File

@@ -201,7 +201,7 @@ export async function startServer({
progress_bar: '',
save_path: '',
downing: false,
decode_key: req.json.decode_key,
decode_key: '',
description: '',
uploader: '',
})
@@ -219,7 +219,7 @@ export async function startServer({
progress_bar: '',
save_path: '',
downing: false,
decode_key: req.json.decode_key,
decode_key: '',
description: '',
uploader: '',
})
@@ -237,7 +237,7 @@ export async function startServer({
progress_bar: '',
save_path: '',
downing: false,
decode_key: req.json.decode_key,
decode_key: '',
description: '',
uploader: '',
})

View File

@@ -66,6 +66,22 @@ function downloadFile(url, decodeKey, fullFileName, progressCallback) {
});
}
function decodeWxFile(fileName, decodeKey, fullFileName) {
let xorStream = xorTransform(getDecryptionArray(decodeKey));
let data = fs.createReadStream(fileName);
return new Promise((resolve, reject) => {
data.on('error', err => reject(err));
data.pipe(xorStream).pipe(
fs.createWriteStream(fullFileName).on('finish', () => {
resolve({
fullFileName,
});
}),
);
});
}
function toSize(size: number) {
if (size > 1048576) {
return (size / 1048576).toFixed(2) + "MB"
@@ -76,4 +92,4 @@ function toSize(size: number) {
return size + 'b'
}
export {downloadFile, toSize}
export {downloadFile, toSize, decodeWxFile}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>资源下载器</title>
<title>爱享素材下载器</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -5,7 +5,7 @@
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<title>资源下载器</title>
<title>爱享素材下载器</title>
</head>
<body>
<div id="app"></div>

View File

@@ -1,6 +1,6 @@
{
"name": "res-downloader",
"version": "1.0.3",
"version": "1.0.4",
"main": "dist-electron/main/index.js",
"description": "Electron + Vue + Vite 实现的资源下载软件,支持微信视频号下载、抖音视频下载、快手视频下载、酷狗音乐下载等",
"author": "putyy@qq.com",

View File

@@ -6,7 +6,7 @@ const jump = (scene: number)=>{
switch (scene) {
case 1:
ipcRenderer.invoke('invoke_open_default_browser', {
url: "https://github.com/putyy/res-downloader"
url: "https://s.gowas.cn/d/4089-quan-ping-tai-zi-yuan-xia-zai-ruan-jian"
})
break;
case 2:
@@ -24,6 +24,16 @@ const jump = (scene: number)=>{
url: "https://github.com/putyy/res-downloader/issues"
})
break;
case 5:
ipcRenderer.invoke('invoke_open_default_browser', {
url: "https://haokawx.lot-ml.com/Product/Index/22550"
})
break;
case 6:
ipcRenderer.invoke('invoke_open_default_browser', {
url: "https://github.com/putyy/res-downloader"
})
break;
}
}
</script>
@@ -33,8 +43,11 @@ div.line
a.item 站长邮箱: gowas.work@gmail.com
a.item(@click="jump(1)") 获取更新
a.item(@click="jump(2)") 云盘资源
a.item(@click="jump(3)") 图片压缩
div.line
a.item(@click="jump(3)") 图片无损压缩
a.item(@click="jump(4)") 问题反馈
a.item(@click="jump(5)") 流量卡推荐
a.item(@click="jump(6)") 软件源码
</template>
<style lang="less" scoped>
@@ -42,6 +55,7 @@ div.line
display: flex;
flex-direction: row;
justify-content: center;
flex-wrap: wrap;
.item{
padding: 0 5px;
cursor: pointer;

View File

@@ -2,7 +2,7 @@
import {inject, onMounted, ref, watch} from 'vue'
import localStorageCache from "../../common/localStorage";
const appName = "资源下载器"
const appName = "爱享素材"
const sidebarCollapse = ref(inject('sidebarCollapse'))
const defaultActive = ref("/index")
@@ -20,11 +20,7 @@ div.sidebar
el-menu-item(key="1" index="/index")
el-icon
VideoCamera
span 拦截下载
el-menu-item(key="7" index="/vip-parse")
el-icon
VideoCamera
span 影视解析
span 嗅探
el-menu-item(key="2" index="/about")
el-icon
Share

View File

@@ -25,11 +25,6 @@ const routes = [
name: 'Setting',
component: () => import('./views/Setting.vue'),
},
{
path: '/vip-parse',
name: 'VipParse',
component: () => import('./views/VipParse.vue'),
},
]
},
]

View File

@@ -24,6 +24,22 @@ const jump = (scene: number)=>{
break;
}
}
const str = "使用方法\n" +
" 1. 打开本软件\n" +
" 2. 软件首页选择要获取的资源类型(默认选中的视频)\n" +
" 3. 打开要捕获的源, 如:视频号、网页、小程序等等\n" +
" 4. 返回软件首页即可看到要下载的资源\n" +
"常见问题\n" +
" 1. 无法拦截获取\n" +
" 手动检测系统代理是否设置正确 本软件代理地址: 127.0.0.1:8899\n" +
" 2. 关闭软件后无法正常上网\n" +
" 手动关闭系统代理设置\n" +
"实现原理\n" +
" 通过代理网络抓包拦截响应,筛选出有用的资源,\n" +
" 同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的\n" +
" 只不过这些软件需要手动进行筛选,对于小白用户上手还是有点难度,本软件对部分资源做了特殊处理,\n" +
" 更适合大众用户,所以就有了本项目这样的软件。\n"
</script>
<template lang="pug">
div.about
@@ -36,6 +52,9 @@ div.about
el-button(@click="jump(3)") 获取更新
div 4. 问题反馈 &nbsp;
el-button(@click="jump(4)") 点击前往
div.more
pre {{str}}
</template>
<style lang="less">
@@ -50,5 +69,8 @@ div.about
padding: .3rem;
white-space: pre-wrap;
}
.more{
}
}
</style>

View File

@@ -56,7 +56,7 @@ onMounted(() => {
}
})
const loading = ElLoading.service({
let loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
@@ -87,9 +87,13 @@ onUnmounted(() => {
// console.log(res)
})
ipcRenderer.invoke('invoke_close_proxy').then((res) => {
ipcRenderer.removeListener('on_down_file_schedule', (res) => {
// console.log(res)
})
// ipcRenderer.invoke('invoke_close_proxy').then((res) => {
// })
localStorageCache.set("res-table-data", tableData.value, -1)
localStorageCache.set("res-type", resType.value, -1)
})
@@ -119,6 +123,10 @@ const handleBatchDown = async () => {
background: 'rgba(0, 0, 0, 0.7)',
})
ipcRenderer.on('on_down_file_schedule', (res: any, data: any) => {
loading.setText(`已下载 ${data.schedule}%`)
})
for (const item of multipleSelection.value) {
let result = await ipcRenderer.invoke('invoke_file_exists', {
save_path: save_dir,
@@ -182,6 +190,10 @@ const handleDown = async (index: number, row: any, high: boolean) => {
return
}
ipcRenderer.on('on_down_file_schedule', (res: any, data: any) => {
loading.setText(`已下载 ${data.schedule}%`)
})
ipcRenderer.invoke('invoke_down_file', {
index: index,
data: Object.assign({}, tableData.value[index]),
@@ -199,18 +211,49 @@ const handleDown = async (index: number, row: any, high: boolean) => {
}
loading.close()
}).catch((err) => {
// console.log('invoke_down_file err', err)
ElMessage({
message: "下载失败",
type: 'warning',
})
loading.close()
})
}
const decodeWxFile = (index: number) => {
let loading = ElLoading.service({
lock: true,
text: "解密中",
background: 'rgba(0, 0, 0, 0.7)',
})
ipcRenderer.invoke('invoke_select_wx_file', {
index: index,
data: Object.assign({}, tableData.value[index]),
}).then((res) => {
if (res !== false) {
ElMessage({
message: "解密成功: " + res.fullFileName,
type: 'success',
})
tableData.value[index].progress_bar = "100%"
tableData.value[index].save_path = res.fullFileName
}else{
ElMessage({
message: "解密失败",
type: 'warning',
})
}
loading.close()
}).catch((err) => {
ElMessage({
message: "解密失败",
type: 'warning',
})
loading.close()
})
}
const handlePreview = (index: number, row: any) => {
// console.log('row.down_url',row)
ipcRenderer.invoke('invoke_resources_preview', {url: row.down_url}).catch(() => {
})
}
@@ -239,19 +282,9 @@ const handleDel = (index: number)=>{
tableData.value = arr
}
const openDir = ()=>{
let save_dir = localStorageCache.get("save_dir")
if (!save_dir) {
ElMessage({
message: '目录不存在',
type: 'warning'
})
return
}
ipcRenderer.invoke('invoke_open_dir', {
dir: save_dir
const openFileDir = (index: number)=>{
ipcRenderer.invoke('invoke_open_file_dir', {
save_path: tableData.value[index].save_path
})
}
@@ -287,14 +320,14 @@ el-container.container
el-button(@click="resType.m3u8=!resType.m3u8" :type="resType.m3u8 ? 'primary' : 'info'" ) m3u8
a(style="color: red") &nbsp;&nbsp;&nbsp;点击左边选项选择需要拦截的资源类型
el-main
el-table(ref="multipleTableRef" @selection-change="handleSelectionChange" :data="tableData" max-height="100%" stripe style="max-content")
el-table-column(type="selection" width="55")
el-table-column(label="预览" show-overflow-tooltip width="350px")
el-table(ref="multipleTableRef" @selection-change="handleSelectionChange" :data="tableData" max-height="100%" stripe)
el-table-column(type="selection")
el-table-column(label="预览" show-overflow-tooltip width="300px")
template(#default="scope")
div.show_res
video(v-if="scope.row.type_str === 'video'" :src="scope.row.down_url" controls preload="none" style="width: 100%;height: auto;") 您的浏览器不支持 video 标签
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")
audio(v-if="scope.row.type_str === 'audio'" controls preload="none")
audio.audio(v-if="scope.row.type_str === 'audio'" controls preload="none")
source(:src="scope.row.down_url" :type="scope.row.type")
div {{scope.row.description}}
el-table-column(prop="type_str" label="类型" show-overflow-tooltip)
@@ -302,14 +335,16 @@ el-container.container
el-table-column(prop="size" label="资源大小")
el-table-column(prop="save_path" label="保存目录")
el-table-column(prop="progress_bar" label="下载进度")
el-table-column(label="操作")
el-table-column(label="操作" width="135px" )
template(#default="scope")
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, false)") {{scope.row.decode_key ? "解密下载" : "下载"}}
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="handleDel(scope.$index)")
el-button(link type="primary" @click="openDir()") 打开目录
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, false)") {{scope.row.decode_key ? "解密下载(视频号)" : "下载"}}
el-button(v-if="scope.row.decode_key" 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="handleDel(scope.$index)") 删除
el-button(v-if="scope.row.save_path" link type="primary" @click="openFileDir(scope.$index)") 打开文件目录
</template>
<style scoped lang="less">
@@ -344,11 +379,17 @@ el-container.container
}
.show_res{
width: 100%;
height: auto;
.img{
width: 100px;
height: auto;
max-height: 200px;
}
}
.actions{
display: flex;
flex-direction: column;
align-items: flex-start;
}
}
</style>

View File

@@ -1,99 +0,0 @@
<script lang="ts" setup>
import {onMounted, ref} from "vue"
import {ElMessage} from "element-plus"
import {ipcRenderer} from 'electron'
import {onUnmounted} from "@vue/runtime-core"
import localStorageCache from "../common/localStorage"
const parseUrls = ref([
"https://www.8090g.cn/jiexi/?url=",
"https://jx.m3u8.tv/jiexi/?url=",
"https://www.playm3u8.cn/jiexi.php?url=",
"https://www.8090.la/8090/?url=",
"https://jx.xmflv.com/?url=",
"https://www.8090g.cn/?url=",
"https://dm.xmflv.com:4433/?url=",
])
const useParseUrl = ref("")
const playUrl = ref("")
const iframeSrc = ref("")
const descText = ref(
"支持各大视频付费、VIP电影电视剧解析免费观看: 爱奇艺、优酷、腾讯、乐视、土豆、芒果等\r\n若视频播放异常或时长不对请尝试【更换线路】或【退出软件重新打开】即可解决\r\n如有线路不行请把页面拉倒最下方发邮件给站长"
)
onMounted(() => {
useParseUrl.value = parseUrls.value[0]
let dataCache = localStorageCache.get("res-vip-parse-data")
if (dataCache) {
useParseUrl.value = dataCache.useParseUrl
playUrl.value = dataCache.playUrl
// iframeSrc.value = useParseUrl.value + encodeURI(playUrl.value)
}
})
const parsePlay = () => {
if (!playUrl) {
ElMessage({
message: "请填写播放地址",
type: 'warning',
})
return
}
iframeSrc.value = useParseUrl.value + encodeURI(playUrl.value)
}
const parseFullPlay = () => {
if (!playUrl) {
ElMessage({
message: "请填写播放地址",
type: 'warning',
})
return
}
ipcRenderer.invoke('invoke_resources_preview', {url: useParseUrl.value + encodeURI(playUrl.value)}).catch(() => {
})
}
onUnmounted(() => {
localStorageCache.set("res-vip-parse-data", {useParseUrl: useParseUrl.value, playUrl: playUrl.value}, -1)
})
</script>
<template lang="pug">
el-main.play-box
iframe.iframe(:src="iframeSrc")
el-form
el-form-item(label="线路选择:")
el-select(v-model="useParseUrl")
el-option(v-for="(v, k) in parseUrls" :value="v" :label="'线路'+(k+1)")
el-form-item(label="播放地址:")
el-input(v-model="playUrl" type="textarea" placeholder="爱奇艺、优酷、腾讯、芒果、乐视、土豆")
el-form-item
el-button(type="primary" @click="parsePlay()") 立即播放
el-button(type="primary" @click="parseFullPlay()") 全屏播放
el-row.desc {{descText}}
</template>
<style scoped lang="less">
.play-box {
width: 100%;
height: 100%;
.iframe {
top: 0;
bottom: 0;
left: 0;
border: 0;
background-color: #211f1f;
width: 100%;
height: 80%;
}
}
.desc {
color: red;
font-size: 20px;
white-space: pre-wrap;
text-align: left;
}
</style>