24 Commits
3.0.0 ... 3.0.3

Author SHA1 Message Date
putyy
791e50411d Update issue templates 2025-02-11 21:45:52 +08:00
putyy
df8eb0e4cd Update issue templates 2025-02-11 21:44:28 +08:00
putyy
3e291171c2 Update issue templates 2025-02-11 21:43:48 +08:00
putyy
bd8f8e80c9 Merge pull request #143 from putyy/wails
Wails
2025-02-11 21:42:19 +08:00
putyy
8ed7e144e1 修改doc、优化Mac设置代理等 2025-02-11 21:40:29 +08:00
putyy
69f8224453 完善文档、新增一些功能 2025-02-10 15:40:32 +08:00
putyy
3c0e51a9e2 文档 2025-02-09 18:58:09 +08:00
putyy
0a6679b983 新增docsify 2025-02-08 17:29:36 +08:00
putyy
6d1024806c Merge pull request #131 from putyy/wails
Wails
2025-01-22 16:19:44 +08:00
putyy
388e3f46a4 更新md 2025-01-22 16:18:32 +08:00
putyy
f7c8e9f7db 更新host 2025-01-21 10:37:10 +08:00
putyy
7ca484f45d Merge pull request #130 from putyy/wails
up:wx rule、downloads
2025-01-14 14:16:21 +08:00
putyy
cecb13fa90 up:wx rule、downloads 2025-01-14 13:59:22 +08:00
putyy
331478d370 Merge pull request #127 from taotieren/update-aur
update README.md
2025-01-11 13:16:40 +08:00
taotieren
2481407093 update README.md 2025-01-11 11:45:37 +08:00
putyy
e024a812d0 Merge pull request #124 from putyy/wails
update md
2025-01-10 17:36:10 +08:00
putyy
f5b5767997 update md 2025-01-10 17:35:39 +08:00
putyy
5e7022ec81 Merge pull request #123 from putyy/wails
Linux支持
2025-01-10 17:33:40 +08:00
putyy
e54177e8bf Linux支持 2025-01-10 17:33:05 +08:00
putyy
69cc5383d1 Merge pull request #122 from taotieren/fix-linux
Add Linux install
2025-01-09 11:30:43 +08:00
taotieren
11f88e86e3 Add Linux install 2025-01-09 11:27:07 +08:00
putyy
9e87e64223 优化 2024-12-24 14:39:13 +08:00
putyy
9ffef9db8e Delete .DS_Store 2024-12-23 17:27:38 +08:00
putyy
6d2705112d 优化 2024-12-23 16:59:26 +08:00
76 changed files with 885 additions and 323 deletions

44
.github/ISSUE_TEMPLATE /bug_report.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
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 '...' \ 1. 进入 '...'
2. Click on '....' \ 2. 点击 '....'
3. Scroll down to '....' \ 3. 向下滚动到 '....'
4. See error \ 4. 查看错误
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
View File

@@ -0,0 +1 @@
blank_issues_enabled: true

View 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
View File

@@ -3,3 +3,4 @@ test
build/bin
node_modules
frontend/dist
.DS_Store

View File

