├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .goreleaser.yaml ├── README.md ├── assets ├── reward.png ├── screenshot1.png ├── screenshot10.png ├── screenshot11.png ├── screenshot12.png ├── screenshot13.png ├── screenshot14.png ├── screenshot2.png ├── screenshot3.png ├── screenshot4.png ├── screenshot5.png ├── screenshot6.png ├── screenshot7.png ├── screenshot8.png ├── screenshot9.png └── sponsors.svg ├── certs └── SunnyRoot.cer ├── go.mod ├── go.sum ├── inject └── main.js ├── lib ├── FileSaver.min.js └── jszip.min.js ├── main.go └── pkg ├── argv └── argv.go ├── certificate └── certificate.go ├── proxy └── proxy.go └── util └── util.go /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | release: 5 | types: [created] # 表示在创建新的 Release 时触发 6 | 7 | jobs: 8 | build-go-binary: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | goos: [windows, darwin] # 需要打包的系统 13 | goarch: [amd64, arm64] # 需要打包的架构 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: wangyoucao577/go-release-action@v1.30 17 | with: 18 | github_token: ${{ secrets.GITHUB_TOKEN }} # 一个默认的变量,用来实现往 Release 中添加文件 19 | goos: ${{ matrix.goos }} 20 | goarch: ${{ matrix.goarch }} 21 | goversion: 1.18 # 可以指定编译使用的 Golang 版本 22 | binary_name: "wx_video_download" # 可以指定二进制文件的名称 23 | extra_files: README.md # 需要包含的额外文件 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.exe 3 | dist/ 4 | 5 | wx_video_download* 6 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | # This is an example .goreleaser.yml file with some sensible defaults. 2 | # Make sure to check the documentation at https://goreleaser.com 3 | 4 | # The lines below are called `modelines`. See `:help modeline` 5 | # Feel free to remove those if you don't want/need to use them. 6 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json 7 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj 8 | 9 | version: 2 10 | 11 | before: 12 | hooks: 13 | # You may remove this if you don't use go modules. 14 | - go mod tidy 15 | # you may remove this if you don't need go generate 16 | - go generate ./... 17 | 18 | builds: 19 | - env: 20 | - CGO_ENABLED=0 21 | goos: 22 | - windows 23 | - darwin 24 | main: ./main.go 25 | binary: wx_video_download 26 | ignore: 27 | - goos: darwin 28 | goarch: "386" 29 | - goos: windows 30 | goarch: "386" 31 | 32 | archives: 33 | - format: tar.gz 34 | # this name template makes the OS and Arch compatible with the results of `uname`. 35 | name_template: >- 36 | {{ .ProjectName }}_ 37 | {{- title .Os }}_ 38 | {{- if eq .Arch "amd64" }}x86_64 39 | {{- else if eq .Arch "386" }}i386 40 | {{- else }}{{ .Arch }}{{ end }} 41 | {{- if .Arm }}v{{ .Arm }}{{ end }} 42 | # use zip for windows archives 43 | format_overrides: 44 | - goos: windows 45 | format: zip 46 | 47 | changelog: 48 | sort: asc 49 | filters: 50 | exclude: 51 | - "^docs:" 52 | - "^test:" 53 | 54 | release: 55 | footer: >- 56 | 57 | --- 58 | 59 | Released by [GoReleaser](https://github.com/goreleaser/goreleaser). 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 微信视频号下载器 2 | 3 | 体积小、使用简单、支持 macOS 和 Windows 系统。 4 | 5 | ## 250514 更新 6 | 7 | 1、修复无法下载图片视频的问题 8 | 9 | ## 25042501 更新 10 | 11 | 1、修复了下载的视频无法播放的问题 12 | 13 |
点击查看更多更新内容 14 | 15 | ## 250425 更新 16 | 17 | 1、修复了无法下载视频的问题 18 | 19 | ## 250424 更新 20 | 21 | 1、修复了 下载按钮样式不一致的问题 22 |
23 | 2、修复了 更多按钮点击不显示更多菜单的问题 24 |
25 | 3、增加了 windows 启动失败时提示是否以管理员身份运行 26 | 27 | 28 | ## 250215 更新 29 | 30 | 1、在控制台显示下载进度,当获取不到进度时显示已下载的字节数 31 |
32 | 2、在「更多」菜单中增加封面图片下载 33 |
34 | 3、自动检测当前网络设备并代理 35 |
36 | 4、支持命令行参数指定要代理的网络设备和程序使用的端口号 37 | 38 | ```bash 39 | ./wx_video_download_xxx --dev=Wi-Fi --port=1080 40 | ``` 41 | 42 | > 一般情况下无需手动指定设备与端口号,直接 ./wx_video_download_xxx 即可 43 | 44 | ## 241216 更新 45 | 46 | 1、支持下载直播回放 47 |
48 | 2、支持 macOS 系统 49 |
50 | 在微信 Version 3.8.9 (28564) 测试可用 51 | 52 | macOS 下使用说明 53 | 54 | ```bash 55 | chmod +x ./wx_video_download_drawin_xxx 56 | sudo ./wx_video_download_drawin_xxx 57 | ``` 58 | 59 | 此时会提示文件不能打开,需要到系统设置中允许,然后重新执行 `sudo ./wx_video_download_drawin_xxx`。 60 |
61 | 62 | 在安装证书的过程中会申请权限,同意即可。后续打开无需使用 `sudo`,只需要双击运行 63 |
64 | 65 | 关闭 `macOS` 终端时请使用 `Command + c` 的方式,否则可能会出现系统代理未取消,导致网络无法访问的问题 66 |
67 | 68 | > 当出现网络无法访问时请检查系统代理并手动取消即可。 69 | 70 |
71 | 72 | 73 | ## 241106 更新 74 | 75 | 1、修复了 非首次打开的视频,下载下来都无法播放的问题 76 | 77 | 现在点击页面上「更多推荐」视频,下载下来的视频可以正常打开播放了。 78 |
79 | 当出现「解密失败,停止下载」的提示,关闭全部视频页面、窗口。重新打开,就可以下载。 80 | 81 | ## 241104 更新 82 | 83 | 1、 支持下载不同质量的视频 84 |
85 | 2、 修复了下载的视频无法拖动进度条的问题 86 |
87 | 3、 修复了长视频内容进度未加载就下载,导致视频无法播放或不完整的问题 88 |
89 | 4、 修复了某些视频误判断为图片导致无法下载的问题 90 |
91 | 5、 修复了直播间一直加载中的问题 92 | 93 | ![下载按钮2](assets/screenshot13.png) 94 | 95 | 关于不同质量的视频,详情见下方使用说明。 96 | 97 | ## 241102 更新 98 | 99 | 在「更多」下拉菜单增加「下载视频」按钮,兼容不同详情页布局不同导致没有下载按钮的问题。 100 | 101 | ![下载按钮2](assets/screenshot10.png) 102 | 103 | ## 241101 更新 104 | 105 | 现在无需手动下载证书并安装了。 106 |
107 | 修复了下载时提示找不到 `lib/jszip.min.js` 的问题。 108 | 109 | ## 241031 更新 110 | 111 | 又遇到之前无法在页面下载的问题,这次改成了下载压缩包,视频在压缩包内的形式。 112 | 目前是可行的,但无法保证之后仍然可行。 113 | 114 | 建议使用 [WechatVideoSniffer2.0](https://github.com/kanadeblisst00/WechatVideoSniffer2.0) 稳定性更高。 115 | 116 | ## 241030 更新 117 | 118 | 当视频号内容是多张图片时,也会出现下载按钮。点击将会下载一个包含了全部图片的压缩包。 119 | 120 | ## 241022 更新 121 | 122 | 当视频被删除时没有正确地显示「被删除」而是一直处于加载中状态。 123 | 下载按钮修改成和其他操作按钮相同的样式。 124 | 125 | ## 241016 更新 126 | 127 | 前一个版本又下载不了,改回在页面直接下载又正常了,是和微信客户端版本有关吗,对这块不了解。 128 | 如果 241016 这个版本用不了,可以试试其他版本。 129 | 我目前微信客户端版本是 `Weixin 3.9.12.17`,可以正常下载的。 130 | 131 | ## 241011 更新 132 | 133 | 应该是视频号又改版了,不能直接在页面下载了。改成点击下载按钮复制视频链接到粘贴板,然后到谷歌或其他浏览器打开下载。 134 | 另外测试了很多视频都可以直接下载,没有加密了。所以如果有加密视频,新版本可能会下载失败。 135 | 136 | > 在页面直接下载,理论上还是能实现,实现上要麻烦许多,后面再研究。 137 | 138 |
139 | 140 | ## 使用说明 141 | 142 | 下载[二进制文件](https://github.com/ltaoo/wx_channels_download/releases),**以管理员身份运行**,首次打开会自动安装证书,然后启动服务。 143 | 144 | 当终端提示「服务已正确启动」就说明可以使用了。 145 | 146 | ![正常使用](assets/screenshot8.png) 147 | 148 | > 已安装证书会跳过安装证书步骤。 149 | 150 | 打开微信 PC 端,点击需要下载的视频,在视频下方的操作按钮一栏,会多出一个下载按钮,如下所示 151 | 152 | ![视频下载按钮](assets/screenshot1.png) 153 | 154 | > 如果没有,可以看看「更多」这里是否有「下载视频」按钮。
> ![下载按钮2](assets/screenshot10.png) 155 | 156 | 等待视频开始播放,然后暂停视频,点击下载按扭即可下载视频。下载成功后,会在上方显示已下载的文件,下载文件名最后面会标志该视频质量。 157 | 158 | ![视频下载成功](assets/screenshot2.png) 159 | 160 | 默认会下载下拉菜单中第一个质量视频。点开更多,可以下载其他质量的视频,包括原始视频。 161 | 162 | ![下载不同质量的视频](assets/screenshot13.png) 163 |
164 | 165 | 不同视频这里显示的选项是不同的,没有找到对 xWT111 具体的说明,属于什么分辨率、尺寸多大等等。 166 |
167 | 经过测试,如果原始视频有 104MB,这里尺寸最大的是 xWT111 为 17MB,最小的是 xWT98 为 7MB。 168 | 169 | ![不同质量视频尺寸统计](assets/screenshot14.png) 170 | 171 | 仅供参考。 172 | 173 | ## 常见问题 174 | 175 | 1、服务启动了,打开视频详情后一直在加载,而且终端没有日志信息。 176 |
177 | 尝试在终端 `Ctrl+C`,按一次即可。 178 | 179 | 2、解密失败,停止下载 180 |
181 | 关闭全部视频页面、窗口。重新打开,就可以下载。 182 | 183 | ## 开发说明 184 | 185 | 先以 管理员身份 启动终端,然后 `go run main.go` 即可。 186 | 187 | ## 打包 188 | 189 | ```bash 190 | go build -o wx_video_download.exe main.go 191 | ``` 192 | 193 | 打包后可以使用 `upx` 压缩,体积可以从 17MB 压缩到 5MB。 194 | 195 | ## 其他 196 | 197 | 此程序大部分参考自以下项目代码 198 |
199 | https://github.com/kanadeblisst00/WechatVideoSniffer2.0 200 | 201 | 此程序的核心实现依赖以下库 202 |
203 | https://github.com/qtgolang/SunnyNet 204 | 205 | ## 我的赞赏码 206 | 207 | 如果我的项目对你有所帮助,可以请我喝杯咖啡 ☕️ 208 | 209 | [![Sponsors](https://sponsorkit-iota.vercel.app/api/sponsors)](https://sponsorkit-iota.vercel.app/api/sponsors) 210 | -------------------------------------------------------------------------------- /assets/reward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/reward.png -------------------------------------------------------------------------------- /assets/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot1.png -------------------------------------------------------------------------------- /assets/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot10.png -------------------------------------------------------------------------------- /assets/screenshot11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot11.png -------------------------------------------------------------------------------- /assets/screenshot12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot12.png -------------------------------------------------------------------------------- /assets/screenshot13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot13.png -------------------------------------------------------------------------------- /assets/screenshot14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot14.png -------------------------------------------------------------------------------- /assets/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot2.png -------------------------------------------------------------------------------- /assets/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot3.png -------------------------------------------------------------------------------- /assets/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot4.png -------------------------------------------------------------------------------- /assets/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot5.png -------------------------------------------------------------------------------- /assets/screenshot6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot6.png -------------------------------------------------------------------------------- /assets/screenshot7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot7.png -------------------------------------------------------------------------------- /assets/screenshot8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot8.png -------------------------------------------------------------------------------- /assets/screenshot9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ltaoo/wx_channels_download/64fb9925405a5e8f258ebeaf4bdeff65c43fa08c/assets/screenshot9.png -------------------------------------------------------------------------------- /assets/sponsors.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | Sponsors 19 | 20 | qq2952151753 21 | 22 | 23 | 24 | 25 | 26 | 27 | 书杰 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /certs/SunnyRoot.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDwjCCAqqgAwIBAgIRAQAAAAAAAAAAAAAAAAAAAAAwDQYJKoZIhvcNAQELBQAw 3 | ajELMAkGA1UEBhMCQ04xEDAOBgNVBAgTB0JlaUppbmcxEDAOBgNVBAcTB0JlaUpp 4 | bmcxETAPBgNVBAoTCFN1bm55TmV0MREwDwYDVQQLEwhTdW5ueU5ldDERMA8GA1UE 5 | AxMIU3VubnlOZXQwIBcNMjIxMTA0MDcwNTM0WhgPMjEyMjEwMTEwNzA1MzRaMGox 6 | CzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlKaW5nMRAwDgYDVQQHEwdCZWlKaW5n 7 | MREwDwYDVQQKEwhTdW5ueU5ldDERMA8GA1UECxMIU3VubnlOZXQxETAPBgNVBAMT 8 | CFN1bm55TmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzU+hPfoE 9 | y+4+VZVUhfb0wKF7YSr79GyxNCo8/l8i1gI3pbaxv4PF/W5xWdE3LHND6b8FVmot 10 | pXqJcalx2FP48JdAKsmlzEZcl3MngHsKH7OPSvz8p76xvlHaFutVQjQFr8R3dX3B 11 | m8yNy6sNcP+3IrxOEUYsMWc5/lVHTyTYkruMAvCZIYzcc5Y2YXzExENbfPxwzNQh 12 | H/XsZlc4FGaZq6DV/0oMOXSSFOXcuJo2ULW/bOQho2jZ2zG1mf+Te3i8Psoanrrf 13 | sMXiOjB6ZH4tKv+O9NjJJi5o64Ulh35lt4qTHwGQD6pMs3yJn/l+N7kv85amLJzi 14 | fBSbJ1eYhjUpPQIDAQABo2EwXzAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYI 15 | KwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKC5 16 | TwkvGBAx7xu1CyvX5chP7zOdMA0GCSqGSIb3DQEBCwUAA4IBAQDDAl162QjUsv7H 17 | 1+pn7MT/RDcqXNqBAUEc9FF6ozkRnLxdWBMLWxI8KHKm8JoBQB+TLiokSkenfMtA 18 | 7eRX7xzCBghuLi2XjMDUlaoVVKp+HNNoPSyn+UE/lUlKoCJCFgyt5p+bp9MP+YDm 19 | pOnNjZTktyvwRj+Bgm1USzVY3IXlV+/H9la3vRi/G5n+yl3ZQMjwh6erbqwUzd6X 20 | 8j/L3BdoOkrOHzpodiAmp7Mf105Nh77EoUsh13TJy1CJLrIJzMDO1ryhzuVyxbJA 21 | evcsWTxTr9qR/P09XImwOFmFKNimKC8IGwP/xVxqdH9WapsX6VZV5NbRG8vnqaM5 22 | V6TbUzep 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module wx_channel 2 | 3 | go 1.22 4 | 5 | toolchain go1.23.4 6 | 7 | require ( 8 | github.com/fatih/color v1.17.0 9 | github.com/qtgolang/SunnyNet v1.0.3 10 | ) 11 | 12 | require ( 13 | github.com/Trisia/gosysproxy v1.1.0 // indirect 14 | github.com/mattn/go-colorable v0.1.13 // indirect 15 | github.com/mattn/go-isatty v0.0.20 // indirect 16 | golang.org/x/crypto v0.31.0 // indirect 17 | golang.org/x/sys v0.28.0 // indirect 18 | golang.org/x/text v0.21.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Trisia/gosysproxy v1.1.0 h1:rBU1mazMXLsZiiaAHXtRoDPt2gzWdA6uxjaLskzaoAA= 2 | github.com/Trisia/gosysproxy v1.1.0/go.mod h1:PTPPgpRpyTJEL/FMxEE2OcQcGNbUm961xZcgihwraZM= 3 | github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= 4 | github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 5 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 6 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 7 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 8 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 9 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 10 | github.com/qtgolang/SunnyNet v1.0.3 h1:E0e9ojaphPjsHKhQ5/WvDcaQWiZjld7NPB2QEZff1Do= 11 | github.com/qtgolang/SunnyNet v1.0.3/go.mod h1:7ijev0f9p7N8ietfayQMkLFEw6SjK5hviWNfCa7NCCQ= 12 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 13 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 14 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 15 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 16 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 17 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 18 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 19 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 20 | -------------------------------------------------------------------------------- /inject/main.js: -------------------------------------------------------------------------------- 1 | const defaultRandomAlphabet = 2 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 3 | function __wx_uid__() { 4 | return random_string(12); 5 | } 6 | /** 7 | * 返回一个指定长度的随机字符串 8 | * @param length 9 | * @returns 10 | */ 11 | function random_string(length) { 12 | return random_string_with_alphabet(length, defaultRandomAlphabet); 13 | } 14 | function random_string_with_alphabet(length, alphabet) { 15 | let b = new Array(length); 16 | let max = alphabet.length; 17 | for (let i = 0; i < b.length; i++) { 18 | let n = Math.floor(Math.random() * max); 19 | b[i] = alphabet[n]; 20 | } 21 | return b.join(""); 22 | } 23 | function sleep() { 24 | return new Promise((resolve) => { 25 | setTimeout(() => { 26 | resolve(); 27 | }, 1000); 28 | }); 29 | } 30 | function __wx_channels_copy(text) { 31 | const textArea = document.createElement("textarea"); 32 | textArea.value = text; 33 | textArea.style.cssText = "position: absolute; top: -999px; left: -999px;"; 34 | document.body.appendChild(textArea); 35 | textArea.select(); 36 | document.execCommand("copy"); 37 | document.body.removeChild(textArea); 38 | } 39 | function __wx_channel_loading() { 40 | if (window.__wx_channels_tip__ && window.__wx_channels_tip__.loading) { 41 | return window.__wx_channels_tip__.loading("下载中"); 42 | } 43 | return { 44 | hide() {}, 45 | }; 46 | } 47 | function __wx_log(msg) { 48 | fetch("/__wx_channels_api/tip", { 49 | method: "POST", 50 | headers: { 51 | "Content-Type": "application/json", 52 | }, 53 | body: JSON.stringify(msg), 54 | }); 55 | } 56 | function __wx_channels_video_decrypt(t, e, p) { 57 | for ( 58 | var r = new Uint8Array(t), n = 0; 59 | n < t.byteLength && e + n < p.decryptor_array.length; 60 | n++ 61 | ) 62 | r[n] ^= p.decryptor_array[n]; 63 | return r; 64 | } 65 | window.VTS_WASM_URL = 66 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/decrypt-video-core/1.3.0/wasm_video_decode.wasm"; 67 | window.MAX_HEAP_SIZE = 33554432; 68 | var decryptor_array; 69 | let decryptor; 70 | /** t 是要解码的视频内容长度 e 是 decryptor_array 的长度 */ 71 | function wasm_isaac_generate(t, e) { 72 | decryptor_array = new Uint8Array(e); 73 | var r = new Uint8Array(Module.HEAPU8.buffer, t, e); 74 | decryptor_array.set(r.reverse()); 75 | if (decryptor) { 76 | decryptor.delete(); 77 | } 78 | } 79 | let loaded = false; 80 | /** 获取 decrypt_array */ 81 | async function __wx_channels_decrypt(seed) { 82 | if (!loaded) { 83 | await __wx_load_script( 84 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/decrypt-video-core/1.3.0/wasm_video_decode.js" 85 | ); 86 | loaded = true; 87 | } 88 | await sleep(); 89 | decryptor = new Module.WxIsaac64(seed); 90 | // 调用该方法时,会调用 wasm_isaac_generate 方法 91 | // 131072 是 decryptor_array 的长度 92 | decryptor.generate(131072); 93 | // decryptor.delete(); 94 | // const r = Uint8ArrayToBase64(decryptor_array); 95 | // decryptor_array = undefined; 96 | return decryptor_array; 97 | } 98 | async function show_progress_or_loaded_size(response) { 99 | const content_length = response.headers.get("Content-Length"); 100 | const chunks = []; 101 | const total_size = content_length ? parseInt(content_length, 10) : 0; 102 | if (total_size) { 103 | __wx_log({ 104 | msg: `${total_size} Bytes`, 105 | }); 106 | } 107 | let loaded_size = 0; 108 | const reader = response.body.getReader(); 109 | while (true) { 110 | const { done, value } = await reader.read(); 111 | if (done) { 112 | break; 113 | } 114 | chunks.push(value); 115 | loaded_size += value.length; 116 | if (total_size) { 117 | const progress = (loaded_size / total_size) * 100; 118 | __wx_log({ 119 | msg: `${progress.toFixed(2)}%`, 120 | }); 121 | } else { 122 | __wx_log({ 123 | msg: `${loaded_size} Bytes`, 124 | }); 125 | } 126 | } 127 | const blob = new Blob(chunks); 128 | return blob; 129 | } 130 | /** 用于下载已经播放的视频内容 */ 131 | async function __wx_channels_download(profile, filename) { 132 | console.log("__wx_channels_download"); 133 | const data = profile.data; 134 | const blob = new Blob(data, { type: "video/mp4" }); 135 | await __wx_load_script( 136 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/FileSaver.min.js" 137 | ); 138 | saveAs(blob, filename + ".mp4"); 139 | } 140 | /** 下载非加密视频 */ 141 | async function __wx_channels_download2(profile, filename) { 142 | console.log("__wx_channels_download2"); 143 | const url = profile.url; 144 | // __wx_log({ 145 | // msg: `${filename} 146 | // ${url} 147 | // ${profile.key}`, 148 | // }); 149 | await __wx_load_script( 150 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/FileSaver.min.js" 151 | ); 152 | const ins = __wx_channel_loading(); 153 | const response = await fetch(url); 154 | const blob = await show_progress_or_loaded_size(response); 155 | __wx_log({ 156 | msg: "下载完成", 157 | }); 158 | ins.hide(); 159 | saveAs(blob, filename + ".mp4"); 160 | } 161 | /** 下载图片视频 */ 162 | async function __wx_channels_download3(profile, filename) { 163 | console.log("__wx_channels_download3"); 164 | const files = profile.files; 165 | await __wx_load_script( 166 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/FileSaver.min.js" 167 | ); 168 | await __wx_load_script( 169 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/jszip.min.js" 170 | ); 171 | const zip = new JSZip(); 172 | zip.file("contact.txt", JSON.stringify(profile.contact, null, 2)); 173 | const folder = zip.folder("images"); 174 | console.log("files", files) 175 | const fetchPromises = files 176 | .map((f) => f.url) 177 | .map(async (url, index) => { 178 | const response = await fetch(url); 179 | const blob = await response.blob(); 180 | folder.file(index + 1 + ".png", blob); 181 | }); 182 | const ins = __wx_channel_loading(); 183 | try { 184 | await Promise.all(fetchPromises); 185 | const content = await zip.generateAsync({ type: "blob" }); 186 | ins.hide(); 187 | saveAs(content, filename + ".zip"); 188 | } catch (err) { 189 | __wx_log({ 190 | msg: "下载失败\n" + err.message, 191 | }); 192 | } 193 | } 194 | /** 下载加密视频 */ 195 | async function __wx_channels_download4(profile, filename) { 196 | console.log("__wx_channels_download4"); 197 | const url = profile.url; 198 | // console.log("__wx_channels_download4", url); 199 | // __wx_log({ 200 | // msg: `${filename} 201 | // ${url}`, 202 | // }); 203 | await __wx_load_script( 204 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/FileSaver.min.js" 205 | ); 206 | const ins = __wx_channel_loading(); 207 | const response = await fetch(url); 208 | const blob = await show_progress_or_loaded_size(response); 209 | __wx_log({ 210 | msg: "下载完成,开始解密", 211 | }); 212 | let array = new Uint8Array(await blob.arrayBuffer()); 213 | if (profile.decryptor_array) { 214 | array = __wx_channels_video_decrypt(array, 0, profile); 215 | } 216 | ins.hide(); 217 | const result = new Blob([array], { type: "video/mp4" }); 218 | saveAs(result, filename + ".mp4"); 219 | } 220 | function __wx_load_script(src) { 221 | return new Promise((resolve, reject) => { 222 | const script = document.createElement("script"); 223 | script.type = "text/javascript"; 224 | script.src = src; 225 | script.onload = resolve; 226 | script.onerror = reject; 227 | document.head.appendChild(script); 228 | }); 229 | } 230 | function __wx_channels_handle_copy__() { 231 | __wx_channels_copy(location.href); 232 | if (window.__wx_channels_tip__ && window.__wx_channels_tip__.toast) { 233 | window.__wx_channels_tip__.toast("复制成功", 1e3); 234 | } 235 | } 236 | async function __wx_channels_handle_log__() { 237 | await __wx_load_script( 238 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/FileSaver.min.js" 239 | ); 240 | const content = document.body.innerHTML; 241 | const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); 242 | saveAs(blob, "log.txt"); 243 | } 244 | async function __wx_channels_handle_click_download__(spec) { 245 | var profile = __wx_channels_store__.profile; 246 | // profile = __wx_channels_store__.profiles.find((p) => p.id === profile.id); 247 | if (!profile) { 248 | alert("检测不到视频,请将本工具更新到最新版"); 249 | return; 250 | } 251 | // console.log(__wx_channels_store__); 252 | var filename = (() => { 253 | if (profile.title) { 254 | return profile.title; 255 | } 256 | if (profile.id) { 257 | return profile.id; 258 | } 259 | return new Date().valueOf(); 260 | })(); 261 | const _profile = { 262 | ...profile, 263 | }; 264 | if (spec) { 265 | _profile.url = profile.url + "&X-snsvideoflag=" + spec.fileFormat; 266 | filename = filename + "_" + spec.fileFormat; 267 | } 268 | // console.log("__wx_channels_handle_click_download__", url); 269 | __wx_log({ 270 | msg: `${filename} 271 | ${location.href} 272 | ${_profile.url} 273 | ${_profile.key || ""}`, 274 | }); 275 | if (_profile.type === "picture") { 276 | __wx_channels_download3(_profile, filename); 277 | return; 278 | } 279 | if (!_profile.key) { 280 | __wx_channels_download2(_profile, filename); 281 | return; 282 | } 283 | _profile.data = __wx_channels_store__.buffers; 284 | try { 285 | const r = await __wx_channels_decrypt(_profile.key); 286 | // console.log("[]after __wx_channels_decrypt", r); 287 | _profile.decryptor_array = r; 288 | } catch (err) { 289 | __wx_log({ 290 | msg: `解密失败,停止下载`, 291 | }); 292 | alert("解密失败,停止下载"); 293 | return; 294 | } 295 | __wx_channels_download4(_profile, filename); 296 | } 297 | function __wx_channels_download_cur__() { 298 | var profile = __wx_channels_store__.profile; 299 | if (!profile) { 300 | alert("检测不到视频,请将本工具更新到最新版"); 301 | return; 302 | } 303 | if (__wx_channels_store__.buffers.length === 0) { 304 | alert("没有可下载的内容"); 305 | return; 306 | } 307 | var filename = (() => { 308 | if (profile.title) { 309 | return profile.title; 310 | } 311 | if (profile.id) { 312 | return profile.id; 313 | } 314 | return new Date().valueOf(); 315 | })(); 316 | profile.data = __wx_channels_store__.buffers; 317 | __wx_channels_download(profile, filename); 318 | } 319 | async function __wx_channels_handle_download_cover() { 320 | var profile = __wx_channels_store__.profile; 321 | // profile = __wx_channels_store__.profiles.find((p) => p.id === profile.id); 322 | if (!profile) { 323 | alert("检测不到视频,请将本工具更新到最新版"); 324 | return; 325 | } 326 | // console.log(__wx_channels_store__); 327 | var filename = (() => { 328 | if (profile.title) { 329 | return profile.title; 330 | } 331 | if (profile.id) { 332 | return profile.id; 333 | } 334 | return new Date().valueOf(); 335 | })(); 336 | const _profile = { 337 | ...profile, 338 | }; 339 | await __wx_load_script( 340 | "https://res.wx.qq.com/t/wx_fed/cdn_libs/res/FileSaver.min.js" 341 | ); 342 | __wx_log({ 343 | msg: `下载封面\n${_profile.coverUrl}`, 344 | }); 345 | const ins = __wx_channel_loading(); 346 | try { 347 | const url = _profile.coverUrl.replace(/^http/, "https"); 348 | const response = await fetch(url); 349 | const blob = await response.blob(); 350 | saveAs(blob, filename + ".jpg"); 351 | } catch (err) { 352 | alert(err.message); 353 | } 354 | ins.hide(); 355 | } 356 | var __wx_channels_tip__ = {}; 357 | var __wx_channels_store__ = { 358 | profile: null, 359 | profiles: [], 360 | keys: {}, 361 | buffers: [], 362 | }; 363 | var $icon = document.createElement("div"); 364 | $icon.innerHTML = 365 | '
'; 366 | var __wx_channels_video_download_btn__ = $icon.firstChild; 367 | __wx_channels_video_download_btn__.onclick = () => { 368 | if (!window.__wx_channels_store__.profile) { 369 | return; 370 | } 371 | __wx_channels_handle_click_download__( 372 | window.__wx_channels_store__.profile.spec[0] 373 | ); 374 | }; 375 | var count = 0; 376 | fetch("/__wx_channels_api/tip", { 377 | method: "POST", 378 | headers: { 379 | "Content-Type": "application/json", 380 | }, 381 | body: JSON.stringify({ 382 | msg: "等待注入下载按钮", 383 | }), 384 | }); 385 | var __timer = setInterval(() => { 386 | count += 1; 387 | // const $wrap1 = document.getElementsByClassName("feed-card-wrap")[0]; 388 | // const $wrap2 = document.getElementsByClassName( 389 | // "operate-row transition-show" 390 | // )[0]; 391 | const $wrap3 = document.getElementsByClassName("full-opr-wrp layout-row")[0]; 392 | if (!$wrap3) { 393 | if (count >= 5) { 394 | clearInterval(__timer); 395 | __timer = null; 396 | fetch("/__wx_channels_api/tip", { 397 | method: "POST", 398 | headers: { 399 | "Content-Type": "application/json", 400 | }, 401 | body: JSON.stringify({ 402 | msg: "没有找到操作栏,注入下载按钮失败\n请在「更多」菜单中下载", 403 | }), 404 | }); 405 | } 406 | return; 407 | } 408 | clearInterval(__timer); 409 | __timer = null; 410 | const relative_node = $wrap3.children[$wrap3.children.length - 1]; 411 | if (!relative_node) { 412 | fetch("/__wx_channels_api/tip", { 413 | method: "POST", 414 | headers: { 415 | "Content-Type": "application/json", 416 | }, 417 | body: JSON.stringify({ msg: "注入下载按钮成功1!" }), 418 | }); 419 | $wrap3.appendChild(__wx_channels_video_download_btn__); 420 | return; 421 | } 422 | fetch("/__wx_channels_api/tip", { 423 | method: "POST", 424 | headers: { 425 | "Content-Type": "application/json", 426 | }, 427 | body: JSON.stringify({ msg: "注入下载按钮成功2!" }), 428 | }); 429 | $wrap3.insertBefore(__wx_channels_video_download_btn__, relative_node); 430 | }, 1000); 431 | -------------------------------------------------------------------------------- /lib/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | (function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)}); 2 | -------------------------------------------------------------------------------- /lib/jszip.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZip v3.10.1 - A JavaScript class for generating and reading zip files 4 | 5 | 6 | (c) 2009-2016 Stuart Knightley 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. 8 | 9 | JSZip uses the library pako released under the MIT license : 10 | https://github.com/nodeca/pako/blob/main/LICENSE 11 | */ 12 | 13 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r` 274 | // script2 := ` 278 | // 282 | // 286 | // 287 | // ` 304 | 305 | if host == "channels.weixin.qq.com" && (path == "/web/pages/feed" || path == "/web/pages/home") { 306 | // Conn.Response.Header.Add("wx-channel-video-download", "1") 307 | script := fmt.Sprintf(``, main_js) 308 | html = strings.Replace(html, "", "\n"+script+script2, 1) 309 | fmt.Println("1. 视频详情页 html 注入 js 成功") 310 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer([]byte(html))) 311 | return 312 | } 313 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer([]byte(html))) 314 | return 315 | } 316 | if content_type == "application/javascript" { 317 | content := string(Body) 318 | dep_reg := regexp.MustCompile(`"js/([^"]{1,})\.js"`) 319 | from_reg := regexp.MustCompile(`from {0,1}"([^"]{1,})\.js"`) 320 | lazy_import_reg := regexp.MustCompile(`import\("([^"]{1,})\.js"\)`) 321 | import_reg := regexp.MustCompile(`import {0,1}"([^"]{1,})\.js"`) 322 | content = from_reg.ReplaceAllString(content, `from"$1.js`+v+`"`) 323 | content = dep_reg.ReplaceAllString(content, `"js/$1.js`+v+`"`) 324 | content = lazy_import_reg.ReplaceAllString(content, `import("$1.js`+v+`")`) 325 | content = import_reg.ReplaceAllString(content, `import"$1.js`+v+`"`) 326 | Conn.Response.Header.Set("__debug", "replace_script") 327 | 328 | if util.Includes(path, "/t/wx_fed/finder/web/web-finder/res/js/index.publish") { 329 | regexp1 := regexp.MustCompile(`this.sourceBuffer.appendBuffer\(h\),`) 330 | replaceStr1 := `(() => { 331 | if (window.__wx_channels_store__) { 332 | window.__wx_channels_store__.buffers.push(h); 333 | } 334 | })(),this.sourceBuffer.appendBuffer(h),` 335 | if regexp1.MatchString(content) { 336 | fmt.Println("2. 视频播放 js 修改成功") 337 | } 338 | content = regexp1.ReplaceAllString(content, replaceStr1) 339 | regexp2 := regexp.MustCompile(`if\(f.cmd===re.MAIN_THREAD_CMD.AUTO_CUT`) 340 | replaceStr2 := `if(f.cmd==="CUT"){ 341 | if (window.__wx_channels_store__) { 342 | console.log("CUT", f, __wx_channels_store__.profile.key); 343 | window.__wx_channels_store__.keys[__wx_channels_store__.profile.key]=f.decryptor_array; 344 | } 345 | } 346 | if(f.cmd===re.MAIN_THREAD_CMD.AUTO_CUT` 347 | content = regexp2.ReplaceAllString(content, replaceStr2) 348 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer([]byte(content))) 349 | return 350 | } 351 | if util.Includes(path, "/t/wx_fed/finder/web/web-finder/res/js/virtual_svg-icons-register") { 352 | regexp1 := regexp.MustCompile(`async finderGetCommentDetail\((\w+)\)\{return(.*?)\}async`) 353 | replaceStr1 := `async finderGetCommentDetail($1) { 354 | var feedResult = await$2; 355 | var data_object = feedResult.data.object; 356 | if (!data_object.objectDesc) { 357 | return feedResult; 358 | } 359 | var media = data_object.objectDesc.media[0]; 360 | var profile = media.mediaType !== 4 ? { 361 | type: "picture", 362 | id: data_object.id, 363 | title: data_object.objectDesc.description, 364 | files: data_object.objectDesc.media, 365 | spec: [], 366 | contact: data_object.contact 367 | } : { 368 | type: "media", 369 | duration: media.spec[0].durationMs, 370 | spec: media.spec, 371 | title: data_object.objectDesc.description, 372 | coverUrl: media.coverUrl, 373 | url: media.url+media.urlToken, 374 | size: media.fileSize, 375 | key: media.decodeKey, 376 | id: data_object.id, 377 | nonce_id: data_object.objectNonceId, 378 | nickname: data_object.nickname, 379 | createtime: data_object.createtime, 380 | fileFormat: media.spec.map(o => o.fileFormat), 381 | contact: data_object.contact 382 | }; 383 | fetch("/__wx_channels_api/profile", { 384 | method: "POST", 385 | headers: { 386 | "Content-Type": "application/json" 387 | }, 388 | body: JSON.stringify(profile) 389 | }); 390 | if (window.__wx_channels_store__) { 391 | __wx_channels_store__.profile = profile; 392 | window.__wx_channels_store__.profiles.push(profile); 393 | } 394 | return feedResult; 395 | }async` 396 | if regexp1.MatchString(content) { 397 | fmt.Println("3. 视频详情页 js 修改成功") 398 | } 399 | content = regexp1.ReplaceAllString(content, replaceStr1) 400 | regex2 := regexp.MustCompile(`i.default={dialog`) 401 | replaceStr2 := `i.default=window.window.__wx_channels_tip__={dialog` 402 | content = regex2.ReplaceAllString(content, replaceStr2) 403 | regex5 := regexp.MustCompile(`this.updateDetail\(o\)`) 404 | replaceStr5 := `(() => { 405 | if (Object.keys(o).length===0){ 406 | return; 407 | } 408 | var data_object = o; 409 | var media = data_object.objectDesc.media[0]; 410 | var profile = media.mediaType !== 4 ? { 411 | type: "picture", 412 | id: data_object.id, 413 | title: data_object.objectDesc.description, 414 | files: data_object.objectDesc.media, 415 | spec: [], 416 | contact: data_object.contact 417 | } : { 418 | type: "media", 419 | duration: media.spec[0].durationMs, 420 | spec: media.spec, 421 | title: data_object.objectDesc.description, 422 | url: media.url+media.urlToken, 423 | size: media.fileSize, 424 | key: media.decodeKey, 425 | id: data_object.id, 426 | nonce_id: data_object.objectNonceId, 427 | nickname: data_object.nickname, 428 | createtime: data_object.createtime, 429 | fileFormat: media.spec.map(o => o.fileFormat), 430 | contact: data_object.contact 431 | }; 432 | if (window.__wx_channels_store__) { 433 | window.__wx_channels_store__.profiles.push(profile); 434 | } 435 | })(),this.updateDetail(o)` 436 | content = regex5.ReplaceAllString(content, replaceStr5) 437 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer([]byte(content))) 438 | return 439 | } 440 | if util.Includes(path, "/t/wx_fed/finder/web/web-finder/res/js/FeedDetail.publish") { 441 | regex := regexp.MustCompile(`,"投诉"\)]`) 442 | replaceStr := `,"投诉_update"),...(() => { 443 | if (window.__wx_channels_store__ && window.__wx_channels_store__.profile) { 444 | return window.__wx_channels_store__.profile.spec.map((sp) => { 445 | return f("div",{class:"context-item",role:"button",onClick:() => __wx_channels_handle_click_download__(sp)},sp.fileFormat); 446 | }); 447 | } 448 | })(),f("div",{class:"context-item",role:"button",onClick:()=>__wx_channels_handle_click_download__()},"原始视频"),f("div",{class:"context-item",role:"button",onClick:__wx_channels_download_cur__},"当前视频"),f("div",{class:"context-item",role:"button",onClick:()=>__wx_channels_handle_download_cover()},"下载封面"),f("div",{class:"context-item",role:"button",onClick:__wx_channels_handle_copy__},"复制页面链接")]` 449 | content = regex.ReplaceAllString(content, replaceStr) 450 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer([]byte(content))) 451 | return 452 | } 453 | if util.Includes(path, "worker_release") { 454 | regex := regexp.MustCompile(`fmp4Index:p.fmp4Index`) 455 | replaceStr := `decryptor_array:p.decryptor_array,fmp4Index:p.fmp4Index` 456 | content = regex.ReplaceAllString(content, replaceStr) 457 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer([]byte(content))) 458 | return 459 | } 460 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer([]byte(content))) 461 | return 462 | } 463 | Conn.Response.Body = io.NopCloser(bytes.NewBuffer(Body)) 464 | } 465 | 466 | } 467 | if Conn.Type == public.HttpRequestFail { 468 | //请求错误 469 | // Body := []byte("Hello Sunny Response") 470 | // Conn.Response = &http.Response{ 471 | // Body: io.NopCloser(bytes.NewBuffer(Body)), 472 | // } 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /pkg/argv/argv.go: -------------------------------------------------------------------------------- 1 | package argv 2 | 3 | // https://github.com/levicook/argmapper/blob/master/argmapper.go 4 | type Map map[string]string 5 | 6 | func ArgsToMap(args []string) (m Map) { 7 | m = make(Map) 8 | if len(args) == 0 { 9 | return 10 | } 11 | 12 | nextopt: 13 | for i, s := range args { 14 | // does s look like an option? 15 | if len(s) > 1 && s[0] == '-' { 16 | k := "" 17 | v := "" 18 | 19 | num_minuses := 1 20 | if s[1] == '-' { 21 | num_minuses++ 22 | } 23 | 24 | k = s[num_minuses:] 25 | if len(k) == 0 || k[0] == '-' || k[0] == '=' { 26 | continue nextopt 27 | } 28 | 29 | for i := 1; i < len(k); i++ { // equals cannot be first 30 | if k[i] == '=' { 31 | v = k[i+1:] 32 | k = k[0:i] 33 | break 34 | } 35 | } 36 | 37 | // It must have a value, which might be the next arg, assuming the next arg isn't an option too. 38 | remaining := args[i+1:] 39 | if v == "" && len(remaining) > 0 && remaining[0][0] != '-' { 40 | v = remaining[0] 41 | } // value is the next arg 42 | m[k] = v 43 | } 44 | } 45 | return m 46 | } 47 | 48 | // 获取指定参数名的值,获取失败返回默认值(多个参数名则返回最先找到的值) 49 | func ArgsValue(margs Map, def string, keys ...string) (value string) { 50 | value = def // 默认值 51 | for _, key := range keys { 52 | if v, ok := margs[key]; ok && v != "" { // 找到参数 53 | value = v // 存储该值 54 | break 55 | } 56 | } 57 | return value 58 | } 59 | -------------------------------------------------------------------------------- /pkg/certificate/certificate.go: -------------------------------------------------------------------------------- 1 | package certificate 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "regexp" 9 | "runtime" 10 | "strings" 11 | ) 12 | 13 | type Subject struct { 14 | CN string 15 | OU string 16 | O string 17 | L string 18 | S string 19 | C string 20 | } 21 | type Certificate struct { 22 | Thumbprint string 23 | Subject Subject 24 | } 25 | 26 | func fetchCertificatesInWindows() ([]Certificate, error) { 27 | // 获取指定 store 所有证书 28 | cmd := fmt.Sprintf("Get-ChildItem Cert:\\LocalMachine\\Root") 29 | ps := exec.Command("powershell.exe", "-Command", cmd) 30 | output, err2 := ps.CombinedOutput() 31 | if err2 != nil { 32 | return nil, errors.New(fmt.Sprintf("获取证书时发生错误,%v\n", err2.Error())) 33 | } 34 | var certificates []Certificate 35 | lines := strings.Split(string(output), "\n") 36 | // 跳过前两行(列名) 37 | for i := 2; i < len(lines)-1; i++ { 38 | line := strings.TrimSpace(lines[i]) 39 | if line == "" { 40 | continue 41 | } 42 | parts := strings.Split(line, " ") 43 | if len(parts) >= 2 { 44 | subject := Subject{} 45 | for _, part := range parts[1:] { 46 | part = strings.Replace(part, ",", "", 1) 47 | kv := strings.Split(part, "=") 48 | if len(kv) == 2 { 49 | key := strings.TrimSpace(kv[0]) 50 | value := strings.TrimSpace(kv[1]) 51 | switch key { 52 | case "CN": 53 | subject.CN = value 54 | case "OU": 55 | subject.OU = value 56 | case "O": 57 | subject.O = value 58 | case "L": 59 | subject.L = value 60 | case "S": 61 | subject.S = value 62 | case "C": 63 | subject.C = value 64 | } 65 | } 66 | } 67 | certificates = append(certificates, Certificate{ 68 | Thumbprint: parts[0], 69 | Subject: subject, 70 | }) 71 | } 72 | } 73 | return certificates, nil 74 | } 75 | func fetchCertificatesInMacOS() ([]Certificate, error) { 76 | cmd := exec.Command("security", "find-certificate", "-a") 77 | output, err2 := cmd.Output() 78 | if err2 != nil { 79 | return nil, errors.New(fmt.Sprintf("获取证书时发生错误,%v\n", err2.Error())) 80 | } 81 | var certificates []Certificate 82 | lines := strings.Split(string(output), "\n") 83 | for i := 0; i < len(lines)-1; i += 13 { 84 | if lines[i] == "" { 85 | continue 86 | } 87 | // if i > len(lines)-1 { 88 | // continue 89 | // } 90 | cenc := lines[i+5] 91 | ctyp := lines[i+6] 92 | hpky := lines[i+7] 93 | labl := lines[i+9] 94 | subj := lines[i+12] 95 | re := regexp.MustCompile(`="([^"]{1,})"`) 96 | // 找到匹配的字符串 97 | matches := re.FindStringSubmatch(labl) 98 | if len(matches) < 1 { 99 | continue 100 | } 101 | label := matches[1] 102 | certificates = append(certificates, Certificate{ 103 | Thumbprint: "", 104 | Subject: Subject{ 105 | CN: label, 106 | OU: cenc, 107 | O: ctyp, 108 | L: hpky, 109 | S: subj, 110 | C: cenc, 111 | }, 112 | }) 113 | } 114 | return certificates, nil 115 | } 116 | 117 | func fetchCertificates() ([]Certificate, error) { 118 | os_env := runtime.GOOS 119 | switch os_env { 120 | case "linux": 121 | fmt.Println("Running on Linux") 122 | case "darwin": 123 | return fetchCertificatesInMacOS() 124 | case "windows": 125 | return fetchCertificatesInWindows() 126 | default: 127 | fmt.Printf("Running on %s\n", os_env) 128 | } 129 | return nil, errors.New(fmt.Sprintf("unknown OS\n")) 130 | 131 | } 132 | func CheckCertificate(cert_name string) (bool, error) { 133 | certificates, err := fetchCertificates() 134 | if err != nil { 135 | return false, err 136 | } 137 | for _, cert := range certificates { 138 | if cert.Subject.CN == cert_name { 139 | return true, nil 140 | } 141 | } 142 | return false, nil 143 | } 144 | func removeCertificate() { 145 | // 删除指定证书 146 | // Remove-Item "Cert:\LocalMachine\Root\D70CD039051F77C30673B8209FC15EFA650ED52C" 147 | } 148 | func installCertificateInWindows(cert_data []byte) error { 149 | cert_file, err := os.CreateTemp("", "SunnyRoot.cer") 150 | if err != nil { 151 | return errors.New(fmt.Sprintf("没有创建证书的权限,%v\n", err.Error())) 152 | } 153 | defer os.Remove(cert_file.Name()) 154 | if _, err := cert_file.Write(cert_data); err != nil { 155 | return errors.New(fmt.Sprintf("获取证书失败,%v\n", err.Error())) 156 | } 157 | if err := cert_file.Close(); err != nil { 158 | return errors.New(fmt.Sprintf("生成证书失败,%v\n", err.Error())) 159 | } 160 | cmd := fmt.Sprintf("Import-Certificate -FilePath '%s' -CertStoreLocation Cert:\\LocalMachine\\Root", cert_file.Name()) 161 | ps := exec.Command("powershell.exe", "-Command", cmd) 162 | output, err2 := ps.CombinedOutput() 163 | if err2 != nil { 164 | return errors.New(fmt.Sprintf("安装证书时发生错误,%v\n", output)) 165 | } 166 | return nil 167 | } 168 | func installCertificateInMacOS(cert_data []byte) error { 169 | cert_file, err := os.CreateTemp("", "SunnyRoot.cer") 170 | if err != nil { 171 | return errors.New(fmt.Sprintf("没有创建证书的权限,%v\n", err.Error())) 172 | } 173 | defer os.Remove(cert_file.Name()) 174 | if _, err := cert_file.Write(cert_data); err != nil { 175 | return errors.New(fmt.Sprintf("获取证书失败,%v\n", err.Error())) 176 | } 177 | if err := cert_file.Close(); err != nil { 178 | return errors.New(fmt.Sprintf("生成证书失败,%v\n", err.Error())) 179 | } 180 | cmd := fmt.Sprintf("security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain '%s'", cert_file.Name()) 181 | ps := exec.Command("bash", "-c", cmd) 182 | output, err2 := ps.CombinedOutput() 183 | if err2 != nil { 184 | return errors.New(fmt.Sprintf("安装证书时发生错误,%v\n", output)) 185 | } 186 | return nil 187 | } 188 | 189 | func InstallCertificate(cert_data []byte) error { 190 | os_env := runtime.GOOS 191 | switch os_env { 192 | case "linux": 193 | fmt.Println("Running on Linux") 194 | case "darwin": 195 | return installCertificateInMacOS(cert_data) 196 | case "windows": 197 | return installCertificateInWindows(cert_data) 198 | default: 199 | fmt.Printf("Running on %s\n", os_env) 200 | } 201 | return errors.New(fmt.Sprintf("unknown OS\n")) 202 | } 203 | -------------------------------------------------------------------------------- /pkg/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | type ProxySettings struct { 11 | Device string 12 | Hostname string 13 | Port string 14 | } 15 | 16 | func (p ProxySettings) WithDefaults() ProxySettings { 17 | if p.Device == "" { 18 | p.Device = "Wi-Fi" // 默认使用 Wi-Fi 设备 19 | device, err := getNetworkInterfaces() 20 | if err == nil { 21 | p.Device = device.Port 22 | } 23 | } 24 | if p.Hostname == "" { 25 | p.Hostname = "127.0.0.1" 26 | } 27 | if p.Port == "" { 28 | p.Port = "2023" 29 | } 30 | return p 31 | } 32 | 33 | type HardwarePort struct { 34 | Device string 35 | Port string 36 | Interface string 37 | } 38 | 39 | func EnableProxyInMacOS(args ProxySettings) error { 40 | args = args.WithDefaults() 41 | cmd1 := exec.Command("networksetup", "-setwebproxy", args.Device, args.Hostname, args.Port) 42 | _, err1 := cmd1.Output() 43 | if err1 != nil { 44 | return fmt.Errorf("设置 HTTP 代理失败,%v", err1.Error()) 45 | } 46 | cmd2 := exec.Command("networksetup", "-setsecurewebproxy", args.Device, args.Hostname, args.Port) 47 | output, err2 := cmd2.Output() 48 | if err2 != nil { 49 | return fmt.Errorf("设置 HTTPS 代理失败,%v", output) 50 | } 51 | return nil 52 | } 53 | 54 | func DisableProxyInMacOS(args ProxySettings) error { 55 | args = args.WithDefaults() 56 | cmd1 := exec.Command("networksetup", "-setwebproxystate", args.Device, "off") 57 | _, err1 := cmd1.Output() 58 | if err1 != nil { 59 | return fmt.Errorf("禁用 HTTP 代理失败,%v", err1.Error()) 60 | } 61 | cmd2 := exec.Command("networksetup", "-setsecurewebproxystate", args.Device, "off") 62 | _, err2 := cmd2.Output() 63 | if err2 != nil { 64 | return fmt.Errorf("禁用 HTTPS 代理失败,%v", err2.Error()) 65 | } 66 | return nil 67 | } 68 | 69 | func getNetworkInterfaces() (*HardwarePort, error) { 70 | // 获取所有硬件端口信息 71 | cmd := exec.Command("networksetup", "-listallhardwareports") 72 | output, err := cmd.Output() 73 | if err != nil { 74 | return nil, fmt.Errorf("执行 networksetup 命令失败: %v", err) 75 | } 76 | // 解析硬件端口信息 77 | var ports []HardwarePort 78 | lines := strings.Split(string(output), "\n") 79 | 80 | var cur_port HardwarePort 81 | for _, line := range lines { 82 | line = strings.TrimSpace(line) 83 | if strings.HasPrefix(line, "Hardware Port:") { 84 | if cur_port.Port != "" { 85 | ports = append(ports, cur_port) 86 | } 87 | cur_port = HardwarePort{} 88 | cur_port.Port = strings.TrimPrefix(line, "Hardware Port: ") 89 | } else if strings.HasPrefix(line, "Device:") { 90 | cur_port.Device = strings.TrimPrefix(line, "Device: ") 91 | } 92 | } 93 | if cur_port.Port != "" { 94 | ports = append(ports, cur_port) 95 | } 96 | // 获取网络接口信息 97 | cmd = exec.Command("scutil", "--nwi") 98 | output, err = cmd.Output() 99 | if err != nil { 100 | return nil, fmt.Errorf("执行 scutil 命令失败: %v", err) 101 | } 102 | // 使用正则解析接口信息 103 | re := regexp.MustCompile(`Network interfaces{0,1}: ([0-9a-zA-Z]{1,})`) 104 | matches := re.FindStringSubmatch(string(output)) 105 | // 将接口信息与硬件端口匹配 106 | if len(matches) >= 2 { 107 | for i := range ports { 108 | if ports[i].Device == matches[1] { 109 | return &ports[i], nil 110 | } 111 | } 112 | } 113 | return nil, fmt.Errorf("未找到硬件端口信息") 114 | } 115 | -------------------------------------------------------------------------------- /pkg/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "strings" 4 | 5 | func Includes(str, substr string) bool { 6 | return strings.Contains(str, substr) 7 | } 8 | --------------------------------------------------------------------------------