3 Commits

Author SHA1 Message Date
putyy
7f2b99b51f fix: upstream proxy settings 2025-05-20 17:16:05 +08:00
putyy
dc46668f0d perf: update build readme 2025-05-20 14:24:32 +08:00
putyy
84b6a142fc perf: update doc, core optimize 2025-05-20 13:53:58 +08:00
20 changed files with 175 additions and 92 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.idea
test
build/bin
node_modules
frontend/dist

BIN
build/.DS_Store vendored

Binary file not shown.

View File

@@ -32,11 +32,20 @@ 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
for f in WebKitNetworkProcess WebKitWebProcess libwebkit2gtkinjectedbundle.so; do
path=$(find /usr/lib* -name "$f" 2>/dev/null | head -n 1)
if [ -n "$path" ]; then
mkdir -p ./$(dirname "$path")
cp --parents "$path" .
else
echo "⚠️ $f not found, you may need to install libwebkit2gtk"
fi
done
popd
# 下载appimagetool
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

View File

@@ -51,8 +51,7 @@ func GetApp(assets embed.FS, wjs string) *App {
Version: version,
Description: "res-downloader是一款集网络资源嗅探 + 高速下载功能于一体的软件,高颜值、高性能和多样化,提供个人用户下载自己上传到各大平台的网络资源功能!",
Copyright: "Copyright © 2023~" + strconv.Itoa(time.Now().Year()),
PublicCrt: []byte(`
-----BEGIN CERTIFICATE-----
PublicCrt: []byte(`-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIUFAnC6268dp/z1DR9E1UepiWgWzkwDQYJKoZIhvcNAQEL
BQAwcDELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUNob25ncWluZzESMBAGA1UEBwwJ
Q2hvbmdxaW5nMQ4wDAYDVQQKDAVnb3dhczEWMBQGA1UECwwNSVQgRGVwYXJ0bWVu
@@ -76,8 +75,7 @@ e3oowvgwikqm6XR6BEcRpPkztqcKST7jPFGHiXWsAqiibc+/plMW9qebhfMXEGhQ
D8HixYbEDg==
-----END CERTIFICATE-----
`),
PrivateKey: []byte(`
-----BEGIN PRIVATE KEY-----
PrivateKey: []byte(`-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDcDt23t6ioBoHG
/Y2mOjxntWQa9dP3eNl+mAC6425DlEtyc6czNAIKuuM9wt+wAwDQAgrd5RaxdcpJ
H1JlMkEtBFkIkdn0Ag98D7nwlVA9ON3xQi5Bkl+sN/oWOE8lOwvNyNNT6ZPu3qUS

View File

@@ -46,6 +46,7 @@ func initConfig() *Config {
"Host": "127.0.0.1",
"Port": "8899",
"Theme": "lightTheme",
"Locale": "zh",
"Quality": 0,
"SaveDirectory": "",
"FilenameLen": 0,
@@ -53,7 +54,7 @@ func initConfig() *Config {
"UpstreamProxy": "",
"OpenProxy": false,
"DownloadProxy": false,
"AutoProxy": true,
"AutoProxy": false,
"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",
@@ -150,6 +151,7 @@ func initConfig() *Config {
func (c *Config) setConfig(config Config) {
oldProxy := c.UpstreamProxy
openProxy := c.OpenProxy
c.Host = config.Host
c.Port = config.Port
c.Theme = config.Theme
@@ -166,7 +168,7 @@ func (c *Config) setConfig(config Config) {
c.TaskNumber = config.TaskNumber
c.WxAction = config.WxAction
c.UseHeaders = config.UseHeaders
if oldProxy != c.UpstreamProxy {
if oldProxy != c.UpstreamProxy || openProxy != c.OpenProxy {
proxyOnce.setTransport()
}
@@ -215,13 +217,15 @@ func (c *Config) getConfig(key string) interface{} {
case "UseHeaders":
return c.UseHeaders
case "MimeMap":
mimeMux.RLock()
defer mimeMux.RUnlock()
return c.MimeMap
default:
return nil
}
}
func (c *Config) TypeSuffix(mime string) (string, string) {
func (c *Config) typeSuffix(mime string) (string, string) {
mimeMux.RLock()
defer mimeMux.RUnlock()
mime = strings.ToLower(strings.Split(mime, ";")[0])

View File

@@ -13,30 +13,19 @@ import (
"time"
)
// 定义常量
const (
MaxRetries = 3 // 最大重试次数
RetryDelay = 3 * time.Second // 重试延迟
MinPartSize = 1 * 1024 * 1024 // 最小分片大小1MB
)
// 错误定义
var (
ErrInvalidFileSize = errors.New("invalid file size")
ErrTaskFailed = errors.New("download task failed")
ErrIncompleteDownload = errors.New("incomplete download")
)
// 进度回调函数
type ProgressCallback func(totalDownloaded float64, totalSize float64, taskID int, taskProgress float64)
// 进度通道
type ProgressChan struct {
taskID int
bytes int64
}
// 下载任务
type DownloadTask struct {
taskID int
rangeStart int64
@@ -143,7 +132,7 @@ func (fd *FileDownloader) init() error {
fd.TotalSize = resp.ContentLength
if fd.TotalSize <= 0 {
return ErrInvalidFileSize
return errors.New("invalid file size")
}
if resp.Header.Get("Accept-Ranges") == "bytes" && fd.TotalSize > MinPartSize {

View File

@@ -22,17 +22,17 @@ func (p *DefaultPlugin) Domains() []string {
}
func (p *DefaultPlugin) OnRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
return nil, nil
return r, nil
}
func (p *DefaultPlugin) OnResponse(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
if resp == nil || resp.Request == nil || (resp.StatusCode != 200 && resp.StatusCode != 206) {
return nil
return resp
}
classify, suffix := p.bridge.TypeSuffix(resp.Header.Get("Content-Type"))
if classify == "" {
return nil
return resp
}
rawUrl := resp.Request.URL.String()
@@ -74,5 +74,5 @@ func (p *DefaultPlugin) OnResponse(resp *http.Response, ctx *goproxy.ProxyCtx) *
}(res)
}
return nil
return resp
}

View File

@@ -37,7 +37,7 @@ func (p *QqPlugin) OnRequest(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Requ
}
}
return r, nil
return nil, nil
}
func (p *QqPlugin) OnResponse(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {

View File

@@ -54,7 +54,7 @@ func init() {
return resourceOnce.getResType(key)
},
TypeSuffix: func(mine string) (string, string) {
return globalConfig.TypeSuffix(mine)
return globalConfig.typeSuffix(mine)
},
MediaIsMarked: func(key string) bool {
return resourceOnce.mediaIsMarked(key)
@@ -145,21 +145,22 @@ func (p *Proxy) matchPlugin(host string) shared.Plugin {
if plugin, ok := pluginRegistry[domain]; ok {
return plugin
}
return pluginRegistry["default"]
return nil
}
func (p *Proxy) httpRequestEvent(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
newReq, newResp := p.matchPlugin(r.Host).OnRequest(r, ctx)
if newResp != nil {
return newReq, newResp
}
plugin := p.matchPlugin(r.Host)
if plugin != nil {
newReq, newResp := plugin.OnRequest(r, ctx)
if newResp != nil {
return newReq, newResp
}
if newReq != nil {
return newReq, nil
if newReq != nil {
return newReq, nil
}
}
return r, nil
return pluginRegistry["default"].OnRequest(r, ctx)
}
func (p *Proxy) httpResponseEvent(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
@@ -167,10 +168,13 @@ func (p *Proxy) httpResponseEvent(resp *http.Response, ctx *goproxy.ProxyCtx) *h
return resp
}
newResp := p.matchPlugin(resp.Request.Host).OnResponse(resp, ctx)
if newResp != nil {
return newResp
plugin := p.matchPlugin(resp.Request.Host)
if plugin != nil {
newResp := plugin.OnResponse(resp, ctx)
if newResp != nil {
return newResp
}
}
return resp
return pluginRegistry["default"].OnResponse(resp, ctx)
}

View File

@@ -1,20 +1,5 @@
package shared
import (
"github.com/elazarl/goproxy"
"net/http"
)
type Bridge struct {
GetVersion func() string
GetResType func(key string) (bool, bool)
TypeSuffix func(mime string) (string, string)
MediaIsMarked func(key string) bool
MarkMedia func(key string)
GetConfig func(key string) interface{}
Send func(t string, data interface{})
}
type MediaInfo struct {
Id string
Url string
@@ -31,10 +16,3 @@ type MediaInfo struct {
ContentType string
OtherData map[string]string
}
type Plugin interface {
SetBridge(*Bridge)
Domains() []string
OnRequest(*http.Request, *goproxy.ProxyCtx) (*http.Request, *http.Response)
OnResponse(*http.Response, *goproxy.ProxyCtx) *http.Response
}

23
core/shared/plugin.go Normal file
View File

@@ -0,0 +1,23 @@
package shared
import (
"github.com/elazarl/goproxy"
"net/http"
)
type Bridge struct {
GetVersion func() string
GetResType func(key string) (bool, bool)
TypeSuffix func(mime string) (string, string)
MediaIsMarked func(key string) bool
MarkMedia func(key string)
GetConfig func(key string) interface{}
Send func(t string, data interface{})
}
type Plugin interface {
SetBridge(*Bridge)
Domains() []string
OnRequest(*http.Request, *goproxy.ProxyCtx) (*http.Request, *http.Response)
OnResponse(*http.Response, *goproxy.ProxyCtx) *http.Response
}

View File

@@ -1,4 +1,7 @@
# res-downloader V3
<div align="center">
<a href="https://github.com/putyy/res-downloader"><img src="images/logo.png" width="120"/></a>
<h1><strong>res-downloader</strong></h1>
</div>
> 全新技术栈,更新、更小、更快、更稳

View File

@@ -1,4 +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)
* [WX](https://www.putyy.com/app/admin/upload/img/20250418/6801d9554dc7.webp)

BIN
docs/images/logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View File

@@ -27,15 +27,13 @@
subMaxLevel: 4,
// 小屏设备下合并导航栏到侧边栏
mergeNavbar: true,
basePath: '/',
homepage: 'readme.md',
search: {
maxAge: 86400000,// 过期时间,单位毫秒,默认一天
paths: 'auto',// 注意:仅适用于 paths: 'auto' 模式
placeholder: '搜索',
// 支持本地化
placeholder: {
'/zh-cn/': '搜索',
'/': 'Type to search'
},
placeholder: '搜索',
noData: '找不到结果',
depth: 4,
hideOtherSidebarContent: false,

View File

@@ -10,8 +10,11 @@
- 双击下载好的dmg文件将res-downloader拖入应用即可如图:
![installation-mac-1.png](images/installation-mac-1.png ':size=50%')
## Linux安装过程
- 执行文件运行方式举例
## Linux安装过程(自行替换掉对应的安装文件目录)
- ubuntu安装deb文件
> sudo apt install res-downloader_3.0.2_linux_x64.deb
- 执行文件运行方式
> chmod +x ./res-downloader_3.0.2_linux_x64
> sudo ./res-downloader_3.0.2_linux_x64

View File

@@ -1,7 +1,3 @@
## 隐藏功能
- 导出\导入 数据1秒内连续点击空白处5次即可开启(单次有效)
![more-4.png](images/more-4.png ':size=30%')
## 清空列表、类型筛选
- 当资源列表过大时,无法快速找到需要的资源,这时可以先清空列表再去刷新需要的资源页面
- 资源列表过多,可以快速根据需要的资源类型进行筛选

View File

@@ -1,15 +1,63 @@
# res-downloader
## 爱享素材下载器
<div align="center">
🎯 基于Go + [wails](https://github.com/wailsapp/wails)
📦 操作简单、可获取不同类型的网络资源
🖥️ 支持Windows、Mac、Linux
🌐 支持视频、音频、图片、m3u8、直播流等常见网络资源
💪 支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源下载
👼 支持设置代理以获取特殊网络下的资源
<a href="https://github.com/putyy/res-downloader"><img src="images/logo.png" width="120"/></a>
<h1>res-downloader</h1>
<h4>📖 中文 | <a href="https://github.com/putyy/res-downloader/blob/master/README-EN.md">English</a></h4>
## 实现 & 初衷
?> 通过设置系统网络代理拦截响应,筛选出需要的资源, 同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的只不过这些软件需要手动进行筛选对于小白用户上手还是有点难度本软件对部分资源做了特殊处理更适合大众用户所以就有了本项目。
[![GitHub stars](https://img.shields.io/github/stars/putyy/res-downloader)](https://github.com/putyy/res-downloader/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/putyy/res-downloader)](https://github.com/putyy/res-downloader/fork)
[![GitHub release](https://img.shields.io/github/release/putyy/res-downloader)](https://github.com/putyy/res-downloader/releases)
![GitHub All Releases](https://img.shields.io/github/downloads/putyy/res-downloader/total)
[![License](https://img.shields.io/github/license/putyy/res-downloader)](https://github.com/putyy/res-downloader/blob/master/LICENSE)
## 免责声明
?> 本软件用于学习研究使用,若因使用本软件造成的一切法律责任均与本人无关!
</div>
---
### 🎉 爱享素材下载器
> 一款基于 Go + [Wails](https://github.com/wailsapp/wails) 的跨平台资源下载工具,简洁易用,支持多种资源嗅探与下载。
## ✨ 功能特色
- 🚀 **简单易用**:操作简单,界面清晰美观
- 🖥️ **多平台支持**Windows / macOS / Linux
- 🌐 **多资源类型支持**:视频 / 音频 / 图片 / m3u8 / 直播流等
- 📱 **平台兼容广泛**支持微信视频号、小程序、抖音、快手、小红书、酷狗音乐、QQ音乐等
- 🌍 **代理抓包**:支持设置代理获取受限网络下的资源
## 📚 文档 & 版本
- 📘 [在线文档](https://res.putyy.com/)
- 💬 [加入交流群](https://www.putyy.com/app/admin/upload/img/20250418/6801d9554dc7.webp)
- 🧩 [最新版](https://github.com/putyy/res-downloader/releases) [Mini版 使用默认浏览器展示UI](https://github.com/putyy/resd-mini) [Electron旧版 支持Win7](https://github.com/putyy/res-downloader/tree/old)
> *群满时可加微信 `AmorousWorld`,请备注“来源”*
## 🧩 下载地址
- 🆕 [GitHub 下载](https://github.com/putyy/res-downloader/releases)
- 🆕 [蓝奏云下载密码9vs5](https://wwjv.lanzoum.com/b04wgtfyb)
- ⚠️ *Win7 用户请下载 `2.3.0` 版本*
## 🖼️ 预览
![预览](images/show.webp)
---
### 🧠 更多问题
- [GitHub Issues](https://github.com/putyy/res-downloader/issues)
- [爱享论坛讨论帖](https://s.gowas.cn/d/4089)
## 💡 实现原理 & 初衷
本工具通过代理方式实现网络抓包,并筛选可用资源。与 Fiddler、Charles、浏览器 DevTools 原理类似,但对资源进行了更友好的筛选、展示和处理,大幅度降低了使用门槛,更适合大众用户使用。
---
## ⚠️ 免责声明
> 本软件仅供学习与研究用途,禁止用于任何商业或违法用途。
如因此产生的任何法律责任,概与作者无关!

View File

@@ -1,3 +1,34 @@
## 视频号拦截了一大堆 找不到想要的
> 设置里面关闭全量拦截,将视频转发好友后打开
## 某某网址拦截不了?
> 本软件实现原理 & 初衷如下,并非万能的,所以有一些应用拦截不了很正常
```
本工具通过代理方式实现网络抓包,并筛选可用资源。与 Fiddler、Charles、浏览器 DevTools 原理类似,但对资源进行了更友好的筛选、展示和处理,大幅度降低了使用门槛,更适合大众用户使用。
```
## 软件打不开了?之前可以打开
> 删除对应目录, 然后重启
```
## Mac执行
rm -rf /Users/$(whoami)/Library/Preferences/res-downloader
## Windows手动删除以下目录Administrator为用户名 通常如下:
C:\Users\Administrator\AppData\Roaming\res-downloader
## Linux手动删除以下目录
/home/user/.config/res-downloader/home/user/.config/res-downloader
```
## 某应用只支持手机打开 如何拦截?
> 这里需要注意的是 应用使用http协议通讯才能拦截且安卓7.0以上系统不再信任用户CA证书 所以没法拦截,解决方案自行查找,
```
1. 将手机和电脑处于同一个网络
2. 在手机端安装res-downloader的证书
3. 将手机网络代理设置为res-downloader的代理
4. 正常使用
```
## Mac 提示“已损坏,无法打开”, 打开命令行执行如下命令:
> sudo xattr -d com.apple.quarantine /Applications/res-downloader.app
@@ -10,7 +41,7 @@
> 手动关闭系统代理设置
## 链接不是私密链接
> 通常是证书未正确安装,最新版证书下载:软件左下角?点击后有下载地址
> 通常是证书未正确安装,最新版证书下载:软件左下角 ?点击后有下载地址
> 根据自己系统进行安装证书操作(不懂的自行百度),手动安装需安装到受信任的根证书
- Mac手动安装证书(V3+版本支持),打开终端复制以下命令 粘贴到终端回车 按照提示输入密码,完成后再打开软件:

View File

@@ -14,7 +14,7 @@
"password_title": "管理员授权",
"password_tip": "本次输入的密码仅在本次运行期间有效,用于安装证书或设置系统代理!",
"password_placeholder": "请输入你的电脑密码",
"password_cache": "是否缓存",
"password_cache": "缓存密码",
"password_empty": "密码不能为空",
"screen_minimize": "最小化",
"screen_restore": "还原",