@@ -1,26 +1,28 @@
## res-downloader V3全新版来袭更快了
### 爱享素材下载器【[加入群聊](https://qm.qq.com/q/HS8FdhpZCK)】
## res-downloader
### 爱享素材下载器
🎯 基于Go + [wails](https://github.com/wailsapp/wails)
📦 操作简单、可获取不同类型资源
🖥️ 支持Windows、Mac、Linux
🌐 支持视频、音频、图片、m3u8、直播流等常见网络资源
💪 支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源下载
👼 支持设置代理以获取特殊网络下的资源
👼 支持设置代理以获取特殊网络下的资源
## 软件下载
## [在线文档](https://res.putyy.com/)、[加入群聊](https://qm.qq.com/q/ImE37ayJmc)、[Electron版](https://github.com/putyy/res-downloader/tree/old)
## 软件下载(Win7下载2.3.0版本)
🆕 [github下载](https://github.com/putyy/res-downloader/releases)
🆕 [蓝奏云下载 密码:9vs5](https://wwjv.lanzoum.com/b04wgtfyb)
🆕 [蓝奏云下载 密码:9vs5](https://wwjv.lanzoum.com/b04wgtfyb)
## 使用方法
> 0. 安装时一定要同意安装证书文件、一定要允许网络访问
> 1. 打开本软件 软件首页左上角点击 “启动代理”
> 2. 软件首页选择要获取的资源类型(默认选中的全部)
> 3. 打开要捕获的源, 如:视频号、网页、小程序等等
> 4. 返回软件首页即可看到资源列表
## 使用方法
> 0. 安装时一定要同意安装证书文件、一定要允许网络访问
> 1. 打开本软件 软件首页左上角点击 “启动代理”
> 2. 软件首页选择要获取的资源类型(默认选中的全部)
> 3. 打开要捕获的源, 如:视频号、网页、小程序等等
> 4. 返回软件首页即可看到资源列表
## 软件截图
![](preview/show.webp)
![](docs/images/show.webp)
## 常见问题
m3u8: 预览和下载:

BIN
build/.DS_Store vendored

Binary file not shown.

View File

@@ -1,35 +1,82 @@
# 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).dmg"
```
## Windows
```bash
wails build -f -nsis -platform "windows/amd64" -webview2 Embed && mv -f "build/bin/res-downloader-amd64-installer.exe" "build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_win_x64.exe"
wails build -f -nsis -platform "windows/arm64" -webview2 Embed && mv -f "build/bin/res-downloader-arm64-installer.exe" "build/bin/res-downloader_$(jq -r '.info.productVersion' wails.json)_win_arm.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
# 打包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)_x64.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)_x64.AppImage
```
> 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
# 打包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)_arm.deb
```
### Arch Linux
[![Packaging status](https://repology.org/badge/vertical-allrepos/res-downloader.svg)](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
View File

@@ -0,0 +1,5 @@
!.gitkeep
debian/usr/local/bin/*
debian/DEBIAN/control
AppImage/usr/bin/*
AppImage/usr/lib/*

View File

@@ -0,0 +1 @@
res-downloader.png

View 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;

Binary file not shown.

View File

View 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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View 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

Binary file not shown.

BIN
build/linux/Debian/DEBIAN/.DS_Store vendored Normal file

Binary file not shown.

View 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

Binary file not shown.

View 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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

29
build/linux/dockerfile Normal file
View File

@@ -0,0 +1,29 @@
FROM golang:1.23.4-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

View File

@@ -14,7 +14,7 @@
!define INFO_PRODUCTNAME "res-downloader"
!endif
!ifndef INFO_PRODUCTVERSION
!define INFO_PRODUCTVERSION "3.0.0"
!define INFO_PRODUCTVERSION "3.0.3"
!endif
!ifndef INFO_COPYRIGHT
!define INFO_COPYRIGHT "Copyright © 2023"

View File

@@ -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
}

View File

@@ -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"`
@@ -28,16 +30,18 @@ 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"
}
@@ -59,9 +63,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

View File

@@ -9,6 +9,7 @@ import (
"net/url"
"os"
"path/filepath"
"strings"
"sync"
)
@@ -68,7 +69,7 @@ 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
@@ -82,6 +83,10 @@ func (fd *FileDownloader) init() error {
fd.TotalSize = resp.ContentLength
if fd.TotalSize <= 0 {
return fmt.Errorf("invalid file")
}
if resp.Header.Get("Accept-Ranges") == "bytes" && fd.TotalSize > 10485760 {
fd.IsMultiPart = true
}
@@ -89,21 +94,21 @@ func (fd *FileDownloader) init() error {
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
}

View File

@@ -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()})

View File

@@ -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)

View File

@@ -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 {
@@ -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
}

View File

@@ -113,13 +113,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 &&

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
View File

9
docs/_coverpage.md Normal file
View File

@@ -0,0 +1,9 @@
# res-downloader V3
> 全新技术栈,更新、更小、更快、更稳
### 简单、高效、轻便 (仅 ~10M)
[开始使用 Let Go](/readme.md)
[下载](/getting-started.md)

4
docs/_navbar.md Normal file
View 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
View File

@@ -0,0 +1,6 @@
* [简介](readme.md)
* [快速开始](getting-started.md)
* [安装指南](installation.md)
* [功能演示](examples.md)
* [更多说明](more.md)
* [常见问题](troubleshooting.md)

19
docs/examples.md Normal file
View File

@@ -0,0 +1,19 @@
## 开启代理
- 安装完成后开启代理,如图:
![](images/examples-1.png ':size=50%')
## 拦截资源
### 视频号
- 打开视频号即可看到本软件中拦截到的资源
![](images/examples-2.webp ':size=50%')
### 网页资源
- 浏览器打开或者其他软件内置浏览器打开的网页
- 这里演示打开百度这个网站https://www.baidu.com/
![](images/examples-4.png ':size=50%')
### 小程序、公众号、抖音、小红书、qq音乐、酷狗等应用内资源获取方式都差不多
## 下载资源到本地
- 选择你想下载的视频 点击下载即可
![](images/examples-3.png ':size=50%')

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

14
docs/getting-started.md Normal file
View File

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

BIN
docs/images/config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/images/examples-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/images/examples-2.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
docs/images/examples-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
docs/images/examples-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
docs/images/more-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
docs/images/more-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
docs/images/more-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/images/more-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

52
docs/index.html Normal file
View 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
View File

@@ -0,0 +1,18 @@
## 下载安装文件
- windows下载.exe结尾的根据自己的系统架构下载合适的安装文件通常下载带有“x64-installer.exe”结尾的文件
- Mac下载.dmg结尾即可
- Linux根据系统类型下载对应的执行文件或安装文件
## Windows安装过程
- 双击下载好的exe 正常安装即可,首次打开记得右键管理员运行
## Mac安装过程
- 双击下载好的dmg文件将res-downloader拖入应用即可如图:
![installation-mac-1.png](images/installation-mac-1.png ':size=50%')
## Linux安装过程
- 执行文件运行方式举例
> chmod +x ./res-downloader_3.0.2_linux_x64
> sudo ./res-downloader_3.0.2_linux_x64

21
docs/more.md Normal file
View File

@@ -0,0 +1,21 @@
## 隐藏功能
- 导出\导入 数据1秒内连续点击空白处5次即可开启(单次有效)
![more-4.png](images/more-4.png ':size=30%')
## 清空列表、类型筛选
- 当资源列表过大时,无法快速找到需要的资源,这时可以先清空列表再去刷新需要的资源页面
- 资源列表过多,可以快速根据需要的资源类型进行筛选
![more-4.png](images/more-1.png ':size=30%')
## 拦截想要的资源类型、批量下载
- 比如只需要视频时就选择视频类型,可以多选
![more-1.png](images/more-2.png ':size=30%')
## 复制链接、视频解密
- 复制链接可用于第三方软件进行下载,下载完成后对该视频解密,点击“视频解密”选择用其他软件下载完成后的视频文件进行解密
![more-3.png](images/more-3.png ':size=30%')
## 设置说明
!> 修改完成后记得点保存
- 几乎每项配置在软件中都有说明(鼠标悬浮在?处即可查看),此处就不进行多余讲解
![config.png](images/config.png ':size=30%')

15
docs/readme.md Normal file
View 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
View 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)

View File

@@ -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

Binary file not shown.

View 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)

View File

@@ -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"])

View 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>

View File

@@ -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)
}

View File

@@ -35,7 +35,6 @@ const minimizeWindow = () => {
WindowMinimise()
}
const maximizeWindow = () => {
console.log("maximizeWindow")
isMaximized.value = !isMaximized.value;
if (isMaximized.value) {
WindowFullscreen()

View File

@@ -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"}))

View File

@@ -1,8 +1,6 @@
export const DwStatus = {
ready: "就绪",
running: "运行中",
pause: "暂停",
wait: "等待",
error: "错误",
done: "完成",
handle: "已下载,后续处理",

View File

@@ -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
}

View File

@@ -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,6 +19,8 @@ export const useIndexStore = defineStore("index-store", () => {
Quality: 0,
SaveDirectory: "",
UpstreamProxy: "",
FilenameLen: 0,
FilenameTime: false,
OpenProxy: false,
DownloadProxy: false,
AutoProxy: false,

View File

@@ -12,6 +12,8 @@ export namespace appType {
Port: string
Quality: number
SaveDirectory: string
FilenameLen: number
FilenameTime: boolean
UpstreamProxy: string
OpenProxy: boolean
DownloadProxy: boolean
@@ -44,4 +46,14 @@ export namespace appType {
Status: string
Message: string
}
interface Message {
code: number
message: string
}
interface Handle {
type: string
event: any
}
}

View File

@@ -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
}
}

View File

@@ -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>

View File

@@ -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">
@@ -37,6 +37,25 @@
<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=""/>

33
go.mod
View File

@@ -1,11 +1,11 @@
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
@@ -15,31 +15,28 @@ require (
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.47.0 // 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.18 // 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
)
// replace github.com/wailsapp/wails/v2 v2.9.2 => /Users/wgc/go/pkg/mod

80
go.sum
View File

@@ -1,95 +1,84 @@
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.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
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.18 h1:SSSCoLA+MYikSp1U0WmvELF/4c3x5kH8Vi31TKyZ4yk=
github.com/wailsapp/go-webview2 v1.0.18/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=
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/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=
@@ -99,8 +88,5 @@ 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/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=

View File

@@ -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) {

View File

@@ -13,7 +13,7 @@
"info": {
"companyName": "res-downloader",
"productName": "res-downloader",
"productVersion": "3.0.0",
"productVersion": "3.0.3",
"copyright": "Copyright © 2023",
"comments": "This is a high-value, high-performance, and diverse resource downloader called res-downloader."
}