Compare commits

..

8 Commits

Author SHA1 Message Date
ZxwyWebSite
9155f34105 2024-06-22 v1.0.3.0622 2024-06-22 23:34:25 +08:00
ZxwyWebSite
3437281f73 fix action build 2024-06-15 01:32:06 +08:00
ZxwyWebSite
55e7c6227e 2024-06-14 v1.0.3.0614 2024-06-15 01:03:38 +08:00
ZxwyWebSite
76e7f5a1b6 2024-05-25 Special Ver 2024-05-25 02:02:14 +08:00
ZxwyWebSite
70238162c1 fix remote module address 2024-05-25 01:43:31 +08:00
Zxwy
d7a2563a98
Merge pull request #30 from ZxwyWebSite/go1.20
support go1.20 and action build
2024-05-25 01:37:47 +08:00
ZxwyWebSite
05b8f3766c support go1.20 and action build 2024-05-25 01:26:48 +08:00
ZxwyWebSite
30b3260617 2024-05-18 v1.0.3.0518 2024-05-18 19:22:22 +08:00
27 changed files with 1662 additions and 74 deletions

107
.github/workflows/action.yml vendored Normal file
View File

@ -0,0 +1,107 @@
name: Action
on:
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Checkout git repo
uses: actions/checkout@v4
with:
path: ./repo
fetch-depth: 0
- name: Set up Golang
uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install Dependencies
run: |
go version && go env && export PATH=$PATH:$(go env GOPATH)/bin
go install golang.org/dl/go1.20.14@latest && go1.20.14 download && go1.20.14 version
sudo apt-get update
sudo apt-get -y install gcc-mingw-w64-x86-64
sudo apt-get -y install gcc-arm-linux-gnueabihf libc6-dev-armhf-cross
sudo apt-get -y install gcc-aarch64-linux-gnu libc6-dev-arm64-cross
wget -q https://dl.google.com/android/repository/android-ndk-r26b-linux.zip && unzip -d ~ android-ndk-r26b-linux.zip && rm android-ndk-r26b-linux.zip
- name: Fetch Modules
run: |
wget -q "https://r2eu.zxwy.link/gh/lx-source/static/ztool_20240525.zip" -O ztool.zip && unzip ztool.zip && rm ztool.zip
wget -q "https://r2eu.zxwy.link/gh/lx-source/static/cr-go-sdk_20240525.zip" -O cr-go-sdk.zip && unzip cr-go-sdk.zip && rm cr-go-sdk.zip
- name: Run Action
run: cd repo && go run action.go && mv dist ../
- name: Short SHA
uses: benjlevesque/short-sha@v3.0
id: short-sha
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: lx-source-bin_${{ env.SHA }}
path: ./dist
- name: Generate Changelog
run: cd repo && echo PACKAGE_VERSION=`go run release.go` >> $GITHUB_ENV && mv changelog.md ../
- name: Create git tag
uses: pkgdeps/git-tag-action@v3
with:
github_token: ${{ github.token }}
github_repo: ${{ github.repository }}
version: ${{ env.PACKAGE_VERSION }}
git_commit_sha: ${{ github.sha }}
git_tag_prefix: "v"
- name: Release
uses: softprops/action-gh-release@v2
with:
body_path: ./changelog.md
prerelease: false
draft: false
tag_name: v${{ env.PACKAGE_VERSION }}
files: |
./dist/lx-source-android-386.zip
./dist/lx-source-android-amd64.zip
./dist/lx-source-android-arm.zip
./dist/lx-source-android-arm64.zip
./dist/lx-source-darwin-amd64v2-go1.20.14.zip
./dist/lx-source-darwin-amd64v3-go1.20.14.zip
./dist/lx-source-darwin-arm64-go1.20.14.zip
./dist/lx-source-linux-amd64v1.zip
./dist/lx-source-linux-amd64v2.zip
./dist/lx-source-linux-amd64v3.zip
./dist/lx-source-linux-amd64v4.zip
./dist/lx-source-linux-arm5.zip
./dist/lx-source-linux-arm5-go1.20.14.zip
./dist/lx-source-linux-arm6.zip
./dist/lx-source-linux-arm6-go1.20.14.zip
./dist/lx-source-linux-arm64.zip
./dist/lx-source-linux-arm64-go1.20.14.zip
./dist/lx-source-linux-arm7.zip
./dist/lx-source-linux-arm7-go1.20.14.zip
./dist/lx-source-linux-mips64hardfloat-go1.20.14.zip
./dist/lx-source-linux-mips64lehardfloat-go1.20.14.zip
./dist/lx-source-linux-mips64lesoftfloat-go1.20.14.zip
./dist/lx-source-linux-mips64softfloat-go1.20.14.zip
./dist/lx-source-linux-mipshardfloat-go1.20.14.zip
./dist/lx-source-linux-mipslehardfloat-go1.20.14.zip
./dist/lx-source-linux-mipslesoftfloat-go1.20.14.zip
./dist/lx-source-linux-mipssoftfloat-go1.20.14.zip
./dist/lx-source-windows-amd64v1-go1.20.14.zip
./dist/lx-source-windows-amd64v2-go1.20.14.zip
./dist/lx-source-windows-amd64v2.zip
./dist/lx-source-windows-amd64v3-go1.20.14.zip
./dist/lx-source-windows-amd64v3.zip
./dist/lx-source-windows-amd64v4.zip
env:
GITHUB_TOKEN: ${{ github.token }}

3
.gitignore vendored
View File

@ -5,5 +5,4 @@ data/
# conf.ini
test.go
test_test.go
# src/sources/builtin/
rsrc_windows_amd64.syso
# src/sources/builtin/

532
action.go Normal file
View File

