From edd097855b8665b49fe4225231cd6adad3834553 Mon Sep 17 00:00:00 2001 From: putyy Date: Mon, 18 Nov 2024 17:41:43 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E3=80=81=E5=A2=9E=E5=8A=A0table=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 2 + electron/main/cert.ts | 99 ++++++----- electron/main/index.ts | 44 ++++- electron/main/ipc.ts | 59 +++++-- electron/main/proxyServer.ts | 276 +++++++++++++++--------------- electron/main/setProxy.ts | 2 +- electron/main/utils.ts | 6 +- src/App.vue | 5 +- src/common/api.ts | 5 - src/common/request.ts | 71 -------- src/components/layout/Sidebar.vue | 4 +- src/route.ts | 2 - src/views/Index.vue | 150 +++++++++++++--- src/views/Setting.vue | 39 +++-- 14 files changed, 445 insertions(+), 319 deletions(-) delete mode 100755 src/common/api.ts delete mode 100755 src/common/request.ts diff --git a/components.d.ts b/components.d.ts index 620980f..a384f3b 100755 --- a/components.d.ts +++ b/components.d.ts @@ -8,6 +8,7 @@ export {} declare module 'vue' { export interface GlobalComponents { ElButton: typeof import('element-plus/es')['ElButton'] + ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElFooter: typeof import('element-plus/es')['ElFooter'] ElForm: typeof import('element-plus/es')['ElForm'] @@ -20,6 +21,7 @@ declare module 'vue' { ElMenu: typeof import('element-plus/es')['ElMenu'] ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] ElOption: typeof import('element-plus/es')['ElOption'] + ElPopover: typeof import('element-plus/es')['ElPopover'] ElSelect: typeof import('element-plus/es')['ElSelect'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] Footer: typeof import('./src/components/layout/Footer.vue')['default'] diff --git a/electron/main/cert.ts b/electron/main/cert.ts index 7ba48df..020569d 100755 --- a/electron/main/cert.ts +++ b/electron/main/cert.ts @@ -9,51 +9,62 @@ export function checkCertInstalled() { return fs.existsSync(CONFIG.INSTALL_CERT_FLAG) } -export async function installCert(checkInstalled = true) { - if (checkInstalled && checkCertInstalled()) { - return; +export function installCert(checkInstalled = true) { + try { + if (checkInstalled && checkCertInstalled()) { + return; + } + mkdirp.sync(path.dirname(CONFIG.INSTALL_CERT_FLAG)) + + if (process.platform === 'darwin') { + handleMacCertInstallation() + } else if (process.platform === 'win32') { + handleWindowsCertInstallation() + } else { + handleOtherCertInstallation() + } + } catch (e) { + handleOtherCertInstallation() } - mkdirp.sync(path.dirname(CONFIG.INSTALL_CERT_FLAG)) - - if (process.platform === 'darwin') { - return new Promise((resolve, reject) => { - clipboard.writeText( - `echo "输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${CONFIG.CERT_PUBLIC_PATH}" && touch ${CONFIG.INSTALL_CERT_FLAG} && echo "安装完成"`, - ) - dialog.showMessageBoxSync({ - type: "info", - message: `命令已复制到剪贴板,粘贴命令到终端并运行以安装并信任证书`, - }); - - reject() - }); - } else if (process.platform === 'linux') { - return new Promise((resolve, reject) => { - clipboard.writeText( - "https://github.com/putyy/res-downloader/blob/master/electron/res/keys/public.pem", - ) - dialog.showMessageBoxSync({ - type: "info", - message: `Linux系统请手动安装证书,已复制下载地址`, - }); - - reject() - }); +} + + +// MacOS 证书安装处理 +function handleMacCertInstallation() { + clipboard.writeText( + `echo "输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${CONFIG.CERT_PUBLIC_PATH}" && touch ${CONFIG.INSTALL_CERT_FLAG} && echo "安装完成"` + ); + + dialog.showMessageBoxSync({ + type: 'info', + message: '命令已复制到剪贴板,粘贴到终端并运行以安装并信任证书', + }); +} + +// Linux 证书安装处理 +function handleOtherCertInstallation() { + clipboard.writeText(CONFIG.CERT_PUBLIC_PATH); + + dialog.showMessageBoxSync({ + type: "info", + message: `请手动安装证书,证书文件路径:${CONFIG.CERT_PUBLIC_PATH} 已复制到剪贴板`, + }); +} + +// Windows 证书安装处理 +function handleWindowsCertInstallation() { + const result = spawn.sync(CONFIG.WIN_CERT_INSTALL_HELPER, [ + '-c', + '-add', + CONFIG.CERT_PUBLIC_PATH, + '-s', + 'root', + ]); + + if (result.stdout.toString().includes('Succeeded')) { + fs.writeFileSync(CONFIG.INSTALL_CERT_FLAG, ''); } else { - return new Promise((resolve: any, reject) => { - const result = spawn.sync(CONFIG.WIN_CERT_INSTALL_HELPER, [ - '-c', - '-add', - CONFIG.CERT_PUBLIC_PATH, - '-s', - 'root', - ]); - if (result.stdout.toString().indexOf('Succeeded') > -1) { - fs.writeFileSync(CONFIG.INSTALL_CERT_FLAG, '') - resolve() - } else { - reject() - } - }) + handleOtherCertInstallation(); } } + diff --git a/electron/main/index.ts b/electron/main/index.ts index dcb1580..92a4cad 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -7,6 +7,8 @@ import {closeProxy} from "./setProxy" import log from "electron-log" import path from 'path' import {spawn} from 'child_process' +import {startServer} from "./proxyServer"; +import fs from "fs"; // The built directory structure // @@ -56,6 +58,16 @@ const preload = join(__dirname, '../preload/index.js') const url = process.env.VITE_DEV_SERVER_URL const indexHtml = join(process.env.DIST, 'index.html') +global.videoList = {} +global.isStartProxy = false +global.isSettingProxy = false +global.resdConfig = { + save_dir: "", + quality: "-1", + proxy: "", + port: 8899, +} + // app.whenReady().then(createWindow) app.on('window-all-closed', () => { @@ -100,8 +112,10 @@ function createWindow() { mainWindow = new BrowserWindow({ title: 'Main window', icon: join(process.env.VITE_PUBLIC, 'favicon.ico'), - width: 800, - height: 600, + width: 1024, + minWidth: 960, + height: 768, + minHeight: 640, webPreferences: { preload, // Warning: Enable nodeIntegration and disable contextIsolation is not secure in production @@ -206,10 +220,32 @@ function createAria2Process() { } } +function initConfig(){ + const configPath = path.join(app.getPath('userData'), 'resd_config.json') + if (!fs.existsSync(configPath)) { + return + } + fs.readFile(configPath, (err, data) => { + if (!err) { + try { + const jsonData = JSON.parse(data) + console.log("jsonData:", jsonData) + global.resdConfig = Object.assign({}, global.resdConfig, jsonData) + if (!global.resdConfig.proxy) { + global.resdConfig.proxy = "8899" + } + } catch (parseErr) { + } + } + }); +} + app.whenReady().then(() => { - initIPC() createWindow() createPreviewWindow(mainWindow) - createAria2Process() setWin(mainWindow, previewWin) + initConfig() + initIPC() + startServer(mainWindow) + createAria2Process() }) \ No newline at end of file diff --git a/electron/main/ipc.ts b/electron/main/ipc.ts index 4b988e1..2b9dfe9 100755 --- a/electron/main/ipc.ts +++ b/electron/main/ipc.ts @@ -7,10 +7,12 @@ import {hexMD5} from '../../src/common/md5' import {Aria2RPC} from './aria2Rpc' import fs from "fs" import urlTool from "url"; +import {setProxy} from "./setProxy"; +import path from 'path' let win: BrowserWindow let previewWin: BrowserWindow -let isStartProxy = false + const aria2RpcClient = new Aria2RPC() export default function initIPC() { @@ -21,23 +23,50 @@ export default function initIPC() { ipcMain.handle('invoke_init_app', (event, arg) => { // 开始 初始化应用 安装证书相关 - installCert(false).then(r => { - }) + installCert(false) + }) + + ipcMain.handle('invoke_set_config', (event, data) => { + const filePath = path.join(app.getPath('userData'), 'resd_config.json'); + fs.writeFile(filePath, JSON.stringify(data), ()=>{}) + global.resdConfig = Object.assign({}, global.resdConfig, data) + global.resdConfig.port = parseInt(global.resdConfig.port) + return true }) - ipcMain.handle('invoke_start_proxy', (event, arg) => { + ipcMain.handle('invoke_set_proxy', async (event, arg) => { // 启动代理服务 - if (isStartProxy) { - return + if (!global.isStartProxy) { + dialog.showMessageBoxSync({ + type: "error", + message: "代理未启动", + }); + return false } - isStartProxy = true - return startServer({ - win: win, - upstreamProxy: arg.upstream_proxy ? arg.upstream_proxy : "", - setProxyErrorCallback: err => { - console.log('setProxyErrorCallback', err) - }, - }) + try { + await setProxy('127.0.0.1', global.resdConfig.proxy) + return true + } catch (err) { + console.error(err); + dialog.showMessageBoxSync({ + type: "error", + message: err.toString(), + }); + return false + } + // let upstream_proxy = "" + // if (arg.upstream_proxy && !arg.upstream_proxy.includes(':8899')) { + // upstream_proxy = arg.upstream_proxy + // } + // + // global.isStartProxy = true + // return startServer({ + // win: win, + // upstreamProxy: upstream_proxy, + // setProxyErrorCallback: err => { + // console.log('setProxyErrorCallback', err) + // }, + // }) }) ipcMain.handle('invoke_select_down_dir', async (event, arg) => { @@ -72,7 +101,7 @@ export default function initIPC() { resolve(false); }); } - if(quality === "0" && data.decode_key){ + if (quality === "0" && data.decode_key) { const urlInfo = urlTool.parse(down_url, true); console.log('urlInfo', urlInfo) if (urlInfo.query["token"] && urlInfo.query["encfilekey"]) { diff --git a/electron/main/proxyServer.ts b/electron/main/proxyServer.ts index a3929b3..2984460 100755 --- a/electron/main/proxyServer.ts +++ b/electron/main/proxyServer.ts @@ -1,19 +1,15 @@ import fs from 'fs' import log from 'electron-log' import CONFIG from './const' -import {setProxy} from './setProxy' import * as urlTool from "url" import {toSize, typeSuffix} from "./utils" // @ts-ignore import {hexMD5} from '../../src/common/md5' import pkg from '../../package.json' +import {dialog} from "electron"; const hoXy = require('hoxy') -const port = 8899 - -global.videoList = {} - if (process.platform === 'win32') { process.env.OPENSSL_BIN = CONFIG.OPEN_SSL_BIN_PATH process.env.OPENSSL_CONF = CONFIG.OPEN_SSL_CNF_PATH @@ -34,144 +30,154 @@ const resObject = { description: "" } -const vv = hexMD5(pkg.version) + (CONFIG.IS_DEV ? Math.random() :"") +const vv = hexMD5(pkg.version) + (CONFIG.IS_DEV ? Math.random() : "") -export async function startServer({win, upstreamProxy, setProxyErrorCallback = f => f,}) { - return new Promise(async (resolve: any, reject) => { - try { - const proxy = hoXy.createServer({ - upstreamProxy: upstreamProxy, - certAuthority: { - key: fs.readFileSync(CONFIG.CERT_PRIVATE_PATH), - cert: fs.readFileSync(CONFIG.CERT_PUBLIC_PATH), - }, +export function startServer(win) { + try { + let upstreamProxy = "" + if (global.resdConfig.proxy && !global.resdConfig.proxy.includes(':' + global.resdConfig.port)) { + upstreamProxy = global.resdConfig?.proxy + } + const proxy = hoXy.createServer({ + upstreamProxy: upstreamProxy, + certAuthority: { + key: fs.readFileSync(CONFIG.CERT_PRIVATE_PATH), + cert: fs.readFileSync(CONFIG.CERT_PUBLIC_PATH), + }, + }) + .listen(global.resdConfig.port, () => { + global.isStartProxy = true + // try { + // await setProxy('127.0.0.1', port) + // resolve() + // } catch (err) { + // console.error(err); + // setProxyErrorCallback(err) + // reject("请手动设置系统代理" + err.toString()) + // } }) - .listen(port, async () => { - try { - await setProxy('127.0.0.1', port) - resolve() - } catch (err) { - console.error(err); - setProxyErrorCallback(err) - reject("请手动设置系统代理" + err.toString()) - } - }) - .on('error', err => { - setProxyErrorCallback(err) - reject('proxy service err: ' + err.toString()) - }) + .on('error', err => { + console.error(err); + dialog.showMessageBoxSync({ + type: 'error', + message: err.toString(), + }); + // setProxyErrorCallback(err) + // reject('proxy service err: ' + err.toString()) + }) + intercept(proxy, win) + } catch (e) { + log.log("--------------proxy catch err--------------", e) + } +} +function intercept(proxy, win) { + proxy.intercept( + { + phase: 'request', + hostname: 'res-downloader.666666.com', + as: 'json', + }, + (req, res) => { + res.string = 'ok' + res.statusCode = 200 + try { + if (req.json?.media?.length <= 0) { + return + } + const media = req.json?.media[0] + const url_sign: string = hexMD5(media.url) + if (!media?.decodeKey || 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, + cover_url: media.coverUrl, + file_format: media.spec.map((res) => res.fileFormat).join('#'), + platform: urlInfo.hostname, + size: toSize(media.fileSize), + type: "video/mp4", + type_str: 'video', + decode_key: media.decodeKey, + description: req.json.description, + })) + } catch (e) { + log.log(e.toString()) + } + }, + ) - proxy.intercept( - { - phase: 'request', - hostname: 'res-downloader.666666.com', - as: 'json', - }, - (req, res) => { - res.string = 'ok' - res.statusCode = 200 - try { - if (req.json?.media?.length <= 0) { - return - } - const media = req.json?.media[0] - const url_sign: string = hexMD5(media.url) - if (!media?.decodeKey || global.videoList.hasOwnProperty(url_sign) === true) { - return - } - const urlInfo = urlTool.parse(media.url, true) - global.videoList[url_sign] = media.url + proxy.intercept( + { + phase: 'response', + hostname: 'channels.weixin.qq.com', + as: 'string', + }, + async (req, res) => { + if (req.url.includes('/web/pages/feed') || req.url.includes('/web/pages/home')) { + res.string = res.string.replaceAll('.js"', '.js?v=' + vv + '"') + res.statusCode = 200 + } + }, + ) + + proxy.intercept( + { + phase: 'response', + hostname: 'res.wx.qq.com', + 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")) { + 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( + { + phase: 'response', + }, + async (req, res) => { + try { + // 拦截响应 + const contentType = res?._data?.headers?.['content-type'] + const [resType, suffix] = typeSuffix(contentType) + if (resType) { + const url_sign: string = hexMD5(req.fullUrl()) + const res_url = req.fullUrl() + const urlInfo = urlTool.parse(res_url, true) + const contentLength = res?._data?.headers?.['content-length'] + 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, - url: media.url + media.urlToken, - cover_url: media.coverUrl, - file_format: media.spec.map((res)=> res.fileFormat).join('#'), platform: urlInfo.hostname, - size: toSize(media.fileSize), - type: "video/mp4", - type_str: 'video', - decode_key: media.decodeKey, - description: req.json.description, + size: toSize(contentLength ? contentLength : 0), + type: contentType, + type_str: resType, })) - } catch (e) { - log.log(e.toString()) - } - }, - ) - - proxy.intercept( - { - phase: 'response', - hostname: 'channels.weixin.qq.com', - as: 'string', - }, - async (req, res) => { - if (req.url.includes('/web/pages/feed') || req.url.includes('/web/pages/home')) { - res.string = res.string.replaceAll('.js"', '.js?v=' + vv + '"') - res.statusCode = 200 - } - }, - ) - - proxy.intercept( - { - phase: 'response', - hostname: 'res.wx.qq.com', - 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")) { - 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( - { - phase: 'response', - }, - async (req, res) => { - try { - // 拦截响应 - const contentType = res?._data?.headers?.['content-type'] - const [resType, suffix] = typeSuffix(contentType) - if (resType) { - const url_sign: string = hexMD5(req.fullUrl()) - const res_url = req.fullUrl() - const urlInfo = urlTool.parse(res_url, true) - const contentLength = res?._data?.headers?.['content-length'] - 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(contentLength ? contentLength : 0), - type: contentType, - type_str: resType, - })) - } - } - } catch (e) { - log.log(e.toString()) - } - }, - ) - } catch (e) { - log.log("--------------proxy catch err--------------", e) - } - }) + } catch (e) { + log.log("--------------proxy response err--------------", e) + } + }, + ) } \ No newline at end of file diff --git a/electron/main/setProxy.ts b/electron/main/setProxy.ts index 1b36148..2524271 100755 --- a/electron/main/setProxy.ts +++ b/electron/main/setProxy.ts @@ -38,7 +38,7 @@ export async function setProxy(host, port) { } else if (process.platform === 'linux') { dialog.showMessageBoxSync({ type: "info", - message: `请手动设置系统代理`, + message: `请手动设置系统代理 默认为: 127.0.0.1:8899`, }); return new Promise((resolve, reject) => {}) } else { diff --git a/electron/main/utils.ts b/electron/main/utils.ts index e6c8f5a..5c2e86d 100755 --- a/electron/main/utils.ts +++ b/electron/main/utils.ts @@ -99,7 +99,7 @@ function toSize(size: number) { } function typeSuffix(type: string) { - switch (type) { + switch (type ? type.toLowerCase() : type) { case "video/mp4": case "video/webm": case "video/ogg": @@ -111,7 +111,7 @@ function typeSuffix(type: string) { case "video/x-matroska": return ["video", ".mp4"]; case "audio/video": - case "video/x-flv": + case "video/x-flv": return ["live", ".mp4"]; case "image/png": case "image/webp": @@ -143,7 +143,7 @@ function typeSuffix(type: string) { case "audio/mp4;charset=UTF-8": return ["audio", ".mp3"]; case "application/vnd.apple.mpegurl": - case "application/x-mpegURL": + case "application/x-mpegurl": return ["m3u8", ".m3u8"]; case "application/pdf": return ["pdf", ".pdf"]; diff --git a/src/App.vue b/src/App.vue index 4d0a6d8..8019bc6 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,8 +1,11 @@