Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f07bae796 | ||
|
|
6086bd7086 | ||
|
|
62bf0e2308 | ||
|
|
0bb1a21a76 | ||
|
|
575e2d8904 | ||
|
|
db21134550 | ||
|
|
3407490f82 | ||
|
|
dad06f6cd6 | ||
|
|
de70fc66b4 | ||
|
|
2282f72b2f | ||
|
|
e00a7c9044 | ||
|
|
e79a7ba2fe | ||
|
|
791e50411d | ||
|
|
df8eb0e4cd | ||
|
|
3e291171c2 | ||
|
|
bd8f8e80c9 | ||
|
|
8ed7e144e1 | ||
|
|
69f8224453 | ||
|
|
3c0e51a9e2 | ||
|
|
0a6679b983 | ||
|
|
6d1024806c | ||
|
|
388e3f46a4 | ||
|
|
f7c8e9f7db | ||
|
|
7ca484f45d | ||
|
|
cecb13fa90 | ||
|
|
331478d370 | ||
|
|
2481407093 | ||
|
|
e024a812d0 | ||
|
|
f5b5767997 | ||
|
|
5e7022ec81 | ||
|
|
e54177e8bf | ||
|
|
69cc5383d1 | ||
|
|
11f88e86e3 | ||
|
|
9e87e64223 | ||
|
|
9ffef9db8e | ||
|
|
6d2705112d |
42
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: "Bug Report \\ 问题反馈"
|
||||
description: "Create a report to help us improve \\ 帮助改进"
|
||||
labels: ["Bug"]
|
||||
|
||||
body:
|
||||
- type: input
|
||||
id: title
|
||||
attributes:
|
||||
label: "Title \\ 标题"
|
||||
description: "A brief summary of the bug. \\ 对于该错误的简要总结。"
|
||||
placeholder: "Enter the bug title here. \\ 在此输入错误标题。"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: "To Reproduce \\ 操作流程"
|
||||
description: "Steps to reproduce the behaviour. \\ 重现该行为的步骤。"
|
||||
placeholder: |
|
||||
1. Go to '...' \ 进入 '...'
|
||||
2. Click on '....' \ 点击 '....'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected-behaviour
|
||||
attributes:
|
||||
label: "Expected Behaviour \\ 预期结果"
|
||||
description: "A clear and concise description of what you expected to happen. \\ 对您期望发生的事情的清晰简明描述。"
|
||||
placeholder: "A clear and concise description of what you expected to happen. \\ 对您期望发生的事情的清晰简明描述。"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: software-version
|
||||
attributes:
|
||||
label: "Software Version \\ 软件版本"
|
||||
description: "Please specify the version of the software you are using. \\ 请指定您使用的软件版本。"
|
||||
placeholder: "Enter the software version here. \\ 在此输入软件版本。"
|
||||
validations:
|
||||
required: true
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: true
|
||||
31
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: "Feature Request \\ 功能建议"
|
||||
description: "Suggest an idea for this project \\ 为这个项目提出一个新想法"
|
||||
labels: ["Enhancement"]
|
||||
|
||||
body:
|
||||
- type: input
|
||||
id: title
|
||||
attributes:
|
||||
label: "Title \\ 标题"
|
||||
description: "A brief summary of your feature request. \\ 对您功能建议的简要总结。"
|
||||
placeholder: "Enter the feature title here. \\ 在此输入功能标题。"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: feature-suggestion
|
||||
attributes:
|
||||
label: "Feature Suggestion \\ 功能建议"
|
||||
description: "A clear and concise description of the feature you would like to suggest. \\ 对您想要建议的功能的清晰简明描述。"
|
||||
placeholder: "Describe your feature suggestion here. \\ 在此描述您的功能建议。"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposed-solution
|
||||
attributes:
|
||||
label: "Proposed Solution \\ 你的方案"
|
||||
description: "A clear and concise description of your proposed solution. \\ 对您提议的解决方案的清晰简明描述。"
|
||||
placeholder: "Describe your proposed solution here. \\ 在此描述您的提议方案。"
|
||||
validations:
|
||||
required: true
|
||||
1
.gitignore
vendored
@@ -3,3 +3,4 @@ test
|
||||
build/bin
|
||||
node_modules
|
||||
frontend/dist
|
||||
.DS_Store
|
||||
102
README-EN.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# res-downloader
|
||||
### 🎉 Aixiang Resource Downloader
|
||||
|
||||
> A cross-platform resource downloader built with Go + [Wails](https://github.com/wailsapp/wails).
|
||||
Clean UI, easy to use, and supports a wide range of resource sniffing and downloading.
|
||||
|
||||
### 📖 [中文](./README.md) | English
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🚀 **User-Friendly**: Simple operation with an intuitive and beautiful UI
|
||||
- 🖥️ **Cross-Platform**: Available on Windows / macOS / Linux
|
||||
- 🌐 **Supports Multiple Resource Types**: Video / Audio / Images / m3u8 / Live streams, and more
|
||||
- 📱 **Wide Platform Compatibility**: Works with WeChat Channels, Mini Programs, Douyin, Kuaishou, Xiaohongshu, KuGou Music, QQ Music, and more
|
||||
- 🌍 **Proxy Capture**: Built-in proxy allows fetching resources behind network restrictions
|
||||
|
||||
---
|
||||
|
||||
## 📚 Docs & Versions
|
||||
|
||||
- 📘 [Online Documentation (Chinese)](https://res.putyy.com/)
|
||||
- 🧩 [Mini Version Ui Display using default browser](https://github.com/putyy/res-downloader) | [Old Electron Version Support Win7](https://github.com/putyy/res-downloader/tree/old)
|
||||
- 💬 [Join the User Group (Chinese)](https://www.putyy.com/app/admin/upload/img/20250418/6801d9554dc7.webp)
|
||||
> *If full, you can add WeChat `AmorousWorld` with a note “From GitHub”*
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Download Links
|
||||
|
||||
- 🆕 [Download from GitHub](https://github.com/putyy/res-downloader/releases)
|
||||
- 🆕 [Download via Lanzou Cloud (Password: 9vs5)](https://wwjv.lanzoum.com/b04wgtfyb)
|
||||
- ⚠️ *Windows 7 users: Please use version `2.3.0`*
|
||||
|
||||
---
|
||||
|
||||
## 🚀 How to Use
|
||||
|
||||
> Follow these steps to use the software correctly:
|
||||
|
||||
1. During installation, be sure to **allow certificate installation** and **grant network access**
|
||||
2. Launch the software → Click **"Start Proxy"** at the top left
|
||||
3. Choose the resource types to capture (default is all)
|
||||
4. Open the target content externally (WeChat, Mini App, Browser, etc.)
|
||||
5. Return to the homepage to view the captured resource list
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Screenshot
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ❓ FAQ
|
||||
|
||||
### 📺 m3u8 Video Resources
|
||||
|
||||
- Online Preview: [m3u8play](https://m3u8play.com/)
|
||||
- Download Tool: [m3u8-down](https://m3u8-down.gowas.cn/)
|
||||
|
||||
### 📡 Live Stream Resources
|
||||
|
||||
- We recommend [OBS](https://obsproject.com/) for recording (search for setup tutorials)
|
||||
|
||||
### 🐢 Slow Downloads or Large File Failures?
|
||||
|
||||
- Recommended download managers:
|
||||
- [Neat Download Manager](https://www.neatdownloadmanager.com/index.php/en/)
|
||||
- [Motrix](https://motrix.app/download)
|
||||
- For WeChat videos, click `Decrypt Video` after download
|
||||
|
||||
### 🧩 Unable to Intercept Resources?
|
||||
|
||||
- Check your system proxy settings:
|
||||
Address: 127.0.0.1
|
||||
Port: 8899
|
||||
|
||||
### 🌐 Can't Access Internet After Closing the App?
|
||||
|
||||
- Manually disable the system proxy settings
|
||||
|
||||
### 🧠 More Questions?
|
||||
|
||||
- [GitHub Issues](https://github.com/putyy/res-downloader/issues)
|
||||
- [Aixiang Forum Thread (Chinese)](https://s.gowas.cn/d/4089)
|
||||
|
||||
---
|
||||
|
||||
## 💡 Principles & Motivation
|
||||
|
||||
This tool captures traffic via a local proxy and filters useful resources.
|
||||
Its working principle is similar to tools like Fiddler, Charles, or browser DevTools, but with a more user-friendly display and enhanced filtering, making it suitable for everyday users with minimal tech background.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Disclaimer
|
||||
|
||||
> This software is for educational and research purposes only.
|
||||
Commercial or illegal use is strictly prohibited.
|
||||
The author is not responsible for any consequences arising from misuse.
|
||||
124
README.md
@@ -1,49 +1,99 @@
|
||||
## res-downloader V3全新版来袭,更快了!
|
||||
### 爱享素材下载器【[加入群聊](https://qm.qq.com/q/HS8FdhpZCK)】
|
||||
🎯 基于Go + [wails](https://github.com/wailsapp/wails)
|
||||
📦 操作简单、可获取不同类型资源
|
||||
# res-downloader
|
||||
### 🎉 爱享素材下载器
|
||||
|
||||
🖥️ 支持Windows、Mac、Linux
|
||||
🌐 支持视频、音频、图片、m3u8、直播流等常见网络资源
|
||||
💪 支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源下载
|
||||
👼 支持设置代理以获取特殊网络下的资源
|
||||
> 一款基于 Go + [Wails](https://github.com/wailsapp/wails) 的跨平台资源下载工具,简洁易用,支持多种资源嗅探与下载。
|
||||
|
||||
## 软件下载
|
||||
🆕 [github下载](https://github.com/putyy/res-downloader/releases)
|
||||
🆕 [蓝奏云下载 密码:9vs5](https://wwjv.lanzoum.com/b04wgtfyb)
|
||||
### 📖 中文 | [English](./README-EN.md)
|
||||
|
||||
## 使用方法
|
||||
> 0. 安装时一定要同意安装证书文件、一定要允许网络访问
|
||||
> 1. 打开本软件 软件首页左上角点击 “启动代理”
|
||||
> 2. 软件首页选择要获取的资源类型(默认选中的全部)
|
||||
> 3. 打开要捕获的源, 如:视频号、网页、小程序等等
|
||||
> 4. 返回软件首页即可看到资源列表
|
||||
---
|
||||
|
||||
## 软件截图
|
||||

|
||||
## ✨ 功能特色
|
||||
|
||||
## 常见问题
|
||||
m3u8: 预览和下载:
|
||||
> [下载](https://m3u8-down.gowas.cn/) [预览](https://m3u8play.com/)
|
||||
- 🚀 **简单易用**:操作简单,界面清晰美观
|
||||
- 🖥️ **多平台支持**:Windows / macOS / Linux
|
||||
- 🌐 **多资源类型支持**:视频 / 音频 / 图片 / m3u8 / 直播流等
|
||||
- 📱 **平台兼容广泛**:支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、QQ音乐等
|
||||
- 🌍 **代理抓包**:支持设置代理获取受限网络下的资源
|
||||
|
||||
直播流: 预览和录制:
|
||||
> [使用obs进行预览和录制 使用教程自行百度]( https://obsproject.com/)
|
||||
---
|
||||
|
||||
下载慢、大视频下载失败
|
||||
> 推荐使用如下工具加速下载,视频号可以下载完成后再到对应视频操作项选择 “视频解密(视频号)” 按钮
|
||||
>> [Neat Download Manager](https://www.neatdownloadmanager.com/index.php/en/)、[Motrix](https://motrix.app/download)等软件进行下载
|
||||
## 📚 文档 & 版本
|
||||
|
||||
打开本软件,无法正常拦截获取
|
||||
> 检查系统代理是否正确设置 代理地址:127.0.0.1 端口:8899
|
||||
- 📘 [在线文档](https://res.putyy.com/)
|
||||
- 🧩 [Mini版 UI使用默认浏览器展示](https://github.com/putyy/resd-mini) | [Electron旧版 支持Win7](https://github.com/putyy/res-downloader/tree/old)
|
||||
- 💬 [加入交流群](https://www.putyy.com/app/admin/upload/img/20250418/6801d9554dc7.webp)
|
||||
> *群满时可加微信 `AmorousWorld`,请备注“来源”*
|
||||
|
||||
关闭软件后无法正常上网
|
||||
> 手动关闭系统代理设置
|
||||
---
|
||||
|
||||
其他问题
|
||||
[github](https://github.com/putyy/res-downloader/issues) 、 [爱享论坛](https://s.gowas.cn/d/4089)
|
||||
## 🧩 下载地址
|
||||
|
||||
## 实现 & 初衷
|
||||
通过代理网络抓包拦截响应,筛选出有用的资源, 同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的,只不过这些软件需要手动进行筛选,对于小白用户上手还是有点难度,本软件对部分资源做了特殊处理,更适合大众用户,所以就有了本项目。
|
||||
- 🆕 [GitHub 下载](https://github.com/putyy/res-downloader/releases)
|
||||
- 🆕 [蓝奏云下载(密码:9vs5)](https://wwjv.lanzoum.com/b04wgtfyb)
|
||||
- ⚠️ *Win7 用户请下载 `2.3.0` 版本*
|
||||
|
||||
## 免责声明
|
||||
本软件用于学习研究使用,若因使用本软件造成的一切法律责任均与本人无关!
|
||||
---
|
||||
|
||||
## 🚀 使用方法
|
||||
|
||||
> 请按以下步骤操作以正确使用软件:
|
||||
|
||||
1. 安装时务必 **允许安装证书文件** 并 **允许网络访问**
|
||||
2. 打开软件 → 首页左上角点击 **“启动代理”**
|
||||
3. 选择要获取的资源类型(默认全部)
|
||||
4. 在外部打开资源页面(如视频号、小程序、网页等)
|
||||
5. 返回软件首页,即可看到资源列表
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ 软件截图
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### 📺 m3u8 视频资源
|
||||
|
||||
- 在线预览:[m3u8play](https://m3u8play.com/)
|
||||
- 视频下载:[m3u8-down](https://m3u8-down.gowas.cn/)
|
||||
|
||||
### 📡 直播流资源
|
||||
|
||||
- 推荐使用 [OBS](https://obsproject.com/) 进行录制(教程请百度)
|
||||
|
||||
### 🐢 下载慢、大文件失败?
|
||||
|
||||
- 推荐工具:
|
||||
- [Neat Download Manager](https://www.neatdownloadmanager.com/index.php/en/)
|
||||
- [Motrix](https://motrix.app/download)
|
||||
- 视频号资源下载后可在操作项点击 `视频解密(视频号)`
|
||||
|
||||
### 🧩 软件无法拦截资源?
|
||||
|
||||
- 检查是否正确设置系统代理:
|
||||
地址:127.0.0.1
|
||||
端口:8899
|
||||
|
||||
### 🌐 关闭软件后无法上网?
|
||||
|
||||
- 手动关闭系统代理设置
|
||||
|
||||
### 🧠 更多问题
|
||||
|
||||
- [GitHub Issues](https://github.com/putyy/res-downloader/issues)
|
||||
- [爱享论坛讨论帖](https://s.gowas.cn/d/4089)
|
||||
|
||||
---
|
||||
|
||||
## 💡 实现原理 & 初衷
|
||||
|
||||
本工具通过代理方式实现网络抓包,并筛选可用资源。与 Fiddler、Charles、浏览器 DevTools 原理类似,但对资源进行了更友好的筛选、展示和处理,大幅度降低了使用门槛,更适合大众用户使用。
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 免责声明
|
||||
|
||||
> 本软件仅供学习与研究用途,禁止用于任何商业或违法用途。
|
||||
如因此产生的任何法律责任,概与作者无关!
|
||||
|
||||
BIN
build/.DS_Store
vendored
111
build/README.md
@@ -1,35 +1,86 @@
|
||||
# Build Directory
|
||||
|
||||
The build directory is used to house all the build files and assets for your application.
|
||||
|
||||
The structure is:
|
||||
|
||||
* bin - Output directory
|
||||
* darwin - macOS specific files
|
||||
* windows - Windows specific files
|
||||
|
||||
## Mac
|
||||
|
||||
The `darwin` directory holds files specific to Mac builds.
|
||||
These may be customised and used as part of the build. To return these files to the default state, simply delete them
|
||||
and
|
||||
build with `wails build`.
|
||||
|
||||
The directory contains the following files:
|
||||
|
||||
- `Info.plist` - the main plist file used for Mac builds. It is used when building using `wails build`.
|
||||
- `Info.dev.plist` - same as the main plist file but used when building using `wails dev`.
|
||||
```bash
|
||||
wails build -platform "darwin/universal"
|
||||
create-dmg 'build/bin/res-downloader.app' --overwrite ./build/bin
|
||||
mv -f "build/bin/res-downloader $(jq -r '.info.productVersion' wails.json).dmg" "build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_mac.dmg"
|
||||
```
|
||||
|
||||
## Windows
|
||||
```bash
|
||||
wails build -f -nsis -platform "windows/amd64" -webview2 Embed -skipbindings && mv -f "build/bin/res-downloader-amd64-installer.exe" "build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_win_amd64.exe"
|
||||
wails build -f -nsis -platform "windows/arm64" -webview2 Embed -skipbindings && mv -f "build/bin/res-downloader-arm64-installer.exe" "build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_win_arm64.exe"
|
||||
```
|
||||
|
||||
The `windows` directory contains the manifest and rc files used when building with `wails build`.
|
||||
These may be customised for your application. To return these files to the default state, simply delete them and
|
||||
build with `wails build`.
|
||||
## Linux
|
||||
|
||||
- `icon.ico` - The icon used for the application. This is used when building using `wails build`. If you wish to
|
||||
use a different icon, simply replace this file with your own. If it is missing, a new `icon.ico` file
|
||||
will be created using the `appicon.png` file in the build directory.
|
||||
- `installer/*` - The files used to create the Windows installer. These are used when building using `wails build`.
|
||||
- `info.json` - Application details used for Windows builds. The data here will be used by the Windows installer,
|
||||
as well as the application itself (right click the exe -> properties -> details)
|
||||
- `wails.exe.manifest` - The main application manifest file.
|
||||
### docker方式
|
||||
> x86_64
|
||||
```bash
|
||||
docker build --network host -f build/linux/dockerfile -t res-downloader-amd-linux .
|
||||
docker run -it --name res-downloader-amd-build --network host --privileged -v ./:/www/res-downloader res-downloader-amd-linux /bin/bash
|
||||
# 容器内
|
||||
cd /www/res-downloader
|
||||
wails build -platform "linux/amd64" -s -skipbindings
|
||||
|
||||
# 打包debian
|
||||
cp build/bin/res-downloader build/linux/Debian/usr/local/bin/
|
||||
echo "$(cat build/linux/Debian/DEBIAN/.control | sed -e "s/{{Version}}/$(jq -r '.info.productVersion' wails.json)/g")" > build/linux/Debian/DEBIAN/control
|
||||
dpkg-deb --build ./build/linux/Debian build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_linux_amd64.deb
|
||||
|
||||
# 打包AppImage
|
||||
cp build/bin/res-downloader build/linux/AppImage/usr/bin/
|
||||
|
||||
# 复制WebKit相关文件
|
||||
pushd build/linux/AppImage
|
||||
find /usr/lib* -name WebKitNetworkProcess -exec mkdir -p $(dirname '{}') \; -exec cp --parents '{}' "." \; || true
|
||||
find /usr/lib* -name WebKitWebProcess -exec mkdir -p $(dirname '{}') \; -exec cp --parents '{}' "." \; || true
|
||||
find /usr/lib* -name libwebkit2gtkinjectedbundle.so -exec mkdir -p $(dirname '{}') \; -exec cp --parents '{}' "." \; || true
|
||||
popd
|
||||
|
||||
wget -O ./build/bin/appimagetool-x86_64.AppImage https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage
|
||||
chmod +x ./build/bin/appimagetool-x86_64.AppImage
|
||||
./build/bin/appimagetool-x86_64.AppImage build/linux/AppImage build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_linux_amd64.AppImage
|
||||
|
||||
mv -f build/bin/res-downloader build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_linux_amd64
|
||||
```
|
||||
|
||||
> arm64
|
||||
```bash
|
||||
# arm
|
||||
docker build --platform linux/arm64 --network host -f build/linux/dockerfile -t res-downloader-arm-linux .
|
||||
docker run --platform linux/arm64 -it --name res-downloader-arm-build --network host --privileged -v ./:/www/res-downloader res-downloader-arm-linux /bin/bash
|
||||
# 容器内
|
||||
cd /www/res-downloader
|
||||
wails build -platform "linux/arm64" -s -skipbindings
|
||||
|
||||
# 打包debian
|
||||
cp build/bin/res-downloader build/linux/Debian/usr/local/bin/
|
||||
echo "$(cat build/linux/Debian/DEBIAN/.control | sed -e "s/{{Version}}/$(jq -r '.info.productVersion' wails.json)/g")" > build/linux/Debian/DEBIAN/control
|
||||
dpkg-deb --build ./build/linux/Debian build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_linux_arm64.deb
|
||||
|
||||
mv -f build/bin/res-downloader build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_linux_arm64
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
[](https://repology.org/project/res-downloader/versions)
|
||||
|
||||
```bash
|
||||
yay -Syu res-downloader
|
||||
```
|
||||
### Linux 本地编译
|
||||
|
||||
```bash
|
||||
git clone https://github.com/putyy/res-downloader.git
|
||||
cd res-downloader
|
||||
# -- GO Proxy --
|
||||
# 如果国内编译时 go 下载慢或报错,可以设置 go 国内代理加速,否则不用设置
|
||||
export GO111MODULE=on
|
||||
export GOPROXY=https://goproxy.cn,direct
|
||||
# -- Go Proxy --
|
||||
wails build
|
||||
cd build
|
||||
sudo install -Dvm755 bin/res-downloader -t /usr/bin
|
||||
sudo install -Dvm644 appicon.png /usr/share/icons/hicolor/512x512/apps/res-downloader.png
|
||||
sudo install -Dvm644 build/linux/Arch/res-downloader.desktop /usr/share/applications/res-downloader.desktop
|
||||
```
|
||||
|
||||
5
build/linux/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
!.gitkeep
|
||||
debian/usr/local/bin/*
|
||||
debian/DEBIAN/control
|
||||
AppImage/usr/bin/*
|
||||
AppImage/usr/lib/*
|
||||
1
build/linux/AppImage/.DirIcon
Symbolic link
@@ -0,0 +1 @@
|
||||
res-downloader.png
|
||||
8
build/linux/AppImage/res-downloader.desktop
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=res-downloader
|
||||
Comment=This is a high-value and high-performance and diverse resource downloader called res-downloader
|
||||
Exec=/usr/bin/res-downloader
|
||||
Icon=/usr/share/icons/hicolor/256x256/apps/res-downloader
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
BIN
.DS_Store → build/linux/AppImage/usr/.DS_Store
vendored
0
build/linux/AppImage/usr/bin/.gitkeep
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=res-downloader
|
||||
Comment=This is a high-value and high-performance and diverse resource downloader called res-downloader
|
||||
Exec=/usr/bin/res-downloader
|
||||
Icon=/usr/share/icons/hicolor/256x256/apps/res-downloader
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
|
After Width: | Height: | Size: 45 KiB |
8
build/linux/Arch/res-downloader.desktop
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=res-downloader
|
||||
Comment=This is a high-value and high-performance and diverse resource downloader called res-downloader
|
||||
Exec=res-downloader
|
||||
Icon=res-downloader.png
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
BIN
build/linux/Debian/.DS_Store
vendored
Normal file
BIN
build/linux/Debian/DEBIAN/.DS_Store
vendored
Normal file
9
build/linux/Debian/DEBIAN/.control
Normal file
@@ -0,0 +1,9 @@
|
||||
Package: res-downloader
|
||||
Version: {{Version}}
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Depends: libwebkit2gtk-4.0-37
|
||||
Maintainer: putyy@qq.com
|
||||
Homepage: https://github.com/putyy/res-downloader
|
||||
Description: This is a high-value and high-performance and diverse resource downloader called res-downloader
|
||||
BIN
build/linux/Debian/usr/local/.DS_Store
vendored
Normal file
0
build/linux/Debian/usr/local/bin/.gitkeep
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=res-downloader
|
||||
Comment=This is a high-value and high-performance and diverse resource downloader called res-downloader
|
||||
Exec=/usr/local/bin/res-downloader
|
||||
Icon=/usr/share/icons/hicolor/256x256/apps/res-downloader.png
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
|
After Width: | Height: | Size: 45 KiB |
29
build/linux/dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
FROM golang:1.24.2-bookworm
|
||||
|
||||
WORKDIR /
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --fix-missing \
|
||||
build-essential \
|
||||
git \
|
||||
jq \
|
||||
kmod \
|
||||
fuse \
|
||||
libgtk-3-dev \
|
||||
libwebkit2gtk-4.0-dev \
|
||||
nsis \
|
||||
wget \
|
||||
curl \
|
||||
gnupg2 \
|
||||
lsb-release \
|
||||
libfuse-dev \
|
||||
libfuse2 \
|
||||
file \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - \
|
||||
&& apt-get install -y nodejs
|
||||
|
||||
RUN go install github.com/wailsapp/wails/v2/cmd/wails@latest
|
||||
|
||||
RUN wails doctor
|
||||
@@ -14,7 +14,7 @@
|
||||
!define INFO_PRODUCTNAME "res-downloader"
|
||||
!endif
|
||||
!ifndef INFO_PRODUCTVERSION
|
||||
!define INFO_PRODUCTVERSION "3.0.0"
|
||||
!define INFO_PRODUCTVERSION "3.0.5"
|
||||
!endif
|
||||
!ifndef INFO_COPYRIGHT
|
||||
!define INFO_COPYRIGHT "Copyright © 2023"
|
||||
|
||||
21
core/app.go
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
sysRuntime "runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -37,12 +38,18 @@ var (
|
||||
httpServerOnce *HttpServer
|
||||
)
|
||||
|
||||
func GetApp(assets embed.FS) *App {
|
||||
func GetApp(assets embed.FS, wjs string) *App {
|
||||
if appOnce == nil {
|
||||
matches := regexp.MustCompile(`"productVersion":\s*"([\d.]+)"`).FindStringSubmatch(wjs)
|
||||
version := "1.0.1"
|
||||
if len(matches) > 0 {
|
||||
version = matches[1]
|
||||
}
|
||||
|
||||
appOnce = &App{
|
||||
assets: assets,
|
||||
AppName: "res-downloader",
|
||||
Version: "3.0.0",
|
||||
Version: version,
|
||||
Description: "res-downloader是一款集网络资源嗅探 + 高速下载功能于一体的软件,高颜值、高性能和多样化,提供个人用户下载自己上传到各大平台的网络资源功能!",
|
||||
Copyright: "Copyright © 2023~" + strconv.Itoa(time.Now().Year()),
|
||||
PublicCrt: []byte(`
|
||||
@@ -142,10 +149,12 @@ func (a *App) OnExit() {
|
||||
func (a *App) installCert() {
|
||||
if res, err := systemOnce.installCert(); err != nil {
|
||||
if sysRuntime.GOOS == "darwin" {
|
||||
DialogErr("证书安装失败,请手动执行安装命令(已复制到剪切板),err:" + err.Error() + ", " + res)
|
||||
_ = runtime.ClipboardSetText(appOnce.ctx, `echo "输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "`+systemOnce.CertFile+`" && touch `+a.LockFile+` && echo "安装完成"`)
|
||||
_ = runtime.ClipboardSetText(appOnce.ctx, `echo "输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "`+systemOnce.CertFile+`" && touch `+a.LockFile+` && echo "安装完成"`)
|
||||
DialogErr("证书安装失败,请打开终端执行安装(命令已复制到剪切板),err:" + err.Error() + ", " + res)
|
||||
} else if sysRuntime.GOOS == "windows" && strings.Contains(err.Error(), "Access is denied.") {
|
||||
DialogErr("首次启用本软件,请使用鼠标右键选择以管理员身份运行")
|
||||
} else if sysRuntime.GOOS == "linux" && strings.Contains(err.Error(), "Access is denied.") {
|
||||
DialogErr("证书路径: " + systemOnce.CertFile + ", 请手动安装,安装完成后请执行: touch" + a.LockFile + " err:" + err.Error() + ", " + res)
|
||||
} else {
|
||||
globalLogger.Esg(err, res)
|
||||
DialogErr("err:" + err.Error() + ", " + res)
|
||||
@@ -166,7 +175,7 @@ func (a *App) OpenSystemProxy() bool {
|
||||
a.IsProxy = true
|
||||
return true
|
||||
}
|
||||
DialogErr("设置失败")
|
||||
DialogErr("设置失败:" + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -179,7 +188,7 @@ func (a *App) UnsetSystemProxy() bool {
|
||||
a.IsProxy = false
|
||||
return true
|
||||
}
|
||||
DialogErr("设置失败")
|
||||
DialogErr("设置失败:" + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ type Config struct {
|
||||
Port string `json:"Port"`
|
||||
Quality int `json:"Quality"`
|
||||
SaveDirectory string `json:"SaveDirectory"`
|
||||
FilenameLen int `json:"FilenameLen"`
|
||||
FilenameTime bool `json:"FilenameTime"`
|
||||
UpstreamProxy string `json:"UpstreamProxy"`
|
||||
OpenProxy bool `json:"OpenProxy"`
|
||||
DownloadProxy bool `json:"DownloadProxy"`
|
||||
@@ -22,24 +24,28 @@ type Config struct {
|
||||
WxAction bool `json:"WxAction"`
|
||||
TaskNumber int `json:"TaskNumber"`
|
||||
UserAgent string `json:"UserAgent"`
|
||||
UseHeaders string `json:"UseHeaders"`
|
||||
}
|
||||
|
||||
func initConfig() *Config {
|
||||
if globalConfig == nil {
|
||||
def := `
|
||||
{
|
||||
"Host": "0.0.0.0",
|
||||
"Host": "127.0.0.1",
|
||||
"Port": "8899",
|
||||
"Theme": "lightTheme",
|
||||
"Quality": 0,
|
||||
"SaveDirectory": "",
|
||||
"FilenameLen": 0,
|
||||
"FilenameTime": true,
|
||||
"UpstreamProxy": "",
|
||||
"OpenProxy": false,
|
||||
"DownloadProxy": false,
|
||||
"AutoProxy": false,
|
||||
"WxAction": false,
|
||||
"AutoProxy": true,
|
||||
"WxAction": true,
|
||||
"TaskNumber": __TaskNumber__,
|
||||
"UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
|
||||
"UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
|
||||
"UseHeaders": "User-Agent,Referer,Authorization,Cookie"
|
||||
}
|
||||
`
|
||||
def = strings.ReplaceAll(def, "__TaskNumber__", strconv.Itoa(runtime.NumCPU()*2))
|
||||
@@ -47,9 +53,23 @@ func initConfig() *Config {
|
||||
storage: NewStorage("config.json", []byte(def)),
|
||||
}
|
||||
|
||||
defaultMap := make(map[string]interface{})
|
||||
_ = json.Unmarshal([]byte(def), &defaultMap)
|
||||
|
||||
data, err := globalConfig.storage.Load()
|
||||
if err == nil {
|
||||
_ = json.Unmarshal(data, &globalConfig)
|
||||
var loadedMap map[string]interface{}
|
||||
_ = json.Unmarshal(data, &loadedMap)
|
||||
|
||||
for key, val := range defaultMap {
|
||||
if _, ok := loadedMap[key]; !ok {
|
||||
loadedMap[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
finalBytes, _ := json.Marshal(loadedMap)
|
||||
_ = json.Unmarshal(finalBytes, &globalConfig)
|
||||
|
||||
} else {
|
||||
globalLogger.Esg(err, "load config err")
|
||||
}
|
||||
@@ -59,9 +79,13 @@ func initConfig() *Config {
|
||||
|
||||
func (c *Config) setConfig(config Config) {
|
||||
oldProxy := c.UpstreamProxy
|
||||
c.Host = config.Host
|
||||
c.Port = config.Port
|
||||
c.Theme = config.Theme
|
||||
c.Quality = config.Quality
|
||||
c.SaveDirectory = config.SaveDirectory
|
||||
c.FilenameLen = config.FilenameLen
|
||||
c.FilenameTime = config.FilenameTime
|
||||
c.UpstreamProxy = config.UpstreamProxy
|
||||
c.UserAgent = config.UserAgent
|
||||
c.OpenProxy = config.OpenProxy
|
||||
@@ -69,6 +93,7 @@ func (c *Config) setConfig(config Config) {
|
||||
c.AutoProxy = config.AutoProxy
|
||||
c.TaskNumber = config.TaskNumber
|
||||
c.WxAction = config.WxAction
|
||||
c.UseHeaders = config.UseHeaders
|
||||
if oldProxy != c.UpstreamProxy {
|
||||
proxyOnce.setTransport()
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -31,17 +31,19 @@ type FileDownloader struct {
|
||||
totalTasks int
|
||||
TotalSize int64
|
||||
IsMultiPart bool
|
||||
Headers map[string]string
|
||||
DownloadTaskList []*DownloadTask
|
||||
progressCallback ProgressCallback
|
||||
}
|
||||
|
||||
func NewFileDownloader(url, filename string, totalTasks int) *FileDownloader {
|
||||
func NewFileDownloader(url, filename string, totalTasks int, headers map[string]string) *FileDownloader {
|
||||
return &FileDownloader{
|
||||
Url: url,
|
||||
FileName: filename,
|
||||
totalTasks: totalTasks,
|
||||
IsMultiPart: false,
|
||||
TotalSize: 0,
|
||||
Headers: headers,
|
||||
DownloadTaskList: make([]*DownloadTask, 0),
|
||||
}
|
||||
}
|
||||
@@ -52,10 +54,16 @@ func (fd *FileDownloader) buildClient() *http.Client {
|
||||
transport.Proxy = http.ProxyURL(fd.ProxyUrl)
|
||||
}
|
||||
// Cookie handle
|
||||
jar, _ := cookiejar.New(nil)
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
Jar: jar,
|
||||
}
|
||||
}
|
||||
|
||||
func (fd *FileDownloader) setHeaders(request *http.Request) {
|
||||
for key, values := range fd.Headers {
|
||||
if strings.Contains(globalConfig.UseHeaders, key) {
|
||||
request.Header.Set(key, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,42 +76,60 @@ func (fd *FileDownloader) init() error {
|
||||
fd.Referer = parsedURL.Scheme + "://" + parsedURL.Host + "/"
|
||||
}
|
||||
|
||||
if globalConfig.DownloadProxy && globalConfig.UpstreamProxy != "" {
|
||||
if globalConfig.DownloadProxy && globalConfig.UpstreamProxy != "" && !strings.Contains(globalConfig.UpstreamProxy, globalConfig.Port) {
|
||||
proxyURL, err := url.Parse(globalConfig.UpstreamProxy)
|
||||
if err == nil {
|
||||
fd.ProxyUrl = proxyURL
|
||||
}
|
||||
}
|
||||
|
||||
resp, e := http.Head(fd.Url)
|
||||
if e != nil {
|
||||
return e
|
||||
request, err := http.NewRequest("HEAD", fd.Url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create request failed")
|
||||
}
|
||||
|
||||
if _, ok := fd.Headers["User-Agent"]; !ok {
|
||||
fd.Headers["User-Agent"] = globalConfig.UserAgent
|
||||
}
|
||||
|
||||
if _, ok := fd.Headers["Referer"]; !ok {
|
||||
fd.Headers["Referer"] = fd.Referer
|
||||
}
|
||||
|
||||
fd.setHeaders(request)
|
||||
|
||||
resp, err := fd.buildClient().Do(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request failed" + err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
fd.TotalSize = resp.ContentLength
|
||||
|
||||
if fd.TotalSize <= 0 {
|
||||
return fmt.Errorf("request init failed: size 0")
|
||||
}
|
||||
|
||||
if resp.Header.Get("Accept-Ranges") == "bytes" && fd.TotalSize > 10485760 {
|
||||
fd.IsMultiPart = true
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
|
||||
fd.FileName = filepath.Clean(fd.FileName)
|
||||
_, err = os.Stat(fd.FileName)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
fd.File, err = os.Create(fd.FileName)
|
||||
if err != nil && fd.TotalSize > 0 {
|
||||
err = fd.File.Truncate(fd.TotalSize)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fd.File, err = os.OpenFile(fd.FileName, os.O_RDWR, os.ModeAppend)
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
dir := filepath.Dir(fd.FileName)
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fd.File, err = os.OpenFile(fd.FileName, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("文件初始化失败: %w", err)
|
||||
}
|
||||
|
||||
if err = fd.File.Truncate(fd.TotalSize); err != nil {
|
||||
fd.File.Close()
|
||||
return fmt.Errorf("文件大小设置失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -164,8 +190,9 @@ func (fd *FileDownloader) startDownloadTask(waitGroup *sync.WaitGroup, progressC
|
||||
globalLogger.Error().Stack().Err(err).Msgf("任务%d创建请求出错", task.taskID)
|
||||
return
|
||||
}
|
||||
request.Header.Set("User-Agent", globalConfig.UserAgent)
|
||||
request.Header.Set("Referer", fd.Referer)
|
||||
|
||||
fd.setHeaders(request)
|
||||
|
||||
if fd.IsMultiPart {
|
||||
request.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", task.rangeStart, task.rangeEnd))
|
||||
}
|
||||
|
||||
40
core/http.go
@@ -1,6 +1,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
@@ -20,15 +21,11 @@ type ResponseData struct {
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type HttpServer struct {
|
||||
broadcast chan []byte
|
||||
}
|
||||
type HttpServer struct{}
|
||||
|
||||
func initHttpServer() *HttpServer {
|
||||
if httpServerOnce == nil {
|
||||
httpServerOnce = &HttpServer{
|
||||
broadcast: make(chan []byte),
|
||||
}
|
||||
httpServerOnce = &HttpServer{}
|
||||
}
|
||||
return httpServerOnce
|
||||
}
|
||||
@@ -38,9 +35,19 @@ func (h *HttpServer) run() {
|
||||
if err != nil {
|
||||
log.Fatalf("无法启动监听: %v", err)
|
||||
}
|
||||
go h.handleMessages()
|
||||
fmt.Println("服务已启动,监听 http://" + globalConfig.Host + ":" + globalConfig.Port)
|
||||
if err := http.Serve(listener, proxyOnce.Proxy); err != nil {
|
||||
if err := http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Host == "127.0.0.1:"+globalConfig.Port && strings.Contains(r.URL.Path, "/cert") {
|
||||
w.Header().Set("Content-Type", "application/x-x509-ca-data")
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=res-downloader-public.crt")
|
||||
w.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(appOnce.PublicCrt)))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err = io.Copy(w, io.NopCloser(bytes.NewReader(appOnce.PublicCrt)))
|
||||
} else {
|
||||
proxyOnce.Proxy.ServeHTTP(w, r) // 代理
|
||||
}
|
||||
})); err != nil {
|
||||
fmt.Printf("服务器异常: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -90,13 +97,6 @@ func (h *HttpServer) preview(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
func (h *HttpServer) handleMessages() {
|
||||
for {
|
||||
msg := <-h.broadcast
|
||||
runtime.EventsEmit(appOnce.ctx, "event", string(msg))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HttpServer) send(t string, data interface{}) {
|
||||
jsonData, err := json.Marshal(map[string]interface{}{
|
||||
"type": t,
|
||||
@@ -106,7 +106,7 @@ func (h *HttpServer) send(t string, data interface{}) {
|
||||
fmt.Println("Error converting map to JSON:", err)
|
||||
return
|
||||
}
|
||||
h.broadcast <- jsonData
|
||||
runtime.EventsEmit(appOnce.ctx, "event", string(jsonData))
|
||||
}
|
||||
|
||||
func (h *HttpServer) writeJson(w http.ResponseWriter, data ResponseData) {
|
||||
@@ -179,13 +179,13 @@ func (h *HttpServer) openFolder(w http.ResponseWriter, r *http.Request) {
|
||||
case "linux":
|
||||
// linux
|
||||
// 尝试使用不同的文件管理器
|
||||
cmd = exec.Command("nautilus", filePath) // 尝试Nautilus
|
||||
cmd = exec.Command("nautilus", filePath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
cmd = exec.Command("thunar", filePath) // 尝试Thunar
|
||||
cmd = exec.Command("thunar", filePath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
cmd = exec.Command("dolphin", filePath) // 尝试Dolphin
|
||||
cmd = exec.Command("dolphin", filePath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
cmd = exec.Command("pcmanfm", filePath) // 尝试PCManFM
|
||||
cmd = exec.Command("pcmanfm", filePath)
|
||||
if err := cmd.Start(); err != nil {
|
||||
globalLogger.err(err)
|
||||
h.writeJson(w, ResponseData{Code: 0, Message: err.Error()})
|
||||
|
||||
@@ -15,14 +15,14 @@ func Middleware(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
func HandleApi(w http.ResponseWriter, r *http.Request) bool {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(r.URL.Path, "/api") {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return true
|
||||
}
|
||||
switch r.URL.Path {
|
||||
case "/api/preview":
|
||||
httpServerOnce.preview(w, r)
|
||||
|
||||
@@ -95,7 +95,7 @@ func (p *Proxy) setTransport() {
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
if globalConfig.UpstreamProxy != "" && globalConfig.OpenProxy {
|
||||
if globalConfig.UpstreamProxy != "" && globalConfig.OpenProxy && !strings.Contains(globalConfig.UpstreamProxy, globalConfig.Port) {
|
||||
proxyURL, err := url.Parse(globalConfig.UpstreamProxy)
|
||||
if err == nil {
|
||||
transport.Proxy = http.ProxyURL(proxyURL)
|
||||
@@ -105,40 +105,18 @@ func (p *Proxy) setTransport() {
|
||||
}
|
||||
|
||||
func (p *Proxy) httpRequestEvent(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
|
||||
if strings.Contains(r.Host, "res-downloader.666666.com") {
|
||||
if strings.Contains(r.URL.Path, "/wechat") {
|
||||
if globalConfig.WxAction && r.URL.Query().Get("type") == "1" {
|
||||
return p.handleWechatRequest(r, ctx)
|
||||
} else if !globalConfig.WxAction && r.URL.Query().Get("type") == "2" {
|
||||
return p.handleWechatRequest(r, ctx)
|
||||
} else {
|
||||
return r, p.buildEmptyResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(r.URL.Path, "/cert") {
|
||||
return p.handleCertRequest(r, ctx)
|
||||
if strings.Contains(r.Host, "res-downloader.666666.com") && strings.Contains(r.URL.Path, "/wechat") {
|
||||
if globalConfig.WxAction && r.URL.Query().Get("type") == "1" {
|
||||
return p.handleWechatRequest(r, ctx)
|
||||
} else if !globalConfig.WxAction && r.URL.Query().Get("type") == "2" {
|
||||
return p.handleWechatRequest(r, ctx)
|
||||
} else {
|
||||
return r, p.buildEmptyResponse(r)
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (p *Proxy) handleCertRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
|
||||
resp := &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: http.Header{
|
||||
"Content-Type": []string{"application/x-x509-ca-data"},
|
||||
"Content-Disposition": []string{"attachment;filename=z-der-data.crt"},
|
||||
"Content-Transfer-Encoding": []string{"binary"},
|
||||
},
|
||||
Body: io.NopCloser(bytes.NewReader(appOnce.PublicCrt)),
|
||||
Request: r,
|
||||
}
|
||||
return r, resp
|
||||
}
|
||||
|
||||
func (p *Proxy) handleWechatRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
@@ -170,12 +148,12 @@ func (p *Proxy) handleWechatRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*ht
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
resourceOnce.markMu.Lock()
|
||||
defer resourceOnce.markMu.Unlock()
|
||||
|
||||
urlSign := Md5(rowUrl.(string))
|
||||
if _, ok := resourceOnce.mark[urlSign]; ok {
|
||||
if resourceOnce.mediaIsMarked(urlSign) {
|
||||
return
|
||||
}
|
||||
|
||||
id, err := gonanoid.New()
|
||||
if err != nil {
|
||||
id = urlSign
|
||||
@@ -197,6 +175,12 @@ func (p *Proxy) handleWechatRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*ht
|
||||
ContentType: "video/mp4",
|
||||
}
|
||||
|
||||
if mediaType, ok := firstMedia["mediaType"].(float64); ok && mediaType == 9 {
|
||||
res.Classify = "image"
|
||||
res.Suffix = ".png"
|
||||
res.ContentType = "image/png"
|
||||
}
|
||||
|
||||
if urlToken, ok := firstMedia["urlToken"].(string); ok {
|
||||
res.Url = res.Url + urlToken
|
||||
}
|
||||
@@ -230,7 +214,7 @@ func (p *Proxy) handleWechatRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*ht
|
||||
|
||||
res.OtherData["wx_file_formats"] = strings.Join(fileFormats, "#")
|
||||
}
|
||||
resourceOnce.mark[urlSign] = true
|
||||
resourceOnce.markMedia(urlSign)
|
||||
httpServerOnce.send("newResources", res)
|
||||
}(body)
|
||||
return r, p.buildEmptyResponse(r)
|
||||
@@ -265,8 +249,10 @@ func (p *Proxy) httpResponseEvent(resp *http.Response, ctx *goproxy.ProxyCtx) *h
|
||||
|
||||
if strings.HasSuffix(host, "res.wx.qq.com") {
|
||||
respTemp := resp
|
||||
is := false
|
||||
if strings.HasSuffix(respTemp.Request.URL.RequestURI(), ".js?v="+p.v()) {
|
||||
respTemp = p.replaceWxJsContent(respTemp, ".js\"", ".js?v="+p.v()+"\"")
|
||||
is = true
|
||||
}
|
||||
|
||||
if strings.Contains(Path, "web/web-finder/res/js/virtual_svg-icons-register.publish") {
|
||||
@@ -308,7 +294,9 @@ func (p *Proxy) httpResponseEvent(resp *http.Response, ctx *goproxy.ProxyCtx) *h
|
||||
respTemp.Header.Set("Content-Length", fmt.Sprintf("%d", len(newBodyBytes)))
|
||||
return respTemp
|
||||
}
|
||||
return respTemp
|
||||
if is {
|
||||
return respTemp
|
||||
}
|
||||
}
|
||||
|
||||
classify, suffix := TypeSuffix(resp.Header.Get("Content-Type"))
|
||||
@@ -322,14 +310,11 @@ func (p *Proxy) httpResponseEvent(resp *http.Response, ctx *goproxy.ProxyCtx) *h
|
||||
}
|
||||
|
||||
rawUrl := resp.Request.URL.String()
|
||||
resourceOnce.markMu.Lock()
|
||||
defer resourceOnce.markMu.Unlock()
|
||||
|
||||
isAll, _ := resourceOnce.getResType("all")
|
||||
isClassify, _ := resourceOnce.getResType(classify)
|
||||
|
||||
urlSign := Md5(rawUrl)
|
||||
if _, ok := resourceOnce.mark[urlSign]; !ok && (isAll || isClassify) {
|
||||
if ok := resourceOnce.mediaIsMarked(urlSign); !ok && (isAll || isClassify) {
|
||||
value, _ := strconv.ParseFloat(resp.Header.Get("content-length"), 64)
|
||||
id, err := gonanoid.New()
|
||||
if err != nil {
|
||||
@@ -351,7 +336,13 @@ func (p *Proxy) httpResponseEvent(resp *http.Response, ctx *goproxy.ProxyCtx) *h
|
||||
Description: "",
|
||||
ContentType: resp.Header.Get("Content-Type"),
|
||||
}
|
||||
resourceOnce.mark[urlSign] = true
|
||||
|
||||
// Store entire request headers as JSON
|
||||
if headers, err := json.Marshal(resp.Request.Header); err == nil {
|
||||
res.OtherData["headers"] = string(headers)
|
||||
}
|
||||
|
||||
resourceOnce.markMedia(urlSign)
|
||||
httpServerOnce.send("newResources", res)
|
||||
}
|
||||
return resp
|
||||
|
||||
@@ -2,6 +2,8 @@ package core
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -26,8 +28,7 @@ type WxFileDecodeResult struct {
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
mark map[string]bool
|
||||
markMu sync.RWMutex
|
||||
mediaMark sync.Map
|
||||
resType map[string]bool
|
||||
resTypeMu sync.RWMutex
|
||||
}
|
||||
@@ -35,7 +36,6 @@ type Resource struct {
|
||||
func initResource() *Resource {
|
||||
if resourceOnce == nil {
|
||||
resourceOnce = &Resource{
|
||||
mark: make(map[string]bool),
|
||||
resType: map[string]bool{
|
||||
"all": true,
|
||||
"image": true,
|
||||
@@ -52,17 +52,13 @@ func initResource() *Resource {
|
||||
return resourceOnce
|
||||
}
|
||||
|
||||
func (r *Resource) getMark(key string) (bool, bool) {
|
||||
r.markMu.RLock()
|
||||
defer r.markMu.RUnlock()
|
||||
value, ok := r.mark[key]
|
||||
return value, ok
|
||||
func (r *Resource) mediaIsMarked(key string) bool {
|
||||
_, loaded := r.mediaMark.Load(key)
|
||||
return loaded
|
||||
}
|
||||
|
||||
func (r *Resource) setMark(key string, value bool) {
|
||||
r.markMu.Lock()
|
||||
defer r.markMu.Unlock()
|
||||
r.mark[key] = value
|
||||
func (r *Resource) markMedia(key string) {
|
||||
r.mediaMark.Store(key, true)
|
||||
}
|
||||
|
||||
func (r *Resource) getResType(key string) (bool, bool) {
|
||||
@@ -93,15 +89,11 @@ func (r *Resource) setResType(n []string) {
|
||||
}
|
||||
|
||||
func (r *Resource) clear() {
|
||||
r.markMu.Lock()
|
||||
defer r.markMu.Unlock()
|
||||
r.mark = make(map[string]bool)
|
||||
r.mediaMark.Clear()
|
||||
}
|
||||
|
||||
func (r *Resource) delete(sign string) {
|
||||
r.markMu.Lock()
|
||||
defer r.markMu.Unlock()
|
||||
delete(r.mark, sign)
|
||||
r.mediaMark.Delete(sign)
|
||||
}
|
||||
|
||||
func (r *Resource) download(mediaInfo MediaInfo, decodeStr string) {
|
||||
@@ -113,13 +105,22 @@ func (r *Resource) download(mediaInfo MediaInfo, decodeStr string) {
|
||||
fileName := Md5(rawUrl)
|
||||
if mediaInfo.Description != "" {
|
||||
fileName = regexp.MustCompile(`[^\w\p{Han}]`).ReplaceAllString(mediaInfo.Description, "")
|
||||
fileLen := globalConfig.FilenameLen
|
||||
if fileLen <= 0 {
|
||||
fileLen = 10
|
||||
}
|
||||
|
||||
runes := []rune(fileName)
|
||||
if len(runes) > 10 {
|
||||
fileName = string(runes[:10])
|
||||
if len(runes) > fileLen {
|
||||
fileName = string(runes[:fileLen])
|
||||
}
|
||||
}
|
||||
|
||||
mediaInfo.SavePath = filepath.Join(globalConfig.SaveDirectory, fileName+"_"+GetCurrentDateTimeFormatted()+mediaInfo.Suffix)
|
||||
if globalConfig.FilenameTime {
|
||||
mediaInfo.SavePath = filepath.Join(globalConfig.SaveDirectory, fileName+"_"+GetCurrentDateTimeFormatted()+mediaInfo.Suffix)
|
||||
} else {
|
||||
mediaInfo.SavePath = filepath.Join(globalConfig.SaveDirectory, fileName+mediaInfo.Suffix)
|
||||
}
|
||||
|
||||
if strings.Contains(rawUrl, "qq.com") {
|
||||
if globalConfig.Quality == 1 &&
|
||||
@@ -143,7 +144,9 @@ func (r *Resource) download(mediaInfo MediaInfo, decodeStr string) {
|
||||
}
|
||||
}
|
||||
|
||||
downloader := NewFileDownloader(rawUrl, mediaInfo.SavePath, globalConfig.TaskNumber)
|
||||
headers, _ := r.parseHeaders(mediaInfo)
|
||||
|
||||
downloader := NewFileDownloader(rawUrl, mediaInfo.SavePath, globalConfig.TaskNumber, headers)
|
||||
downloader.progressCallback = func(totalDownloaded float64) {
|
||||
r.progressEventsEmit(mediaInfo, strconv.Itoa(int(totalDownloaded))+"%", DownloadStatusRunning)
|
||||
}
|
||||
@@ -163,6 +166,27 @@ func (r *Resource) download(mediaInfo MediaInfo, decodeStr string) {
|
||||
}(mediaInfo)
|
||||
}
|
||||
|
||||
// 解析并组装 headers
|
||||
func (r *Resource) parseHeaders(mediaInfo MediaInfo) (map[string]string, error) {
|
||||
headers := make(map[string]string)
|
||||
|
||||
if hh, ok := mediaInfo.OtherData["headers"]; ok {
|
||||
var tempHeaders map[string][]string
|
||||
// 解析 JSON 字符串为 map[string][]string
|
||||
if err := json.Unmarshal([]byte(hh), &tempHeaders); err != nil {
|
||||
return headers, fmt.Errorf("parse headers JSON err: %v", err)
|
||||
}
|
||||
|
||||
for key, values := range tempHeaders {
|
||||
if len(values) > 0 {
|
||||
headers[key] = values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
func (r *Resource) wxFileDecode(mediaInfo MediaInfo, fileName, decodeStr string) (string, error) {
|
||||
sourceFile, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,94 +5,99 @@ package core
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (s *SystemSetup) getActiveInterface() (string, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
func (s *SystemSetup) getNetworkServices() ([]string, error) {
|
||||
cmd := exec.Command("networksetup", "-listallnetworkservices")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, fmt.Errorf("failed to execute command: %v", err)
|
||||
}
|
||||
|
||||
for _, inter := range interfaces {
|
||||
if inter.Flags&net.FlagUp != 0 && inter.Flags&net.FlagLoopback == 0 {
|
||||
return inter.Name, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no active network interface found")
|
||||
}
|
||||
services := strings.Split(string(output), "\n")
|
||||
|
||||
func (s *SystemSetup) getNetworkServiceName(interfaceName string) (string, error) {
|
||||
cmd := exec.Command("networksetup", "-listallhardwareports")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", err
|
||||
var activeServices []string
|
||||
for _, service := range services {
|
||||
service = strings.TrimSpace(service)
|
||||
if service == "" || strings.Contains(service, "*") || strings.Contains(service, "Serial Port") {
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查服务是否活动
|
||||
infoCmd := exec.Command("networksetup", "-getinfo", service)
|
||||
infoOutput, err := infoCmd.Output()
|
||||
if err != nil {
|
||||
fmt.Printf("failed to get info for service %s: %v\n", service, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果输出中包含 "IP address:",说明服务是活动的
|
||||
if strings.Contains(string(infoOutput), "IP address:") {
|
||||
activeServices = append(activeServices, service)
|
||||
}
|
||||
}
|
||||
|
||||
output := out.String()
|
||||
lines := strings.Split(output, "\n")
|
||||
var serviceName string
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "Hardware Port:") {
|
||||
serviceName = strings.TrimSpace(strings.Split(line, ":")[1])
|
||||
}
|
||||
if strings.Contains(line, "Device: "+interfaceName) {
|
||||
return serviceName, nil
|
||||
}
|
||||
if len(activeServices) == 0 {
|
||||
return nil, fmt.Errorf("no active network services found")
|
||||
}
|
||||
return "", fmt.Errorf("no matching network service found for interface %s", interfaceName)
|
||||
|
||||
return activeServices, nil
|
||||
}
|
||||
|
||||
func (s *SystemSetup) setProxy() error {
|
||||
interfaceName, err := s.getActiveInterface()
|
||||
services, err := s.getNetworkServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName, err := s.getNetworkServiceName(interfaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commands := [][]string{
|
||||
{"networksetup", "-setwebproxy", serviceName, "127.0.0.1", globalConfig.Port},
|
||||
{"networksetup", "-setsecurewebproxy", serviceName, "127.0.0.1", globalConfig.Port},
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
if err := exec.Command(cmd[0], cmd[1:]...).Run(); err != nil {
|
||||
return err
|
||||
is := false
|
||||
for _, serviceName := range services {
|
||||
if err := exec.Command("networksetup", "-setwebproxy", serviceName, "127.0.0.1", globalConfig.Port).Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
is = true
|
||||
}
|
||||
if err := exec.Command("networksetup", "-setsecurewebproxy", serviceName, "127.0.0.1", globalConfig.Port).Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
is = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
if is {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to set proxy for any active network service")
|
||||
}
|
||||
|
||||
func (s *SystemSetup) unsetProxy() error {
|
||||
interfaceName, err := s.getActiveInterface()
|
||||
services, err := s.getNetworkServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceName, err := s.getNetworkServiceName(interfaceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commands := [][]string{
|
||||
{"networksetup", "-setwebproxystate", serviceName, "off"},
|
||||
{"networksetup", "-setsecurewebproxystate", serviceName, "off"},
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
if err := exec.Command(cmd[0], cmd[1:]...).Run(); err != nil {
|
||||
log.Println("UnsetProxy failed:", err)
|
||||
return err
|
||||
is := false
|
||||
for _, serviceName := range services {
|
||||
if err := exec.Command("networksetup", "-setwebproxystate", serviceName, "off").Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
is = true
|
||||
}
|
||||
if err := exec.Command("networksetup", "-setsecurewebproxystate", serviceName, "off").Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
is = true
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
if is {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to set proxy for any active network service")
|
||||
}
|
||||
|
||||
func (s *SystemSetup) installCert() (string, error) {
|
||||
@@ -101,7 +106,7 @@ func (s *SystemSetup) installCert() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
getPasswordCmd := exec.Command("osascript", "-e", `tell app "System Events" to display dialog "请输入密码,用于安装证书:" default answer "" with hidden answer`, "-e", `text returned of result`)
|
||||
getPasswordCmd := exec.Command("osascript", "-e", `tell app "System Events" to display dialog "请输入你的电脑密码,用于安装证书文件:" default answer "" with hidden answer`, "-e", `text returned of result`)
|
||||
passwordOutput, err := getPasswordCmd.Output()
|
||||
if err != nil {
|
||||
return string(passwordOutput), err
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
@@ -15,13 +15,18 @@ func (s *SystemSetup) setProxy() error {
|
||||
{"gsettings", "set", "org.gnome.system.proxy.https", "host", "127.0.0.1"},
|
||||
{"gsettings", "set", "org.gnome.system.proxy.https", "port", globalConfig.Port},
|
||||
}
|
||||
|
||||
is := false
|
||||
for _, cmd := range commands {
|
||||
if err := exec.Command(cmd[0], cmd[1:]...).Run(); err != nil {
|
||||
return err
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
is = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
if is {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Failed to activate proxy")
|
||||
}
|
||||
|
||||
func (s *SystemSetup) unsetProxy() error {
|
||||
@@ -30,21 +35,40 @@ func (s *SystemSetup) unsetProxy() error {
|
||||
}
|
||||
|
||||
func (s *SystemSetup) installCert() (string, error) {
|
||||
certData, err := s.initCert()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
destFile := "/usr/local/share/ca-certificates/" + appOnce.AppName + ".crt"
|
||||
|
||||
err = os.WriteFile(destFile, certData, 0644)
|
||||
_, err := s.initCert()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd := exec.Command("sudo", "update-ca-certificates")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return string(output), err
|
||||
actions := [][]string{
|
||||
{"/usr/local/share/ca-certificates/", "update-ca-certificates"},
|
||||
{"/usr/share/ca-certificates/trust-source/anchors/", "update-ca-trust"},
|
||||
{"/usr/share/ca-certificates/trust-source/anchors/", "trust extract-compat"},
|
||||
{"/etc/pki/ca-trust/source/anchors/", "update-ca-trust"},
|
||||
{"/etc/ssl/ca-certificates/", "update-ca-certificates"},
|
||||
}
|
||||
|
||||
is := false
|
||||
|
||||
for _, action := range actions {
|
||||
dir := action[0]
|
||||
if err := exec.Command("sudo", "cp", "-f", s.CertFile, dir+appOnce.AppName+".crt").Run(); err != nil {
|
||||
fmt.Printf("Failed to copy to %s: %v\n", dir, err)
|
||||
continue
|
||||
}
|
||||
|
||||
cmd := action[1]
|
||||
if err := exec.Command("sudo", cmd).Run(); err != nil {
|
||||
fmt.Printf("Failed to refresh certificates using %s: %v\n", cmd, err)
|
||||
continue
|
||||
}
|
||||
|
||||
is = true
|
||||
}
|
||||
|
||||
if !is {
|
||||
return "", fmt.Errorf("Certificate installation failed")
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -14,9 +15,11 @@ func Empty(data interface{}) {
|
||||
}
|
||||
|
||||
func DialogErr(message string) {
|
||||
httpServerOnce.send("message", map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": message,
|
||||
_, _ = runtime.MessageDialog(appOnce.ctx, runtime.MessageDialogOptions{
|
||||
Type: runtime.ErrorDialog,
|
||||
Title: "Error",
|
||||
Message: message,
|
||||
DefaultButton: "Cancel",
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
0
docs/.nojekyll
Normal file
9
docs/_coverpage.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# res-downloader V3
|
||||
|
||||
> 全新技术栈,更新、更小、更快、更稳
|
||||
|
||||
### 简单、高效、轻便 (仅 ~10M)
|
||||
|
||||
|
||||
[开始使用 Let Go](/readme.md)
|
||||
[下载](/getting-started.md)
|
||||
4
docs/_navbar.md
Normal file
@@ -0,0 +1,4 @@
|
||||
* [论坛](https://s.gowas.cn/d/4089)
|
||||
* [反馈](https://github.com/putyy/res-downloader/issues)
|
||||
* [日志](https://github.com/putyy/res-downloader/releases)
|
||||
* [QQ群](https://qm.qq.com/q/ImE37ayJmc)
|
||||
6
docs/_sidebar.md
Normal file
@@ -0,0 +1,6 @@
|
||||
* [简介](readme.md)
|
||||
* [快速开始](getting-started.md)
|
||||
* [安装指南](installation.md)
|
||||
* [功能演示](examples.md)
|
||||
* [更多说明](more.md)
|
||||
* [常见问题](troubleshooting.md)
|
||||
19
docs/examples.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## 开启代理
|
||||
- 安装完成后开启代理,如图:
|
||||

|
||||
|
||||
## 拦截资源
|
||||
### 视频号
|
||||
- 打开视频号即可看到本软件中拦截到的资源
|
||||

|
||||
|
||||
### 网页资源
|
||||
- 浏览器打开或者其他软件内置浏览器打开的网页
|
||||
- 这里演示打开百度这个网站:https://www.baidu.com/
|
||||

|
||||
|
||||
### 小程序、公众号、抖音、小红书、qq音乐、酷狗等应用内资源获取方式都差不多!
|
||||
|
||||
## 下载资源到本地
|
||||
- 选择你想下载的视频 点击下载即可
|
||||

|
||||
BIN
docs/favicon.ico
Normal file
|
After Width: | Height: | Size: 264 KiB |
14
docs/getting-started.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 软件下载
|
||||
🆕 [github下载](https://github.com/putyy/res-downloader/releases)
|
||||
🆕 [蓝奏云下载 密码:9vs5](https://wwjv.lanzoum.com/b04wgtfyb)
|
||||
|
||||
!> Win7用户请使用2.3.0版本
|
||||
|
||||
## 使用方法
|
||||
- 安装时一定要同意安装证书文件、一定要允许网络访问
|
||||
- 打开本软件(win系统首次使用管理员打开-鼠标右键选择管理员打开)
|
||||
- 打开后,左上角点击 “启动代理”
|
||||
- 打开要捕获的源, 如:视频号、网页、小程序等等
|
||||
- 返回软件首页即可看到资源列
|
||||
|
||||

|
||||
BIN
docs/images/config.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
docs/images/examples-1.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
docs/images/examples-2.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
docs/images/examples-3.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
docs/images/examples-4.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
docs/images/installation-mac-1.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/images/more-1.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
docs/images/more-2.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
docs/images/more-3.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/images/more-4.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
52
docs/index.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>res-downloader</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="keywords" content="res-downloader,视频号下载,抖音下载,快手下载,小红书下载,万能下载器,爱享素材,爱享素材下载器">
|
||||
<meta name="description" content="res-downloader是一款集网络资源嗅探 + 高速下载功能于一体的软件,高颜值、高性能和多样化,提供个人用户下载自己上传到各大平台的网络资源功能!">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'res-downloader',
|
||||
repo: 'https://github.com/putyy/res-downloader',
|
||||
// 侧边栏支持,默认加载的是项目根目录下的_sidebar.md文件
|
||||
loadSidebar: true,
|
||||
// 导航栏支持,默认加载的是项目根目录下的_navbar.md文件
|
||||
loadNavbar: true,
|
||||
// 封面支持,默认加载的是项目根目录下的_coverpage.md文件
|
||||
coverpage: true,
|
||||
// 最大支持渲染的标题层级
|
||||
maxLevel: 5,
|
||||
// 自定义侧边栏后默认不会再生成目录,设置生成目录的最大层级(建议配置为2-4)
|
||||
subMaxLevel: 4,
|
||||
// 小屏设备下合并导航栏到侧边栏
|
||||
mergeNavbar: true,
|
||||
search: {
|
||||
maxAge: 86400000,// 过期时间,单位毫秒,默认一天
|
||||
paths: 'auto',// 注意:仅适用于 paths: 'auto' 模式
|
||||
placeholder: '搜索',
|
||||
// 支持本地化
|
||||
placeholder: {
|
||||
'/zh-cn/': '搜索',
|
||||
'/': 'Type to search'
|
||||
},
|
||||
noData: '找不到结果',
|
||||
depth: 4,
|
||||
hideOtherSidebarContent: false,
|
||||
namespace: 'Docsify-Guide',
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
18
docs/installation.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## 下载安装文件
|
||||
- windows下载.exe结尾的,根据自己的系统架构下载合适的安装文件,通常下载带有“x64-installer.exe”结尾的文件
|
||||
- Mac下载.dmg结尾即可
|
||||
- Linux根据系统类型下载对应的执行文件或安装文件
|
||||
|
||||
## Windows安装过程
|
||||
- 双击下载好的exe 正常安装即可,首次打开记得右键管理员运行
|
||||
|
||||
## Mac安装过程
|
||||
- 双击下载好的dmg文件,将res-downloader拖入应用即可,如图:
|
||||

|
||||
|
||||
## Linux安装过程
|
||||
- 执行文件运行方式举例
|
||||
> chmod +x ./res-downloader_3.0.2_linux_x64
|
||||
> sudo ./res-downloader_3.0.2_linux_x64
|
||||
|
||||
|
||||
21
docs/more.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## 隐藏功能
|
||||
- 导出\导入 数据,1秒内连续点击空白处5次即可开启(单次有效)
|
||||

|
||||
|
||||
## 清空列表、类型筛选
|
||||
- 当资源列表过大时,无法快速找到需要的资源,这时可以先清空列表再去刷新需要的资源页面
|
||||
- 资源列表过多,可以快速根据需要的资源类型进行筛选
|
||||

|
||||
|
||||
## 拦截想要的资源类型、批量下载
|
||||
- 比如只需要视频时就选择视频类型,可以多选
|
||||

|
||||
|
||||
## 复制链接、视频解密
|
||||
- 复制链接可用于第三方软件进行下载,下载完成后对该视频解密,点击“视频解密”选择用其他软件下载完成后的视频文件进行解密
|
||||

|
||||
|
||||
## 设置说明
|
||||
!> 修改完成后记得点保存
|
||||
- 几乎每项配置在软件中都有说明(鼠标悬浮在?处即可查看),此处就不进行多余讲解
|
||||

|
||||
15
docs/readme.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# res-downloader
|
||||
## 爱享素材下载器
|
||||
|
||||
🎯 基于Go + [wails](https://github.com/wailsapp/wails)
|
||||
📦 操作简单、可获取不同类型的网络资源
|
||||
🖥️ 支持Windows、Mac、Linux
|
||||
🌐 支持视频、音频、图片、m3u8、直播流等常见网络资源
|
||||
💪 支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源下载
|
||||
👼 支持设置代理以获取特殊网络下的资源
|
||||
|
||||
## 实现 & 初衷
|
||||
?> 通过设置系统网络代理拦截响应,筛选出需要的资源, 同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的,只不过这些软件需要手动进行筛选,对于小白用户上手还是有点难度,本软件对部分资源做了特殊处理,更适合大众用户,所以就有了本项目。
|
||||
|
||||
## 免责声明
|
||||
?> 本软件用于学习研究使用,若因使用本软件造成的一切法律责任均与本人无关!
|
||||
46
docs/troubleshooting.md
Normal file
@@ -0,0 +1,46 @@
|
||||
## Mac 提示“已损坏,无法打开”, 打开命令行执行如下命令:
|
||||
> sudo xattr -d com.apple.quarantine /Applications/res-downloader.app
|
||||
|
||||
## 打开本软件,无法正常拦截获取
|
||||
> 检查系统证书是否安装
|
||||
> 关闭网络防火墙
|
||||
> 系统代理是否正确设置(代理地址:127.0.0.1 端口:8899)
|
||||
|
||||
## 关闭软件后无法正常上网
|
||||
> 手动关闭系统代理设置
|
||||
|
||||
## 链接不是私密链接
|
||||
> 通常是证书未正确安装,最新版证书下载:软件左下角?点击后有下载地址
|
||||
> 根据自己系统进行安装证书操作(不懂的自行百度),手动安装需安装到受信任的根证书
|
||||
|
||||
- Mac手动安装证书(V3+版本支持),打开终端复制以下命令 粘贴到终端回车 按照提示输入密码,完成后再打开软件:
|
||||
```shell
|
||||
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /Users/$(whoami)/Library/Preferences/res-downloader/cert.crt && touch /Users/$(whoami)/Library/Preferences/res-downloader/install.lock && echo "安装完成"
|
||||
```
|
||||
|
||||
## 拦截不到小程序中的资源
|
||||
清理微信缓存,删除小程序后,重新打开
|
||||
> 设置->存储空间->缓存
|
||||
|
||||
## 只拦截打开的视频号视频
|
||||
关闭全量拦截,打开视频号视频详情,通常分享好友后打开的页面属于详情页
|
||||
|
||||
## 拦截视频号账号视频
|
||||
打开对应作者个人主页,浏览即可
|
||||
|
||||
## 下载慢、大视频下载失败
|
||||
推荐使用如下工具加速下载,视频号可以下载完成后再到对应视频操作项选择 “视频解密” 按钮
|
||||
> [Neat Download Manager](https://www.neatdownloadmanager.com/index.php/en/)、[Motrix](https://motrix.app/download)等软件进行下载
|
||||
|
||||
## 直播流: 预览和录制:
|
||||
> [使用obs进行预览和录制 使用教程自行百度, 点击下载obs]( https://obsproject.com/)
|
||||
|
||||
## m3u8: 预览和下载:
|
||||
> [在线下载](https://m3u8-down.gowas.cn/)、[在线预览](https://m3u8play.com/)
|
||||
|
||||
## 安装证书后还会提示安装
|
||||
使用命令行打开本软件,查看 “lockfile:” 这串字符后面的锁文件路径,然后创建该文件即可
|
||||
例如 mac系统下终端执行如下命令即可创建
|
||||
> touch /Users/你的用户名/Library/Preferences/res-downloader/install.lock
|
||||
|
||||
## 更多问题 请前往github进行[反馈](https://github.com/putyy/res-downloader/issues)
|
||||
1
frontend/components.d.ts
vendored
@@ -8,6 +8,7 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Footer: typeof import('./src/components/Footer.vue')['default']
|
||||
ImportJson: typeof import('./src/components/ImportJson.vue')['default']
|
||||
Index: typeof import('./src/components/layout/Index.vue')['default']
|
||||
NaiveProvider: typeof import('./src/components/NaiveProvider.vue')['default']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
|
||||
BIN
frontend/src/.DS_Store
vendored
Normal file
@@ -12,9 +12,9 @@
|
||||
import NaiveProvider from '@/components/NaiveProvider.vue'
|
||||
import {darkTheme, lightTheme, zhCN} from 'naive-ui'
|
||||
import {useIndexStore} from "@/stores"
|
||||
import {computed, onMounted, watch} from "vue"
|
||||
import type {wsType} from "@/types/ws"
|
||||
import {computed, onMounted} from "vue"
|
||||
import {useEventStore} from "@/stores/event"
|
||||
import {appType} from "@/types/app";
|
||||
|
||||
const store = useIndexStore()
|
||||
const eventStore = useEventStore()
|
||||
@@ -33,7 +33,7 @@ onMounted(async () => {
|
||||
eventStore.init()
|
||||
eventStore.addHandle({
|
||||
type: "message",
|
||||
event: (res: wsType.Message)=>{
|
||||
event: (res: appType.Message)=>{
|
||||
switch (res?.code) {
|
||||
case 0:
|
||||
window?.$message?.error(res.message)
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<div>{{ store.appInfo.Copyright }}</div>
|
||||
<div class="flex">
|
||||
<button class="pl-4" @click="toWebsite('https://s.gowas.cn/d/4089')">论坛</button>
|
||||
<button class="pl-4" @click="toWebsite('http://127.0.0.1:8899/cert')">证书</button>
|
||||
<button class="pl-4" @click="toWebsite('https://github.com/putyy/res-downloader')">软件源码</button>
|
||||
<button class="pl-4" @click="toWebsite('https://github.com/putyy/res-downloader/issues')">帮助支持</button>
|
||||
<button class="pl-4" @click="toWebsite('https://github.com/putyy/res-downloader/releases')">更新日志</button>
|
||||
@@ -58,7 +59,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {useIndexStore} from "@/stores"
|
||||
import {BrowserOpenURL} from "../../wailsjs/runtime";
|
||||
import {BrowserOpenURL} from "../../wailsjs/runtime"
|
||||
const store = useIndexStore()
|
||||
const props = defineProps(["showModal"])
|
||||
const emits = defineEmits(["update:showModal"])
|
||||
|
||||
36
frontend/src/components/ImportJson.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<NModal
|
||||
:show="showModal"
|
||||
:on-update:show="changeShow"
|
||||
style="--wails-draggable:no-drag"
|
||||
preset="card"
|
||||
class="w-[640px]"
|
||||
title="导入数据"
|
||||
>
|
||||
<NForm
|
||||
size="medium"
|
||||
label-placement="left"
|
||||
label-width="auto"
|
||||
require-mark-placement="right-hanging"
|
||||
style="--wails-draggable:no-drag"
|
||||
>
|
||||
<NFormItem>
|
||||
<NInput type="textarea" v-model:value="content" rows="8" :autosize="false" placeholder="添加多个时,请确保每行只有一个(每个链接回车换行)"></NInput>
|
||||
</NFormItem>
|
||||
<NFormItem>
|
||||
<NButton strong secondary type="success" @click="emits('submit', content)" class="w-20">提交</NButton>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</NModal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {ref} from "vue"
|
||||
|
||||
const content = ref("")
|
||||
const props = defineProps<{
|
||||
showModal: boolean
|
||||
}>()
|
||||
|
||||
const emits = defineEmits(["update:showModal", "submit"])
|
||||
const changeShow = (value: boolean) => emits("update:showModal", value)
|
||||
</script>
|
||||
@@ -12,6 +12,9 @@
|
||||
<NButton v-if="row.DecodeKey" type="warning" :tertiary="true" size="small" @click="action('decode')">
|
||||
视频解密
|
||||
</NButton>
|
||||
<NButton v-if="isDebug" type="info" :tertiary="true" size="small" @click="action('json')">
|
||||
复制数据
|
||||
</NButton>
|
||||
<NButton type="error" :tertiary="true" size="small" @click="action('delete')">
|
||||
删除
|
||||
</NButton>
|
||||
@@ -19,6 +22,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {inject} from "vue"
|
||||
|
||||
const props = defineProps<{
|
||||
row: any,
|
||||
@@ -27,6 +31,8 @@ const props = defineProps<{
|
||||
|
||||
const emits = defineEmits(["action"])
|
||||
|
||||
const isDebug = inject('isDebug')
|
||||
|
||||
const action = (type: string) => {
|
||||
emits('action', props.row, props.index, type)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ const minimizeWindow = () => {
|
||||
WindowMinimise()
|
||||
}
|
||||
const maximizeWindow = () => {
|
||||
console.log("maximizeWindow")
|
||||
isMaximized.value = !isMaximized.value;
|
||||
if (isMaximized.value) {
|
||||
WindowFullscreen()
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type {MenuOption} from "naive-ui"
|
||||
import {MenuOption} from "naive-ui"
|
||||
import {NIcon} from "naive-ui"
|
||||
import {computed, h, ref, watch} from "vue"
|
||||
import {useRoute, useRouter} from "vue-router"
|
||||
@@ -55,11 +55,12 @@ import {
|
||||
CloudOutline,
|
||||
SettingsOutline,
|
||||
HelpCircleOutline,
|
||||
MoonOutline
|
||||
MoonOutline, SunnyOutline, LogoGithub
|
||||
} from "@vicons/ionicons5"
|
||||
import {useIndexStore} from "@/stores"
|
||||
import Footer from "@/components/Footer.vue"
|
||||
import Screen from "@/components/Screen.vue";
|
||||
import {BrowserOpenURL} from "../../../wailsjs/runtime";
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -75,6 +76,10 @@ const globalConfig = computed(()=>{
|
||||
return store.globalConfig
|
||||
})
|
||||
|
||||
const theme = computed(() => {
|
||||
return store.globalConfig.Theme === "darkTheme" ? renderIcon(SunnyOutline) : renderIcon(MoonOutline)
|
||||
})
|
||||
|
||||
watch(() => route.path, (newPath, oldPath) => {
|
||||
menuValue.value = route.fullPath.substring(1)
|
||||
});
|
||||
@@ -100,7 +105,12 @@ const footerOptions = ref([
|
||||
{
|
||||
label: "主题",
|
||||
key: 'theme',
|
||||
icon: renderIcon(MoonOutline),
|
||||
icon: theme,
|
||||
},
|
||||
{
|
||||
label: "github",
|
||||
key: 'github',
|
||||
icon: renderIcon(LogoGithub),
|
||||
},
|
||||
{
|
||||
label: "关于",
|
||||
@@ -118,6 +128,11 @@ const handleFooterUpdate = (key: string, item: MenuOption) => {
|
||||
showAppInfo.value = true
|
||||
return
|
||||
}
|
||||
|
||||
if (key === "github") {
|
||||
BrowserOpenURL("https://github.com/putyy/res-downloader")
|
||||
return
|
||||
}
|
||||
if (key === "theme") {
|
||||
if (globalConfig.value.Theme === "darkTheme") {
|
||||
store.setConfig(Object.assign({}, globalConfig.value, {Theme: "lightTheme"}))
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
export const DwStatus = {
|
||||
ready: "就绪",
|
||||
running: "运行中",
|
||||
pause: "暂停",
|
||||
wait: "等待",
|
||||
error: "错误",
|
||||
done: "完成",
|
||||
handle: "已下载,后续处理",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {defineStore} from "pinia"
|
||||
import {ref} from "vue"
|
||||
import type {wsType} from "@/types/ws"
|
||||
import {EventsOn} from "../../wailsjs/runtime"
|
||||
import {appType} from "@/types/app"
|
||||
|
||||
export const useEventStore = defineStore('ws-store', () => {
|
||||
const handles = ref<any>({})
|
||||
@@ -17,7 +17,7 @@ export const useEventStore = defineStore('ws-store', () => {
|
||||
})
|
||||
}
|
||||
|
||||
const addHandle = (handle: wsType.Handle) => {
|
||||
const addHandle = (handle: appType.Handle) => {
|
||||
handles.value[handle.type] = handle.event
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import {defineStore} from 'pinia'
|
||||
import {ref} from "vue"
|
||||
import type {appType} from "@/types/app"
|
||||
import appApi from "@/api/app"
|
||||
import {Environment} from "../../wailsjs/runtime";
|
||||
import {Environment} from "../../wailsjs/runtime"
|
||||
|
||||
export const useIndexStore = defineStore("index-store", () => {
|
||||
const appInfo = ref<appType.App>({
|
||||
@@ -19,12 +19,15 @@ export const useIndexStore = defineStore("index-store", () => {
|
||||
Quality: 0,
|
||||
SaveDirectory: "",
|
||||
UpstreamProxy: "",
|
||||
FilenameLen: 0,
|
||||
FilenameTime: false,
|
||||
OpenProxy: false,
|
||||
DownloadProxy: false,
|
||||
AutoProxy: false,
|
||||
WxAction: false,
|
||||
TaskNumber: 8,
|
||||
UserAgent: "",
|
||||
UseHeaders: "",
|
||||
})
|
||||
|
||||
const envInfo = ref({
|
||||
|
||||
13
frontend/src/types/app.d.ts
vendored
@@ -12,6 +12,8 @@ export namespace appType {
|
||||
Port: string
|
||||
Quality: number
|
||||
SaveDirectory: string
|
||||
FilenameLen: number
|
||||
FilenameTime: boolean
|
||||
UpstreamProxy: string
|
||||
OpenProxy: boolean
|
||||
DownloadProxy: boolean
|
||||
@@ -19,6 +21,7 @@ export namespace appType {
|
||||
WxAction: boolean
|
||||
TaskNumber: number
|
||||
UserAgent: string
|
||||
UseHeaders: string
|
||||
}
|
||||
|
||||
interface MediaInfo {
|
||||
@@ -44,4 +47,14 @@ export namespace appType {
|
||||
Status: string
|
||||
Message: string
|
||||
}
|
||||
|
||||
interface Message {
|
||||
code: number
|
||||
message: string
|
||||
}
|
||||
|
||||
interface Handle {
|
||||
type: string
|
||||
event: any
|
||||
}
|
||||
}
|
||||
21
frontend/src/types/ws.d.ts
vendored
@@ -1,21 +0,0 @@
|
||||
export namespace wsType {
|
||||
interface Event {
|
||||
type: string
|
||||
event: any
|
||||
data: any
|
||||
}
|
||||
|
||||
interface Handle {
|
||||
type: string
|
||||
event: any
|
||||
}
|
||||
|
||||
interface Message {
|
||||
code: number
|
||||
message: string
|
||||
}
|
||||
|
||||
interface Clipboard {
|
||||
content: string
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<div class="flex flex-col px-5 py-5">
|
||||
<div class="pb-2 z-40">
|
||||
<div class="pb-2 z-40" @click="triggerEvent">
|
||||
<NSpace>
|
||||
<NButton v-if="isProxy" secondary type="primary" @click="close" style="--wails-draggable:no-drag">关闭代理</NButton>
|
||||
<NButton v-else tertiary type="tertiary" @click="open" style="--wails-draggable:no-drag">开启代理</NButton>
|
||||
<NButton tertiary type="info" @click="batchDown" style="--wails-draggable:no-drag">批量下载</NButton>
|
||||
<NButton tertiary type="error" @click="clear" style="--wails-draggable:no-drag">清空列表</NButton>
|
||||
<NButton v-if="isProxy" secondary type="primary" @click.stop="close" style="--wails-draggable:no-drag">关闭代理</NButton>
|
||||
<NButton v-else tertiary type="tertiary" @click.stop="open" style="--wails-draggable:no-drag">开启代理</NButton>
|
||||
<NButton tertiary type="info" @click.stop="batchDown" style="--wails-draggable:no-drag">批量下载</NButton>
|
||||
<NButton tertiary type="error" @click.stop="clear" style="--wails-draggable:no-drag">清空列表</NButton>
|
||||
<NSelect style="min-width: 100px;--wails-draggable:no-drag" placeholder="拦截类型" v-model:value="resourcesType" multiple clearable :max-tag-count="3" :options="options"></NSelect>
|
||||
<NButton v-if="isDebug" tertiary type="info" @click.stop="showImport=true" style="--wails-draggable:no-drag">导入数据</NButton>
|
||||
</NSpace>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
@@ -25,12 +26,13 @@
|
||||
</div>
|
||||
<Preview v-model:showModal="showPreviewRow" :previewRow="previewRow"/>
|
||||
<ShowLoading :loadingText="loadingText" :isLoading="loading"/>
|
||||
<ImportJson v-model:showModal="showImport" @submit="handleImport"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {NButton, NImage, NTooltip} from "naive-ui"
|
||||
import {computed, h, onMounted, ref, watch} from "vue"
|
||||
import {computed, h, onMounted, ref, watch, provide} from "vue"
|
||||
import type {appType} from "@/types/app"
|
||||
|
||||
import type {DataTableRowKey, ImageRenderToolbarProps} from "naive-ui"
|
||||
@@ -40,25 +42,21 @@ import ShowLoading from "@/components/ShowLoading.vue"
|
||||
import {getDecryptionArray} from '@/assets/js/decrypt.js'
|
||||
import {useIndexStore} from "@/stores"
|
||||
import appApi from "@/api/app"
|
||||
import {DwStatus} from "@/const";
|
||||
import {DwStatus} from "@/const"
|
||||
import ResAction from "@/components/ResAction.vue"
|
||||
import {useEventStore} from "@/stores/event";
|
||||
import {BrowserOpenURL, ClipboardSetText} from "../../wailsjs/runtime";
|
||||
import ImportJson from "@/components/ImportJson.vue"
|
||||
import {useEventStore} from "@/stores/event"
|
||||
import {BrowserOpenURL, ClipboardSetText} from "../../wailsjs/runtime"
|
||||
|
||||
const eventStore = useEventStore()
|
||||
|
||||
const isProxy = computed(() => {
|
||||
return store.isProxy
|
||||
})
|
||||
|
||||
const data = ref<any[]>([])
|
||||
|
||||
const store = useIndexStore()
|
||||
|
||||
const tableHeight = computed(() => {
|
||||
return store.tableHeight - 132
|
||||
})
|
||||
|
||||
const resourcesType = ref<string[]>(["all"])
|
||||
const options = [
|
||||
{
|
||||
@@ -216,12 +214,17 @@ const columns = ref<any[]>([
|
||||
}
|
||||
])
|
||||
const downIndex = ref(0)
|
||||
|
||||
const checkedRowKeysValue = ref<DataTableRowKey[]>([])
|
||||
const showPreviewRow = ref(false)
|
||||
const previewRow = ref<appType.MediaInfo>()
|
||||
const loading = ref(false)
|
||||
const loadingText = ref("")
|
||||
const isDebug = ref(false)
|
||||
const showImport = ref(false)
|
||||
let clickCount = 0
|
||||
let clickTimeout: any = null
|
||||
|
||||
provide('isDebug', isDebug);
|
||||
|
||||
onMounted(() => {
|
||||
const temp = localStorage.getItem("resources-type")
|
||||
@@ -297,6 +300,15 @@ const dataAction = (row: appType.MediaInfo, index: number, type: string) => {
|
||||
}
|
||||
})
|
||||
break
|
||||
case "json":
|
||||
ClipboardSetText(encodeURIComponent(JSON.stringify(row))).then((is: boolean) => {
|
||||
if (is) {
|
||||
window?.$message?.success("复制成功")
|
||||
} else {
|
||||
window?.$message?.error("复制失败")
|
||||
}
|
||||
})
|
||||
break
|
||||
case "open":
|
||||
BrowserOpenURL(row.Url)
|
||||
break;
|
||||
@@ -444,4 +456,39 @@ const decodeWxFile = (row: appType.MediaInfo, index: number) => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const triggerEvent = ()=>{
|
||||
if(isDebug.value) {
|
||||
return
|
||||
}
|
||||
clickCount++
|
||||
if (clickCount === 5) {
|
||||
// 连续点击5次开启debug
|
||||
isDebug.value = true
|
||||
clickCount = 0
|
||||
} else {
|
||||
clearTimeout(clickTimeout);
|
||||
clickTimeout = setTimeout(() => {
|
||||
clickCount = 0
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
const handleImport = (content: string)=>{
|
||||
content.split("\n").forEach((line, index) => {
|
||||
try {
|
||||
let res = JSON.parse(decodeURIComponent(line))
|
||||
if (res && res?.Id) {
|
||||
res.Id = res.Id + Math.floor(Math.random() * 100000)
|
||||
res.SavePath = ""
|
||||
res.Status = "ready"
|
||||
data.value.unshift(res)
|
||||
}
|
||||
}catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
});
|
||||
localStorage.setItem("resources-data", JSON.stringify(data.value))
|
||||
showImport.value = false
|
||||
}
|
||||
</script>
|
||||
@@ -10,7 +10,7 @@
|
||||
class="px-5 py-5"
|
||||
>
|
||||
<NFormItem label="代理Host" path="Port" size="small">
|
||||
<NInput v-model:value="formValue.Host" placeholder="0.0.0.0" style="width:256px"/>
|
||||
<NInput v-model:value="formValue.Host" placeholder="127.0.0.1" style="width:256px"/>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NIcon size="20" class="pl-1">
|
||||
@@ -33,10 +33,29 @@
|
||||
</NFormItem>
|
||||
<NFormItem label="保存位置" path="SaveDirectory" size="small">
|
||||
<NSpace>
|
||||
<NInput v-model:value="formValue.SaveDirectory" disabled placeholder="保存位置" style="width:256px"/>
|
||||
<NInput :value="formValue.SaveDirectory" placeholder="保存位置" style="width:256px"/>
|
||||
<NButton strong secondary type="success" @click="selectDir">选择</NButton>
|
||||
</NSpace>
|
||||
</NFormItem>
|
||||
<NFormItem label="文件命名" path="FilenameLen" size="small">
|
||||
<NInputNumber v-model:value="formValue.FilenameLen" :min="0" :max="9999" placeholder="0" style="width:256px"/>
|
||||
<NSwitch class="pl-1" v-model:value="formValue.FilenameTime" aria-placeholder="随机数">
|
||||
<template #checked>
|
||||
是
|
||||
</template>
|
||||
<template #unchecked>
|
||||
否
|
||||
</template>
|
||||
</NSwitch>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NIcon size="20" class="pl-1">
|
||||
<HelpCircleOutline />
|
||||
</NIcon>
|
||||
</template>
|
||||
<span>输入框控制文件命名的长度(不含时间、0为无效,此选项有描述信息时有效),开关控制文件末尾是否添加时间标识</span>
|
||||
</NTooltip>
|
||||
</NFormItem>
|
||||
<NFormItem label="主题" path="theme" size="small">
|
||||
<NRadio :checked="formValue.Theme === 'lightTheme'" value="lightTheme" name="theme" @change="handleChange">浅色主题</NRadio>
|
||||
<NRadio :checked="formValue.Theme === 'darkTheme'" value="darkTheme" name="theme" @change="handleChange">深色主题</NRadio>
|
||||
@@ -76,6 +95,7 @@
|
||||
</NFormItem>
|
||||
<NFormItem label="上游代理" path="UpstreamProxy" size="small">
|
||||
<NInput v-model:value="formValue.UpstreamProxy" placeholder="例如: http://127.0.0.1:7890" style="width:256px"/>
|
||||
<NSwitch class="pl-1" v-model:value="formValue.OpenProxy" />
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NIcon size="20" class="pl-1">
|
||||
@@ -85,9 +105,6 @@
|
||||
<span>可结合其他代理工具,用于访问国外网站、以及正常网络无法访问的资源(格式http://username:password@your.proxy.server:port)</span>
|
||||
</NTooltip>
|
||||
</NFormItem>
|
||||
<NFormItem label="开启上游代理" path="OpenProxy" size="small">
|
||||
<NSwitch v-model:value="formValue.OpenProxy" />
|
||||
</NFormItem>
|
||||
<NFormItem label="下载代理" path="DownloadProxy" size="small">
|
||||
<NSwitch v-model:value="formValue.DownloadProxy" />
|
||||
<NTooltip trigger="hover">
|
||||
@@ -101,6 +118,14 @@
|
||||
</NFormItem>
|
||||
<NFormItem label="连接数" path="TaskNumber" size="small">
|
||||
<NInputNumber v-model:value="formValue.TaskNumber" :min="2" :max="64" class="w-64"/>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NIcon size="20" class="pl-1">
|
||||
<HelpCircleOutline />
|
||||
</NIcon>
|
||||
</template>
|
||||
<span>如不清楚请保持默认,通常CPU核心数*2,用于分片下载</span>
|
||||
</NTooltip>
|
||||
</NFormItem>
|
||||
<NFormItem label="UserAgent" path="UserAgent" size="small">
|
||||
<NInput v-model:value="formValue.UserAgent" style="width:256px" placeholder=""/>
|
||||
@@ -113,8 +138,16 @@
|
||||
<span>如不清楚请保持默认</span>
|
||||
</NTooltip>
|
||||
</NFormItem>
|
||||
<NFormItem label=" " path="UserAgent" size="small">
|
||||
<NButton strong secondary type="success" @click="save" class="w-20">保存</NButton>
|
||||
<NFormItem label="UseHeaders" path="UseHeaders" size="small">
|
||||
<NInput v-model:value="formValue.UseHeaders" style="width:256px" placeholder=""/>
|
||||
<NTooltip trigger="hover">
|
||||
<template #trigger>
|
||||
<NIcon size="20" class="pl-1">
|
||||
<HelpCircleOutline />
|
||||
</NIcon>
|
||||
</template>
|
||||
<span>3.0.4版本缓存了请求header信息,这个参数定义在下载时可使用的header参数: ,分割</span>
|
||||
</NTooltip>
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</div>
|
||||
@@ -150,6 +183,10 @@ const options = [
|
||||
|
||||
const formValue = ref<appType.Config>(Object.assign({}, store.globalConfig))
|
||||
|
||||
watch(formValue.value, () => {
|
||||
store.setConfig(Object.assign({}, store.globalConfig, formValue.value))
|
||||
}, {deep: true})
|
||||
|
||||
watch(()=>{
|
||||
return store.globalConfig.Theme
|
||||
}, ()=>{
|
||||
@@ -169,9 +206,4 @@ const selectDir = () => {
|
||||
window?.$message?.error(err)
|
||||
});
|
||||
}
|
||||
|
||||
const save = () => {
|
||||
store.setConfig(formValue.value)
|
||||
window?.$message?.success("保存成功")
|
||||
}
|
||||
</script>
|
||||
2
frontend/wailsjs/runtime/runtime.d.ts
vendored
@@ -134,7 +134,7 @@ export function WindowIsFullscreen(): Promise<boolean>;
|
||||
|
||||
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
|
||||
// Sets the width and height of the window.
|
||||
export function WindowSetSize(width: number, height: number): Promise<Size>;
|
||||
export function WindowSetSize(width: number, height: number): void;
|
||||
|
||||
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
|
||||
// Gets the width and height of the window.
|
||||
|
||||
43
go.mod
@@ -1,45 +1,42 @@
|
||||
module res-downloader
|
||||
|
||||
go 1.21
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.2
|
||||
|
||||
require (
|
||||
github.com/elazarl/goproxy v0.0.0-20241221210044-9faedc2f9e9f
|
||||
github.com/elazarl/goproxy v0.0.0-20241223171911-d5978cb8c956
|
||||
github.com/matoous/go-nanoid/v2 v2.1.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
|
||||
github.com/wailsapp/wails/v2 v2.9.2
|
||||
golang.org/x/sys v0.28.0
|
||||
github.com/wailsapp/wails/v2 v2.10.1
|
||||
golang.org/x/sys v0.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
|
||||
github.com/labstack/echo/v4 v4.10.2 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
|
||||
github.com/leaanthony/gosod v1.0.3 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.3 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
github.com/leaanthony/gosod v1.0.4 // indirect
|
||||
github.com/leaanthony/slicer v1.6.0 // indirect
|
||||
github.com/leaanthony/u v1.1.0 // indirect
|
||||
github.com/leaanthony/u v1.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/samber/lo v1.38.1 // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.6 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/samber/lo v1.49.1 // indirect
|
||||
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.16 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.19 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
)
|
||||
|
||||
// replace github.com/wailsapp/wails/v2 v2.9.2 => /Users/wgc/go/pkg/mod
|
||||
|
||||
100
go.sum
@@ -1,106 +1,92 @@
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elazarl/goproxy v0.0.0-20241221210044-9faedc2f9e9f h1:88OAVe185Z0YRj/KtiYdFxrEfGBlWBd6cspLJJvgMKo=
|
||||
github.com/elazarl/goproxy v0.0.0-20241221210044-9faedc2f9e9f/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/elazarl/goproxy v0.0.0-20241223171911-d5978cb8c956 h1:HyPt0ZkHkpke+HFl/4dDMz55A/AjFn7ZnLSm8GfdnwU=
|
||||
github.com/elazarl/goproxy v0.0.0-20241223171911-d5978cb8c956/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
|
||||
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/leaanthony/gosod v1.0.4 h1:YLAbVyd591MRffDgxUOU1NwLhT9T1/YiwjKZpkNFeaI=
|
||||
github.com/leaanthony/gosod v1.0.4/go.mod h1:GKuIL0zzPj3O1SdWQOdgURSuhkF+Urizzxh26t9f1cw=
|
||||
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
|
||||
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
|
||||
github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI=
|
||||
github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
|
||||
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
|
||||
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
|
||||
github.com/matoous/go-nanoid/v2 v2.1.0 h1:P64+dmq21hhWdtvZfEAofnvJULaRR1Yib0+PnU669bE=
|
||||
github.com/matoous/go-nanoid/v2 v2.1.0/go.mod h1:KlbGNQ+FhrUNIHUxZdL63t7tl4LaPkZNpUULS8H4uVM=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
|
||||
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
|
||||
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
|
||||
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68 h1:Ah2/69Z24rwD6OByyOdpJDmttftz0FTF8Q4QZ/SF1E4=
|
||||
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68/go.mod h1:EqKqAeKddSL9XSGnfXd/7iLncccKhR16HBKVva7ENw8=
|
||||
github.com/wailsapp/go-webview2 v1.0.16 h1:wffnvnkkLvhRex/aOrA3R7FP7rkvOqL/bir1br7BekU=
|
||||
github.com/wailsapp/go-webview2 v1.0.16/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
|
||||
github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
|
||||
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||
github.com/wailsapp/wails/v2 v2.9.2 h1:Xb5YRTos1w5N7DTMyYegWaGukCP2fIaX9WF21kPPF2k=
|
||||
github.com/wailsapp/wails/v2 v2.9.2/go.mod h1:uehvlCwJSFcBq7rMCGfk4rxca67QQGsbg5Nm4m9UnBs=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
github.com/wailsapp/wails/v2 v2.10.1 h1:QWHvWMXII2nI/nXz77gpPG8P3ehl6zKe+u4su5BWIns=
|
||||
github.com/wailsapp/wails/v2 v2.10.1/go.mod h1:zrebnFV6MQf9kx8HI4iAv63vsR5v67oS7GTEZ7Pz1TY=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
7
main.go
@@ -23,9 +23,12 @@ var assets embed.FS
|
||||
//go:embed build/appicon.png
|
||||
var icon []byte
|
||||
|
||||
//go:embed wails.json
|
||||
var wailsJson string
|
||||
|
||||
func main() {
|
||||
// Create an instance of the app structure
|
||||
app := core.GetApp(assets)
|
||||
app := core.GetApp(assets, wailsJson)
|
||||
isMac := runtime.GOOS == "darwin"
|
||||
// menu
|
||||
appMenu := menu.NewMenu()
|
||||
@@ -58,6 +61,8 @@ func main() {
|
||||
|_| \___| |___/ \__,_| \___/ \_/\_/ |_| |_| |_| \___/ \__ ,_| \__,_| \___| |_|`
|
||||
|
||||
log.Println(logo)
|
||||
fmt.Println("version:", app.Version)
|
||||
fmt.Println("lockfile:", app.LockFile)
|
||||
app.Startup(ctx)
|
||||
},
|
||||
OnShutdown: func(ctx context.Context) {
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"info": {
|
||||
"companyName": "res-downloader",
|
||||
"productName": "res-downloader",
|
||||
"productVersion": "3.0.0",
|
||||
"productVersion": "3.0.5",
|
||||
"copyright": "Copyright © 2023",
|
||||
"comments": "This is a high-value, high-performance, and diverse resource downloader called res-downloader."
|
||||
"comments": "This is a high-value high-performance and diverse resource downloader called res-downloader."
|
||||
}
|
||||
}
|
||||
|
||||