@ -0,0 +1,532 @@
//go:build ignore
package main
import (
"archive/zip"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
const (
// 运行参数
args_name = `lx-source` // 程序名称
args_path = `dist/` // 输出目录
args_zpak = true // 打包文件
// args_repo = `repo/` // 源码目录
args_home = `/home/runner` // 用户目录
)
var (
workDir string
homeDir string
)
func init() {
if runtime.GOOS != `linux` {
fmt.Println(`不兼容的运行环境:`, runtime.GOOS)
os.Exit(0)
}
workDir, _ = os.Getwd()
fmt.Println(`运行目录:`, workDir)
homeDir = os.Getenv(`HOME`)
if homeDir == `` {
homeDir = args_home
}
fmt.Println(`用户目录:`, homeDir)
}
type (
// 架构参数 [v2]
list_vers map[string]struct {
Tags string
}
// CGO参数 [nil]
list_cgos struct {
AR string
CC string
CXX string
}
// 架构列表 [amd64]
list_arch map[string]struct {
Cgos *list_cgos
Vers list_vers
Venv string // 覆盖架构参数名 'mipsle'->'GOMIPS'
}
// 目标系统 [linux]
list_goos map[string]struct {
Arch list_arch
}
// 编译环境 [go1.20.14]
list_conf map[string]struct {
Args []string
GoOS list_goos
}
)
// 构建参数
var def_args = []string{
`-trimpath`, `-buildvcs=false`,
`-ldflags`, `-s -w -linkmode external`,
}
type param struct {
GoVer string // 环境 go1.20.14
GoOS string // 系统 linux
GoArch string // 架构 amd64
GoIns string // 指令 GOAMD64=v2
Args []string // 参数 ldflags
Tag string // 标志 go_json
Cgos *list_cgos
Venv string
}
// 获取相对用户目录
func home(str string) string {
return homeDir + `/` + str
}
// 检测环境是否存在
func chkenv(s ...string) (err error) {
for _, f := range s {
if _, e := exec.LookPath(f); e != nil && !errors.Is(e, exec.ErrDot) {
err = fmt.Errorf(`未找到指定环境: %s`, e)
break
}
}
return
}
func main() {
var def_list = list_conf{
`go`: {
Args: def_args,
GoOS: list_goos{
`linux`: {
Arch: list_arch{
`amd64`: {
Cgos: &list_cgos{
AR: `x86_64-linux-gnu-ar`,
CC: `x86_64-linux-gnu-gcc`,
CXX: `x86_64-linux-gnu-g++`,
},
Vers: list_vers{
`v1`: {
Tags: `go_json`,
},
`v2`: {
Tags: `go_json`,
},
`v3`: {
Tags: `sonic avx`,
},
`v4`: {
Tags: `sonic avx`,
},
},
},
`arm`: {
Cgos: &list_cgos{
AR: `arm-linux-gnueabihf-gcc-ar`,
CC: `arm-linux-gnueabihf-gcc`,
CXX: `arm-linux-gnueabihf-cpp`,
},
Vers: list_vers{
`5`: {
Tags: `go_json`,
},
`6`: {
Tags: `go_json`,
},
`7`: {
Tags: `go_json`,
},
},
},
`arm64`: {
Cgos: &list_cgos{
AR: `aarch64-linux-gnu-gcc-ar`,
CC: `aarch64-linux-gnu-gcc`,
CXX: `aarch64-linux-gnu-cpp`,
},
Vers: list_vers{
``: {
Tags: `go_json`,
},
},
},
},
},
`windows`: {
Arch: list_arch{
`amd64`: {
Cgos: &list_cgos{
AR: `x86_64-w64-mingw32-ar`,
CC: `x86_64-w64-mingw32-gcc`,
CXX: `x86_64-w64-mingw32-cpp`,
},
Vers: list_vers{
`v2`: {
Tags: `go_json`,
},
`v3`: {
Tags: `sonic avx`,
},
`v4`: {
Tags: `sonic avx`,
},
},
},
},
},
`android`: {
Arch: list_arch{
`amd64`: {
Cgos: &list_cgos{
AR: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`),
CC: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang`),
CXX: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang++`),
},
Vers: list_vers{
``: {
Tags: `go_json`,
},
},
},
`arm64`: {
Cgos: &list_cgos{
AR: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`),
CC: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang`),
CXX: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++`),
},
Vers: list_vers{
``: {
Tags: `go_json`,
},
},
},
`386`: {
Cgos: &list_cgos{
AR: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`),
CC: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang`),
CXX: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang++`),
},
Vers: list_vers{
``: {
Tags: `go_json`,
},
},
},
`arm`: {
Cgos: &list_cgos{
AR: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar`),
CC: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang`),
CXX: home(`android-ndk-r26b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++`),
},
Vers: list_vers{
``: {
Tags: `go_json`,
},
},
},
},
},
},
},
home(`go/bin/go1.20.14`): {
Args: []string{
`-trimpath`, `-buildvcs=false`,
`-ldflags`, `-s -w -extldflags '-v -static'`,
},
GoOS: list_goos{
`windows`: {
Arch: list_arch{
`amd64`: {
Cgos: &list_cgos{
AR: `x86_64-w64-mingw32-ar`,
CC: `x86_64-w64-mingw32-gcc`,
CXX: `x86_64-w64-mingw32-cpp`,
},
Vers: list_vers{
`v1`: {
Tags: `go_json`,
},
`v2`: {
Tags: `go_json`,
},
`v3`: {
Tags: `sonic avx`,
},
},
},
},
},
`linux`: {
Arch: list_arch{
`arm`: {
Cgos: &list_cgos{
AR: `arm-linux-gnueabihf-gcc-ar`,
CC: `arm-linux-gnueabihf-gcc`,
CXX: `arm-linux-gnueabihf-cpp`,
},
Vers: list_vers{
`5`: {
Tags: `go_json`,
},
`6`: {
Tags: `go_json`,
},
`7`: {
Tags: `go_json`,
},
},
},
`arm64`: {
Cgos: &list_cgos{
AR: `aarch64-linux-gnu-gcc-ar`,
CC: `aarch64-linux-gnu-gcc`,
CXX: `aarch64-linux-gnu-cpp`,
},
Vers: list_vers{
``: {
Tags: `go_json`,
},
},
},
// 针对部分OpenWrt路由器系统 暂不支持开启CGO
`mips`: {
Vers: list_vers{
`hardfloat`: {
Tags: `go_json`,
},
`softfloat`: {
Tags: `go_json`,
},
},
},
`mipsle`: {
Vers: list_vers{
`hardfloat`: {
Tags: `go_json`,
},
`softfloat`: {
Tags: `go_json`,
},
},
Venv: `MIPS`,
},
`mips64`: {
Vers: list_vers{
`hardfloat`: {
Tags: `go_json`,
},
`softfloat`: {
Tags: `go_json`,
},
},
},
`mips64le`: {
Vers: list_vers{
`hardfloat`: {
Tags: `go_json`,
},
`softfloat`: {
Tags: `go_json`,
},
},
Venv: `MIPS64`,
},
},
},
// Mac OS
`darwin`: {
Arch: list_arch{
`amd64`: {
Vers: list_vers{
`v2`: {
Tags: `go_json`,
},
`v3`: {
Tags: `sonic avx`,
},
},
},
`arm64`: {
Vers: list_vers{
``: {
Tags: `go_json`,
},
},
},
},
},
},
},
}
fmt.Printf(`
================================
| Action 一键编译脚本
| 程序名称%v
| 输出目录%v
| 打包文件%v
================================
`, args_name, args_path, args_zpak)
// 解析配置文件
for goVer, conf_list := range def_list {
// 环境检测
if err := chkenv(goVer); err != nil {
fmt.Println(err, `跳过该环境`)
continue
}
for goOS, goos_list := range conf_list.GoOS {
for goArch, arch_list := range goos_list.Arch {
// 工具链检测
if arch_list.Cgos != nil {
if err := chkenv(
arch_list.Cgos.AR,
arch_list.Cgos.CC,
arch_list.Cgos.CXX,
); err != nil {
fmt.Println(err, `跳过该架构`)
continue
}
}
for goIns, vers_list := range arch_list.Vers {
// 构建程序二进制
if err := build(&param{
GoVer: goVer,
GoOS: goOS,
GoArch: goArch,
GoIns: goIns,
Args: conf_list.Args,
Tag: vers_list.Tags,
Cgos: arch_list.Cgos,
Venv: arch_list.Venv,
}); err != nil {
fmt.Println(`err:`, err)
}
}
}
}
}
fmt.Println(`执行结束`)
}
func build(p *param) (err error) {
// 拼接程序名称
var b strings.Builder
b.WriteString(args_name) // lx-source
b.WriteByte('-') // lx-source-
b.WriteString(p.GoOS) // lx-source-linux
b.WriteByte('-') // lx-source-linux-
b.WriteString(p.GoArch) // lx-source-linux-amd64
/*var digit byte
if p.Venv != `` {
digit = p.Venv[len(p.Venv)-1]
} else {
digit = p.GoArch[len(p.GoArch)-1]
}
if !unicode.IsDigit(rune(digit)) {
// 架构名结尾不是数字的再加一个连字符
b.WriteByte('-') // lx-source-linux-mipsle-softfloat
}*/
b.WriteString(p.GoIns) // lx-source-linux-amd64v2
if biname := filepath.Base(p.GoVer); biname != `go` {
b.WriteByte('-') // lx-source-linux-amd64v2-
b.WriteString(biname) // lx-source-linux-amd64v2-go1.20.14
}
// 拼接输出名称
oname := args_path + b.String() // dist/lx-source-linux-amd64v2
if p.GoOS == `windows` {
oname += `.exe` // dist/lx-source-linux-amd64v2.exe
}
fmt.Println(`开始编译:`, oname)
fmt.Printf("编译参数: %+v\n", *p)
// 填入参数并构建
var args = []string{
`build`, `-o`, oname,
// `-asmflags=-trimpath="` + workDir + `"`,
// `-gcflags=-trimpath="` + workDir + `"`,
`-tags`, p.Tag,
}
cmd := exec.Command(
p.GoVer,
// append(append(args, p.Args...), args_repo)...,
append(args, p.Args...)...,
)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = cmd.Stdout
cmd.Dir = workDir
cmd.Env = append(os.Environ(), []string{
`GOOS=` + p.GoOS,
`GOARCH=` + p.GoArch,
}...)
if p.Cgos != nil {
cmd.Env = append(cmd.Env, []string{
`AR=` + p.Cgos.AR,
`CC=` + p.Cgos.CC,
`CXX=` + p.Cgos.CXX,
`CGO_ENABLED=1`,
}...)
} /*else {
cmd.Env = append(cmd.Env, `CGO_ENABLED=0`)
}*/
if p.GoIns != `` {
if p.Venv != `` {
cmd.Env = append(cmd.Env, `GO`+p.Venv+`=`+p.GoIns)
} else {
cmd.Env = append(cmd.Env, `GO`+strings.ToUpper(p.GoArch)+`=`+p.GoIns)
}
}
if err = cmd.Start(); err == nil {
err = cmd.Wait()
}
if err != nil || !args_zpak {
return
}
// 打包输出文件
/*apath := filepath.Join(args_path, `archieve`)
if _, e := os.Stat(apath); e != nil {
if os.IsNotExist(e) {
err = os.MkdirAll(apath, os.ModePerm)
if err != nil {
return
}
}
}*/
zipname := filepath.Join(args_path, b.String()+`.zip`)
fmt.Println(`打包文件:`, zipname)
zipfile, err := os.Create(zipname)
if err != nil {
return err
}
archive := zip.NewWriter(zipfile)
info, err := os.Lstat(oname)
if err == nil {
header, _ := zip.FileInfoHeader(info)
header.Method = zip.Deflate
header.Name = filepath.Base(oname)
writer, err := archive.CreateHeader(header)
if err == nil {
file, err := os.Open(oname)
if err == nil {
_, err = io.Copy(writer, file)
file.Close()
if err == nil {
err = os.Remove(oname)
}
}
}
}
archive.Close()
zipfile.Close()
return err
}

9
go.mod
View File

@ -1,11 +1,13 @@
module lx-source
go 1.21
go 1.20
require (
github.com/ZxwyWebSite/cr-go-sdk v0.0.2
github.com/ZxwyWebSite/ztool v0.0.1
github.com/gin-contrib/gzip v1.0.0
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.6.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
)
@ -43,4 +45,7 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/ZxwyWebSite/ztool v0.0.1 => ./pkg/ztool // ../ztool
replace (
github.com/ZxwyWebSite/cr-go-sdk v0.0.2 => ../cr-go-sdk
github.com/ZxwyWebSite/ztool v0.0.1 => ../ztool // ./pkg/ztool
)

7
go.sum
View File

@ -24,7 +24,6 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@ -34,8 +33,9 @@ github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaC
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@ -43,7 +43,6 @@ github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuV
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@ -63,7 +62,6 @@ github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -102,7 +100,6 @@ google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGm
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

43
init.go
View File

@ -3,6 +3,7 @@ package main
import (
"encoding/base64"
"lx-source/src/caches"
"lx-source/src/caches/cloudcache"
"lx-source/src/caches/localcache"
"lx-source/src/env"
@ -12,6 +13,7 @@ import (
stdurl "net/url"
"path/filepath"
"github.com/ZxwyWebSite/cr-go-sdk"
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/logs"
"github.com/ZxwyWebSite/ztool/zcypt"
@ -169,22 +171,31 @@ func initMain() {
// }
// icl.Info(`使用本地缓存,文件路径 %q绑定地址 %v`, LocalCachePath, env.Config.Apis.BindAddr)
case `2`, `cloudreve`:
icl.Fatal(`Cloudreve驱动暂未完善未兼容新版调用方式当前版本禁用`)
// icl.Warn(`Cloudreve驱动暂未完善使用非本机存储时存在兼容性问题请谨慎使用`)
// cs, err := cloudreve.NewSite(&cloudreve.Config{
// SiteUrl: env.Config.Cache.Cloud_Site,
// Username: env.Config.Cache.Cloud_User,
// Password: env.Config.Cache.Cloud_Pass,
// Session: env.Config.Cache.Cloud_Sess,
// })
// if err != nil {
// icl.Error(`驱动["cloudreve"]初始化失败: %v, 将禁用缓存功能`, err)
// }
// UseCache = &crcache.Cache{
// Cs: cs,
// Path: env.Config.Cache.Cloud_Path,
// IsOk: err == nil,
// }
icl.Warn(`欢迎使用新版 Cloudreve 驱动, 由 cr-go-sdk 提供强力支持`)
site := &cr.SiteObj{
Addr: env.Config.Cache.Cloud_Site,
ApiVer: cr.ApiV383,
Users: &cr.UserObj{
Mail: env.Config.Cache.Cloud_User,
Pass: env.Config.Cache.Cloud_Pass,
Cookie: cr.ParseCookie(env.Config.Cache.Cloud_Sess),
},
}
cache, err := caches.New(&cloudcache.Cache{
Site: site,
Path: env.Config.Cache.Cloud_Path,
})
if err != nil {
icl.Error(`驱动["cloudreve"]初始化失败: %v, 将禁用缓存功能`, err)
} else {
env.Tasker.Add(`cloud_sess`, func(l *logs.Logger, i int64) error {
if sess := site.Users.Cookie.String(); sess != env.Config.Cache.Cloud_Sess {
env.Config.Cache.Cloud_Sess = sess
}
return env.Cfg.Save(``)
}, 3600, true)
}
caches.UseCache = cache
default:
icl.Error(`未定义的缓存模式,请检查配置 [Cache].Mode本次启动禁用缓存`)
}

42
menu.go
View File

@ -1,8 +1,11 @@
package main
import (
"fmt"
"lx-source/src/env"
"lx-source/src/sources/custom/tx"
wm "lx-source/src/sources/custom/wy/modules"
"runtime"
"strings"
"time"
@ -22,6 +25,8 @@ func parseEtag(etag *string) {
// menuMian()
case `wyqr`:
wyQrLogin()
case `txqq`:
txQqLogin()
default:
loger.Fatal(`未知参数:%q`, *etag)
}
@ -33,6 +38,17 @@ func wyQrLogin() {
loger := env.Loger.NewGroup(`WyQrLogin`)
defer loger.Free()
loger.Info(`执行模块: 网易云扫码登录`)
if env.Config.Custom.Wy_Api_Cookie != `` {
loger.Warn("已存在账号数据, 继续操作可能导致数据覆盖丢失!")
fmt.Print(`输入'y'继续: `)
var input string
fmt.Scanln(&input)
if input != `y` {
loger.Fatal(`用户取消操作`)
}
}
res, err := wm.LoginQrKey()
if err != nil {
loger.Fatal(`无法创建请求: %s`, err)
@ -80,6 +96,32 @@ func wyQrLogin() {
}
}
// QQ快速登录
func txQqLogin() {
loger := env.Loger.NewGroup(`TxQqLogin`)
defer loger.Free()
loger.Info(`执行模块: QQ快速登录`)
if runtime.GOOS != `windows` {
loger.Fatal(`该模块仅支持在windows环境下使用`)
return
}
if env.Config.Custom.Tx_Ukey != `` {
loger.Warn("已存在账号数据, 继续操作可能导致数据覆盖丢失!")
fmt.Print(`输入'y'继续: `)
var input string
fmt.Scanln(&input)
if input != `y` {
loger.Fatal(`用户取消操作`)
}
}
if err := tx.Qlogin_graph(loger); err != nil {
loger.Fatal(err.Error())
}
}
// func menuMian() {
// app := menu.NewApp(`Lx-Source`)
// app.Data = menu.Data{

107
release.go Normal file
View File

@ -0,0 +1,107 @@
//go:build ignore
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// 获取版本号
func version() string {
fenv, _ := os.Open(`src/env/env.go`)
benv := bufio.NewReader(fenv)
var ever string
for {
line, _, _ := benv.ReadLine()
length := len(line)
if length == 0 {
continue
}
sline := string(line)
if strings.HasPrefix(sline, ` Version`) {
ever = `v` + sline[12:length-1]
break
}
}
fenv.Close()
if ever == `` {
panic(`No Version`)
} else {
return ever
}
}
// 生成更新日志
func changelog(ever string) string {
fupd, _ := os.Open(`update.md`)
bupd := bufio.NewReader(fupd)
var eupd strings.Builder
eupd.WriteString(`### 更新内容:`)
eupd.WriteByte('\n')
for {
line, _, _ := bupd.ReadLine()
length := len(line)
if length == 0 {
continue
}
if strings.Contains(string(line), ever) {
for {
lline, _, _ := bupd.ReadLine()
length := len(lline)
if length == 0 {
break
}
eupd.WriteString(string(lline))
eupd.WriteByte('\n')
}
break
}
}
fupd.Close()
eupd.WriteByte('\n')
eupd.WriteString(`### CDN加速下载`)
eupd.WriteByte('\n')
for _, v := range []string{
`lx-source-android-arm.zip`,
`lx-source-android-arm64.zip`,
`lx-source-linux-amd64v2.zip`,
`lx-source-linux-amd64v3.zip`,
`lx-source-linux-arm7.zip`,
`lx-source-linux-arm64.zip`,
`lx-source-windows-amd64v2.zip`,
`lx-source-windows-amd64v2-go1.20.14.zip`,
`lx-source-windows-amd64v3.zip`,
} {
eupd.WriteByte('+')
eupd.WriteByte(' ')
eupd.WriteByte('[')
eupd.WriteString(v)
eupd.WriteByte(']')
eupd.WriteByte('(')
eupd.WriteString(`https://r2eu.zxwy.link/gh/lx-source/`)
eupd.WriteString(ever)
eupd.WriteByte('/')
eupd.WriteString(v)
eupd.WriteByte(')')
eupd.WriteByte('\n')
}
return eupd.String()
}
func main() {
ever := version()
fmt.Println(ever)
eupd := changelog(ever)
file, err := os.Create(`changelog.md`)
if err != nil {
panic(err)
}
file.WriteString(eupd)
file.Close()
}

BIN
rsrc_windows_amd64.syso Normal file

Binary file not shown.

View File

@ -1 +1,121 @@
package cloudcache
import (
"lx-source/src/caches"
"lx-source/src/env"
"net/http"
"strings"
cr "github.com/ZxwyWebSite/cr-go-sdk"
"github.com/ZxwyWebSite/cr-go-sdk/service/explorer"
"github.com/ZxwyWebSite/ztool"
)
type Cache struct {
Site *cr.SiteObj
Path string
state bool
}
func (c *Cache) Get(q *caches.Query) string {
var b strings.Builder
b.WriteString(c.Path)
b.WriteByte('/')
b.WriteString(q.Source)
b.WriteByte('/')
b.WriteString(q.MusicID)
list, err := c.Site.Directory(b.String())
if err != nil {
caches.Loger.Debug(`列出目录: %v`, err)
return ``
}
name := q.Quality + `.` + q.Extname
var id string
for _, v := range list.Objects {
if v.Name == name && v.Type == `file` {
id = v.ID
break
}
}
if id == `` {
caches.Loger.Debug(`文件不存在`)
return ``
}
srcs, err := c.Site.FileSource(cr.GenerateSrc(false, id))
if err != nil {
caches.Loger.Debug(`生成外链: %v`, err)
return ``
}
return (*srcs)[0].URL
/*link, err := c.Site.FileDownload(id)
if err != nil {
caches.Loger.Debug(`下载文件: %v`, err)
return ``
}
if (*link)[0] == '/' {
return c.Site.Addr + (*link)[1:]
}
return *link*/
}
func (c *Cache) Set(q *caches.Query, l string) string {
var b strings.Builder
b.WriteString(c.Path)
b.WriteByte('/')
b.WriteString(q.Source)
b.WriteByte('/')
b.WriteString(q.MusicID)
dir := b.String()
err := c.Site.DirectoryNew(&explorer.DirectoryService{
Path: dir,
})
if err != nil {
caches.Loger.Debug(`创建目录: %v`, err)
return ``
}
/*var buf bytes.Buffer
err = ztool.Net_Download(l, &buf, nil)
if err != nil {
caches.Loger.Debug(`下载文件: %v`, err)
return ``
}*/
name := q.Quality + `.` + q.Extname
err = ztool.Net_Request(
http.MethodGet, l, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
return (&cr.UploadTask{
Site: c.Site,
File: res.Body,
Size: uint64(res.ContentLength),
Name: name,
Mime: `audio/mpeg`,
}).Do(dir)
}},
)
if err != nil {
caches.Loger.Debug(`上传文件: %v`, err)
return ``
}
return c.Get(q)
}
func (c *Cache) Stat() bool {
return c.state
}
func (c *Cache) Init() error {
cr.Cr_Debug = env.Config.Main.Debug
err := c.Site.SdkInit()
if err != nil {
return err
}
if c.Site.Users.Cookie == nil || c.Site.Config.User.Anonymous {
err = c.Site.SdkLogin()
if err != nil {
return err
}
}
c.state = true
return nil
}

62
src/env/env.go vendored
View File

@ -12,7 +12,7 @@ import (
)
const (
Version = `1.0.3.0430`
Version = `1.0.3.0622`
)
var (
@ -36,10 +36,10 @@ type (
Print bool `comment:"控制台输出 (影响io性能后台使用建议关闭)"`
SysLev bool `comment:"(实验性) 设置进程高优先级"`
// FFConv bool `comment:"(实验性) 使用FFMpeg修复音频(本地缓存)"`
NgProxy bool `comment:"兼容反向代理(beta)"`
Timeout int64 `comment:"网络请求超时(单位:秒,海外服务器可适当调大)"`
Store string `comment:"内存缓存持久化文件地址"`
NgProxy bool `comment:"兼容反向代理(beta)"`
Timeout int64 `comment:"网络请求超时(单位:秒,海外服务器可适当调大)"`
Store string `comment:"内存缓存持久化文件地址"`
ErrMp3 string `comment:"获取失败默认音频"`
}
// 接口
Conf_Apis struct {
@ -124,6 +124,7 @@ type (
Kw_Bd_DevId string `comment:"field user.device_id"`
// kw kwdes
Kw_Des_Type string `comment:"返回格式 0: text, 1: json"`
Kw_Des_Source string `comment:"query source"`
Kw_Des_Header string `comment:"请求头 User-Agent"`
// kg
@ -158,12 +159,22 @@ type (
// Lx_Enable bool `comment:"是否启用小洛源"`
}
// 脚本
Conf_Script struct {
Conf_Script_Update struct {
Ver string `comment:"自定义脚本版本" json:"ver"`
Log string `comment:"更新日志" json:"log"`
Url string `comment:"脚本下载地址 (public目录内文件名)" json:"url"`
Force bool `comment:"强制推送更新" json:"force"`
Auto int `comment:"自动填写配置(beta) 0: 关闭, 1: 仅api地址, 2: 包含密钥" json:"-"`
}
Conf_Script struct {
Name string `comment:"源的名字建议不要过长24个字符以内"`
Descript string `comment:"源的描述建议不要过长36个字符以内可不填"`
Version string `comment:"源的版本号,可不填"`
Author string `comment:"脚本作者名字,可不填"`
Homepage string `comment:"脚本主页,可不填"`
Update Conf_Script_Update `ini:"Script"`
Auto int `comment:"自动填写配置(beta) 0: 关闭, 1: 仅api地址, 2: 包含密钥"`
}
// 缓存
Conf_Cache struct {
@ -174,11 +185,11 @@ type (
Local_Bind string `comment:"本地缓存外部访问地址"`
Local_Auto bool `comment:"自适应缓存访问地址(beta)"`
// 云盘
// Cloud_Site string `comment:"Cloudreve站点地址"`
// Cloud_User string `comment:"Cloudreve用户名"`
// Cloud_Pass string `comment:"Cloudreve密码"`
// Cloud_Sess string `comment:"Cloudreve会话"`
// Cloud_Path string `comment:"Cloudreve存储路径"`
Cloud_Site string `comment:"Cloudreve站点地址"`
Cloud_User string `comment:"Cloudreve用户名"`
Cloud_Pass string `comment:"Cloudreve密码"`
Cloud_Sess string `comment:"Cloudreve会话"`
Cloud_Path string `comment:"Cloudreve存储路径"`
}
// 结构
Conf struct {
@ -204,6 +215,7 @@ var (
SysLev: false,
Timeout: 30,
Store: `/data/memo.bin`,
ErrMp3: `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`,
},
Apis: Conf_Apis{
// BindAddr: `http://192.168.10.22:1011/`,
@ -264,23 +276,31 @@ var (
Tx_Refresh_Interval: 86000,
},
Script: Conf_Script{
Log: `发布更新 (请删除旧源后重新导入)进行了部分优化修复了部分Bug`, // 更新日志
Name: `Lx-Source-Script`,
Descript: `洛雪音乐自定义源脚本`,
Version: `1.1.0`,
Author: `Zxwy`,
Homepage: `https://github.com/ZxwyWebSite/lx-script`,
Ver: `1.0.3`, // 自定义脚本版本
Force: true, // 强制推送更新
Update: Conf_Script_Update{
Log: `发布更新 (请删除旧源后重新导入)进行了部分优化修复了部分Bug`, // 更新日志
Url: `lx-custom-source.js`, // 脚本下载地址
Ver: `1.0.3`, // 自定义脚本版本
Force: true, // 强制推送更新
Url: `lx-custom-source.js`, // 脚本下载地址
},
},
Cache: Conf_Cache{
Mode: `local`, // 缓存模式
LinkMode: `1`,
Local_Path: `data/cache`,
Local_Bind: `http://127.0.0.1:1011/`,
// Cloud_Site: `https://cloudreveplus-demo.onrender.com/`,
// Cloud_User: `admin@cloudreve.org`,
// Cloud_Pass: `CloudrevePlusDemo`,
// Cloud_Sess: ``,
// Cloud_Path: `/Lx-Source/cache`,
Cloud_Site: `https://cloudreveplus-demo.onrender.com/`,
Cloud_User: `admin@cloudreve.org`,
Cloud_Pass: `CloudrevePlusDemo`,
Cloud_Sess: ``,
Cloud_Path: `/Lx-Source/cache`,
},
}
Config = DefCfg

View File

@ -2,6 +2,7 @@
package resp
import (
"lx-source/src/env"
"net/http"
"github.com/gin-gonic/gin"
@ -26,7 +27,7 @@ type Resp struct {
}
// 获取失败默认音频
var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
// var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
// 返回请求
/*
@ -43,7 +44,7 @@ func (o *Resp) Execute(c *gin.Context) {
case 2:
status = http.StatusServiceUnavailable
if o.Data == nil || o.Data == `` {
o.Data = ErrMp3
o.Data = env.Config.Main.ErrMp3 //ErrMp3
}
case 3:
status = http.StatusUnauthorized

View File

@ -103,6 +103,7 @@ func musicHandler(c *gin.Context) {
cstat = caches.UseCache.Stat()
}
uquery := caches.NewQuery(ps, pid, pq)
uquery.Request = c.Request
defer uquery.Free()
if cstat {
loger.Debug(`FileGet: %v`, uquery.Query())

View File

@ -10,6 +10,7 @@ import (
"lx-source/src/env"
"net/http"
"path/filepath"
"strings"
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/bytesconv"
@ -106,6 +107,63 @@ func loadPublic(r *gin.Engine) {
} else {
r.StaticFileFS(`/lx-custom-source.js`, `lx-custom-source.js`, httpFS)
}
// 新版源脚本
{
// 构建文件头
var b strings.Builder
b.Grow(75 +
len(env.Config.Script.Name) +
len(env.Config.Script.Descript) +
len(env.Config.Script.Version) +
len(env.Config.Script.Author) +
len(env.Config.Script.Homepage),
)
b.WriteString("/*!\n * @name ")
b.WriteString(env.Config.Script.Name)
b.WriteString("\n * @description ")
b.WriteString(env.Config.Script.Descript)
b.WriteString("\n * @version v")
b.WriteString(env.Config.Script.Version)
b.WriteString("\n * @author ")
b.WriteString(env.Config.Script.Author)
b.WriteString("\n * @homepage ")
b.WriteString(env.Config.Script.Homepage)
b.WriteString("\n */\n")
// 构建文件体
file, _ := publicFS.Open(`lx-source-script.js`)
data, _ := io.ReadAll(file)
file.Close()
r.GET(`/lx-source-script.js`, func(c *gin.Context) {
var mime string
if _, ok := c.GetQuery(`raw`); ok {
mime = `application/octet-stream`
} else {
mime = `text/javascript; charset=utf-8`
}
// 构建文件尾
var d strings.Builder
d.WriteString(`globalThis.ls={api:{addr:'`)
d.WriteString(env.Config.Cache.Local_Bind)
d.WriteString(`',pass:'`)
if env.Config.Auth.ApiKey_Enable {
if env.Config.Script.Auto >= 2 {
d.WriteString(env.Config.Auth.ApiKey_Value)
} else {
if key, ok := c.GetQuery(`key`); ok {
d.WriteString(key)
}
}
}
d.WriteString(`'}};`)
d.WriteByte('\n')
// Render
c.Status(http.StatusOK)
c.Writer.Header()[`Content-Type`] = []string{mime}
c.Writer.WriteString(b.String())
c.Writer.WriteString(d.String())
c.Writer.Write(data)
})
}
r.StaticFileFS(`/favicon.ico`, `lx-icon.ico`, httpFS)
r.StaticFileFS(`/status`, `status.html`, httpFS)
r.StaticFS(`/public`, httpFS)

File diff suppressed because one or more lines are too long

View File

@ -16,7 +16,7 @@
const api = './';
function l(s, id, q) {
let url = `${api}link/${s}/${id[Math.floor(Math.random() * id.length)]}/${q}`;
const key = localStorage.getItem('apipass'); if (key) url += `?key=${key}`;
const key = localStorage.getItem('apipass'); if (key) url += `?key=${encodeURIComponent(key)}`;
fetch(url)
.then((response) => response.json())
.then((res) => {

View File

@ -50,7 +50,7 @@ func InitRouter() *gin.Engine {
sources.S_lx: qmap[sources.I_lx],
},
// 自定义源脚本更新
`script`: env.DefCfg.Script, //env.Config.Script,
`script`: env.Config.Script.Update, //env.Config.Script,
// 数据统计
`summary`: gin.H{
`StartAt`: startime, // 启动时间

View File

@ -3,10 +3,10 @@ package kg
import (
"lx-source/src/env"
"lx-source/src/sources"
"slices"
"strings"
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/slices"
"github.com/ZxwyWebSite/ztool/zcypt"
)

View File

@ -1,15 +1,19 @@
package kw
import (
"errors"
"io"
"lx-source/src/env"
"lx-source/src/sources"
"lx-source/src/sources/custom/utils"
"net/http"
"strconv"
"strings"
"sync"
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/x/bytesconv"
"github.com/ZxwyWebSite/ztool/zcypt"
)
var (
@ -19,6 +23,7 @@ var (
parsemod bool
convtype string
desource string
// desParse func([]byte, *playInfo) error
// desParse func(any) ztool.Net_ResHandlerFunc
)
@ -36,11 +41,13 @@ func init() {
) {
loger.Fatal(`使用bdapi且验证参数为空`)
}
// bdheader[`token`] = env.Config.Custom.Kw_Bd_Token
bdheader[`uid`] = env.Config.Custom.Kw_Bd_Uid
bdheader[`devId`] = env.Config.Custom.Kw_Bd_DevId
kw_pool = &sync.Pool{New: func() any { return new(kwApi_Song) }}
Url = bdapi
case `1`, `kwdes`:
Url = kwdes
switch env.Config.Custom.Kw_Des_Type {
case `0`, `text`:
loger.Debug(`use kwdes text`)
@ -51,12 +58,20 @@ func init() {
convtype = `convert_url_with_sign`
// desParse = ztool.Net_ResToStruct
parsemod = true
case `2`, `anti`:
loger.Debug(`use kwdes anti`)
Url = manti
default:
loger.Fatal(`未定义的返回格式,请检查配置 [Custom].Kw_Des_Type`)
}
desheader[`User-Agent`] = env.Config.Custom.Kw_Des_Header
kw_pool = &sync.Pool{New: func() any { return new(playInfo) }}
Url = kwdes
if env.Config.Custom.Kw_Des_Source != `` {
desource = env.Config.Custom.Kw_Des_Source
} else {
dec, _ := zcypt.HexDecode([]byte{0x36, 0x62, 0x37, 0x37, 0x37, 0x30, 0x36, 0x63, 0x36, 0x31, 0x37, 0x39, 0x36, 0x35, 0x37, 0x32, 0x36, 0x38, 0x36, 0x34, 0x35, 0x66, 0x36, 0x31, 0x37, 0x32, 0x35, 0x66, 0x33, 0x35, 0x32, 0x65, 0x33, 0x31, 0x32, 0x65, 0x33, 0x30, 0x32, 0x65, 0x33, 0x30, 0x35, 0x66, 0x34, 0x32, 0x35, 0x66, 0x36, 0x61, 0x36, 0x39, 0x36, 0x31, 0x36, 0x62, 0x36, 0x66, 0x36, 0x65, 0x36, 0x37, 0x35, 0x66, 0x37, 0x36, 0x36, 0x38, 0x32, 0x65, 0x36, 0x31, 0x37, 0x30, 0x36, 0x62})
desource = bytesconv.BytesToString(dec)
}
default:
loger.Fatal(`未定义的接口模式,请检查配置 [Custom].Kw_Mode`)
}
@ -92,7 +107,7 @@ func bdapi(songMid, quality string) (ourl, msg string) {
loger.Debug(`Resp: %+v`, resp)
if resp.Code != 200 || resp.Data.AudioInfo.Bitrate == `1` {
// jx.Debug(`Kw, Err: %#v`, resp)
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
msg = ztool.Str_FastConcat(strconv.Itoa(resp.Code), `: `, resp.Msg)
return
}
ourl = utils.DelQuery(resp.Data.URL) //strings.Split(resp.Data.URL, `?`)[0]
@ -110,12 +125,12 @@ func kwdes(songMid, quality string) (ourl, msg string) {
target_url := ztool.Str_FastConcat(
`https://mobi.kuwo.cn/mobi.s?f=kuwo&q=`,
base64_encrypt(ztool.Str_FastConcat(
`user=0&android_id=0&prod=kwplayer_ar_8.5.5.0&corp=kuwo&newver=3&vipver=8.5.5.0&source=kwplayer_ar_8.5.5.0_apk_keluze.apk&p2p=1&notrace=0`,
`corp=kuwo&p2p=1&sig=0&notrace=0&priority=bitrate&network=WIFI&mode=down`,
`&source=`, desource,
`&type=`, convtype,
`&br=`, infoFile.H, infoFile.E,
`&format=`, infoFile.E,
`&rid=`, songMid,
`&priority=bitrate&loginUid=0&network=WIFI&loginSid=0&mode=down`,
)),
)
if parsemod {
@ -131,6 +146,7 @@ func kwdes(songMid, quality string) (ourl, msg string) {
msg = sources.ErrHttpReq
return
}
resp.Data.URL = utils.DelQuery(resp.Data.URL)
loger.Debug(`Resp: %+v`, resp)
if resp.Code != http.StatusOK {
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
@ -138,13 +154,13 @@ func kwdes(songMid, quality string) (ourl, msg string) {
return
}
realQuality := strconv.Itoa(resp.Data.Bitrate)
if realQuality != infoFile.H[:len(infoFile.H)-1] {
if realQuality != infoFile.H[:len(infoFile.H)-1] /*&& resp.Data.Bitrate != 1*/ {
msg = sources.E_QNotMatch
if !env.Config.Source.ForceFallback {
return
}
}
ourl = utils.DelQuery(resp.Data.URL) //resp.Data.URL[:strings.Index(resp.Data.URL, `?`)]
ourl = resp.Data.URL //resp.Data.URL[:strings.Index(resp.Data.URL, `?`)]
return
}
ztool.Net_Request(http.MethodGet, target_url, nil,
@ -179,3 +195,133 @@ func kwdes(songMid, quality string) (ourl, msg string) {
)
return
}
// 一种替代方案仅在试听可用时获取cdn前缀缺点是无法获取不能试听的歌曲
func manti(songMid, quality string) (ourl, msg string) {
loger := env.Loger.NewGroup(`Kw`)
defer loger.Free()
infoFile, ok := fileInfo[quality]
if !ok {
msg = sources.E_QNotSupport
return
}
// 获取CDN地址
// var out_a string
/*var out_c struct {
// Timestamp int `json:"timestamp"`
Songs []struct {
ID int `json:"id"`
Duration int `json:"duration"`
URL string `json:"url"`
HTTPS string `json:"https"`
CarURL string `json:"car_url"`
CarURLHTTPS string `json:"car_url_https"`
Format string `json:"format"`
Br int `json:"br"`
OverseasCopyright string `json:"overseas_copyright"`
Start int `json:"start"`
End int `json:"end"`
Group string `json:"group"`
} `json:"songs"`
IP string `json:"ip"`
Country string `json:"country"`
Region string `json:"region"`
Locationid int `json:"locationid"`
Code int `json:"code"`
Result string `json:"result"`
}
err := ztool.Net_Request(
http.MethodGet,
`https://musicpay30.kuwo.cn/audi.tion?op=query&ids=`+songMid,
nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&out_c)},
)
if err != nil {
loger.Error(`Request: %s`, err)
msg = sources.ErrHttpReq
return
}*/
// 获取音频路径
if quality != sources.Q_128k {
ourl, msg = manti(songMid, sources.Q_128k)
if msg != `` {
return
}
if i := strings.LastIndexByte(ourl, '/'); i != -1 {
if ourl[i+1:] == `2272659253.mp3` {
msg = sources.ErrNoLink
return
}
}
target_url := ztool.Str_FastConcat(
`https://mobi.kuwo.cn/mobi.s?f=web&type=convert_url`,
`&br=`, infoFile.H, infoFile.E,
`&format=`, infoFile.E,
`&rid=`, songMid,
)
var out_u, realQuality string
err := ztool.Net_Request(
http.MethodGet, target_url, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) (err error) {
var data []byte
data, err = io.ReadAll(res.Body)
if err != nil {
return
}
if res.StatusCode != http.StatusOK {
return errors.New(`failed: ` + res.Status)
}
infoData := mkMap(data)
loger.Debug(`uData: %+v`, infoData)
realQuality = infoData[`bitrate`]
out_u = utils.DelQuery(infoData[`url`])
return
}},
)
if err != nil {
loger.Error(`Request: %s`, err)
msg = sources.ErrHttpReq
return
}
if realQuality != infoFile.H[:len(infoFile.H)-1] {
msg = sources.E_QNotMatch
if !env.Config.Source.ForceFallback {
return
}
}
if ourl != `` && out_u != `` {
ourl = ourl[:24] + out_u
}
return
}
resp := kw_pool.Get().(*playInfo)
defer kw_pool.Put(resp)
err := ztool.Net_Request(
http.MethodGet, `https://mobi.kuwo.cn/mobi.s?f=web&type=convert_url_with_sign&br=128kmp3&format=mp3&rid=`+songMid, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(desheader)},
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&resp)},
)
if err != nil {
loger.Error(`Request: %s`, err)
msg = sources.ErrHttpReq
return
}
loger.Debug(`tData: %+v`, resp)
if resp.Code != http.StatusOK {
msg = ztool.Str_FastConcat(`failed: `, resp.Msg)
loger.Debug(msg)
return
}
realQuality := strconv.Itoa(resp.Data.Bitrate)
if realQuality != infoFile.H[:len(infoFile.H)-1] && resp.Data.Bitrate != 1 {
msg = sources.E_QNotMatch
if !env.Config.Source.ForceFallback {
return
}
}
ourl = utils.DelQuery(resp.Data.URL)
return
}

View File

@ -13,8 +13,8 @@ type (
Format string `json:"format"`
P2PAudiosourceid string `json:"p2p_audiosourceid"`
Rid int `json:"rid"`
Source string `json:"source"`
URL string `json:"url"`
// Source string `json:"source"`
URL string `json:"url"`
} `json:"data"`
Msg string `json:"msg"`
}

View File

@ -40,25 +40,64 @@ var (
// `User-Agent`: `okhttp/3.10.0`,
}
bdheader = map[string]string{
`channel`: `qq`,
`channel`: `guanfang`,
`plat`: `ar`,
`net`: `wifi`,
`ver`: `3.1.2`,
// `uid`: ``,
// `devId`: `0`,
`ver`: `3.1.4`,
`api-ver`: `application/json`,
`user-agent`: `Dart/2.18 (dart:io)`, //`Dalvik/2.1.0 (Linux; U; Android 7.1.1; OPPO R9sk Build/NMF26F)`,
}
// bdsreg = regexp.MustCompile(`[^a-zA-Z0-9]`)
)
func mkMap(data []byte) map[string]string {
out := make(map[string]string)
sep := bytes.Split(data, []byte{13, 10})
for i, r := 0, len(sep); i < r; i++ {
pat := bytes.Split(sep[i], []byte{61})
if len(pat) == 2 {
var s = sep[i]
if p := bytes.IndexByte(s, '='); p != -1 {
out[bytesconv.BytesToString(s[:p])] = bytesconv.BytesToString(s[p+1:])
continue
} else {
out[`_`] += bytesconv.BytesToString(s) + `;`
}
/*pat := bytes.Split(sep[i], []byte{61})
if len(pat) >= 2 {
out[bytesconv.BytesToString(pat[0])] = bytesconv.BytesToString(pat[1])
continue
}
out[`_`] += bytesconv.BytesToString(pat[0]) + `;`
out[`_`] += bytesconv.BytesToString(pat[0]) + `;`*/
}
return out
}
// 波点签名算法
/*func Bdsign(str string, m, m2 map[string]string) *strings.Builder {
var b strings.Builder
b.WriteString(`uid=`)
b.WriteString(env.Config.Custom.Kw_Bd_Uid)
b.WriteByte('&')
b.WriteString(`token=`)
b.WriteString(env.Config.Custom.Kw_Bd_Token)
b.WriteByte('&')
b.WriteString(`timestamp=`)
b.WriteString(strconv.FormatInt(time.Now().UnixMilli(), 10))
for k, v := range m2 {
b.WriteByte('&')
b.WriteString(k)
b.WriteByte('=')
b.WriteString(url.QueryEscape(v))
}
// 取 strings.Builder.buf []byte 地址
pb := (*[]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&b)) + unsafe.Sizeof((*strings.Builder)(nil))))
charArray := bdsreg.ReplaceAll(*pb, []byte{})
slices.Sort(charArray)
str3 := string(charArray)
fmt.Println(str3)
lowerCase := zcypt.MD5EncStr(`kuwotest` + str3 + `/api/play/music/v2/audioUrl`)
b.WriteByte('&')
b.WriteString(`sign=`)
b.WriteString(lowerCase)
return &b
}*/

View File

@ -0,0 +1,355 @@
package tx
import (
"bytes"
"errors"
"fmt"
"io"
"lx-source/src/env"
"math/rand"
"net/http"
"net/http/cookiejar"
"net/url"
"strconv"
"strings"
"time"
_ "unsafe"
"github.com/ZxwyWebSite/ztool"
"github.com/ZxwyWebSite/ztool/logs"
"github.com/ZxwyWebSite/ztool/x/json"
"github.com/google/uuid"
)
//go:linkname request github.com/ZxwyWebSite/ztool.request
func request(client *http.Client, method, url string, body io.Reader, reqh []ztool.Net_ReqHandlerFunc, resh []ztool.Net_ResHandlerFunc) error
// QQ快速登录 - 直接使用本机已登录账号
func Qlogin_graph(l *logs.Logger) error {
// 参考文章: https://learnku.com/articles/33970
jar, _ := cookiejar.New(nil)
client := &http.Client{
Timeout: time.Second * 10,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Jar: jar,
}
// Step0 - 获取本机QQ服务地址
// (应该没有人会占用4301端口导致qq切换备用端口吧先不写了...懒)
err := request(
client, http.MethodGet, `https://localhost.ptlogin2.qq.com:4301/pc_querystatus`, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
_, err := io.Copy(io.Discard, res.Body)
return err
}},
)
if err != nil {
return errors.New(`step0: 无法连接本机QQ服务`)
}
// Step1 - 获取 pt_local_token
var pt_local_token string
err = request(
client, http.MethodGet,
`https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=716027609&daid=383&style=33&login_text=%E7%99%BB%E5%BD%95&hide_title_bar=1&hide_border=1&target=self&s_url=https://graph.qq.com/oauth2.0/login_jump&pt_3rd_aid=100497308&pt_feedback_link=https://support.qq.com/products/77942?customInfo=.appid100497308&theme=2&verify_theme=`,
nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
`Referer`: `https://graph.qq.com/`,
})},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
for _, v := range res.Cookies() {
if v.Name == `pt_local_token` {
pt_local_token = v.Value
break
}
}
_, err := io.Copy(io.Discard, res.Body)
return err
}},
)
if err != nil {
return err
}
l.Info(`pt_local_token: %v`, pt_local_token)
// Step2 - 获取本机登录的 QQ 号
var url2 strings.Builder
url2.WriteString(`https://localhost.ptlogin2.qq.com:4301/pt_get_uins?callback=ptui_getuins_CB&r=`)
url2.WriteString(strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
url2.WriteString(`&pt_local_tk=`)
url2.WriteString(pt_local_token)
var out2 []struct {
Uin int `json:"uin"`
// FaceIndex int `json:"face_index"`
// Gender int `json:"gender"`
Nickname string `json:"nickname"`
// ClientType int `json:"client_type"`
// UinFlag int `json:"uin_flag"`
// Account int `json:"account"`
}
var header2 = map[string]string{
`Referer`: `https://xui.ptlogin2.qq.com/`,
// `Cookie`: `pt_local_token=` + pt_local_token,
}
err = request(
client, http.MethodGet, url2.String(), nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(header2)},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
data, err := io.ReadAll(res.Body)
if err != nil {
return err
}
sep_b := bytes.IndexByte(data, '[')
sep_e := bytes.LastIndexByte(data, ']')
if sep_b == -1 || sep_e == -1 {
return errors.New(`step2: 无法解析返回数据`)
}
sep := data[sep_b : sep_e+1]
return json.Unmarshal(sep, &out2)
}},
)
if err != nil {
return err
}
var uin string
length := len(out2)
switch length {
case 0:
return errors.New(`step2: 无可用账号`)
case 1:
uin = strconv.Itoa(out2[0].Uin)
default:
fmt.Println(`请选择要登录的账号:`)
for i, v := range out2 {
fmt.Println(i, v.Nickname, v.Uin)
}
for {
fmt.Print(`输入序号: `)
var input string
fmt.Scanln(&input)
i, err := strconv.Atoi(input)
if err != nil {
l.Error(`err: %v`, err)
continue
}
if i >= length {
l.Error(`err: 下标越界`)
continue
}
uin = strconv.Itoa(out2[i].Uin)
break
}
}
l.Info(`uin: %v`, uin)
// Step3 - 获取 clientkey
var url3 strings.Builder
url3.WriteString(`https://localhost.ptlogin2.qq.com:4301/pt_get_st?clientuin=`)
url3.WriteString(uin)
url3.WriteString(`&r=`)
url3.WriteString(strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
url3.WriteString(`&pt_local_tk=`)
url3.WriteString(pt_local_token)
url3.WriteString(`&callback=__jp0`)
// var clientkey string
err = request(
client, http.MethodGet, url3.String(), nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(header2)},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
/*for _, v := range res.Cookies() {
if v.Name == `clientkey` {
clientkey = v.Value
break
}
}*/
_, err := io.Copy(io.Discard, res.Body)
return err
}},
)
if err != nil {
return err
}
// Step4 - 获取 skey
var url4 strings.Builder
url4.WriteString(`https://ssl.ptlogin2.qq.com/jump?clientuin=`)
url4.WriteString(uin)
url4.WriteString(`&keyindex=9&pt_aid=716027609&daid=383&u1=https://graph.qq.com/oauth2.0/login_jump&pt_local_tk=`)
url4.WriteString(pt_local_token)
url4.WriteString(`&pt_3rd_aid=100497308&ptopt=1&style=40`)
/*var cookie4 strings.Builder
cookie4.WriteString(`pt_local_token=`)
cookie4.WriteString(pt_local_token)
cookie4.WriteByte(';')
cookie4.WriteString(`clientuin=`)
cookie4.WriteString(uin)
cookie4.WriteByte(';')
cookie4.WriteString(`clientkey=`)
cookie4.WriteString(clientkey)*/
var jurl string
err = request(
client, http.MethodGet, url4.String(), nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders( /*map[string]string{
`Referer`: `https://xui.ptlogin2.qq.com/`,
`Cookie`: cookie4.String(),
}*/header2)},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
data, err := io.ReadAll(res.Body)
if err != nil {
return err
}
sep_b := bytes.IndexByte(data, ',')
sep_e := bytes.LastIndexByte(data, ' ')
if sep_b == -1 || sep_e == -1 {
return errors.New(`step4: 无法解析返回数据`)
}
jurl = string(data[sep_b+3 : sep_e-2])
return nil
}},
)
if err != nil {
return err
}
// Step5 - 获取 p_skey
var p_skey string
err = request(
client, http.MethodGet, jurl, nil,
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(header2)},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
for _, v := range res.Cookies() {
if v.Name == `p_skey` && v.Value != `` {
p_skey = v.Value
break
}
}
_, err := io.Copy(io.Discard, res.Body)
return err
}},
)
if err != nil {
return err
}
// Step6 - 登录账号
getGtk := func(skey string) string {
var hash = 5381
for _, v := range skey {
hash += (hash << 5) + int(v)
}
return strconv.Itoa(hash & 0x7fffffff)
}
now := time.Now()
var authcode string
err = request(
client, http.MethodPost,
`https://graph.qq.com/oauth2.0/authorize`,
strings.NewReader(ztool.Str_FastConcat(
`response_type=code&client_id=100497308&redirect_uri=https%3A%2F%2Fy.qq.com%2Fportal%2Fwx_redirect.html%3Flogin_type%3D1%26surl%3Dhttps%3A%2F%2Fy.qq.com%2F&scope=get_user_info%2Cget_app_friends&state=state&switch=&from_ptlogin=1&src=1&update_auth=1&openapi=1010_1030`,
`&g_tk=`, getGtk(p_skey),
`&auth_time=`, strconv.FormatInt(now.UnixMilli(), 10),
`&ui=`, strings.ToUpper(uuid.NewString()),
)),
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
`Referer`: `https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=100497308&redirect_uri=https%3A%2F%2Fy.qq.com%2Fportal%2Fwx_redirect.html%3Flogin_type%3D1%26surl%3Dhttps%3A%2F%2Fy.qq.com%2F&state=state&display=pc&scope=get_user_info%2Cget_app_friends`,
`Content-Type`: `application/x-www-form-urlencoded`,
})},
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
/*if res.StatusCode != 302 {
return errors.New(`step6: not redirect`)
}*/
location := res.Header[`Location`][0]
l.Info(`loc: %v`, location)
loc, err := url.Parse(location)
if err != nil {
return err
}
authcode = loc.Query()[`code`][0]
return nil
}},
)
if err != nil {
return err
}
l.Info(`authcode: %v`, authcode)
var out6 struct {
Code int `json:"code"`
// Ts int64 `json:"ts"`
// StartTs int64 `json:"start_ts"`
// Traceid string `json:"traceid"`
Req struct {
Code int `json:"code"`
Data struct {
// Openid string `json:"openid"`
RefreshToken string `json:"refresh_token"`
AccessToken string `json:"access_token"`
ExpiredAt int `json:"expired_at"`
// Musicid int `json:"musicid"`
Musickey string `json:"musickey"`
// MusickeyCreateTime int `json:"musickeyCreateTime"`
// FirstLogin int `json:"first_login"`
// ErrMsg string `json:"errMsg"`
// SessionKey string `json:"sessionKey"`
// Unionid string `json:"unionid"`
StrMusicid string `json:"str_musicid"`
// Errtip string `json:"errtip"`
// Nick string `json:"nick"`
// Logo string `json:"logo"`
// FeedbackURL string `json:"feedbackURL"`
// EncryptUin string `json:"encryptUin"`
// Userip string `json:"userip"`
// LastLoginTime int `json:"lastLoginTime"`
// KeyExpiresIn int `json:"keyExpiresIn"`
// RefreshKey string `json:"refresh_key"`
// LoginType int `json:"loginType"`
// Prompt2Bind int `json:"prompt2bind"`
// LogoffStatus int `json:"logoffStatus"`
// OtherAccounts []interface{} `json:"otherAccounts"`
// OtherPhoneNo string `json:"otherPhoneNo"`
// Token string `json:"token"`
// IsPrized int `json:"isPrized"`
// IsShowDevManage int `json:"isShowDevManage"`
// ErrTip2 string `json:"errTip2"`
// Tip3 string `json:"tip3"`
// EncryptedPhoneNo string `json:"encryptedPhoneNo"`
// PhoneNo string `json:"phoneNo"`
// BindAccountType int `json:"bindAccountType"`
// NeedRefreshKeyIn int `json:"needRefreshKeyIn"`
} `json:"data"`
} `json:"req"`
}
err = request(
client, http.MethodPost,
`https://u.y.qq.com/cgi-bin/musicu.fcg`,
strings.NewReader(ztool.Str_FastConcat(
`{"comm":{"g_tk":5381,"platform":"yqq","ct":24,"cv":0},"req":{"module":"QQConnectLogin.LoginServer","method":"QQLogin","param":{"code":"`, authcode, `"}}}`,
)),
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
`Referer`: `https://y.qq.com/`,
`Content-Type`: `application/x-www-form-urlencoded`,
})},
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&out6)},
)
if err != nil {
return err
}
l.Info(`res: %+v`, out6)
l.Info(`登录成功`)
env.Config.Custom.Tx_Enable = true
env.Config.Custom.Tx_Uuin = out6.Req.Data.StrMusicid
env.Config.Custom.Tx_Ukey = out6.Req.Data.Musickey
env.Config.Custom.Tx_Refresh_Enable = false
// env.Config.Custom.Tx_Refresh_Interval = time.Date(now.Year(), now.Month(), now.Day()+5, 0, 0, 0, 0, now.Location()).Unix()
// env.Config.Custom.Tx_RefreshToken = out6.Req.Data.RefreshToken
// env.Config.Custom.Tx_AccessToken = out6.Req.Data.AccessToken
return env.Cfg.Save(``)
}
// QQ扫码登录(todo)
// func qlogin_qr_()

