diff --git a/.gitignore b/.gitignore index 7166e88..6e5ada7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ lerna-debug.log* node_modules dist temp +test dist-ssr dist-electron release @@ -28,4 +29,5 @@ release # lockfile package-lock.json +yarn.lock pnpm-lock.yaml \ No newline at end of file diff --git a/README.md b/README.md index 8f9ba9e..cd67baa 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ 🎯 基于 [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue.git) 📦 操作简单、可获取不同类型的资源 -💪 支持获取视频、音频、图片、m3u8 -🖥 支持获取视频号、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源 +💪 支持获取视频、音频、图片、m3u8 +🖥 支持获取视频号、抖音、快手、小红书、酷狗音乐、qq音乐、微信小程序等网络资源 +🍊 支持设置代理以获取特殊网络下的资源 ## 软件下载 🆕 [github下载](https://github.com/putyy/res-downloader/releases) @@ -28,9 +29,6 @@ yarn run build --mac yarn run build --win ``` -## 软件截图 -![](public/show.jpg) - ## 使用方法 > 1. 打开本软件 > 2. 软件首页选择要获取的资源类型(默认选中的视频) @@ -42,6 +40,13 @@ yarn run build --win > > 手动检测系统代理是否设置正确 本软件代理地址: 127.0.0.1:8899 > 2. 关闭软件后无法正常上网 > > 手动关闭系统代理设置 +> 3. 视频号抓取流程 +> > 将需要下载的视频发给好友或者文件助手 再打开即可拦截,通常会出现解密下载按钮 +> > +> > 大视频可以复制链接通过其他工具加速下载,然后再通过对应的视频操作项进行"视频解密" + +## 软件截图 +![](public/show.webp) ## 实现原理 > 通过代理网络抓包拦截响应,筛选出有用的资源,同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的,只不过这些软件需要手动进行筛选,对于小白用户上手还是有点难度,所以就有了本项目这样的软件。 @@ -49,3 +54,4 @@ yarn run build --win ## 参考项目 - [WeChatVideoDownloader](https://github.com/lecepin/WeChatVideoDownloader) 原项目是react写的,本项目参考原项目用vue3重写了一下,核心逻辑没什么变化,主要是增加了一些新的功能,再次感谢! + diff --git a/components.d.ts b/components.d.ts index e62d0ea..13d3cfb 100755 --- a/components.d.ts +++ b/components.d.ts @@ -14,6 +14,7 @@ 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'] diff --git a/electron/main/const.ts b/electron/main/const.ts index ecb586d..c79ec21 100755 --- a/electron/main/const.ts +++ b/electron/main/const.ts @@ -13,14 +13,13 @@ const EXECUTABLE_PATH = path.join( ) const HOME_PATH = path.join(os.homedir(), '.res-downloader@putyy') - export default { IS_DEV: isDev, EXECUTABLE_PATH, HOME_PATH, CERT_PRIVATE_PATH: path.join(EXECUTABLE_PATH, './keys/private.pem'), CERT_PUBLIC_PATH: path.join(EXECUTABLE_PATH, './keys/public.pem'), - INSTALL_CERT_FLAG: path.join(HOME_PATH, './installed.lock'), + INSTALL_CERT_FLAG: path.join(HOME_PATH, './res-downloader-installed.lock'), WIN_CERT_INSTALL_HELPER: path.join(EXECUTABLE_PATH, './w_c.exe'), APP_CN_NAME: '爱享素材下载器', APP_EN_NAME: 'ResDownloader', diff --git a/electron/main/index.ts b/electron/main/index.ts index 6a3dbac..89191b0 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -156,11 +156,6 @@ function createPreviewWindow(parent: BrowserWindow) { // previewWin.hide() previewWin.setTitle("预览") - previewWin.webContents.session.on('will-download', (event, item, webContents) => { - // console.log("取消下载") - item.cancel() - }) - previewWin.on("page-title-updated", (event) => { // 阻止该事件 event.preventDefault() diff --git a/electron/main/ipc.ts b/electron/main/ipc.ts index 84c8d3b..53a422a 100755 --- a/electron/main/ipc.ts +++ b/electron/main/ipc.ts @@ -1,42 +1,20 @@ import {ipcMain, dialog, BrowserWindow, app, shell} from 'electron' import {startServer} from './proxyServer' import {installCert, checkCertInstalled} from './cert' -import {downloadFile, decodeWxFile} from './utils' +import {downloadFile, decodeWxFile, suffix} 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"; +import {floor} from "lodash" let getMac = require("getmac").default let win: BrowserWindow let previewWin: BrowserWindow let isStartProxy = false -let isOpenProxy = false let aesKey = "as5d45as4d6qe6wqfar6gt4749q6y7w6h34v64tv7t37ty5qwtv6t6qv" -const suffix = (type: string) => { - switch (type) { - case "video/mp4": - return ".mp4"; - case "image/png": - return ".png"; - case "image/webp": - return ".webp"; - case "image/svg+xml": - return ".svg"; - case "image/gif": - return ".gif"; - case "audio/mpeg": - return ".mp3"; - case "application/vnd.apple.mpegurl": - return ".m3u8"; - } -} - export default function initIPC() { ipcMain.handle('invoke_app_is_init', async (event, arg) => { @@ -44,47 +22,26 @@ export default function initIPC() { return checkCertInstalled() }) - ipcMain.handle('invoke_init_app', (event, arg) => { + ipcMain.handle('invoke_init_app', (event, arg) => { // 开始 初始化应用 安装证书相关 - // console.log('invoke_init_app') - return installCert(false) + installCert(false).then(r => {}) }) - ipcMain.handle('invoke_start_proxy', async (event, arg) => { + ipcMain.handle('invoke_start_proxy', (event, arg) => { // 启动代理服务 if (isStartProxy) { - if (isOpenProxy === false) { - isOpenProxy = true - setProxy('127.0.0.1', 8899) - .then(() => { - }) - .catch((err) => { - }) - } return } isStartProxy = true - isOpenProxy = true return startServer({ win: win, + upstreamProxy: arg.upstream_proxy ? arg.upstream_proxy : "", setProxyErrorCallback: err => { - isStartProxy = false - isOpenProxy = false + }, }) }) - ipcMain.handle('invoke_close_proxy', (event, arg) => { - // 关闭代理 - try { - isOpenProxy = false - return closeProxy() - } catch (error) { - log.log("--------------closeProxy error--------------", error) - } - - }) - ipcMain.handle('invoke_select_down_dir', async (event, arg) => { // 选择下载位置 const result = dialog.showOpenDialogSync({title: '保存', properties: ['openDirectory']}) @@ -123,6 +80,10 @@ export default function initIPC() { let url_sign = hexMD5(down_url) let save_path_file = `${save_path}/${url_sign}` + suffix(data.type) + if (process.platform === 'win32'){ + save_path_file = `${save_path}\\${url_sign}` + suffix(data.type) + } + if (fs.existsSync(save_path_file)) { return {fullFileName: save_path_file, totalLen: ""} } @@ -135,7 +96,6 @@ export default function initIPC() { win?.webContents.send('on_down_file_schedule', {schedule: floor(res * 100)}) } ).catch(err => { - // console.log('invoke_down_file:err', err) return false }) }) @@ -156,6 +116,8 @@ export default function initIPC() { return } + console.log('url', url) + previewWin.loadURL(url).then(r => { return }).catch(res => { @@ -171,6 +133,11 @@ export default function initIPC() { ipcMain.handle('invoke_open_file_dir', (event, {save_path}) => { shell.showItemInFolder(save_path) }) + + ipcMain.handle('invoke_window_restart', (event) => { + app.relaunch() + app.exit() + }) } export function setWin(w, p) { diff --git a/electron/main/proxyServer.ts b/electron/main/proxyServer.ts index b8c090f..31e1882 100755 --- a/electron/main/proxyServer.ts +++ b/electron/main/proxyServer.ts @@ -76,106 +76,128 @@ setTimeout(() => { export async function startServer({ win, + upstreamProxy, setProxyErrorCallback = f => f, }) { return new Promise(async (resolve: any, reject) => { - const proxy = hoXy.createServer({ + try { + const proxy = hoXy.createServer({ + upstreamProxy: upstreamProxy, certAuthority: { key: fs.readFileSync(CONFIG.CERT_PRIVATE_PATH), cert: fs.readFileSync(CONFIG.CERT_PUBLIC_PATH), }, }) - .listen(port, () => { - setProxy('127.0.0.1', port) - .then(() => { - // log.log("--------------setProxy success--------------") - resolve() - }) - .catch((err) => { - // log.log("--------------setProxy error--------------") - // setProxyErrorCallback(data); - setProxyErrorCallback({}); - reject('设置代理失败'); - }); - }) - .on('error', err => { - log.log("--------------proxy err--------------", err) - }); - - - proxy.intercept( - { - phase: 'request', - hostname: 'res-downloader.666666.com', - as: 'json', - }, - (req, res) => { - // console.log('req.json: ', req.json) - 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: '', + .listen(port, () => { + setProxy('127.0.0.1', port) + .then((res) => { + resolve() + }) + .catch((err) => { + setProxyErrorCallback(err); + reject('setting proxy err: '+ err.toString()); + }); }) - }, - ); + .on('error', err => { + setProxyErrorCallback(err); + reject('proxy service err: ' + err.toString()); + }); - proxy.intercept( - { - phase: 'response', - hostname: 'channels.weixin.qq.com', - as: 'string', - }, - async (req, res) => { - // console.log('inject[channels.weixin.qq.com] req.url:', req.url); - if (req.url.includes('/web/pages/feed') || req.url.includes('/web/pages/home')) { - res.string = res.string.replace('', '\n\n'); - res.statusCode = 200; - // console.log('inject[channels.weixin.qq.com]:', req.url, res.string.length); - } - }, - ); - proxy.intercept( - { - 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": - if (videoList.hasOwnProperty(url_sign) === false) { - videoList[url_sign] = req.fullUrl() - let high_url = '' - let down_url = res_url - // console.log('down_url', down_url) + proxy.intercept( + { + phase: 'request', + hostname: 'res-downloader.666666.com', + as: 'json', + }, + (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: '', + }) + }, + ); + + 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.replace('', '\n\n'); + res.statusCode = 200; + } + }, + ); + + proxy.intercept( + { + 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": + 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', { + 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), + 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": win?.webContents?.send?.('on_get_queue', { url_sign: url_sign, - url: down_url, - down_url: down_url, - high_url: high_url, + url: res_url, + down_url: res_url, + high_url: '', platform: urlInfo.hostname, size: toSize(res?._data?.headers?.['content-length'] ?? 0), type: ctype, - type_str: 'video', + type_str: 'image', progress_bar: '', save_path: '', downing: false, @@ -183,70 +205,51 @@ export async function startServer({ description: '', uploader: '', }) - } - break; - case "image/png": - case "image/webp": - case "image/svg+xml": - case "image/gif": - 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": - 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": - 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; + break; + case "audio/mpeg": + 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": + 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; - } + } - }, - ) + }, + ) + } catch (e) { + log.log("--------------proxy catch err--------------", e) + } }) } diff --git a/electron/main/setProxy.ts b/electron/main/setProxy.ts index a5d6903..dbc9503 100755 --- a/electron/main/setProxy.ts +++ b/electron/main/setProxy.ts @@ -22,7 +22,13 @@ export async function setProxy(host, port) { if (error) { reject(null) } else { - resolve(network) + exec(`networksetup -setwebproxy "${network}" ${host} ${port}`, error => { + if (error) { + reject(null) + } else { + resolve(network) + } + }); } }); }); @@ -63,7 +69,13 @@ export async function closeProxy() { if (error) { reject(null) } else { - resolve(network) + exec(`networksetup -setwebproxystate "${network}" off`, error => { + if (error) { + reject(null) + } else { + resolve(network) + } + }); } }); }); diff --git a/electron/main/utils.ts b/electron/main/utils.ts index f80938c..9e0dfbc 100755 --- a/electron/main/utils.ts +++ b/electron/main/utils.ts @@ -1,6 +1,6 @@ import fs from 'fs' import {Transform } from 'stream' -import {getDecryptionArray} from '../wxjs/decrypt' +import {getDecryptionArray} from '../wxjs/decrypt.js' const axios = require('axios') function xorTransform(decryptionArray) { @@ -92,4 +92,30 @@ function toSize(size: number) { return size + 'b' } -export {downloadFile, toSize, decodeWxFile} +function suffix(type: string) { + switch (type) { + case "video/mp4": + return ".mp4"; + case "image/png": + return ".png"; + case "image/webp": + return ".webp"; + case "image/svg+xml": + return ".svg"; + case "image/gif": + return ".gif"; + case "audio/mpeg": + return ".mp3"; + case "application/vnd.apple.mpegurl": + return ".m3u8"; + case "image/jpeg": + return ".jpeg"; + case "image/jpg": + return ".jpg"; + case "image/avif": + return ".avif"; + } + return "" +} + +export {downloadFile, toSize, decodeWxFile, suffix} diff --git a/electron/res/keys/private.key b/electron/res/keys/private.key index 8b994f1..25be5e3 100755 --- a/electron/res/keys/private.key +++ b/electron/res/keys/private.key @@ -1,27 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAsmAqn3hYd/YZcrfgqM1Q6xgHI50EBckbOkfCqTWS1yVFZjLF -bMehWb9xGFZJD21A5sxl4xelIWblhety+YTVa/mn2CEJh3je069oeULfXdzhhHyf -/ci0IloJhvX+2RJ+176uTKKcWhuOtNVs5VeFoHDoUcISnTqkaVyWeeLfafgrOW7w -N8ip128nuBx19ylIygb/DELmjKRRCSpx2vOw2JErTM8L5r0f4eWdqiwBOwu0NHWy -Svh9YG8B31UPga4I8FbFhybOP9cQNQPafOSfjwuZoi5CAtyJbwT7KyII9iMD74bZ -1mTx2xokmQ2TeiCSKSF8Mx9/8Gq+95mzvvIbRwIDAQABAoIBAHNt++caj9WBclJk -X4Oc6eJYuDX5o+LCk1YRngy12IJVYiWScWPFg8p6MouXOsw63Sb92mksofWNirYw -+UQzC5FGC7G3H12FgFzoQ+lEtxscluuPYlFukfMw5L1rbzG14FNo145MJHXDI4Qu -ILwA+T4sEorl1fndOwvbmJzjjcQaeRNz7/R9e6QTOlZ2+IEMKnHSBXXGJbDj6mPN -+f1/ec6nVENdxazgRCi0xfinyft4Ipst93Eb+wGcpk+J43aF+0leWQCdl6Y9U1Lz -zpv5H5XOQdwpX+dpuioRp73zwPwIialq+hTUN28Bn9U1jW2tjxUl/vgIpjy1s94a -UipRwSECgYEA57vYB+wGnxQxY9IPpr9H/y3HciIwCnuOEsWBzjYe8sIqBif2tEpO -OgDZZMQY7+JJrDQbDRs442TuRjKhJ5hiW+MyoiFWaYkBBoNVM8RBTkIjHfrh+uB2 -XT15FbEyyxo3n9QY610ZJFRnW4Uf5V0osjOqqUgQRrVXvamk6NQH6FkCgYEAxQ3v -jFYPL3EkZe1br6X0RM42ykGv5Di5Q6NnjpSPcyn9a2obA1cZuCd5S1lhrkuZGsdI -iFapeL+7vpts9gu9/ii9y+CgEKplOMmm0ZrChBKAcXMZvdDKV3y5SmTMZPas4X5i -hqNqatx9/J93sMYWc0CuoosDEJYKtSz8GE+1rJ8CgYAmp5rdl21zU7b5Y6zgr7+e -vVArpbBFz15fmzqP309CR0kjRb9NS6fI3SNmP5+5RBHt+7MXeJcAt3FXnFJtfGnL -0hY8HTuA1y2onHe17uLF3xpkgdj4NEEKRJrSF4DViEYHDyYo/JqZCMtE5OvxIp0L -PLsXCcJNSSqdpJKxk8zN4QKBgEuoxSAh7uStUWddUkXHt1kvwDO6MtmyuddxhxJk -kguKxMWYUNTgfXyKk3TN1caBOkDg4UWP2LQHEgPmU1jJO2K5q9362hpsAj9ilY2H -GUZygCSPKAQMhZQ/zDj3KM9fMxPFXfkKB5MOI8V6SQ9zjy0jWaoJK90TbvsPUZ/Y -Aw5LAoGADifZwCHPiXhTfJjOom2uBgXmL03yTXcCw4EDIX3ZR0sP6ACPQq4T4jxZ -UJLXLjOb2pzCq0c5+k0cG6ahYINq4tGOo+vQ9fDvhKg0nlf1FrzxSd7S12o+un2q -+U+dBllYIDlRMgMhXu9CxFDjUsCwPRmsBvmVZiH4XSs6QVnfn90= ------END RSA PRIVATE KEY----- \ No newline at end of file +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcDt23t6ioBoHG +/Y2mOjxntWQa9dP3eNl+mAC6425DlEtyc6czNAIKuuM9wt+wAwDQAgrd5RaxdcpJ +H1JlMkEtBFkIkdn0Ag98D7nwlVA9ON3xQi5Bkl+sN/oWOE8lOwvNyNNT6ZPu3qUS +KY8SXZVY315daqz2eRWsm2otqjqbWGhh9c7FGHr3r9aAG08dyaO6OvK14GJIhNV+ +UOPH5hDMMaxurDt8znaSUw93b7D++aEninGro/s2LY4G91dgM8i4t88UWobXpqs5 +GMGTI0InLX2I66HkteH4RRfXXC9svA2CxN3yP294FIP7gdRQ1CGJeJcluzsjtx0V +i2G9vrT1AgMBAAECggEAF0obfQ4a82183qqHC0iui+tOpOvPeyl3G0bLDPx09wIC +2iITV//xF2GgGzE8q0wmEd2leMZ+GFn3BrYh6kPfUfxbz+RfxMtTCDZB34xt6YzT +MG1op9ft+DQUa7WZ6r7NCQJwGzllRqqZncp4MeFlpPo+6nQXyh4WhSYNnredbENE +uPZ63Kme4RZfMvtVso+XgAQM3oDih0onv1YitmNQpL9rRzlthTfybAT4737DBINq +zsmBNE6QIsXnSKpzo11OtDgof2QM9ac6eAXf73oTpDxfodwCotILytKn+8WYvlR+ +T15uuknb4M3XI1FPVolkF4qtK5SLAAbVzV4DsCmuIQKBgQD6bTKKbL2huvU6dEKx +bgS079LfQUxxOTClgwkhVsMxRtvcPBnHYMAsPK4mnMhEh9x+TF6wxMx0pmhQluPI +ZULNBj/qdoiBL0RwVLA+9jgE0NeWB3XXFDsEavQBr9Q8CC0uzrsgsxFcvHpqqs2Q +RtngxRWtJP06D6mKC23s4YjDHwKBgQDg9KUCFqOmWcRXyeg9gYMC4jFFQw4lUQBd +sYpqSMHDw1b+T1W/dCPbwbxZL/+d8y930BYy9QYDtQwHdLyXCH0pHM7S6rfgr5xk +2Szd8xBUIqmeV/zcR00mTeQHJ1M50VHfclAVgZgkpWSoLwbX+bXyx/mfqLAtynZ5 +yU9RfrT5awKBgQC0uJ8TlFvZXjFgyMvkfY/5/2R/ZwFCaFI573FkVNeyNP+vVNQJ +tUGZ6wSGqvg/tIgjwPtIuA0QVZLMLcgeMy1dBhiUHIxwJetO4V77YPaWSxx5kdKx +r1DT5FdI7FnOJNxufhQ/CdsKwJ3bYn3Mk8TiV3hIJnx0LR9dltfybeQjYwKBgDOY +6aApATBOtrJMJXC2HA61QwfX8Y6tnZ/f8RefyJHWZEXAfLKFORRWw5TRZZgdB247 +1Furx81h4Xh0Vi1uTQb5DJdkLvjiTsTy60+dSMmDidQ/6ke8Mv3uL7dUVcqVMGpI +FgZYy0TcitHot3EiXZFqPN9aGc7m+XXFruPKZEgxAoGBAMA96jsow7CzulU+GRW8 +Njg4zWuAEVErgPoNBcOXAVWLCTU/qGIEMNpZL6Ok34kf13pJDMjQ8eDuQHu5CSqf +0ul5Zy85fwfVq2IvNAyYT8eflQprTejFw22CHhfPBfADVW9ro8dK/Jw+J/31Vh7V +ILKEQKmPPzKs7kp/7Nz+2cT3 +-----END PRIVATE KEY----- diff --git a/electron/res/keys/private.pem b/electron/res/keys/private.pem index 8b994f1..25be5e3 100755 --- a/electron/res/keys/private.pem +++ b/electron/res/keys/private.pem @@ -1,27 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAsmAqn3hYd/YZcrfgqM1Q6xgHI50EBckbOkfCqTWS1yVFZjLF -bMehWb9xGFZJD21A5sxl4xelIWblhety+YTVa/mn2CEJh3je069oeULfXdzhhHyf -/ci0IloJhvX+2RJ+176uTKKcWhuOtNVs5VeFoHDoUcISnTqkaVyWeeLfafgrOW7w -N8ip128nuBx19ylIygb/DELmjKRRCSpx2vOw2JErTM8L5r0f4eWdqiwBOwu0NHWy -Svh9YG8B31UPga4I8FbFhybOP9cQNQPafOSfjwuZoi5CAtyJbwT7KyII9iMD74bZ -1mTx2xokmQ2TeiCSKSF8Mx9/8Gq+95mzvvIbRwIDAQABAoIBAHNt++caj9WBclJk -X4Oc6eJYuDX5o+LCk1YRngy12IJVYiWScWPFg8p6MouXOsw63Sb92mksofWNirYw -+UQzC5FGC7G3H12FgFzoQ+lEtxscluuPYlFukfMw5L1rbzG14FNo145MJHXDI4Qu -ILwA+T4sEorl1fndOwvbmJzjjcQaeRNz7/R9e6QTOlZ2+IEMKnHSBXXGJbDj6mPN -+f1/ec6nVENdxazgRCi0xfinyft4Ipst93Eb+wGcpk+J43aF+0leWQCdl6Y9U1Lz -zpv5H5XOQdwpX+dpuioRp73zwPwIialq+hTUN28Bn9U1jW2tjxUl/vgIpjy1s94a -UipRwSECgYEA57vYB+wGnxQxY9IPpr9H/y3HciIwCnuOEsWBzjYe8sIqBif2tEpO -OgDZZMQY7+JJrDQbDRs442TuRjKhJ5hiW+MyoiFWaYkBBoNVM8RBTkIjHfrh+uB2 -XT15FbEyyxo3n9QY610ZJFRnW4Uf5V0osjOqqUgQRrVXvamk6NQH6FkCgYEAxQ3v -jFYPL3EkZe1br6X0RM42ykGv5Di5Q6NnjpSPcyn9a2obA1cZuCd5S1lhrkuZGsdI -iFapeL+7vpts9gu9/ii9y+CgEKplOMmm0ZrChBKAcXMZvdDKV3y5SmTMZPas4X5i -hqNqatx9/J93sMYWc0CuoosDEJYKtSz8GE+1rJ8CgYAmp5rdl21zU7b5Y6zgr7+e -vVArpbBFz15fmzqP309CR0kjRb9NS6fI3SNmP5+5RBHt+7MXeJcAt3FXnFJtfGnL -0hY8HTuA1y2onHe17uLF3xpkgdj4NEEKRJrSF4DViEYHDyYo/JqZCMtE5OvxIp0L -PLsXCcJNSSqdpJKxk8zN4QKBgEuoxSAh7uStUWddUkXHt1kvwDO6MtmyuddxhxJk -kguKxMWYUNTgfXyKk3TN1caBOkDg4UWP2LQHEgPmU1jJO2K5q9362hpsAj9ilY2H -GUZygCSPKAQMhZQ/zDj3KM9fMxPFXfkKB5MOI8V6SQ9zjy0jWaoJK90TbvsPUZ/Y -Aw5LAoGADifZwCHPiXhTfJjOom2uBgXmL03yTXcCw4EDIX3ZR0sP6ACPQq4T4jxZ -UJLXLjOb2pzCq0c5+k0cG6ahYINq4tGOo+vQ9fDvhKg0nlf1FrzxSd7S12o+un2q -+U+dBllYIDlRMgMhXu9CxFDjUsCwPRmsBvmVZiH4XSs6QVnfn90= ------END RSA PRIVATE KEY----- \ No newline at end of file +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcDt23t6ioBoHG +/Y2mOjxntWQa9dP3eNl+mAC6425DlEtyc6czNAIKuuM9wt+wAwDQAgrd5RaxdcpJ +H1JlMkEtBFkIkdn0Ag98D7nwlVA9ON3xQi5Bkl+sN/oWOE8lOwvNyNNT6ZPu3qUS +KY8SXZVY315daqz2eRWsm2otqjqbWGhh9c7FGHr3r9aAG08dyaO6OvK14GJIhNV+ +UOPH5hDMMaxurDt8znaSUw93b7D++aEninGro/s2LY4G91dgM8i4t88UWobXpqs5 +GMGTI0InLX2I66HkteH4RRfXXC9svA2CxN3yP294FIP7gdRQ1CGJeJcluzsjtx0V +i2G9vrT1AgMBAAECggEAF0obfQ4a82183qqHC0iui+tOpOvPeyl3G0bLDPx09wIC +2iITV//xF2GgGzE8q0wmEd2leMZ+GFn3BrYh6kPfUfxbz+RfxMtTCDZB34xt6YzT +MG1op9ft+DQUa7WZ6r7NCQJwGzllRqqZncp4MeFlpPo+6nQXyh4WhSYNnredbENE +uPZ63Kme4RZfMvtVso+XgAQM3oDih0onv1YitmNQpL9rRzlthTfybAT4737DBINq +zsmBNE6QIsXnSKpzo11OtDgof2QM9ac6eAXf73oTpDxfodwCotILytKn+8WYvlR+ +T15uuknb4M3XI1FPVolkF4qtK5SLAAbVzV4DsCmuIQKBgQD6bTKKbL2huvU6dEKx +bgS079LfQUxxOTClgwkhVsMxRtvcPBnHYMAsPK4mnMhEh9x+TF6wxMx0pmhQluPI +ZULNBj/qdoiBL0RwVLA+9jgE0NeWB3XXFDsEavQBr9Q8CC0uzrsgsxFcvHpqqs2Q +RtngxRWtJP06D6mKC23s4YjDHwKBgQDg9KUCFqOmWcRXyeg9gYMC4jFFQw4lUQBd +sYpqSMHDw1b+T1W/dCPbwbxZL/+d8y930BYy9QYDtQwHdLyXCH0pHM7S6rfgr5xk +2Szd8xBUIqmeV/zcR00mTeQHJ1M50VHfclAVgZgkpWSoLwbX+bXyx/mfqLAtynZ5 +yU9RfrT5awKBgQC0uJ8TlFvZXjFgyMvkfY/5/2R/ZwFCaFI573FkVNeyNP+vVNQJ +tUGZ6wSGqvg/tIgjwPtIuA0QVZLMLcgeMy1dBhiUHIxwJetO4V77YPaWSxx5kdKx +r1DT5FdI7FnOJNxufhQ/CdsKwJ3bYn3Mk8TiV3hIJnx0LR9dltfybeQjYwKBgDOY +6aApATBOtrJMJXC2HA61QwfX8Y6tnZ/f8RefyJHWZEXAfLKFORRWw5TRZZgdB247 +1Furx81h4Xh0Vi1uTQb5DJdkLvjiTsTy60+dSMmDidQ/6ke8Mv3uL7dUVcqVMGpI +FgZYy0TcitHot3EiXZFqPN9aGc7m+XXFruPKZEgxAoGBAMA96jsow7CzulU+GRW8 +Njg4zWuAEVErgPoNBcOXAVWLCTU/qGIEMNpZL6Ok34kf13pJDMjQ8eDuQHu5CSqf +0ul5Zy85fwfVq2IvNAyYT8eflQprTejFw22CHhfPBfADVW9ro8dK/Jw+J/31Vh7V +ILKEQKmPPzKs7kp/7Nz+2cT3 +-----END PRIVATE KEY----- diff --git a/electron/res/keys/public.crt b/electron/res/keys/public.crt index 0735da0..7446031 100755 --- a/electron/res/keys/public.crt +++ b/electron/res/keys/public.crt @@ -1,17 +1,23 @@ ------BEGIN CERTIFICATE----- -MIICuDCCAaACCQC7PQmrxgWOlTANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJs -ZWNlcGluLTIwMjItMDUtMTkwIBcNMjIwNTE5MTI1NjA0WhgPMzAyMTA5MTkxMjU2 -MDRaMB0xGzAZBgNVBAMMEmxlY2VwaW4tMjAyMi0wNS0xOTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBALJgKp94WHf2GXK34KjNUOsYByOdBAXJGzpHwqk1 -ktclRWYyxWzHoVm/cRhWSQ9tQObMZeMXpSFm5YXrcvmE1Wv5p9ghCYd43tOvaHlC -313c4YR8n/3ItCJaCYb1/tkSfte+rkyinFobjrTVbOVXhaBw6FHCEp06pGlclnni -32n4Kzlu8DfIqddvJ7gcdfcpSMoG/wxC5oykUQkqcdrzsNiRK0zPC+a9H+Hlnaos -ATsLtDR1skr4fWBvAd9VD4GuCPBWxYcmzj/XEDUD2nzkn48LmaIuQgLciW8E+ysi -CPYjA++G2dZk8dsaJJkNk3ogkikhfDMff/BqvveZs77yG0cCAwEAATANBgkqhkiG -9w0BAQsFAAOCAQEADymHk+wLJAdv3p+4hHo57VLaBtwVYXc5oRUbUzgMYTTtPWIs -xuILEqXftMspt6PzdEt0V1WeCWNyypsAbur/CKpAOoVjBDPIo09TiYnYIn9xt5wQ -AmR5kVEZheuazcvzW3C9NAY1T6QDmxNvFCiCXRbtklOg2HqFDZX+pkj8CylQ9TDk -rroUg17b/FD1ds1uyPXzucEWfxqkOaujvsCnzrbFs9luB5VfM+QzLU+l9QRN9Tmj -z7CpGuP6vKvhXJLUjXkZ0q5JyL5wEAe6Ttbu+c/8HhPFKQsW6q/lQSDo0v0LGDrd -ikjWXhSrVjd8+qTTVgia/UNqv/wi+bkWnVdRzQ== ------END CERTIFICATE----- \ No newline at end of file +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUFAnC6268dp/z1DR9E1UepiWgWzkwDQYJKoZIhvcNAQEL +BQAwcDELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUNob25ncWluZzESMBAGA1UEBwwJ +Q2hvbmdxaW5nMQ4wDAYDVQQKDAVnb3dhczEWMBQGA1UECwwNSVQgRGVwYXJ0bWVu +dDERMA8GA1UEAwwIZ293YXMuY24wIBcNMjQwMjE4MDIwOTI2WhgPMjEyNDAxMjUw +MjA5MjZaMHAxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlDaG9uZ3FpbmcxEjAQBgNV +BAcMCUNob25ncWluZzEOMAwGA1UECgwFZ293YXMxFjAUBgNVBAsMDUlUIERlcGFy +dG1lbnQxETAPBgNVBAMMCGdvd2FzLmNuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA3A7dt7eoqAaBxv2Npjo8Z7VkGvXT93jZfpgAuuNuQ5RLcnOnMzQC +CrrjPcLfsAMA0AIK3eUWsXXKSR9SZTJBLQRZCJHZ9AIPfA+58JVQPTjd8UIuQZJf +rDf6FjhPJTsLzcjTU+mT7t6lEimPEl2VWN9eXWqs9nkVrJtqLao6m1hoYfXOxRh6 +96/WgBtPHcmjujryteBiSITVflDjx+YQzDGsbqw7fM52klMPd2+w/vmhJ4pxq6P7 +Ni2OBvdXYDPIuLfPFFqG16arORjBkyNCJy19iOuh5LXh+EUX11wvbLwNgsTd8j9v +eBSD+4HUUNQhiXiXJbs7I7cdFYthvb609QIDAQABo1MwUTAdBgNVHQ4EFgQUdI8p +aY1A47rWCRvQKSTRCCk6FoMwHwYDVR0jBBgwFoAUdI8paY1A47rWCRvQKSTRCCk6 +FoMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEArMCAfqidgXL7 +cW5TAZTCqnUeKzbbqMJgk6iFsma8scMRsUXz9ZhF0UVf98376KvoJpy4vd81afbi +TehQ8wVBuKTtkHeh/MkXMWC/FU4HqSjtvxpic2+Or5dMjIrfa5VYPgzfqNaBIUh4 +InD5lo8b/n5V+jdwX7RX9VYAKug6QZlCg5YSKIvgNRChb36JmrGcvsp5R0Vejnii +e3oowvgwikqm6XR6BEcRpPkztqcKST7jPFGHiXWsAqiibc+/plMW9qebhfMXEGhQ +5yVNeSxX2zqasZvP/fRy+3I5iVilxtKvJuVpPZ0UZzGS0CJ/lF67ntibktiPa3sR +D8HixYbEDg== +-----END CERTIFICATE----- diff --git a/electron/res/keys/public.pem b/electron/res/keys/public.pem index 0735da0..7446031 100755 --- a/electron/res/keys/public.pem +++ b/electron/res/keys/public.pem @@ -1,17 +1,23 @@ ------BEGIN CERTIFICATE----- -MIICuDCCAaACCQC7PQmrxgWOlTANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJs -ZWNlcGluLTIwMjItMDUtMTkwIBcNMjIwNTE5MTI1NjA0WhgPMzAyMTA5MTkxMjU2 -MDRaMB0xGzAZBgNVBAMMEmxlY2VwaW4tMjAyMi0wNS0xOTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBALJgKp94WHf2GXK34KjNUOsYByOdBAXJGzpHwqk1 -ktclRWYyxWzHoVm/cRhWSQ9tQObMZeMXpSFm5YXrcvmE1Wv5p9ghCYd43tOvaHlC -313c4YR8n/3ItCJaCYb1/tkSfte+rkyinFobjrTVbOVXhaBw6FHCEp06pGlclnni -32n4Kzlu8DfIqddvJ7gcdfcpSMoG/wxC5oykUQkqcdrzsNiRK0zPC+a9H+Hlnaos -ATsLtDR1skr4fWBvAd9VD4GuCPBWxYcmzj/XEDUD2nzkn48LmaIuQgLciW8E+ysi -CPYjA++G2dZk8dsaJJkNk3ogkikhfDMff/BqvveZs77yG0cCAwEAATANBgkqhkiG -9w0BAQsFAAOCAQEADymHk+wLJAdv3p+4hHo57VLaBtwVYXc5oRUbUzgMYTTtPWIs -xuILEqXftMspt6PzdEt0V1WeCWNyypsAbur/CKpAOoVjBDPIo09TiYnYIn9xt5wQ -AmR5kVEZheuazcvzW3C9NAY1T6QDmxNvFCiCXRbtklOg2HqFDZX+pkj8CylQ9TDk -rroUg17b/FD1ds1uyPXzucEWfxqkOaujvsCnzrbFs9luB5VfM+QzLU+l9QRN9Tmj -z7CpGuP6vKvhXJLUjXkZ0q5JyL5wEAe6Ttbu+c/8HhPFKQsW6q/lQSDo0v0LGDrd -ikjWXhSrVjd8+qTTVgia/UNqv/wi+bkWnVdRzQ== ------END CERTIFICATE----- \ No newline at end of file +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUFAnC6268dp/z1DR9E1UepiWgWzkwDQYJKoZIhvcNAQEL +BQAwcDELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUNob25ncWluZzESMBAGA1UEBwwJ +Q2hvbmdxaW5nMQ4wDAYDVQQKDAVnb3dhczEWMBQGA1UECwwNSVQgRGVwYXJ0bWVu +dDERMA8GA1UEAwwIZ293YXMuY24wIBcNMjQwMjE4MDIwOTI2WhgPMjEyNDAxMjUw +MjA5MjZaMHAxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlDaG9uZ3FpbmcxEjAQBgNV +BAcMCUNob25ncWluZzEOMAwGA1UECgwFZ293YXMxFjAUBgNVBAsMDUlUIERlcGFy +dG1lbnQxETAPBgNVBAMMCGdvd2FzLmNuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA3A7dt7eoqAaBxv2Npjo8Z7VkGvXT93jZfpgAuuNuQ5RLcnOnMzQC +CrrjPcLfsAMA0AIK3eUWsXXKSR9SZTJBLQRZCJHZ9AIPfA+58JVQPTjd8UIuQZJf +rDf6FjhPJTsLzcjTU+mT7t6lEimPEl2VWN9eXWqs9nkVrJtqLao6m1hoYfXOxRh6 +96/WgBtPHcmjujryteBiSITVflDjx+YQzDGsbqw7fM52klMPd2+w/vmhJ4pxq6P7 +Ni2OBvdXYDPIuLfPFFqG16arORjBkyNCJy19iOuh5LXh+EUX11wvbLwNgsTd8j9v +eBSD+4HUUNQhiXiXJbs7I7cdFYthvb609QIDAQABo1MwUTAdBgNVHQ4EFgQUdI8p +aY1A47rWCRvQKSTRCCk6FoMwHwYDVR0jBBgwFoAUdI8paY1A47rWCRvQKSTRCCk6 +FoMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEArMCAfqidgXL7 +cW5TAZTCqnUeKzbbqMJgk6iFsma8scMRsUXz9ZhF0UVf98376KvoJpy4vd81afbi +TehQ8wVBuKTtkHeh/MkXMWC/FU4HqSjtvxpic2+Or5dMjIrfa5VYPgzfqNaBIUh4 +InD5lo8b/n5V+jdwX7RX9VYAKug6QZlCg5YSKIvgNRChb36JmrGcvsp5R0Vejnii +e3oowvgwikqm6XR6BEcRpPkztqcKST7jPFGHiXWsAqiibc+/plMW9qebhfMXEGhQ +5yVNeSxX2zqasZvP/fRy+3I5iVilxtKvJuVpPZ0UZzGS0CJ/lF67ntibktiPa3sR +D8HixYbEDg== +-----END CERTIFICATE----- diff --git a/override/hoxy/lib/cycle.js b/override/hoxy/lib/cycle.js new file mode 100644 index 0000000..3a555c5 --- /dev/null +++ b/override/hoxy/lib/cycle.js @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2015 by Greg Reimer + * MIT License. See mit-license.txt for more info. + */ + +'use strict'; + +var _createClass = require('babel-runtime/helpers/create-class')['default']; + +var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default']; + +var _get = require('babel-runtime/helpers/get')['default']; + +var _inherits = require('babel-runtime/helpers/inherits')['default']; + +var _Promise = require('babel-runtime/core-js/promise')['default']; + +var _regeneratorRuntime = require('babel-runtime/regenerator')['default']; + +var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _request = require('./request'); + +var _request2 = _interopRequireDefault(_request); + +var _response = require('./response'); + +var _response2 = _interopRequireDefault(_response); + +var _streams = require('./streams'); + +var _streams2 = _interopRequireDefault(_streams); + +var _await = require('await'); + +var _await2 = _interopRequireDefault(_await); + +var _mkdirp = require('mkdirp'); + +var _mkdirp2 = _interopRequireDefault(_mkdirp); + +var _lodash = require('lodash'); + +var _lodash2 = _interopRequireDefault(_lodash); + +var _nodeStatic = require('node-static'); + +var _http = require('http'); + +var _http2 = _interopRequireDefault(_http); + +var _https = require('https'); + +var _https2 = _interopRequireDefault(_https); + +var _url = require('url'); + +var _url2 = _interopRequireDefault(_url); + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +var _util = require('util'); + +var _util2 = _interopRequireDefault(_util); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _zlib = require('zlib'); + +var _zlib2 = _interopRequireDefault(_zlib); + +var _events = require('events'); + +var _co = require('co'); + +var _co2 = _interopRequireDefault(_co); + +var _uglyAdapter = require('ugly-adapter'); + +var _uglyAdapter2 = _interopRequireDefault(_uglyAdapter); + +var _wait = require('./wait'); + +var _wait2 = _interopRequireDefault(_wait); + +var _task = require('./task'); + +var _task2 = _interopRequireDefault(_task); + +var _urlPath = require('./url-path'); +const url = require("url"); + +var _urlPath2 = _interopRequireDefault(_urlPath); + +var staticServer = (function () { + + var getStatic = (function () { + var statics = {}; + return function (docroot) { + var stat = statics[docroot]; + if (!stat) { + stat = statics[docroot] = new _nodeStatic.Server(docroot); + } + return stat; + }; + })(); + + // Start up the server and serve out of various docroots. + var server = _http2['default'].createServer(function (req, resp) { + var docroot = req.headers['x-hoxy-static-docroot']; + var pDocroot = new _urlPath2['default'](docroot); + var stat = getStatic(pDocroot.toSystemPath()); + stat.serve(req, resp); + }).listen(0, 'localhost'); + + return server; +})(); + +var httpsOverHttpAgent, httpsOverHttpsAgent, httpOverHttpsAgent; +var tunnelAgent = require('tunnel-agent'); +var ptGetTunnelAgent = function (requestIsSSL, externalProxyUrl) { + var urlObject = url.parse(externalProxyUrl); + var protocol = urlObject.protocol || 'http:'; + var port = urlObject.port; + if (!port) { + port = protocol === 'http:' ? 80 : 443; + } + var hostname = urlObject.hostname || 'localhost'; + + if (requestIsSSL) { + if (protocol === 'http:') { + if (!httpsOverHttpAgent) { + httpsOverHttpAgent = tunnelAgent.httpsOverHttp({ + proxy: { + host: hostname, + port: port + } + }); + } + return httpsOverHttpAgent; + } else { + if (!httpsOverHttpsAgent) { + httpsOverHttpsAgent = tunnelAgent.httpsOverHttps({ + proxy: { + host: hostname, + port: port + } + }); + } + return httpsOverHttpsAgent; + } + } else { + if (protocol === 'http:') { + // if (!httpOverHttpAgent) { + // httpOverHttpAgent = tunnelAgent.httpOverHttp({ + // proxy: { + // host: hostname, + // port: port + // } + // }); + // } + return false; + } else { + if (!httpOverHttpsAgent) { + httpOverHttpsAgent = tunnelAgent.httpOverHttps({ + proxy: { + host: hostname, + port: port + } + }); + } + return httpOverHttpsAgent; + } + } +} + +var ProvisionableRequest = (function () { + function ProvisionableRequest(opts) { + _classCallCheck(this, ProvisionableRequest); + + this._respProm = (0, _task2['default'])(); + var h = /https/i.test(opts.protocol) ? _https2['default'] : _http2['default']; + if (opts.proxy) { + // var proxyInfo = _url2['default'].parse(opts.proxy), + // proxyPort = proxyInfo.port, + // proxyHostname = proxyInfo.hostname, + // proxyPath = 'http://' + opts.hostname + (opts.port ? ':' + opts.port : '') + opts.path; + // opts.hostname = proxyHostname; + // opts.port = proxyPort; + // opts.path = proxyPath; + opts.agent = ptGetTunnelAgent(/https/i.test(opts.protocol), opts.proxy); + // console.log('opts.agent', opts.proxy) + opts.proxy = '' + } + + this._writable = h.request(opts, this._respProm.resolve); + this._writable.on('error', this._respProm.reject); + } + + /* + * This check() function made me scratch my head when I came back to + * it months later. It simply does too many things. It still isn't perfect, + * but hopefully now this beast is slightly easier to follow. It returns + * a promise on a boolean indicating whether or not the passed file was + * created. IF the strategy is NOT 'mirror' it resolves false since 'mirror' + * is the only strategy that creates files. Otherwise if the file exists + * it resolves false. Otherwise it has a side effect of creating the file + * by requesting out to the remote server and writing the result to the + * file, then resolves true. + */ + + _createClass(ProvisionableRequest, [{ + key: 'send', + value: function send(readable) { + var _this = this; + + return new _Promise(function (resolve, reject) { + if (!readable || typeof readable === 'string') { + _this._writable.end(readable || '', resolve); + } else { + readable.on('error', reject); + readable.on('end', resolve); + readable.pipe(_this._writable); + } + }); + } + }, { + key: 'receive', + value: function receive() { + return this._respProm; + } + }]); + + return ProvisionableRequest; +})(); + +function check(strategy, file, req, upstreamProxy) { + var parsed = _url2['default'].parse(file); + file = parsed.pathname; // stripped of query string. + + return (0, _co2['default'])(_regeneratorRuntime.mark(function callee$1$0() { + var provReq, mirrResp, writeToFile, gunzip; + return _regeneratorRuntime.wrap(function callee$1$0$(context$2$0) { + while (1) switch (context$2$0.prev = context$2$0.next) { + case 0: + if (!(strategy !== 'mirror')) { + context$2$0.next = 2; + break; + } + + return context$2$0.abrupt('return', false); + + case 2: + context$2$0.prev = 2; + context$2$0.next = 5; + return (0, _uglyAdapter2['default'])(_fs2['default'].stat, file); + + case 5: + return context$2$0.abrupt('return', false); + + case 8: + context$2$0.prev = 8; + context$2$0.t0 = context$2$0['catch'](2); + + case 10: + context$2$0.next = 12; + return (0, _uglyAdapter2['default'])(_mkdirp2['default'], _path2['default'].dirname(file)); + + case 12: + provReq = new ProvisionableRequest({ + protocol: req.protocol, + proxy: upstreamProxy, + method: 'GET', + hostname: req.hostname, + port: req.port, + path: req.url + }); + + provReq.send(); + context$2$0.next = 16; + return provReq.receive(); + + case 16: + mirrResp = context$2$0.sent; + + if (!(mirrResp.statusCode !== 200)) { + context$2$0.next = 19; + break; + } + + throw new Error('mirroring failed: ' + req.fullUrl() + ' => ' + mirrResp.statusCode); + + case 19: + writeToFile = _fs2['default'].createWriteStream(file); + + if (mirrResp.headers['content-encoding'] === 'gzip') { + gunzip = _zlib2['default'].createGunzip(); + + mirrResp = mirrResp.pipe(gunzip); + } + context$2$0.next = 23; + return new _Promise(function (resolve, reject) { + mirrResp.pipe(writeToFile); + mirrResp.on('end', resolve); + writeToFile.on('error', reject); + }); + + case 23: + case 'end': + return context$2$0.stop(); + } + }, callee$1$0, this, [[2, 8]]); + })); +} + +// --------------------------- + +var Cycle = (function (_EventEmitter) { + _inherits(Cycle, _EventEmitter); + + function Cycle(proxy) { + var _this2 = this; + + _classCallCheck(this, Cycle); + + _get(Object.getPrototypeOf(Cycle.prototype), 'constructor', this).call(this); + this._proxy = proxy; + this._request = new _request2['default'](); + this._response = new _response2['default'](); + this._request.on('log', function (log) { + return _this2.emit('log', log); + }); + this._response.on('log', function (log) { + return _this2.emit('log', log); + }); + } + + _createClass(Cycle, [{ + key: 'data', + value: function data(name, val) { + if (!this._userData) { + this._userData = {}; + } + if (arguments.length === 2) { + this._userData[name] = val; + } + return this._userData[name]; + } + }, { + key: 'serve', + value: function serve(opts) { + + return _co2['default'].call(this, _regeneratorRuntime.mark(function callee$2$0() { + var req, resp, _opts, docroot, path, strategy, headers, pDocroot, pPath, pFullPath, fullSysPath, + created, staticResp, code, useResponse, isError, message; + + return _regeneratorRuntime.wrap(function callee$2$0$(context$3$0) { + while (1) switch (context$3$0.prev = context$3$0.next) { + case 0: + req = this._request; + resp = this._response; + + if (typeof opts === 'string') { + opts = {path: opts}; + } + opts = _lodash2['default'].extend({ + docroot: _path2['default'].sep, + path: _url2['default'].parse(req.url).pathname, + strategy: 'replace' + }, opts); + _opts = opts; + docroot = _opts.docroot; + path = _opts.path; + strategy = _opts.strategy; + headers = _lodash2['default'].extend({ + 'x-hoxy-static-docroot': docroot + }, req.headers); + + delete headers['if-none-match']; + delete headers['if-modified-since']; + + // Now call the static file service. + pDocroot = new _urlPath2['default'](docroot), pPath = new _urlPath2['default'](path), pFullPath = pPath.rootTo(pDocroot), fullSysPath = pFullPath.toSystemPath(); + context$3$0.next = 14; + return check(strategy, fullSysPath, req, this._proxy._upstreamProxy); + + case 14: + created = context$3$0.sent; + + if (created) { + this.emit('log', { + level: 'info', + message: 'copied ' + req.fullUrl() + ' to ' + fullSysPath + }); + } + context$3$0.next = 18; + return new _Promise(function (resolve, reject) { + var addr = staticServer.address(); + _http2['default'].get({ + hostname: addr.address, + port: addr.port, + headers: headers, + path: pPath.toUrlPath() + }, resolve).on('error', reject); + }); + + case 18: + staticResp = context$3$0.sent; + code = staticResp.statusCode, useResponse = undefined, isError = undefined; + + if (/^2\d\d$/.test(code)) { + useResponse = true; + } else if (/^4\d\d$/.test(code)) { + if (strategy === 'replace') { + useResponse = true; + } else if (strategy === 'mirror') { + isError = true; + } + } else { + isError = true; // nope + } + + if (!isError) { + context$3$0.next = 26; + break; + } + + message = _util2['default'].format('Failed to serve static file: %s => %s. Static server returned %d. Strategy: %s', req.fullUrl(), fullSysPath, staticResp.statusCode, strategy); + throw new Error(message); + + case 26: + if (useResponse) { + resp._setHttpSource(staticResp); + } + + case 27: + case 'end': + return context$3$0.stop(); + } + }, callee$2$0, this); + })); + } + }, { + key: '_setPhase', + value: function _setPhase(phase) { + this._phase = this._request.phase = this._response.phase = phase; + } + + /* + * This returns a promise on a partially fulfilled request + * (an instance of class ProvisionableRequest). At the time + * the promise is fulfilled, the request is in a state where + * it's been fully piped out, but nothing received. It's up + * to the caller of this function to call receive() on it, thus + * getting a promise on the serverResponse object. That enables + * hoxy to implement the 'request-sent' phase. + */ + }, { + key: '_sendToServer', + value: function _sendToServer() { + var req = this._request._finalize(), + resp = this._response, + upstreamProxy = this._proxy._upstreamProxy, + source = req._source, + pSlow = this._proxy._slow || {}, + rSlow = req.slow() || {}, + latency = rSlow.latency || 0; + if (resp._populated) { + return _Promise.resolve(undefined); + } + return _co2['default'].call(this, _regeneratorRuntime.mark(function callee$2$0() { + var provisionableReq, brake, groupedBrake; + return _regeneratorRuntime.wrap(function callee$2$0$(context$3$0) { + while (1) switch (context$3$0.prev = context$3$0.next) { + case 0: + provisionableReq = new ProvisionableRequest({ + protocol: req.protocol, + proxy: upstreamProxy, + hostname: req.hostname, + port: req.port || req._getDefaultPort(), + method: req.method, + path: req.url, + headers: req.headers + }); + + if (!(latency > 0)) { + context$3$0.next = 4; + break; + } + + context$3$0.next = 4; + return (0, _wait2['default'])(latency); + + case 4: + if (rSlow.rate > 0) { + brake = _streams2['default'].brake(rSlow.rate); + + source = source.pipe(brake); + } + if (pSlow.rate) { + groupedBrake = pSlow.rate.throttle(); + + source = source.pipe(groupedBrake); + } + if (pSlow.up) { + groupedBrake = pSlow.up.throttle(); + + source = source.pipe(groupedBrake); + } + req._tees().forEach(function (writable) { + return source.pipe(writable); + }); + context$3$0.next = 10; + return provisionableReq.send(source); + + case 10: + return context$3$0.abrupt('return', provisionableReq); + + case 11: + case 'end': + return context$3$0.stop(); + } + }, callee$2$0, this); + })); + } + }, { + key: '_sendToClient', + value: function _sendToClient(outResp) { + var resp = this._response._finalize(), + source = resp._source, + rSlow = resp.slow() || {}, + pSlow = this._proxy._slow || {}, + rLatency = rSlow.latency || 0, + pLatency = pSlow.latency || 0, + latency = Math.max(pLatency, rLatency); + return _co2['default'].call(this, _regeneratorRuntime.mark(function callee$2$0() { + var brake, groupedBrake, tees; + return _regeneratorRuntime.wrap(function callee$2$0$(context$3$0) { + while (1) switch (context$3$0.prev = context$3$0.next) { + case 0: + if (!(latency > 0)) { + context$3$0.next = 3; + break; + } + + context$3$0.next = 3; + return (0, _wait2['default'])(latency); + + case 3: + outResp.writeHead(resp.statusCode, resp.headers); + if (rSlow.rate > 0) { + brake = _streams2['default'].brake(rSlow.rate); + + source = source.pipe(brake); + } + if (pSlow.rate) { + groupedBrake = pSlow.rate.throttle(); + + source = source.pipe(groupedBrake); + } + if (pSlow.down) { + groupedBrake = pSlow.down.throttle(); + + source = source.pipe(groupedBrake); + } + tees = resp._tees(); + + tees.forEach(function (writable) { + return source.pipe(writable); + }); + context$3$0.next = 11; + return new _Promise(function (resolve, reject) { + source.on('error', reject); + source.on('end', resolve); + source.pipe(outResp); + }); + + case 11: + case 'end': + return context$3$0.stop(); + } + }, callee$2$0, this); + })); + } + }, { + key: '_start', + value: function _start() { + // for now, an immediately-kept promise + return (0, _await2['default'])('started').keep('started'); + } + }]); + + return Cycle; +})(_events.EventEmitter); + +exports['default'] = Cycle; +module.exports = exports['default']; + +// file does not exist, so continue + +// TODO: test coverage for mkdirp + +// TODO: test coverage + +// First, get all our ducks in a row WRT to +// options, setting variables, etc. +// return the outer promise +// wait for it all to pipe out \ No newline at end of file diff --git a/package.json b/package.json index b0b3a8f..b795c26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "res-downloader", - "version": "1.0.4", + "version": "1.0.5", "main": "dist-electron/main/index.js", "description": "Electron + Vue + Vite 实现的资源下载软件,支持微信视频号下载、抖音视频下载、快手视频下载、酷狗音乐下载等", "author": "putyy@qq.com", @@ -56,6 +56,7 @@ "axios": "^1.5.0", "electron-store": "^8.1.0", "getmac": "^5.20.0", - "hoxy": "^3.3.1" + "hoxy": "^3.3.1", + "tunnel-agent": "^0.6.0" } } diff --git a/public/show.jpg b/public/show.jpg deleted file mode 100644 index 1ba74e2..0000000 Binary files a/public/show.jpg and /dev/null differ diff --git a/public/show.webp b/public/show.webp new file mode 100644 index 0000000..7749793 Binary files /dev/null and b/public/show.webp differ diff --git a/src/views/About.vue b/src/views/About.vue index 0bd7eb5..a7e230e 100644 --- a/src/views/About.vue +++ b/src/views/About.vue @@ -35,6 +35,10 @@ const str = "使用方法\n" + " 手动检测系统代理是否设置正确 本软件代理地址: 127.0.0.1:8899\n" + " 2. 关闭软件后无法正常上网\n" + " 手动关闭系统代理设置\n" + + " 3. 视频号抓取流程\n" + + " 将需要下载的视频发给好友或者文件助手 再打开该视频即可拦截到,通常软件界面对应视频会出现标题描述、对应操作会出现解密下载按钮\n" + + " 大视频可以复制链接通过其他工具加速下载,然后再通过对应的视频操作项进行\"视频解密\"\n" + + "实现原理\n" + " 通过代理网络抓包拦截响应,筛选出有用的资源,\n" + " 同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的,\n" + diff --git a/src/views/Index.vue b/src/views/Index.vue index d1aa179..6306c82 100755 --- a/src/views/Index.vue +++ b/src/views/Index.vue @@ -1,7 +1,6 @@