View File

@ -103,6 +103,7 @@ Loop:
if quality != sources.Q_128k && infoBody.TrackInfo.Pay.PayPlay == 0 {
msg = `Fallback to 128k`
infoFile = fileInfo[sources.Q_128k]
quality = sources.Q_128k
} else {
msg = `Fallbacked`
tryLink = true

View File

@ -123,9 +123,18 @@ func refresh(loger *logs.Logger, now int64) error {
// loger.Error(`请求Api失败: %s`, err)
return errors.New(`请求Api失败: ` + err.Error())
}
loger.Debug(`Resp: %+v`, resp)
if resp.Req1.Code != 0 {
switch resp.Req1.Code {
case 1000:
return fmt.Errorf(`%v: Token无效或已过期`, resp.Req1.Code)
case 2000:
return fmt.Errorf(`%v: 该Token不支持刷新`, resp.Req1.Code)
default:
return fmt.Errorf(`%v: 刷新登录失败`, resp.Req1.Code)
}
// loger.Warn("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
return fmt.Errorf("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
// return fmt.Errorf("刷新登录失败, code: %v\n响应体: %+v", resp.Req1.Code, resp)
}
loger.Info(`刷新登录成功`)
env.Config.Custom.Tx_Uuin = resp.Req1.Data.StrMusicId

View File

@ -20,7 +20,11 @@ import (
const anonymous_token = `1f5fa7b6a6a9f81a11886e5186fde7fb98e25cf0036d7afd055b980b2261f5464b7f5273fc3921d1262bfec66a19a30c41d8da00c3685f5ace96f0d5a48b6db334d974731083682e3324751bcc9aaf44c3061cd1`
var wapiReg = regexp.MustCompile(`\w*api`)
var (
wapiReg = regexp.MustCompile(`\w*api`)
csrfReg = regexp.MustCompile(`_csrf=([^(;|$)]+)`)
domaReg = regexp.MustCompile(`\s*Domain=[^(;|$)]+;*`)
)
var userAgentMap = map[string]string{
`mobile`: `Mozilla/5.0 (iPhone; CPU iPhone OS 17_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1`,
@ -103,8 +107,7 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
switch options.Crypto {
case `weapi`:
options.Headers[`User-Agent`] = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69`
reg := regexp.MustCompile(`_csrf=([^(;|$)]+)`)
csrfToken := reg.FindStringSubmatch(options.Headers[`Cookie`])
csrfToken := csrfReg.FindStringSubmatch(options.Headers[`Cookie`])
if len(csrfToken) > 1 {
data[`csrf_token`] = csrfToken[1]
} else {
@ -189,9 +192,8 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
}
if err == nil {
answer.Cookie = res.Header[`Set-Cookie`] //res.Header.Values(`set-cookie`)
reg := regexp.MustCompile(`\s*Domain=[^(;|$)]+;*`)
for i, v := range answer.Cookie {
answer.Cookie[i] = reg.ReplaceAllString(v, ``)
answer.Cookie[i] = domaReg.ReplaceAllString(v, ``)
}
if options.Crypto == `eapi` && body[0] != '{' {
err = json.Unmarshal(decrypt(body), &answer.Body)

View File

@ -3,7 +3,6 @@ package wy
import (
"lx-source/src/env"
wy "lx-source/src/sources/custom/wy/modules"
"maps"
"time"
// "time"
@ -48,7 +47,10 @@ func refresh(loger *logs.Logger, now int64) error {
if out, ok := res.Body[`cookie`].(string); ok {
loger.Info(`获取数据成功`)
cmap := cookie.ToMap(cookie.Parse(out))
maps.Copy(cookies, cmap)
// inline call to maps.Copy
for k, v := range cmap {
cookies[k] = v
}
env.Config.Custom.Wy_Api_Cookie = cookie.Marshal(cookies)
loger.Debug(`Cookie: %#v`, cookies)
// if _, ok := cmap[`MUSIC_U`]; ok {

View File

@ -3,6 +3,38 @@
<!-- #### \# 2024-02-14 v1.0.3-rel (release)
+ **停止更新:感谢这三个月的陪伴,现因无力维护,停止后续更新,发布最后版本,大家有缘再见** -->
#### \# 2024-06-22 v1.0.3.0622 (dev)
+ 临近期末暂缓更新计划假期重构服务端v1.1.0
+ 更新源脚本,使用 WebPack 构建,支持自定义更多参数
+ 体验地址 `/lx-source-script.js`,添加 `?key=请求密钥`
+ 更新编译脚本支持MuslLibC、静态构建支持自动发布Release
#### \# 2024-06-14 v1.0.3.0614 (dev)
+ 更新编译脚本,支持更多架构
#### \# 2024-05-31 v1.0.3.0531 (dev)
+ Wy源优化模块同步部分更改
+ Kw源测试接口无需白名单包名二次甚至三次查询
+ Kw源允许自定义Des_Source参数
#### \# 2024-05-25 Special Ver (dev)
+ 更新Action构建脚本正式兼容Go1.20(windows7)
#### \# 2024-05-18 v1.0.3.0518 (dev)
+ (据用户反馈,当前账号添加方式过于复杂,很多参数不知道怎么填,故增加部分平台简化登录方式)
+ Tx源支持QQ快速登录(beta), 启动参数 `-e txqq`
+ 支持自定义错误音频地址(不填禁用), 位置 [Main].ErrMp3
+ ~~修复若干已知Bug~~
<!-- #### \# 2024-05-12 v1.0.3.0503 (dev)
+ 支持对接Python版ApiServer(二次分销?)
+ 同时开发了一套数据共享接口
(太过逆天,先不发了,容易导致滥用问题) -->
#### \# 2024-05-03 v1.0.3.0503 (dev)
+ 基于 cr-go-sdk 重新支持 Cloudreve 缓存
+ 解决Tx源一处Fallback死循环问题
#### \# 2024-04-30 v1.0.3.0430 (beta)
+ Tx源支持自定义CDN链接地址
+ Wy源支持扫码登录(beta), 启动参数 `-e wyqr`