mirror of
https://github.com/ZxwyWebSite/lx-source.git
synced 2025-05-23 21:37:42 +08:00
2024-01-31 v1.0.2-b12-d3
This commit is contained in:
parent
ad7614c564
commit
32c2617f3f
8
go.mod
8
go.mod
@ -12,7 +12,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.10.2 // indirect
|
github.com/bytedance/sonic v1.10.2 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/chenzhuoyu/iasm v0.9.0 // indirect
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-ini/ini v1.67.0 // indirect
|
github.com/go-ini/ini v1.67.0 // indirect
|
||||||
@ -21,7 +21,7 @@ require (
|
|||||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
@ -29,10 +29,10 @@ require (
|
|||||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.7.0 // indirect
|
||||||
golang.org/x/crypto v0.9.0 // indirect
|
golang.org/x/crypto v0.9.0 // indirect
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/net v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.14.0 // indirect
|
golang.org/x/sys v0.16.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/text v0.9.0 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
17
go.sum
17
go.sum
@ -6,8 +6,9 @@ github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F
|
|||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||||
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||||
|
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||||
|
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@ -45,8 +46,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
|||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
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/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=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
@ -98,8 +99,8 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95
|
|||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
@ -110,11 +111,11 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
176
init.go
Normal file
176
init.go
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"lx-source/src/caches"
|
||||||
|
"lx-source/src/caches/localcache"
|
||||||
|
"lx-source/src/env"
|
||||||
|
"lx-source/src/sources"
|
||||||
|
"lx-source/src/sources/builtin"
|
||||||
|
"net/http"
|
||||||
|
stdurl "net/url"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 生成连接码
|
||||||
|
func genAuth() {
|
||||||
|
ga := env.Loger.NewGroup(`LxM-Auth`)
|
||||||
|
// 检测Key是否存在, 否则生成并保存
|
||||||
|
if env.Config.Auth.ApiKey_Value == `` {
|
||||||
|
pass := zcypt.Base64ToString(base64.StdEncoding, zcypt.RandomBytes(4*4))
|
||||||
|
env.Config.Auth.ApiKey_Value = pass // env.Config.Apis.LxM_Auth
|
||||||
|
ga.Info(`已生成默认连接码: %q`, pass)
|
||||||
|
ga.Info(`可在配置文件 [Auth].ApiKey_Value 中修改`) //可在配置文件 [Apis].LxM_Auth 中修改, 填写 "null" 关闭验证
|
||||||
|
if err := env.Cfg.Save(``); err != nil {
|
||||||
|
ga.Error(`写入配置文件失败: %s, 将导致下次启动时连接码发生变化`, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !env.Config.Auth.ApiKey_Enable {
|
||||||
|
ga.Warn(`已关闭Key验证, 公开部署可能导致安全隐患`)
|
||||||
|
} else {
|
||||||
|
ga.Warn(`已开启Key验证, 记得在脚本中填写 apipass=%q`, env.Config.Auth.ApiKey_Value)
|
||||||
|
}
|
||||||
|
ga.Free()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载文件日志 (请在初始化配置文件后调用)
|
||||||
|
func loadFileLoger() {
|
||||||
|
// 最后加载FileLoger保证必要日志已输出 (Debug模式强制在控制台输出日志)
|
||||||
|
if env.Config.Main.LogPath != `` {
|
||||||
|
lg := env.Loger.NewGroup(`FileLoger`)
|
||||||
|
printout := env.Config.Main.Print // || env.Config.Main.Debug
|
||||||
|
_, do, err := env.Loger.SetOutFile(ztool.Str_FastConcat(env.RunPath, env.Config.Main.LogPath), printout)
|
||||||
|
if err == nil {
|
||||||
|
env.Defer.Add(do)
|
||||||
|
gin.DefaultWriter = env.Loger.GetOutput()
|
||||||
|
gin.ForceConsoleColor()
|
||||||
|
// lg.Info(`文件日志初始化成功`)
|
||||||
|
} else {
|
||||||
|
lg.Error(`文件日志初始化失败:%v`, err)
|
||||||
|
}
|
||||||
|
lg.Free()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化基础功能
|
||||||
|
func initMain() {
|
||||||
|
// 初始化代理
|
||||||
|
ipr := env.Loger.NewGroup(`InitProxy`)
|
||||||
|
switch env.Config.Source.FakeIP_Mode {
|
||||||
|
case `0`, `off`:
|
||||||
|
break
|
||||||
|
case `1`, `req`:
|
||||||
|
ipr.Fatal(`暂未实现此功能`)
|
||||||
|
case `2`, `val`:
|
||||||
|
if env.Config.Source.FakeIP_Value != `` {
|
||||||
|
ipr.Info(`已开启伪装IP,当前值: %v`, env.Config.Source.FakeIP_Value)
|
||||||
|
ztool.Net_header[`X-Real-IP`] = env.Config.Source.FakeIP_Value
|
||||||
|
ztool.Net_header[`X-Forwarded-For`] = env.Config.Source.FakeIP_Value
|
||||||
|
} else {
|
||||||
|
ipr.Error(`伪装IP为空,请检查配置 [Source].FakeIP_Value`)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ipr.Error(`未定义的代理模式,请检查配置 [Source].FakeIP_Mode,本次启动禁用IP伪装`)
|
||||||
|
}
|
||||||
|
if env.Config.Source.Proxy_Enable {
|
||||||
|
ipr.Debug(`ProxyAddr: %v`, env.Config.Source.Proxy_Address)
|
||||||
|
addr, err := stdurl.Parse(env.Config.Source.Proxy_Address)
|
||||||
|
if err != nil {
|
||||||
|
ipr.Error(`代理Url解析失败: %s, 将禁用代理功能`, err)
|
||||||
|
} else {
|
||||||
|
type chkRegion struct {
|
||||||
|
AmapFlag int `json:"amap_flag"`
|
||||||
|
IPFlag int `json:"ip_flag"`
|
||||||
|
AmapAddress string `json:"amap_address"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
Flag int `json:"flag"`
|
||||||
|
Errcode int `json:"errcode"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
var out chkRegion
|
||||||
|
oldval := ztool.Net_client.Transport
|
||||||
|
ztool.Net_client.Transport = &http.Transport{Proxy: http.ProxyURL(addr)}
|
||||||
|
err := ztool.Net_Request(http.MethodGet,
|
||||||
|
`https://mips.kugou.com/check/iscn?&format=json`, nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(ztool.Net_header)},
|
||||||
|
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&out)},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
ztool.Net_client.Transport = oldval
|
||||||
|
ipr.Error(`地区验证失败: %s, 已恢复默认配置`, err)
|
||||||
|
} else {
|
||||||
|
ipr.Debug(`Resp: %+v`, out)
|
||||||
|
if out.Flag != 1 {
|
||||||
|
ipr.Warn(`您正在使用非中国大陆(%v)代理,可能导致部分音乐不可用`, out.Country)
|
||||||
|
} else {
|
||||||
|
ipr.Warn(`代理开启成功,请注意潜在的Cookie泄露问题`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipr.Free()
|
||||||
|
|
||||||
|
// 初始化缓存
|
||||||
|
icl := env.Loger.NewGroup(`InitCache`)
|
||||||
|
switch env.Config.Cache.Mode {
|
||||||
|
case `0`, `off`:
|
||||||
|
// NothingToDo... (已默认禁用缓存)
|
||||||
|
break
|
||||||
|
case `1`, `local`:
|
||||||
|
// 注:由于需要修改LocalCachePath参数,必须在InitRouter之前执行
|
||||||
|
cache, err := caches.New(&localcache.Cache{
|
||||||
|
Path: filepath.Join(env.RunPath, env.Config.Cache.Local_Path),
|
||||||
|
Bind: env.Config.Cache.Local_Bind,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
icl.Error(`驱动["local"]初始化失败: %v, 将禁用缓存功能`, err)
|
||||||
|
}
|
||||||
|
caches.UseCache = cache
|
||||||
|
icl.Warn(`本地缓存绑定地址:%q,请确认其与实际访问地址相符`, env.Config.Cache.Local_Bind)
|
||||||
|
// LocalCachePath = filepath.Join(runPath, env.Config.Cache.Local_Path)
|
||||||
|
// UseCache = &localcache.Cache{
|
||||||
|
// Path: LocalCachePath,
|
||||||
|
// Addr: env.Config.Apis.BindAddr,
|
||||||
|
// }
|
||||||
|
// 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,
|
||||||
|
// }
|
||||||
|
default:
|
||||||
|
icl.Error(`未定义的缓存模式,请检查配置 [Cache].Mode,本次启动禁用缓存`)
|
||||||
|
}
|
||||||
|
icl.Free()
|
||||||
|
|
||||||
|
// 初始化音乐源
|
||||||
|
ise := env.Loger.NewGroup(`InitSource`)
|
||||||
|
switch env.Config.Source.Mode {
|
||||||
|
case `0`, `off`:
|
||||||
|
break
|
||||||
|
case `1`, `builtin`:
|
||||||
|
sources.UseSource = &builtin.Source{}
|
||||||
|
case `2`, `custom`:
|
||||||
|
ise.Fatal(`暂未实现账号解析源`)
|
||||||
|
default:
|
||||||
|
ise.Error(`未定义的音乐源,请检查配置 [Source].Mode,本次启动禁用内置源`)
|
||||||
|
}
|
||||||
|
ise.Free()
|
||||||
|
}
|
222
main.go
222
main.go
@ -2,19 +2,16 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"flag"
|
"flag"
|
||||||
"lx-source/src/caches"
|
"io/fs"
|
||||||
"lx-source/src/caches/localcache"
|
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
"lx-source/src/router"
|
"lx-source/src/server"
|
||||||
"lx-source/src/sources"
|
|
||||||
"lx-source/src/sources/builtin"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -23,50 +20,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func genAuth() {
|
|
||||||
ga := env.Loger.NewGroup(`LxM-Auth`)
|
|
||||||
// 检测Key是否存在, 否则生成并保存
|
|
||||||
if env.Config.Auth.ApiKey_Value == `` {
|
|
||||||
randomBytes := func(size int) []byte {
|
|
||||||
buf := make([]byte, size)
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
buf[i] = byte(rand.New(rand.NewSource(time.Now().UnixNano() + int64(i*i+rand.Intn(256)))).Intn(256))
|
|
||||||
}
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
pass := base64.StdEncoding.EncodeToString(randomBytes(4 * 4))
|
|
||||||
env.Config.Auth.ApiKey_Value = pass // env.Config.Apis.LxM_Auth
|
|
||||||
ga.Info(`已生成默认连接码: %q`, pass)
|
|
||||||
ga.Info(`可在配置文件 [Auth].ApiKey_Value 中修改`) //可在配置文件 [Apis].LxM_Auth 中修改, 填写 "null" 关闭验证
|
|
||||||
if err := env.Cfg.Save(``); err != nil {
|
|
||||||
ga.Error(`写入配置文件失败: %s, 将导致下次启动时连接码发生变化`, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !env.Config.Auth.ApiKey_Enable {
|
|
||||||
ga.Warn(`已关闭Key验证, 公开部署可能导致安全隐患`)
|
|
||||||
} else {
|
|
||||||
ga.Warn(`已开启Key验证, 记得在脚本中填写 apipass=%q`, env.Config.Auth.ApiKey_Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载文件日志 (请在初始化配置文件后调用)
|
|
||||||
func loadFileLoger() {
|
|
||||||
// 最后加载FileLoger保证必要日志已输出 (Debug模式强制在控制台输出日志)
|
|
||||||
if env.Config.Main.LogPath != `` {
|
|
||||||
lg := env.Loger.NewGroup(`FileLoger`)
|
|
||||||
printout := env.Config.Main.Print // || env.Config.Main.Debug
|
|
||||||
_, do, err := env.Loger.SetOutFile(ztool.Str_FastConcat(env.RunPath, env.Config.Main.LogPath), printout)
|
|
||||||
if err == nil {
|
|
||||||
env.Defer.Add(do)
|
|
||||||
gin.DefaultWriter = env.Loger.GetOutput()
|
|
||||||
gin.ForceConsoleColor()
|
|
||||||
// lg.Info(`文件日志初始化成功`)
|
|
||||||
} else {
|
|
||||||
lg.Error(`文件日志初始化失败:%v`, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
func init() {
|
func init() {
|
||||||
ztool.Cmd_FastPrint(ztool.Str_FastConcat(`
|
ztool.Cmd_FastPrint(ztool.Str_FastConcat(`
|
||||||
@ -82,18 +35,26 @@ func init() {
|
|||||||
env.RunPath, _ = os.Getwd()
|
env.RunPath, _ = os.Getwd()
|
||||||
var confPath string
|
var confPath string
|
||||||
flag.StringVar(&confPath, `c`, ztool.Str_FastConcat(env.RunPath, `/data/conf.ini`), `指定配置文件路径`)
|
flag.StringVar(&confPath, `c`, ztool.Str_FastConcat(env.RunPath, `/data/conf.ini`), `指定配置文件路径`)
|
||||||
|
etag := flag.String(`e`, ``, `扩展启动参数`)
|
||||||
|
perm := flag.Uint(`p`, 0, `自定义文件权限(8进制前面加0)`)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
// fileLoger() // 注:记录日志必然会影响性能,自行选择是否开启
|
if perm != nil && *perm != 0 {
|
||||||
// logs.DefLogger(`LX-SOURCE`, logs.LevelDebu)
|
ztool.Fbj_DefPerm = fs.FileMode(*perm)
|
||||||
// logs.Main = `LX-SOURCE`
|
fp := env.Loger.NewGroup(`FilePerm`)
|
||||||
env.Cfg.MustInit(confPath) //conf.InitConfig(confPath)
|
// if ztool.Fbj_DefPerm > 777 {
|
||||||
|
// fp.Fatal(`请在实际权限前面加0`)
|
||||||
|
// }
|
||||||
|
fp.Info(`设置默认文件权限为 %o (%v)`, *perm, ztool.Fbj_DefPerm).Free()
|
||||||
|
}
|
||||||
|
parseEtag(etag)
|
||||||
|
env.Cfg.MustInit(confPath)
|
||||||
// fmt.Printf("%+v\n", env.Config)
|
// fmt.Printf("%+v\n", env.Config)
|
||||||
env.Loger.NewGroup(`ServHello`).Info(`欢迎使用 LX-SOURCE 洛雪音乐自定义源`)
|
env.Loger.NewGroup(`ServHello`).Info(`欢迎使用 LX-SOURCE 洛雪音乐自定义源`).Free()
|
||||||
if !env.Config.Main.Debug {
|
if !env.Config.Main.Debug {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
} else {
|
} else {
|
||||||
logs.Levell = logs.LevelDebu // logs.Level = 3
|
logs.Levell = logs.LevelDebu // logs.Level = 3
|
||||||
env.Loger.NewGroup(`DebugMode`).Debug(`已开启调试模式, 将输出更详细日志 (配置文件中 [Main].Debug 改为 false 关闭)`)
|
env.Loger.NewGroup(`DebugMode`).Debug(`已开启调试模式, 将输出更详细日志 (配置文件中 [Main].Debug 改为 false 关闭)`).Free()
|
||||||
}
|
}
|
||||||
genAuth()
|
genAuth()
|
||||||
if env.Config.Main.SysLev {
|
if env.Config.Main.SysLev {
|
||||||
@ -103,102 +64,93 @@ func init() {
|
|||||||
} else {
|
} else {
|
||||||
sl.Warn(`成功设置较高优先级,此功能可能导致系统不稳定`)
|
sl.Warn(`成功设置较高优先级,此功能可能导致系统不稳定`)
|
||||||
}
|
}
|
||||||
|
sl.Free()
|
||||||
|
}
|
||||||
|
if env.Config.Main.Timeout != env.DefCfg.Main.Timeout {
|
||||||
|
ztool.Net_client.Timeout = time.Second * time.Duration(env.Config.Main.Timeout) // 自定义请求超时
|
||||||
|
env.Loger.NewGroup(`InitNet`).Info(`请求超时已设为 %s`, ztool.Net_client.Timeout).Free()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
defer env.Defer.Do()
|
defer env.Defer.Do()
|
||||||
|
// 初始化基础功能
|
||||||
// 初始化缓存
|
initMain()
|
||||||
icl := env.Loger.NewGroup(`InitCache`)
|
|
||||||
switch env.Config.Cache.Mode {
|
|
||||||
case `0`, `off`:
|
|
||||||
// NothingToDo... (已默认禁用缓存)
|
|
||||||
break
|
|
||||||
case `1`, `local`:
|
|
||||||
// 注:由于需要修改LocalCachePath参数,必须在InitRouter之前执行
|
|
||||||
cache, err := caches.New(&localcache.Cache{
|
|
||||||
Path: filepath.Join(env.RunPath, env.Config.Cache.Local_Path),
|
|
||||||
Bind: env.Config.Cache.Local_Bind,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
icl.Error(`驱动["local"]初始化失败: %v, 将禁用缓存功能`, err)
|
|
||||||
}
|
|
||||||
caches.UseCache = cache
|
|
||||||
icl.Warn(`本地缓存绑定地址: %q, 请确认其与实际访问地址相符`, env.Config.Cache.Local_Bind)
|
|
||||||
// LocalCachePath = filepath.Join(runPath, env.Config.Cache.Local_Path)
|
|
||||||
// UseCache = &localcache.Cache{
|
|
||||||
// Path: LocalCachePath,
|
|
||||||
// Addr: env.Config.Apis.BindAddr,
|
|
||||||
// }
|
|
||||||
// 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,
|
|
||||||
// }
|
|
||||||
default:
|
|
||||||
icl.Error(`未定义的缓存模式,请检查配置 [Cache].Mode,本次启动禁用缓存`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化音乐源
|
|
||||||
ise := env.Loger.NewGroup(`InitSource`)
|
|
||||||
switch env.Config.Source.Mode {
|
|
||||||
case `0`, `off`:
|
|
||||||
break
|
|
||||||
case `1`, `builtin`:
|
|
||||||
sources.UseSource = &builtin.Source{}
|
|
||||||
case `2`, `custom`:
|
|
||||||
ise.Fatal(`暂未实现账号解析源`)
|
|
||||||
default:
|
|
||||||
ise.Error(`未定义的音乐源,请检查配置 [Source].Mode,本次启动禁用内置源`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 载入必要模块
|
// 载入必要模块
|
||||||
env.Inits.Do()
|
env.Inits.Do()
|
||||||
env.Loger.NewGroup(`ServStart`).Info(`服务端启动, 监听地址 %s`, env.Config.Main.Listen)
|
env.Loger.NewGroup(`ServInit`).Info(`服务端启动, 监听地址 %s`, strings.Join(env.Config.Main.Listen, `|`)).Free()
|
||||||
loadFileLoger()
|
loadFileLoger()
|
||||||
env.Defer.Add(env.Tasker.Run(env.Loger)) // wait
|
env.Defer.Add(env.Tasker.Run(env.Loger)) // wait
|
||||||
|
|
||||||
// 启动Http服务
|
// 启动Http服务
|
||||||
r := router.InitRouter() //InitRouter()
|
listenAndServe(server.InitRouter(), env.Config.Main.Listen)
|
||||||
server := &http.Server{
|
|
||||||
Addr: env.Config.Main.Listen,
|
|
||||||
Handler: r,
|
|
||||||
}
|
}
|
||||||
go func() {
|
|
||||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
||||||
env.Loger.NewGroup(`InitRouter().Run`).Fatal(`%s`, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
|
// 监听多端口
|
||||||
|
func listenAndServe(handler http.Handler, addrs []string) {
|
||||||
|
// 前置检测
|
||||||
|
length := len(addrs)
|
||||||
|
ss := env.Loger.NewGroup(`ServStart`)
|
||||||
|
if length == 0 {
|
||||||
|
ss.Fatal(`监听地址列表为空`)
|
||||||
|
}
|
||||||
|
// ss.Info(`服务端启动,请稍候...`)
|
||||||
|
srvlist := make(map[int]*http.Server, length) // 伪数组,便于快速删除数据
|
||||||
|
lock := new(sync.Mutex)
|
||||||
|
var failnum int32
|
||||||
|
length32 := int32(length)
|
||||||
|
// 启动服务
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
lock.Lock()
|
||||||
|
srvlist[i] = &http.Server{Addr: addrs[i], Handler: handler}
|
||||||
|
lock.Unlock()
|
||||||
|
go func(n int) {
|
||||||
|
server := srvlist[n]
|
||||||
|
// ss.Info(`开始监听 %v`, server.Addr)
|
||||||
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
ss.Error(`监听%q失败: %s`, server.Addr, err) // 监听":1011"失败: http: Server closed
|
||||||
|
fn := atomic.AddInt32(&failnum, 1)
|
||||||
|
if fn == length32 {
|
||||||
|
ss.Fatal(`所有地址监听失败,程序被迫退出`)
|
||||||
|
}
|
||||||
|
lock.Lock()
|
||||||
|
delete(srvlist, n)
|
||||||
|
lock.Unlock()
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
// time.Sleep(time.Millisecond * 300)
|
||||||
|
// if len(srvlist) == 0 {
|
||||||
|
// ss.Fatal(`所有地址监听失败,程序被迫退出`)
|
||||||
|
// }
|
||||||
|
// ss.Free()
|
||||||
|
// 安全退出
|
||||||
quit := make(chan os.Signal, 1)
|
quit := make(chan os.Signal, 1)
|
||||||
signal.Notify(quit, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
signal.Notify(quit, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||||
<-quit
|
<-quit
|
||||||
sc := env.Loger.NewGroup(`ServClose`)
|
sc := env.Loger.NewGroup(`ServClose`)
|
||||||
sc.Info(`等待结束活动连接...`) // Shutdown Server ...
|
sc.Info(`等待结束活动连接...`)
|
||||||
|
// 停止服务
|
||||||
|
var unsafenum int32
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
for i := range srvlist {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(n int) {
|
||||||
|
server := srvlist[n]
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := server.Shutdown(ctx); err != nil {
|
if err := server.Shutdown(ctx); err != nil {
|
||||||
sc.Fatal(`未安全退出: %s`, err) // Server Shutdown
|
sc.Warn(`连接%q未安全退出: %s`, server.Addr, err) // 连接":1011"未安全退出: timeout
|
||||||
|
atomic.AddInt32(&unsafenum, 1)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
wg.Done()
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
if unsafenum != 0 {
|
||||||
|
sc.Warn(`未安全退出 :(`)
|
||||||
|
} else {
|
||||||
|
sc.Info(`已安全退出 :)`)
|
||||||
}
|
}
|
||||||
sc.Info(`已安全退出 :)`) // Server exited
|
|
||||||
|
|
||||||
// if err := InitRouter().Run(env.Config.Main.Listen); err != nil {
|
|
||||||
// env.Loger.NewGroup(`InitRouter().Run`).Fatal(`%s`, err)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
31
menu.go
Normal file
31
menu.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lx-source/src/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseEtag(etag *string) {
|
||||||
|
if etag == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loger := env.Loger.NewGroup(`ParseEtag`)
|
||||||
|
switch *etag {
|
||||||
|
case ``:
|
||||||
|
break
|
||||||
|
case `menu`:
|
||||||
|
loger.Fatal(`暂不支持交互菜单,敬请期待...`)
|
||||||
|
// menuMian()
|
||||||
|
default:
|
||||||
|
loger.Fatal(`未知参数:%q`, *etag)
|
||||||
|
}
|
||||||
|
loger.Free()
|
||||||
|
}
|
||||||
|
|
||||||
|
// func menuMian() {
|
||||||
|
// app := menu.NewApp(`Lx-Source`)
|
||||||
|
// app.Data = menu.Data{
|
||||||
|
// `Main`: func(this *menu.App) string { return ` ` },
|
||||||
|
// }
|
||||||
|
// app.Run()
|
||||||
|
// os.Exit(0)
|
||||||
|
// }
|
15
readme.md
15
readme.md
@ -11,11 +11,11 @@
|
|||||||
## ZxwyWebSite/LX-Source
|
## ZxwyWebSite/LX-Source
|
||||||
### 简介
|
### 简介
|
||||||
+ LX-Music 解析源 (洛雪音乐自定义源)
|
+ LX-Music 解析源 (洛雪音乐自定义源)
|
||||||
+ **由于本项目的特殊性,请低调使用,切勿宣传**
|
+ **由于本项目的特殊性,请低调使用,切勿大肆宣传**<!--(~~传的越多,寄的越快~~)-->
|
||||||
+ 测试阶段,不代表最终品质
|
+ 测试阶段,不代表最终品质
|
||||||
+ 验证部分暂未完善,建议仅本地部署,不要公开发布
|
+ 验证部分暂未完善,建议仅本地部署,不要公开发布
|
||||||
+ 视频教程:[使用教程.mp4](https://r2eu.zxwy.link/gh/lx-source/v1.0.2-b0.1/%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B.mp4)
|
+ 视频教程:[使用教程.mp4](https://r2eu.zxwy.link/gh/lx-source/v1.0.2-b0.1/%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B.mp4)
|
||||||
<!-- + **锟斤拷** -->
|
<!-- + *手持两把锟斤拷,口中疾呼烫烫烫。 脚踏千朵屯屯屯,笑看万物锘锘锘。* -->
|
||||||
|
|
||||||
### 使用
|
### 使用
|
||||||
#### 服务端
|
#### 服务端
|
||||||
@ -48,8 +48,8 @@
|
|||||||
<!-- + ... -->
|
<!-- + ... -->
|
||||||
|
|
||||||
### 音乐源
|
### 音乐源
|
||||||
+ 内置源 (抓取自网络公开接口) **注:文明上网,请勿滥用**
|
+ 内置源 (抓取自网络公开接口) **注:文明上网,请勿滥用,否则停止后续更新**
|
||||||
+ 账号源 (登录Vip账号解析) **注:可能导致封号,如出问题本项目不负责**
|
+ 账号源 (登录会员账号解析) **注:可能导致封号,如出问题本项目不负责**
|
||||||
|
|
||||||
### 开发
|
### 开发
|
||||||
+ 环境要求:Golang 1.21 (建议 >=1.20)
|
+ 环境要求:Golang 1.21 (建议 >=1.20)
|
||||||
@ -61,18 +61,21 @@
|
|||||||
- pkg/ 依赖包,一般在外部调用,不轻易修改
|
- pkg/ 依赖包,一般在外部调用,不轻易修改
|
||||||
- src/ 源码包,用于实现各种功能
|
- src/ 源码包,用于实现各种功能
|
||||||
* env 公用变量,需要全局调用的参数
|
* env 公用变量,需要全局调用的参数
|
||||||
|
* database 数据库相关
|
||||||
* caches 文件缓存封装
|
* caches 文件缓存封装
|
||||||
* router Gin路由
|
* server Gin路由
|
||||||
* middleware 请求中间件
|
* middleware 请求中间件
|
||||||
* sources 音乐源
|
* sources 音乐源
|
||||||
<!-- - public/ 静态资源,打包进程序,新环境运行自动释放 -->
|
<!-- - public/ 静态资源,打包进程序,新环境运行自动释放 -->
|
||||||
<!-- - scripts/ 一些快捷脚本 -->
|
<!-- - scripts/ 一些快捷脚本 -->
|
||||||
- build.go 快速构建脚本 (请先根据本地环境编辑配置)
|
- build.go 快速构建脚本 (请先根据本地环境编辑配置)
|
||||||
|
- init.go 初始化检测
|
||||||
|
- menu.go 交互菜单 (暂未实现)
|
||||||
- main.go 主程序
|
- main.go 主程序
|
||||||
|
|
||||||
### 其它
|
### 其它
|
||||||
+ 基于 Golang + Gin框架 编写
|
+ 基于 Golang + Gin框架 编写
|
||||||
+ 部分功能参考 [Python版](https://github.com/lxmusics/lx-music-api-server-python) 实现
|
+ 感谢以下项目提供参考:[Python版](https://github.com/lxmusics/lx-music-api-server-python),[WyApi](https://github.com/ZxwyWebSite/NeteaseCloudMusicApi),...
|
||||||
|
|
||||||
### 更新
|
### 更新
|
||||||
+ 见 `update.md`
|
+ 见 `update.md`
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package caches
|
package caches
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"lx-source/src/env"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/ZxwyWebSite/ztool/logs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -15,6 +18,7 @@ type (
|
|||||||
Quality string // quality 音质 128k / 320k / flac / flac24bit
|
Quality string // quality 音质 128k / 320k / flac / flac24bit
|
||||||
Extname string // rext 扩展名 mp3 / flac (没有前缀点)
|
Extname string // rext 扩展名 mp3 / flac (没有前缀点)
|
||||||
query string // 查询字符串缓存
|
query string // 查询字符串缓存
|
||||||
|
Request *http.Request
|
||||||
}
|
}
|
||||||
// 缓存需实现以下接口
|
// 缓存需实现以下接口
|
||||||
Cache interface {
|
Cache interface {
|
||||||
@ -40,7 +44,6 @@ func (*Nullcache) Stat() bool { return false }
|
|||||||
func (*Nullcache) Init() error { return nil }
|
func (*Nullcache) Init() error { return nil }
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Loger = env.Loger.NewGroup(`Caches`)
|
|
||||||
UseCache Cache = &Nullcache{}
|
UseCache Cache = &Nullcache{}
|
||||||
// ErrNotInited = errors.New(`缓存策略未初始化`)
|
// ErrNotInited = errors.New(`缓存策略未初始化`)
|
||||||
query_pool = sync.Pool{New: func() any { return new(Query) }}
|
query_pool = sync.Pool{New: func() any { return new(Query) }}
|
||||||
@ -111,3 +114,12 @@ func MustNew(c Cache) Cache {
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Loger *logs.Logger
|
||||||
|
|
||||||
|
// 初始化Loger
|
||||||
|
func init() {
|
||||||
|
env.Inits.Add(func() {
|
||||||
|
Loger = env.Loger.NewGroup(`Caches`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -4,9 +4,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"lx-source/src/caches"
|
"lx-source/src/caches"
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
|
"lx-source/src/middleware/util"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
)
|
)
|
||||||
@ -17,10 +19,17 @@ type Cache struct {
|
|||||||
state bool // 激活状态
|
state bool // 激活状态
|
||||||
}
|
}
|
||||||
|
|
||||||
var loger = env.Loger.NewGroup(`Caches`) //caches.Loger.AppGroup(`local`)
|
// var loger = env.Loger.NewGroup(`Caches`) //caches.Loger.AppGroup(`local`)
|
||||||
|
|
||||||
func (c *Cache) getLink(q string) string {
|
func (c *Cache) getLink(q *caches.Query) (furl string) {
|
||||||
return ztool.Str_FastConcat(c.Bind, `/file/`, q) // c.Addr + `file/` + q
|
// fmt.Printf("%#v\n", q.Request)
|
||||||
|
if env.Config.Cache.Local_Auto {
|
||||||
|
// 注:此方式无法确定是否支持HTTPS,暂时默认HTTP,让Nginx重定向
|
||||||
|
furl = ztool.Str_FastConcat(util.GetPath(q.Request, `link/`), `file/`, q.Query())
|
||||||
|
} else {
|
||||||
|
furl = ztool.Str_FastConcat(c.Bind, `/file/`, q.Query())
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Get(q *caches.Query) string {
|
func (c *Cache) Get(q *caches.Query) string {
|
||||||
@ -31,7 +40,7 @@ func (c *Cache) Get(q *caches.Query) string {
|
|||||||
}
|
}
|
||||||
// env.Cache.Set(q.Query(), struct{}{}, 3600)
|
// env.Cache.Set(q.Query(), struct{}{}, 3600)
|
||||||
// }
|
// }
|
||||||
return c.getLink(q.Query())
|
return c.getLink(q)
|
||||||
// fpath := filepath.Join(c.Path, q.Source, q.MusicID, q.Quality)
|
// fpath := filepath.Join(c.Path, q.Source, q.MusicID, q.Quality)
|
||||||
// if _, e := os.Stat(fpath); e != nil {
|
// if _, e := os.Stat(fpath); e != nil {
|
||||||
// return ``
|
// return ``
|
||||||
@ -54,14 +63,23 @@ func (c *Cache) Set(q *caches.Query, l string) string {
|
|||||||
// }
|
// }
|
||||||
// loger.Debug(`FFMpeg_Out: %v`, out)
|
// loger.Debug(`FFMpeg_Out: %v`, out)
|
||||||
// } else {
|
// } else {
|
||||||
|
for i := 0; true; i++ {
|
||||||
err := ztool.Net_DownloadFile(l, fpath, nil)
|
err := ztool.Net_DownloadFile(l, fpath, nil)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
loger.Error(`DownloadFile: %v`, err)
|
break
|
||||||
|
}
|
||||||
|
caches.Loger.Error(`DownloadFile: %v, Retry: %v`, err, i)
|
||||||
|
if i == 1 || !strings.Contains(err.Error(), `context deadline exceeded`) {
|
||||||
|
if err := os.Remove(fpath); err != nil {
|
||||||
|
caches.Loger.Error(`RemoveFile: %s`, err)
|
||||||
|
}
|
||||||
return ``
|
return ``
|
||||||
}
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
// }
|
// }
|
||||||
// env.Cache.Set(q.Query(), struct{}{}, 3600)
|
// env.Cache.Set(q.Query(), struct{}{}, 3600)
|
||||||
return c.getLink(q.Query())
|
return c.getLink(q)
|
||||||
// fpath := filepath.Join(c.Path, q.String)
|
// fpath := filepath.Join(c.Path, q.String)
|
||||||
// os.MkdirAll(filepath.Dir(fpath), fs.ModePerm)
|
// os.MkdirAll(filepath.Dir(fpath), fs.ModePerm)
|
||||||
// g := c.Loger.NewGroup(`localcache`)
|
// g := c.Loger.NewGroup(`localcache`)
|
||||||
|
113
src/env/env.go
vendored
113
src/env/env.go
vendored
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = `1.0.2-b12`
|
Version = `1.0.2-dev`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -26,20 +26,31 @@ var (
|
|||||||
序号 名称 描述
|
序号 名称 描述
|
||||||
*/
|
*/
|
||||||
type (
|
type (
|
||||||
|
// 系统
|
||||||
Conf_Main struct {
|
Conf_Main struct {
|
||||||
Debug bool `comment:"调试模式"`
|
Debug bool `comment:"调试模式"`
|
||||||
Listen string `comment:"监听地址"`
|
Listen []string `comment:"监听地址 (多端口以','分隔)"`
|
||||||
Gzip bool `comment:"开启GZip (对已压缩的内容使用会产生反效果)"`
|
Gzip bool `comment:"开启GZip (对已压缩的内容使用会产生反效果)"`
|
||||||
LogPath string `comment:"文件日志路径,不填禁用"`
|
LogPath string `comment:"文件日志路径,不填禁用"`
|
||||||
Print bool `comment:"控制台输出"`
|
Print bool `comment:"控制台输出 (影响io性能,后台使用建议关闭)"`
|
||||||
SysLev bool `comment:"(实验性) 设置进程高优先级"`
|
SysLev bool `comment:"(实验性) 设置进程高优先级"`
|
||||||
// FFConv bool `comment:"(实验性) 使用FFMpeg修复音频(本地缓存)"`
|
// FFConv bool `comment:"(实验性) 使用FFMpeg修复音频(本地缓存)"`
|
||||||
|
NgProxy bool `comment:"兼容反向代理(beta)"`
|
||||||
|
Timeout int64 `comment:"网络请求超时(单位:秒,海外服务器可适当调大)"`
|
||||||
}
|
}
|
||||||
|
// 接口
|
||||||
Conf_Apis struct {
|
Conf_Apis struct {
|
||||||
// 预留:后期可能会出一个WebUI,/webui,相关设置
|
// 预留:后期可能会出一个WebUI,/webui,相关设置
|
||||||
// BindAddr string `comment:"外部访问地址,用于生成文件链接"`
|
// WebUI
|
||||||
// LxM_Auth string `comment:"验证Key,自动生成,填写null禁用"`
|
// WebUI_Enable bool `comment:"是否开启WebUI相关接口"`
|
||||||
|
// App
|
||||||
|
// App_Enable bool `comment:"是否开启软件接口"`
|
||||||
|
// App Lx-Music
|
||||||
|
// App_LX_Enable bool `comment:"是否开启Lx-Music相关接口"`
|
||||||
|
// App MusicFree
|
||||||
|
// App_MF_Enable bool `comment:"是否开启MusicFree相关接口"`
|
||||||
}
|
}
|
||||||
|
// 验证
|
||||||
Conf_Auth struct {
|
Conf_Auth struct {
|
||||||
// ApiKey
|
// ApiKey
|
||||||
ApiKey_Enable bool `comment:"是否开启Key验证"`
|
ApiKey_Enable bool `comment:"是否开启Key验证"`
|
||||||
@ -47,15 +58,16 @@ type (
|
|||||||
// 速率限制
|
// 速率限制
|
||||||
RateLimit_Enable bool `comment:"是否开启速率限制"`
|
RateLimit_Enable bool `comment:"是否开启速率限制"`
|
||||||
RateLimit_Block uint32 `comment:"检测范围,每分区为x秒"` // 每x秒一个分区
|
RateLimit_Block uint32 `comment:"检测范围,每分区为x秒"` // 每x秒一个分区
|
||||||
RateLimit_Global uint32 `comment:"全局速率限制,单位次每x秒(暂未开放)"`
|
// RateLimit_Global uint32 `comment:"全局速率限制,单位次每x秒(暂未开放)"`
|
||||||
RateLimit_Single uint32 `comment:"单IP速率限制,单位次每x秒"`
|
RateLimit_Single uint32 `comment:"单IP速率限制,单位次每x秒"`
|
||||||
RateLimit_BanNum uint32 `comment:"容忍限度,超出限制N次后封禁"`
|
RateLimit_BanNum uint32 `comment:"容忍限度,超出限制N次后封禁"`
|
||||||
RateLimit_BanTim uint32 `comment:"封禁后每次延长时间"`
|
RateLimit_BanTim uint32 `comment:"封禁后每次延长时间"`
|
||||||
// 黑白名单
|
// 黑白名单
|
||||||
BanList_Mode string `comment:"名单模式 0: off(关闭), 1: white(白名单), 2: black(黑名单)"`
|
// BanList_Mode string `comment:"名单模式 0: off(关闭), 1: white(白名单), 2: black(黑名单)"`
|
||||||
BanList_White []string `comment:"host白名单"`
|
// BanList_White []string `comment:"host白名单"`
|
||||||
BanList_Black []string `comment:"host黑名单"`
|
// BanList_Black []string `comment:"host黑名单"`
|
||||||
}
|
}
|
||||||
|
// 来源
|
||||||
Conf_Source struct {
|
Conf_Source struct {
|
||||||
Mode string `comment:"音乐来源 0: off(关闭 仅本地), 1: builtin(内置), 2: custom(登录账号 暂不支持)"`
|
Mode string `comment:"音乐来源 0: off(关闭 仅本地), 1: builtin(内置), 2: custom(登录账号 暂不支持)"`
|
||||||
// 伪装IP
|
// 伪装IP
|
||||||
@ -67,19 +79,32 @@ type (
|
|||||||
// 验证
|
// 验证
|
||||||
MusicIdVerify bool `comment:"(beta) 验证音乐ID可用性"`
|
MusicIdVerify bool `comment:"(beta) 验证音乐ID可用性"`
|
||||||
ForceFallback bool `comment:"忽略音质限制,强制获取试听音频"`
|
ForceFallback bool `comment:"忽略音质限制,强制获取试听音频"`
|
||||||
|
// 总开关(解决部分源无法彻底禁用问题)?
|
||||||
|
Enable_Wy bool `comment:"是否开启小芸源"`
|
||||||
|
Enable_Mg bool `comment:"是否开启小蜜源"`
|
||||||
|
Enable_Kw bool `comment:"是否开启小蜗源"`
|
||||||
|
Enable_Kg bool `comment:"是否开启小枸源"`
|
||||||
|
Enable_Tx bool `comment:"是否开启小秋源"`
|
||||||
|
Enable_Lx bool `comment:"是否开启小洛源"`
|
||||||
} // `comment:""`
|
} // `comment:""`
|
||||||
|
// 账号
|
||||||
Conf_Custom struct {
|
Conf_Custom struct {
|
||||||
// wy
|
// wy
|
||||||
Wy_Enable bool `comment:"是否开启小芸源"`
|
Wy_Enable bool `comment:"是否启用小芸源"`
|
||||||
Wy_Cookie string `comment:"账号cookie数据"`
|
Wy_Mode string `comment:"获取方式 0: builtin, 1: 163api"`
|
||||||
Wy_Refresh_Enable bool `comment:"是否启用刷新登录"`
|
// wy 163api
|
||||||
Wy_Refresh_Interval int64 `comment:"下次刷新时间 (由程序维护)"`
|
Wy_Api_Type string `comment:"调用方式 0: native(内置模块), 1: remote(指定地址)"`
|
||||||
|
Wy_Api_Address string `comment:"NeteaseCloudMusicApi项目地址"`
|
||||||
|
Wy_Api_Cookie string `comment:"账号cookie数据"`
|
||||||
|
// wy refresh
|
||||||
|
// Wy_Refresh_Enable bool `comment:"是否启用刷新登录"`
|
||||||
|
// Wy_Refresh_Interval int64 `comment:"下次刷新时间 (由程序维护)"`
|
||||||
|
|
||||||
// mg (暂未实现)
|
// mg (暂未实现)
|
||||||
// Mg_Enable bool `comment:"是否开启小蜜源"`
|
Mg_Enable bool `comment:"是否启用小蜜源"`
|
||||||
|
|
||||||
// kw
|
// kw
|
||||||
Kw_Enable bool `comment:"是否开启小蜗源"`
|
Kw_Enable bool `comment:"是否启用小蜗源"`
|
||||||
Kw_Mode string `comment:"接口模式 0: bdapi(需验证), 1: kwdes"`
|
Kw_Mode string `comment:"接口模式 0: bdapi(需验证), 1: kwdes"`
|
||||||
// kw bdapi
|
// kw bdapi
|
||||||
Kw_Bd_Uid string `comment:"field user.uid"`
|
Kw_Bd_Uid string `comment:"field user.uid"`
|
||||||
@ -90,35 +115,43 @@ type (
|
|||||||
Kw_Des_Header string `comment:"请求头 User-Agent"`
|
Kw_Des_Header string `comment:"请求头 User-Agent"`
|
||||||
|
|
||||||
// kg (暂未实现)
|
// kg (暂未实现)
|
||||||
// Kg_Enable bool `comment:"是否开启小枸源"`
|
Kg_Enable bool `comment:"是否启用小枸源"`
|
||||||
|
|
||||||
// tx
|
// tx
|
||||||
Tx_Enable bool `comment:"是否开启小秋源"`
|
Tx_Enable bool `comment:"是否启用小秋源"`
|
||||||
Tx_Ukey string `comment:"Cookie中/客户端的请求体中的(comm.authst)"`
|
Tx_Ukey string `comment:"Cookie中/客户端的请求体中的(comm.authst)"`
|
||||||
Tx_Uuin string `comment:"key对应的QQ号"`
|
Tx_Uuin string `comment:"key对应的QQ号"`
|
||||||
// tx refresh_login
|
// tx refresh_login
|
||||||
Tx_Refresh_Enable bool `comment:"是否启动刷新登录"`
|
Tx_Refresh_Enable bool `comment:"是否启动刷新登录"`
|
||||||
Tx_Refresh_Interval int64 `comment:"刷新间隔 (由程序维护,非必要无需修改)"`
|
Tx_Refresh_Interval int64 `comment:"刷新间隔 (由程序维护,非必要无需修改)"`
|
||||||
|
|
||||||
|
// lx (local)
|
||||||
|
// Lx_Enable bool `comment:"是否启用小洛源"`
|
||||||
}
|
}
|
||||||
|
// 脚本
|
||||||
Conf_Script struct {
|
Conf_Script struct {
|
||||||
Ver string `comment:"自定义脚本版本" json:"ver"`
|
Ver string `comment:"自定义脚本版本" json:"ver"`
|
||||||
Log string `comment:"更新日志" json:"log"`
|
Log string `comment:"更新日志" json:"log"`
|
||||||
Url string `comment:"脚本下载地址 (public目录内文件名)" json:"url"`
|
Url string `comment:"脚本下载地址 (public目录内文件名)" json:"url"`
|
||||||
Force bool `comment:"强制推送更新" json:"force"`
|
Force bool `comment:"强制推送更新" json:"force"`
|
||||||
|
Auto int `comment:"自动填写配置(beta) 0: 关闭, 1: 仅api地址, 2: 包含密钥" json:"-"`
|
||||||
}
|
}
|
||||||
|
// 缓存
|
||||||
Conf_Cache struct {
|
Conf_Cache struct {
|
||||||
Mode string `comment:"缓存模式 0: off(关闭), 1: local(本地), 2: cloudreve(云盘 未完善)"`
|
Mode string `comment:"缓存模式 0: off(关闭), 1: local(本地), 2: cloudreve(云盘 未完善)"`
|
||||||
LinkMode string `comment:"外链样式 1: static(永久链), 2: dynamic(临时链)"`
|
LinkMode string `comment:"外链样式 1: static(永久链), 2: dynamic(临时链)"`
|
||||||
// 本地
|
// 本地
|
||||||
Local_Path string `comment:"本地缓存保存路径"`
|
Local_Path string `comment:"本地缓存保存路径"`
|
||||||
Local_Bind string `comment:"本地缓存外部访问地址"`
|
Local_Bind string `comment:"本地缓存外部访问地址"`
|
||||||
|
Local_Auto bool `comment:"自适应缓存访问地址(beta)"`
|
||||||
// 云盘
|
// 云盘
|
||||||
Cloud_Site string `comment:"Cloudreve站点地址"`
|
// Cloud_Site string `comment:"Cloudreve站点地址"`
|
||||||
Cloud_User string `comment:"Cloudreve用户名"`
|
// Cloud_User string `comment:"Cloudreve用户名"`
|
||||||
Cloud_Pass string `comment:"Cloudreve密码"`
|
// Cloud_Pass string `comment:"Cloudreve密码"`
|
||||||
Cloud_Sess string `comment:"Cloudreve会话"`
|
// Cloud_Sess string `comment:"Cloudreve会话"`
|
||||||
Cloud_Path string `comment:"Cloudreve存储路径"`
|
// Cloud_Path string `comment:"Cloudreve存储路径"`
|
||||||
}
|
}
|
||||||
|
// 结构
|
||||||
Conf struct {
|
Conf struct {
|
||||||
Main Conf_Main `comment:"程序主配置"`
|
Main Conf_Main `comment:"程序主配置"`
|
||||||
Apis Conf_Apis `comment:"接口设置"`
|
Apis Conf_Apis `comment:"接口设置"`
|
||||||
@ -135,11 +168,12 @@ var (
|
|||||||
DefCfg = Conf{
|
DefCfg = Conf{
|
||||||
Main: Conf_Main{
|
Main: Conf_Main{
|
||||||
Debug: false,
|
Debug: false,
|
||||||
Listen: `127.0.0.1:1011`,
|
Listen: []string{`127.0.0.1:1011`},
|
||||||
Gzip: false,
|
Gzip: false,
|
||||||
LogPath: `/data/logfile.log`,
|
LogPath: `/data/logfile.log`,
|
||||||
Print: true,
|
Print: true,
|
||||||
SysLev: false,
|
SysLev: false,
|
||||||
|
Timeout: 30,
|
||||||
},
|
},
|
||||||
Apis: Conf_Apis{
|
Apis: Conf_Apis{
|
||||||
// BindAddr: `http://192.168.10.22:1011/`,
|
// BindAddr: `http://192.168.10.22:1011/`,
|
||||||
@ -148,12 +182,12 @@ var (
|
|||||||
ApiKey_Enable: true,
|
ApiKey_Enable: true,
|
||||||
RateLimit_Enable: false,
|
RateLimit_Enable: false,
|
||||||
RateLimit_Block: 30,
|
RateLimit_Block: 30,
|
||||||
RateLimit_Global: 1,
|
// RateLimit_Global: 1,
|
||||||
RateLimit_Single: 15,
|
RateLimit_Single: 15,
|
||||||
RateLimit_BanNum: 5,
|
RateLimit_BanNum: 5,
|
||||||
RateLimit_BanTim: 10,
|
RateLimit_BanTim: 10,
|
||||||
BanList_Mode: `off`,
|
// BanList_Mode: `off`,
|
||||||
BanList_White: []string{`127.0.0.1`},
|
// BanList_White: []string{`127.0.0.1`},
|
||||||
},
|
},
|
||||||
Source: Conf_Source{
|
Source: Conf_Source{
|
||||||
Mode: `builtin`,
|
Mode: `builtin`,
|
||||||
@ -161,16 +195,29 @@ var (
|
|||||||
FakeIP_Value: `192.168.10.2`,
|
FakeIP_Value: `192.168.10.2`,
|
||||||
Proxy_Enable: false,
|
Proxy_Enable: false,
|
||||||
Proxy_Address: `{protocol}://({user}:{password})@{address}:{port}`,
|
Proxy_Address: `{protocol}://({user}:{password})@{address}:{port}`,
|
||||||
|
|
||||||
|
Enable_Wy: true,
|
||||||
|
Enable_Mg: true,
|
||||||
|
Enable_Kw: true,
|
||||||
|
Enable_Kg: true,
|
||||||
|
Enable_Tx: true,
|
||||||
|
Enable_Lx: true,
|
||||||
},
|
},
|
||||||
Custom: Conf_Custom{
|
Custom: Conf_Custom{
|
||||||
Wy_Enable: true,
|
Wy_Enable: true,
|
||||||
Wy_Refresh_Interval: 1633622400,
|
Wy_Mode: `builtin`,
|
||||||
|
Wy_Api_Type: `native`,
|
||||||
|
// Wy_Refresh_Interval: 1633622400,
|
||||||
|
|
||||||
|
Mg_Enable: true,
|
||||||
|
|
||||||
Kw_Enable: true,
|
Kw_Enable: true,
|
||||||
Kw_Mode: `kwdes`,
|
Kw_Mode: `kwdes`,
|
||||||
Kw_Des_Type: `json`,
|
Kw_Des_Type: `json`,
|
||||||
Kw_Des_Header: `okhttp/3.10.0`,
|
Kw_Des_Header: `okhttp/3.10.0`,
|
||||||
|
|
||||||
|
Kg_Enable: true,
|
||||||
|
|
||||||
Tx_Enable: false,
|
Tx_Enable: false,
|
||||||
Tx_Refresh_Enable: false,
|
Tx_Refresh_Enable: false,
|
||||||
Tx_Refresh_Interval: 86000,
|
Tx_Refresh_Interval: 86000,
|
||||||
@ -181,18 +228,18 @@ var (
|
|||||||
Ver: `1.0.3`, // 自定义脚本版本
|
Ver: `1.0.3`, // 自定义脚本版本
|
||||||
Force: true, // 强制推送更新
|
Force: true, // 强制推送更新
|
||||||
|
|
||||||
Url: `public/lx-custom-source.js`, // 脚本下载地址
|
Url: `lx-custom-source.js`, // 脚本下载地址
|
||||||
},
|
},
|
||||||
Cache: Conf_Cache{
|
Cache: Conf_Cache{
|
||||||
Mode: `local`, // 缓存模式
|
Mode: `local`, // 缓存模式
|
||||||
LinkMode: `1`,
|
LinkMode: `1`,
|
||||||
Local_Path: `data/cache`,
|
Local_Path: `data/cache`,
|
||||||
Local_Bind: `http://127.0.0.1:1011/`,
|
Local_Bind: `http://127.0.0.1:1011/`,
|
||||||
Cloud_Site: `https://cloudreveplus-demo.onrender.com/`,
|
// Cloud_Site: `https://cloudreveplus-demo.onrender.com/`,
|
||||||
Cloud_User: `admin@cloudreve.org`,
|
// Cloud_User: `admin@cloudreve.org`,
|
||||||
Cloud_Pass: `CloudrevePlusDemo`,
|
// Cloud_Pass: `CloudrevePlusDemo`,
|
||||||
Cloud_Sess: ``,
|
// Cloud_Sess: ``,
|
||||||
Cloud_Path: `/Lx-Source/cache`,
|
// Cloud_Path: `/Lx-Source/cache`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Config = DefCfg
|
Config = DefCfg
|
||||||
|
@ -19,19 +19,6 @@ type (
|
|||||||
|
|
||||||
func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
||||||
loger := env.Loger.NewGroup(`AuthHandler`)
|
loger := env.Loger.NewGroup(`AuthHandler`)
|
||||||
// ApiKey 请求头验证
|
|
||||||
if env.Config.Auth.ApiKey_Enable {
|
|
||||||
loger.Debug(`ApiKeyAuth Enabled`)
|
|
||||||
out = append(out, func(c *gin.Context) {
|
|
||||||
resp.Wrap(c, func() *resp.Resp {
|
|
||||||
if auth := c.Request.Header.Get(`X-LxM-Auth`); auth != env.Config.Auth.ApiKey_Value {
|
|
||||||
loger.Debug(`验证失败: %q`, auth)
|
|
||||||
return &resp.Resp{Code: 3, Msg: `验证Key失败, 请联系网站管理员`}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// RateLimit 速率限制
|
// RateLimit 速率限制
|
||||||
/*
|
/*
|
||||||
逻辑:
|
逻辑:
|
||||||
@ -51,9 +38,16 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
|||||||
block_mem := int(env.Config.Auth.RateLimit_Block * env.Config.Auth.RateLimit_BanNum)
|
block_mem := int(env.Config.Auth.RateLimit_Block * env.Config.Auth.RateLimit_BanNum)
|
||||||
bannum := env.Config.Auth.RateLimit_Single + env.Config.Auth.RateLimit_BanNum
|
bannum := env.Config.Auth.RateLimit_Single + env.Config.Auth.RateLimit_BanNum
|
||||||
bantim := int64(env.Config.Auth.RateLimit_BanTim)
|
bantim := int64(env.Config.Auth.RateLimit_BanTim)
|
||||||
|
var getIp func(c *gin.Context) string
|
||||||
|
if env.Config.Main.NgProxy {
|
||||||
|
loger.Info(`已开启反向代理兼容模式`)
|
||||||
|
getIp = func(c *gin.Context) string { return c.ClientIP() }
|
||||||
|
} else {
|
||||||
|
getIp = func(c *gin.Context) string { return c.RemoteIP() }
|
||||||
|
}
|
||||||
out = append(out, func(c *gin.Context) {
|
out = append(out, func(c *gin.Context) {
|
||||||
resp.Wrap(c, func() *resp.Resp {
|
resp.Wrap(c, func() *resp.Resp {
|
||||||
rip := c.RemoteIP()
|
rip := getIp(c)
|
||||||
if rip == `` {
|
if rip == `` {
|
||||||
rip = `0.0.0.0`
|
rip = `0.0.0.0`
|
||||||
}
|
}
|
||||||
@ -84,21 +78,18 @@ func InitHandler(h gin.HandlerFunc) (out []gin.HandlerFunc) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// ApiKey 请求头验证
|
||||||
|
if env.Config.Auth.ApiKey_Enable {
|
||||||
|
loger.Debug(`ApiKeyAuth Enabled`)
|
||||||
|
out = append(out, func(c *gin.Context) {
|
||||||
|
resp.Wrap(c, func() *resp.Resp {
|
||||||
|
if auth := c.Request.Header.Get(`X-LxM-Auth`); auth != env.Config.Auth.ApiKey_Value {
|
||||||
|
loger.Debug(`验证失败: %q`, auth)
|
||||||
|
return &resp.Resp{Code: 3, Msg: `验证Key失败, 请联系网站管理员`}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
return append(out, h)
|
return append(out, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求验证
|
|
||||||
// func AuthHandler(c *gin.Context) {
|
|
||||||
// loger := env.Loger.NewGroup(`AuthHandler`)
|
|
||||||
// resp.Wrap(c, func() *resp.Resp {
|
|
||||||
// // ApiKey
|
|
||||||
// if env.Config.Auth.ApiKey_Enable {
|
|
||||||
// if auth := c.Request.Header.Get(`X-LxM-Auth`); auth != env.Config.Auth.ApiKey_Value {
|
|
||||||
// loger.Debug(`验证失败: %q`, auth)
|
|
||||||
// return &resp.Resp{Code: 3, Msg: `验证Key失败, 请联系网站管理员`}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
// })
|
|
||||||
// // c.Next()
|
|
||||||
// }
|
|
||||||
|
@ -36,7 +36,7 @@ var statusMap = map[int]int{
|
|||||||
6: http.StatusBadRequest, // 参数错误
|
6: http.StatusBadRequest, // 参数错误
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrMp3 = `https://r2eu.zw-cdn.tk/gh/lx-source/static/error.mp3`
|
var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
|
||||||
|
|
||||||
// 返回请求
|
// 返回请求
|
||||||
/*
|
/*
|
||||||
|
@ -1,8 +1,21 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import "github.com/gin-gonic/gin"
|
import (
|
||||||
|
"lx-source/src/env"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
// 将路由参数转为Map
|
// 将路由参数转为Map
|
||||||
|
/*
|
||||||
|
`:s/:id/:q` -> {
|
||||||
|
`s`: `source`,
|
||||||
|
`id`: `musicId`,
|
||||||
|
`q`: `quality`,
|
||||||
|
}
|
||||||
|
*/
|
||||||
func ParaMap(c *gin.Context) map[string]string {
|
func ParaMap(c *gin.Context) map[string]string {
|
||||||
parmlen := len(c.Params)
|
parmlen := len(c.Params)
|
||||||
parms := make(map[string]string, parmlen)
|
parms := make(map[string]string, parmlen)
|
||||||
@ -11,3 +24,28 @@ func ParaMap(c *gin.Context) map[string]string {
|
|||||||
}
|
}
|
||||||
return parms
|
return parms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pathCache string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
env.Inits.Add(func() {
|
||||||
|
if !env.Config.Main.NgProxy {
|
||||||
|
pathCache = `/`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态获取相对路径 <Ctx, 特征> <路径>
|
||||||
|
/*
|
||||||
|
HOST = `192.168.10.22:1011`
|
||||||
|
URI = `/path/to/lxs/link/wy/2049512697/flac`
|
||||||
|
sub = `link/`
|
||||||
|
-> http://192.168.10.22:1011/path/to/lxs/
|
||||||
|
*/
|
||||||
|
func GetPath(c *http.Request, sub string) string {
|
||||||
|
// 从缓存读取相对路径 `/path/to/lxs/` or `/`
|
||||||
|
if pathCache == `` {
|
||||||
|
pathCache = ztool.Str_Before(c.RequestURI, sub)
|
||||||
|
}
|
||||||
|
return ztool.Str_FastConcat(`http://`, c.Host, pathCache)
|
||||||
|
}
|
||||||
|
59
src/server/app_lxmusic.go
Normal file
59
src/server/app_lxmusic.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lx-source/src/middleware/resp"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type queryObj struct {
|
||||||
|
Name string `json:"name"` // 歌名
|
||||||
|
Singer string `json:"singer"` // 歌手
|
||||||
|
Source string `json:"source"` // 平台
|
||||||
|
Songmid string `json:"songmid"` // 音乐ID
|
||||||
|
Interval string `json:"interval"` // 时长
|
||||||
|
AlbumName string `json:"albumName"` // 专辑
|
||||||
|
Img string `json:"img"` // 封面
|
||||||
|
// TypeURL struct {
|
||||||
|
// } `json:"typeUrl"` // 未知
|
||||||
|
AlbumID string `json:"albumId"` // 专辑ID
|
||||||
|
// 支持音质
|
||||||
|
Types []struct {
|
||||||
|
Type string `json:"type"` // 音质
|
||||||
|
Size string `json:"size"` // 大小
|
||||||
|
Hash string `json:"hash"` // 哈希(kg only)
|
||||||
|
} `json:"types"`
|
||||||
|
// tx
|
||||||
|
StrMediaMid string `json:"strMediaMid"` // 当前文件ID
|
||||||
|
AlbumMid string `json:"albumMid"` // 专辑ID
|
||||||
|
SongID int `json:"songId"` // 音乐ID
|
||||||
|
// mg
|
||||||
|
CopyrightID string `json:"copyrightId"` // 音乐ID
|
||||||
|
LrcURL string `json:"lrcUrl"` // lrc歌词
|
||||||
|
MrcURL string `json:"mrcUrl"` // mrc歌词
|
||||||
|
TrcURL string `json:"trcUrl"` // trc歌词
|
||||||
|
// kg
|
||||||
|
Hash string `json:"hash"` // 文件哈希(kg only)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadLxMusic(lx *gin.RouterGroup) {
|
||||||
|
// 获取链接
|
||||||
|
lx.POST(`/link/:q`, func(c *gin.Context) {
|
||||||
|
resp.Wrap(c, func() *resp.Resp {
|
||||||
|
var obj queryObj
|
||||||
|
if err := c.ShouldBindJSON(&obj); err != nil {
|
||||||
|
return &resp.Resp{Code: 6, Msg: `解析错误: ` + err.Error()}
|
||||||
|
}
|
||||||
|
pams := map[string]string{
|
||||||
|
`s`: obj.Source,
|
||||||
|
`id`: ztool.Str_Select(obj.Hash, obj.CopyrightID, obj.Songmid),
|
||||||
|
}
|
||||||
|
for k, v := range pams {
|
||||||
|
c.Params = append(c.Params, gin.Param{Key: k, Value: v})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}, linkHandler)
|
||||||
|
// lx.GET(`/info`)
|
||||||
|
}
|
34
src/server/app_musicfree.go
Normal file
34
src/server/app_musicfree.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lx-source/src/middleware/util"
|
||||||
|
"lx-source/src/sources"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadMusicFree(mf *gin.RouterGroup) {
|
||||||
|
// 插件订阅
|
||||||
|
mf.GET(`/subscribe`, func(c *gin.Context) {
|
||||||
|
slist := []string{sources.S_wy, sources.S_mg, sources.S_kw, sources.S_kg, sources.S_tx, sources.S_lx}
|
||||||
|
type plugins struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
length := len(slist)
|
||||||
|
plgs := make([]plugins, length)
|
||||||
|
url := ztool.Str_FastConcat(util.GetPath(c.Request, `app/`), `public/musicfree/`)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
name := `lxs-` + slist[i]
|
||||||
|
plgs[i] = plugins{
|
||||||
|
Name: name,
|
||||||
|
Url: ztool.Str_FastConcat(url, name, `.js`),
|
||||||
|
Version: `0.0.0`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{`plugins`: plgs})
|
||||||
|
})
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
// 静态资源
|
// 静态资源
|
||||||
package loadpublic
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -11,14 +12,16 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/ZxwyWebSite/ztool/x/bytesconv"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gin-gonic/gin/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed public
|
//go:embed public
|
||||||
var publicEM embed.FS // 打包默认Public目录 src/router/router.go
|
var publicEM embed.FS // 打包默认Public目录 src/server/public
|
||||||
|
|
||||||
// 载入Public目录并设置路由
|
// 载入Public目录并设置路由
|
||||||
func LoadPublic(r *gin.Engine) {
|
func loadPublic(r *gin.Engine) {
|
||||||
pf := env.Loger.NewGroup(`PublicFS`)
|
pf := env.Loger.NewGroup(`PublicFS`)
|
||||||
dir := ztool.Str_FastConcat(env.RunPath, `/data/public`)
|
dir := ztool.Str_FastConcat(env.RunPath, `/data/public`)
|
||||||
publicFS, err := fs.Sub(publicEM, `public`)
|
publicFS, err := fs.Sub(publicEM, `public`)
|
||||||
@ -57,6 +60,8 @@ func LoadPublic(r *gin.Engine) {
|
|||||||
pf.Info(`全部静态资源导出完成, 祝你使用愉快 ^_^`)
|
pf.Info(`全部静态资源导出完成, 祝你使用愉快 ^_^`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pf.Free()
|
||||||
|
// 使用本地public目录
|
||||||
// httpFS = gin.Dir(dir, false)
|
// httpFS = gin.Dir(dir, false)
|
||||||
// r.GET(`/:file`, func(c *gin.Context) {
|
// r.GET(`/:file`, func(c *gin.Context) {
|
||||||
// file := c.Param(`file`)
|
// file := c.Param(`file`)
|
||||||
@ -69,7 +74,32 @@ func LoadPublic(r *gin.Engine) {
|
|||||||
// c.FileFromFS(file, httpFS)
|
// c.FileFromFS(file, httpFS)
|
||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
r.StaticFileFS(`/favicon.ico`, `icon.ico`, httpFS)
|
// 自动填写源脚本参数
|
||||||
|
if env.Config.Script.Auto > 0 {
|
||||||
|
file, _ := publicFS.Open(`lx-custom-source.js`)
|
||||||
|
data, _ := io.ReadAll(file)
|
||||||
|
file.Close()
|
||||||
|
data = bytes.Replace(data,
|
||||||
|
bytesconv.StringToBytes(`http://127.0.0.1:1011/`),
|
||||||
|
bytesconv.StringToBytes(env.Config.Cache.Local_Bind), 1,
|
||||||
|
)
|
||||||
|
if env.Config.Auth.ApiKey_Enable && env.Config.Script.Auto >= 2 {
|
||||||
|
data = bytes.Replace(data,
|
||||||
|
bytesconv.StringToBytes(`apipass = ''`),
|
||||||
|
bytesconv.StringToBytes(ztool.Str_FastConcat(
|
||||||
|
`apipass = '`, env.Config.Auth.ApiKey_Value, `'`,
|
||||||
|
)), 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
r.GET(`/lx-custom-source.js`, func(c *gin.Context) {
|
||||||
|
c.Render(http.StatusOK, render.Data{
|
||||||
|
ContentType: `text/javascript; charset=utf-8`,
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
r.StaticFileFS(`/lx-custom-source.js`, `lx-custom-source.js`, httpFS)
|
r.StaticFileFS(`/lx-custom-source.js`, `lx-custom-source.js`, httpFS)
|
||||||
|
}
|
||||||
|
r.StaticFileFS(`/favicon.ico`, `lx-icon.ico`, httpFS)
|
||||||
r.StaticFS(`/public`, httpFS)
|
r.StaticFS(`/public`, httpFS)
|
||||||
}
|
}
|
56
src/server/loadquality.go
Normal file
56
src/server/loadquality.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import "lx-source/src/env"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// 默认音质
|
||||||
|
defQuality = []string{`128k`, `320k`, `flac`, `flac24bit`}
|
||||||
|
// 试听音质
|
||||||
|
tstQuality = []string{`128k`}
|
||||||
|
// 标准音质
|
||||||
|
stdQuality = []string{`128k`, `320k`, `flac`}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自动生成支持的音质表
|
||||||
|
func loadQMap() [][]string {
|
||||||
|
m := make([][]string, 6)
|
||||||
|
// 0.wy
|
||||||
|
if env.Config.Source.Enable_Wy {
|
||||||
|
if env.Config.Custom.Wy_Enable {
|
||||||
|
m[0] = defQuality
|
||||||
|
} else {
|
||||||
|
m[0] = tstQuality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 1.mg
|
||||||
|
if env.Config.Source.Enable_Mg {
|
||||||
|
if env.Config.Custom.Mg_Enable {
|
||||||
|
m[1] = defQuality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2.kw
|
||||||
|
if env.Config.Source.Enable_Kw {
|
||||||
|
if env.Config.Custom.Kw_Enable {
|
||||||
|
m[2] = stdQuality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3.kg
|
||||||
|
if env.Config.Source.Enable_Kg {
|
||||||
|
if env.Config.Custom.Kg_Enable {
|
||||||
|
m[3] = tstQuality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 4.tx
|
||||||
|
if env.Config.Source.Enable_Tx {
|
||||||
|
if env.Config.Custom.Tx_Enable {
|
||||||
|
m[4] = stdQuality
|
||||||
|
} else {
|
||||||
|
m[4] = tstQuality
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 5.lx
|
||||||
|
// if env.Config.Source.Enable_Lx {
|
||||||
|
// m[5] = defQuality
|
||||||
|
// }
|
||||||
|
return m
|
||||||
|
}
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
@ -3,23 +3,19 @@
|
|||||||
* @description Client
|
* @description Client
|
||||||
* version 1.0.1
|
* version 1.0.1
|
||||||
* @author Zxwy
|
* @author Zxwy
|
||||||
* @homepage https://github.com/ZxwyWebSite/lx-source
|
* homepage null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 脚本配置
|
// 脚本配置
|
||||||
const version = '1.0.3' // 脚本版本
|
const version = '1.0.3' // 脚本版本
|
||||||
const apiaddr = 'http://127.0.0.1:1011/' // 服务端地址,末尾加斜杠
|
const apiaddr = 'http://127.0.0.1:1011/' // 服务端地址,末尾加斜杠
|
||||||
const apipass = '' // 验证密钥,由服务端自动生成 '${apipass}'
|
const apipass = '' // 验证密钥,填在单引号内
|
||||||
const devmode = true // 调试模式
|
const devmode = true // 调试模式
|
||||||
|
// const timeout = 60 * 1000 // 请求超时(ms)
|
||||||
|
|
||||||
// 常量 & 默认值
|
// 常量 & 默认值
|
||||||
const { EVENT_NAMES, request, on, send } = window.lx ?? globalThis.lx
|
const { EVENT_NAMES, request, on, send } = window.lx ?? globalThis.lx
|
||||||
const defs = { type: 'music', actions: ['musicUrl'] }
|
const defs = { type: 'music', actions: ['musicUrl'] }
|
||||||
// const defaults = {
|
|
||||||
// type: 'music', // 目前固定为 music
|
|
||||||
// actions: ['musicUrl'], // 目前固定为 ['musicUrl']
|
|
||||||
// qualitys: ['128k', '320k', 'flac', 'flac24bit'], // 当前脚本的该源所支持获取的Url音质,有效的值有:['128k', '320k', 'flac', 'flac24bit']
|
|
||||||
// }
|
|
||||||
const defheaders = {
|
const defheaders = {
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 HBPC/12.1.2.300',
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 HBPC/12.1.2.300',
|
||||||
'Accept': 'application/json, text/plain, */*',
|
'Accept': 'application/json, text/plain, */*',
|
||||||
@ -39,6 +35,7 @@ const conf = {
|
|||||||
|
|
||||||
const httpRequest = (url, options) => new Promise((resolve, reject) => {
|
const httpRequest = (url, options) => new Promise((resolve, reject) => {
|
||||||
options.headers = { ...defheaders, ...options.headers } // 添加默认请求头
|
options.headers = { ...defheaders, ...options.headers } // 添加默认请求头
|
||||||
|
// options.timeout ? options.timeout : timeout // 添加默认请求超时
|
||||||
request(url, options, (err, resp) => {
|
request(url, options, (err, resp) => {
|
||||||
if (err) return reject(err)
|
if (err) return reject(err)
|
||||||
resolve(resp.body)
|
resolve(resp.body)
|
||||||
@ -80,8 +77,7 @@ const init = () => {
|
|||||||
'use strict';
|
'use strict';
|
||||||
console.log('初始化脚本, 版本: %s, 服务端地址: %s', version, apiaddr)
|
console.log('初始化脚本, 版本: %s, 服务端地址: %s', version, apiaddr)
|
||||||
var stat = false; var msg = ''; var updUrl = ''; var sourcess = {}
|
var stat = false; var msg = ''; var updUrl = ''; var sourcess = {}
|
||||||
httpRequest(apiaddr, { method: 'get' })
|
httpRequest(apiaddr, { method: 'get', timeout: 1000 * 10 })
|
||||||
.catch((err) => { msg = '初始化失败: ' + err ?? '连接服务端超时'; console.log(msg) })
|
|
||||||
.then((body) => {
|
.then((body) => {
|
||||||
if (!body) { msg = '初始化失败:' + '无返回数据'; return }
|
if (!body) { msg = '初始化失败:' + '无返回数据'; return }
|
||||||
console.log('获取服务端数据成功: %o, 版本: %s', body, body.version)
|
console.log('获取服务端数据成功: %o, 版本: %s', body, body.version)
|
||||||
@ -104,7 +100,7 @@ const init = () => {
|
|||||||
if (source[v] != null /*== true*/) {
|
if (source[v] != null /*== true*/) {
|
||||||
sourcess[v] = {
|
sourcess[v] = {
|
||||||
name: v,
|
name: v,
|
||||||
...defs, // ...defaults,
|
...defs,
|
||||||
qualitys: source[v], // 支持返回音质时启用 使用后端音质表
|
qualitys: source[v], // 支持返回音质时启用 使用后端音质表
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,18 +108,13 @@ const init = () => {
|
|||||||
// 完成初始化
|
// 完成初始化
|
||||||
stat = true
|
stat = true
|
||||||
})
|
})
|
||||||
|
.catch((err) => { msg = '初始化失败: ' + err ?? '连接服务端超时'; console.log(msg) })
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// 脚本初始化完成后需要发送inited事件告知应用
|
// 脚本初始化完成后需要发送inited事件告知应用
|
||||||
send(EVENT_NAMES.inited, {
|
send(EVENT_NAMES.inited, {
|
||||||
status: stat, // 初始化成功 or 失败 (初始化失败不打开控制台, 使用更新提示接口返回信息)
|
status: stat, // 初始化成功 or 失败 (初始化失败不打开控制台, 使用更新提示接口返回信息)
|
||||||
openDevTools: stat ? devmode : false, // 是否打开开发者工具,方便用于调试脚本 'devmode' or 'stat ? devmode : false'
|
openDevTools: stat ? devmode : false, // 是否打开开发者工具,方便用于调试脚本 'devmode' or 'stat ? devmode : false'
|
||||||
sources: sourcess, // 使用服务端源列表
|
sources: sourcess, // 使用服务端源列表
|
||||||
// sources: { // 当前脚本支持的源
|
|
||||||
// wy: { name: '网易音乐', ...defaults, },
|
|
||||||
// mg: { name: '咪咕音乐', ...defaults, },
|
|
||||||
// kw: { name: '酷我音乐', ...defaults, },
|
|
||||||
// // kg: { name: '酷狗音乐', ...defaults, }, // 暂不支持,仅供换源
|
|
||||||
// },
|
|
||||||
})
|
})
|
||||||
// 发送更新提示
|
// 发送更新提示
|
||||||
if (msg) send(EVENT_NAMES.updateAlert, { log: '提示:' + msg, updateUrl: updUrl ? apiaddr + updUrl : '' })
|
if (msg) send(EVENT_NAMES.updateAlert, { log: '提示:' + msg, updateUrl: updUrl ? apiaddr + updUrl : '' })
|
BIN
src/server/public/lx-icon.ico
Normal file
BIN
src/server/public/lx-icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 162 KiB |
@ -1,11 +1,10 @@
|
|||||||
package router
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"lx-source/src/caches"
|
"lx-source/src/caches"
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
"lx-source/src/middleware/auth"
|
"lx-source/src/middleware/auth"
|
||||||
"lx-source/src/middleware/dynlink"
|
"lx-source/src/middleware/dynlink"
|
||||||
"lx-source/src/middleware/loadpublic"
|
|
||||||
"lx-source/src/middleware/resp"
|
"lx-source/src/middleware/resp"
|
||||||
"lx-source/src/middleware/util"
|
"lx-source/src/middleware/util"
|
||||||
"lx-source/src/sources"
|
"lx-source/src/sources"
|
||||||
@ -16,41 +15,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// 默认音质
|
|
||||||
defQuality = []string{`128k`, `320k`, `flac`, `flac24bit`}
|
|
||||||
// 试听音质
|
|
||||||
tstQuality = []string{`128k`}
|
|
||||||
// 标准音质
|
|
||||||
stdQuality = []string{`128k`, `320k`, `flac`}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 自动生成支持的音质表
|
|
||||||
func loadQMap() [][]string {
|
|
||||||
m := make([][]string, 6)
|
|
||||||
// 0.wy
|
|
||||||
if env.Config.Custom.Wy_Enable {
|
|
||||||
m[0] = defQuality
|
|
||||||
} else {
|
|
||||||
m[0] = tstQuality
|
|
||||||
}
|
|
||||||
// 1.mg
|
|
||||||
m[1] = defQuality
|
|
||||||
// 2.kw
|
|
||||||
m[2] = defQuality
|
|
||||||
// 3.kg
|
|
||||||
m[3] = tstQuality
|
|
||||||
// 4.tx
|
|
||||||
if env.Config.Custom.Tx_Enable {
|
|
||||||
m[4] = stdQuality
|
|
||||||
} else {
|
|
||||||
m[4] = tstQuality
|
|
||||||
}
|
|
||||||
// 5.lx
|
|
||||||
// m[sources.S_lx] = defQuality
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// 载入路由
|
// 载入路由
|
||||||
func InitRouter() *gin.Engine {
|
func InitRouter() *gin.Engine {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
@ -70,14 +34,11 @@ func InitRouter() *gin.Engine {
|
|||||||
// `github`: `https://github.com/ZxwyWebSite/lx-source`,
|
// `github`: `https://github.com/ZxwyWebSite/lx-source`,
|
||||||
// 可用平台
|
// 可用平台
|
||||||
`source`: gin.H{
|
`source`: gin.H{
|
||||||
sources.S_wy: qmap[0], //true,
|
sources.S_wy: qmap[0],
|
||||||
sources.S_mg: qmap[1], //true,
|
sources.S_mg: qmap[1],
|
||||||
sources.S_kw: qmap[2], //true,
|
sources.S_kw: qmap[2],
|
||||||
sources.S_kg: qmap[3], //[]string{`128k`, `320k`}, // 测试结构2, 启用时返回音质列表, 禁用为false
|
sources.S_kg: qmap[3],
|
||||||
sources.S_tx: qmap[4], //gin.H{ // "测试结构 不代表最终方式"
|
sources.S_tx: qmap[4],
|
||||||
// `enable`: false,
|
|
||||||
// `qualitys`: []string{`128k`, `320k`, `flac`, `flac24bit`},
|
|
||||||
// },
|
|
||||||
sources.S_lx: qmap[5],
|
sources.S_lx: qmap[5],
|
||||||
},
|
},
|
||||||
// 自定义源脚本更新
|
// 自定义源脚本更新
|
||||||
@ -85,12 +46,13 @@ func InitRouter() *gin.Engine {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
// 静态文件
|
// 静态文件
|
||||||
loadpublic.LoadPublic(r)
|
loadPublic(r)
|
||||||
// r.StaticFile(`/favicon.ico`, `public/icon.ico`)
|
// r.StaticFile(`/favicon.ico`, `public/icon.ico`)
|
||||||
// r.StaticFile(`/lx-custom-source.js`, `public/lx-custom-source.js`)
|
// r.StaticFile(`/lx-custom-source.js`, `public/lx-custom-source.js`)
|
||||||
// 解析接口
|
// 解析接口
|
||||||
r.GET(`/link/:s/:id/:q`, auth.InitHandler(linkHandler)...)
|
r.GET(`/link/:s/:id/:q`, auth.InitHandler(linkHandler)...)
|
||||||
dynlink.LoadHandler(r)
|
dynlink.LoadHandler(r)
|
||||||
|
// 动态链?
|
||||||
// r.GET(`/file/:t/:x/:f`, dynlink.FileHandler())
|
// r.GET(`/file/:t/:x/:f`, dynlink.FileHandler())
|
||||||
// if cache, ok := caches.UseCache.(*localcache.Cache); ok {
|
// if cache, ok := caches.UseCache.(*localcache.Cache); ok {
|
||||||
// r.Static(`/file`, cache.Path)
|
// r.Static(`/file`, cache.Path)
|
||||||
@ -98,10 +60,16 @@ func InitRouter() *gin.Engine {
|
|||||||
// if env.Config.Cache.Mode == `local` {
|
// if env.Config.Cache.Mode == `local` {
|
||||||
// r.Static(`/file`, env.Config.Cache.Local_Path)
|
// r.Static(`/file`, env.Config.Cache.Local_Path)
|
||||||
// }
|
// }
|
||||||
// 软件接口
|
// 功能接口
|
||||||
// api := r.Group(`/api`)
|
// api := r.Group(`/api`)
|
||||||
// {
|
// {
|
||||||
// api.GET(`/lx`, lxHandler) // 洛雪音乐
|
// api.GET(`/:s/:m/:q`) // {source}/{method}/{query}
|
||||||
|
// }
|
||||||
|
// 软件接口
|
||||||
|
// app := r.Group(`/app`)
|
||||||
|
// {
|
||||||
|
// loadLxMusic(app.Group(`/lxmusic`))
|
||||||
|
// loadMusicFree(app.Group(`/musicfree`))
|
||||||
// }
|
// }
|
||||||
// 数据接口
|
// 数据接口
|
||||||
// r.GET(`/file/:t/:hq/:n`, func(c *gin.Context) {
|
// r.GET(`/file/:t/:hq/:n`, func(c *gin.Context) {
|
||||||
@ -136,11 +104,12 @@ func linkHandler(c *gin.Context) {
|
|||||||
s := parms[`s`] //c.Param(`s`) //getParam(`s`) // source 平台 wy, mg, kw
|
s := parms[`s`] //c.Param(`s`) //getParam(`s`) // source 平台 wy, mg, kw
|
||||||
id := parms[`id`] //c.Param(`id`) //getParam(`id`) // sid 音乐ID wy: songmid, mg: copyrightId
|
id := parms[`id`] //c.Param(`id`) //getParam(`id`) // sid 音乐ID wy: songmid, mg: copyrightId
|
||||||
q := parms[`q`] //c.Param(`q`) //getParam(`q`) // quality 音质 128k / 320k / flac / flac24bit
|
q := parms[`q`] //c.Param(`q`) //getParam(`q`) // quality 音质 128k / 320k / flac / flac24bit
|
||||||
env.Loger.NewGroup(`LinkQuery`).Debug(`s: %v, id: %v, q: %v`, s, id, q)
|
env.Loger.NewGroup(`LinkQuery`).Debug(`s: %v, id: %v, q: %v`, s, id, q).Free()
|
||||||
if ztool.Chk_IsNilStr(s, q, id) {
|
if ztool.Chk_IsNilStr(s, q, id) {
|
||||||
return &resp.Resp{Code: 6, Msg: `参数不全`} // http.StatusBadRequest
|
return &resp.Resp{Code: 6, Msg: `参数不全`} // http.StatusBadRequest
|
||||||
}
|
}
|
||||||
cquery := caches.NewQuery(s, id, q)
|
cquery := caches.NewQuery(s, id, q)
|
||||||
|
cquery.Request = c.Request
|
||||||
// fmt.Printf("%+v\n", cquery)
|
// fmt.Printf("%+v\n", cquery)
|
||||||
defer cquery.Free()
|
defer cquery.Free()
|
||||||
// _, ok := sources.UseSource.Verify(cquery) // 获取请求音质 同时检测是否支持(如kw源没有flac24bit) qualitys[q][s]rquery
|
// _, ok := sources.UseSource.Verify(cquery) // 获取请求音质 同时检测是否支持(如kw源没有flac24bit) qualitys[q][s]rquery
|
||||||
@ -152,7 +121,7 @@ func linkHandler(c *gin.Context) {
|
|||||||
clink, ok := env.Cache.Get(cquery.Query())
|
clink, ok := env.Cache.Get(cquery.Query())
|
||||||
if ok {
|
if ok {
|
||||||
if str, ok := clink.(string); ok {
|
if str, ok := clink.(string); ok {
|
||||||
env.Loger.NewGroup(`MemCache`).Debug(`MemHIT [%q]=>[%q]`, cquery.Query(), str)
|
env.Loger.NewGroup(`MemCache`).Debug(`MemHIT [%q]=>[%q]`, cquery.Query(), str).Free()
|
||||||
if str == `` {
|
if str == `` {
|
||||||
return &resp.Resp{Code: 2, Msg: memRej} // 拒绝请求,当前一段时间内解析出错 `MemCache Reject`
|
return &resp.Resp{Code: 2, Msg: memRej} // 拒绝请求,当前一段时间内解析出错 `MemCache Reject`
|
||||||
}
|
}
|
||||||
@ -165,6 +134,7 @@ func linkHandler(c *gin.Context) {
|
|||||||
cstat = caches.UseCache.Stat()
|
cstat = caches.UseCache.Stat()
|
||||||
}
|
}
|
||||||
sc := env.Loger.NewGroup(`StatCache`)
|
sc := env.Loger.NewGroup(`StatCache`)
|
||||||
|
defer sc.Free()
|
||||||
if cstat {
|
if cstat {
|
||||||
sc.Debug(`Method: Get, Query: %v`, cquery.Query())
|
sc.Debug(`Method: Get, Query: %v`, cquery.Query())
|
||||||
if link := caches.UseCache.Get(cquery); link != `` {
|
if link := caches.UseCache.Get(cquery); link != `` {
|
@ -7,6 +7,7 @@ import (
|
|||||||
"lx-source/src/sources"
|
"lx-source/src/sources"
|
||||||
"lx-source/src/sources/custom/kw"
|
"lx-source/src/sources/custom/kw"
|
||||||
"lx-source/src/sources/custom/tx"
|
"lx-source/src/sources/custom/tx"
|
||||||
|
"lx-source/src/sources/custom/wy"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@ -50,12 +51,22 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
|||||||
}
|
}
|
||||||
// var outlink string
|
// var outlink string
|
||||||
jx := env.Loger.NewGroup(`Sources`) //sources.Loger.AppGroup(`builtin`) //env.Loger.NewGroup(`JieXiApis`)
|
jx := env.Loger.NewGroup(`Sources`) //sources.Loger.AppGroup(`builtin`) //env.Loger.NewGroup(`JieXiApis`)
|
||||||
|
defer jx.Free()
|
||||||
switch c.Source {
|
switch c.Source {
|
||||||
case sources.S_wy:
|
case sources.S_wy:
|
||||||
if !env.Config.Custom.Wy_Enable {
|
if !env.Config.Custom.Wy_Enable {
|
||||||
msg = sources.ErrDisable
|
msg = sources.ErrDisable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if wy.Url != nil {
|
||||||
|
ourl, emsg := wy.Url(c.MusicID, c.Quality)
|
||||||
|
if emsg != `` {
|
||||||
|
msg = emsg
|
||||||
|
return
|
||||||
|
}
|
||||||
|
outlink = ourl
|
||||||
|
break
|
||||||
|
}
|
||||||
// 可用性验证
|
// 可用性验证
|
||||||
if env.Config.Source.MusicIdVerify {
|
if env.Config.Source.MusicIdVerify {
|
||||||
vef := wv_pool.Get().(*WyApi_Vef)
|
vef := wv_pool.Get().(*WyApi_Vef)
|
||||||
@ -120,6 +131,10 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
|||||||
// jx.Info(`WyLink, RealQuality: %v`, data.Level)
|
// jx.Info(`WyLink, RealQuality: %v`, data.Level)
|
||||||
outlink = data.URL
|
outlink = data.URL
|
||||||
case sources.S_mg:
|
case sources.S_mg:
|
||||||
|
if !env.Config.Custom.Mg_Enable {
|
||||||
|
msg = sources.ErrDisable
|
||||||
|
return
|
||||||
|
}
|
||||||
resp := mg_pool.Get().(*MgApi_Song)
|
resp := mg_pool.Get().(*MgApi_Song)
|
||||||
defer mg_pool.Put(resp)
|
defer mg_pool.Put(resp)
|
||||||
|
|
||||||
@ -134,9 +149,9 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
|||||||
jx.Debug(`Mg, Resp: %+v`, resp)
|
jx.Debug(`Mg, Resp: %+v`, resp)
|
||||||
if link := resp.Data.PlayURL; link != `` {
|
if link := resp.Data.PlayURL; link != `` {
|
||||||
outlink = `https:` + link
|
outlink = `https:` + link
|
||||||
} // else {
|
} else {
|
||||||
// jx.Debug(`Mg, Err: %#v`, resp)
|
msg = ztool.Str_FastConcat(resp.Code, `: `, resp.Msg)
|
||||||
// }
|
}
|
||||||
case sources.S_kw:
|
case sources.S_kw:
|
||||||
if !env.Config.Custom.Kw_Enable {
|
if !env.Config.Custom.Kw_Enable {
|
||||||
msg = sources.ErrDisable
|
msg = sources.ErrDisable
|
||||||
@ -149,6 +164,10 @@ func (s *Source) GetLink(c *caches.Query) (outlink string, msg string) {
|
|||||||
}
|
}
|
||||||
outlink = ourl
|
outlink = ourl
|
||||||
case sources.S_kg:
|
case sources.S_kg:
|
||||||
|
if !env.Config.Custom.Kg_Enable {
|
||||||
|
msg = sources.ErrDisable
|
||||||
|
return
|
||||||
|
}
|
||||||
resp := kg_pool.Get().(*KgApi_Song)
|
resp := kg_pool.Get().(*KgApi_Song)
|
||||||
defer kg_pool.Put(resp)
|
defer kg_pool.Put(resp)
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package builtin
|
package builtin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"lx-source/src/sources"
|
"lx-source/src/sources"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -288,6 +290,7 @@ func init() {
|
|||||||
Header_Wy: &header_wy,
|
Header_Wy: &header_wy,
|
||||||
Header_Mg: &header_mg,
|
Header_Mg: &header_mg,
|
||||||
}
|
}
|
||||||
data := []byte{0x4a, 0x7f, 0x3, 0x1, 0x2, 0xff, 0x80, 0x0, 0x1, 0x5, 0x1, 0x6, 0x41, 0x70, 0x69, 0x5f, 0x57, 0x79, 0x1, 0xc, 0x0, 0x1, 0x6, 0x41, 0x70, 0x69, 0x5f, 0x4d, 0x67, 0x1, 0xc, 0x0, 0x1, 0x6, 0x56, 0x65, 0x66, 0x5f, 0x57, 0x79, 0x1, 0xc, 0x0, 0x1, 0x9, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x57, 0x79, 0x1, 0xff, 0x82, 0x0, 0x1, 0x9, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x4d, 0x67, 0x1, 0xff, 0x82, 0x0, 0x0, 0x0, 0x21, 0xff, 0x81, 0x4, 0x1, 0x1, 0x11, 0x6d, 0x61, 0x70, 0x5b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1, 0xff, 0x82, 0x0, 0x1, 0xc, 0x1, 0xc, 0x0, 0x0, 0xfe, 0x1, 0xca, 0xff, 0x80, 0x1, 0x23, 0x70, 0x74, 0x2e, 0x73, 0x61, 0x79, 0x71, 0x7a, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x3f, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x61, 0x70, 0x69, 0x53, 0x6f, 0x6e, 0x67, 0x55, 0x72, 0x6c, 0x56, 0x31, 0x1, 0x36, 0x6d, 0x2e, 0x6d, 0x75, 0x73, 0x69, 0x63, 0x2e, 0x6d, 0x69, 0x67, 0x75, 0x2e, 0x63, 0x6e, 0x2f, 0x6d, 0x69, 0x67, 0x75, 0x6d, 0x75, 0x73, 0x69, 0x63, 0x2f, 0x68, 0x35, 0x2f, 0x70, 0x6c, 0x61, 0x79, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x67, 0x65, 0x74, 0x53, 0x6f, 0x6e, 0x67, 0x50, 0x6c, 0x61, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x1, 0x24, 0x70, 0x74, 0x2e, 0x73, 0x61, 0x79, 0x71, 0x7a, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x3f, 0x74, 0x79, 0x70, 0x65, 0x3d, 0x63, 0x73, 0x6d, 0x43, 0x68, 0x65, 0x61, 0x6b, 0x4d, 0x75, 0x73, 0x69, 0x63, 0x1, 0x3, 0xa, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x5e, 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x4e, 0x54, 0x20, 0x36, 0x2e, 0x31, 0x3b, 0x20, 0x57, 0x4f, 0x57, 0x36, 0x34, 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, 0x74, 0x2f, 0x35, 0x33, 0x37, 0x2e, 0x33, 0x36, 0x20, 0x28, 0x4b, 0x48, 0x54, 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x35, 0x30, 0x2e, 0x30, 0x2e, 0x32, 0x36, 0x36, 0x31, 0x2e, 0x38, 0x37, 0x10, 0x58, 0x2d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x57, 0x69, 0x74, 0x68, 0xe, 0x58, 0x4d, 0x4c, 0x48, 0x74, 0x74, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x7, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, 0x15, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x70, 0x74, 0x2e, 0x73, 0x61, 0x79, 0x71, 0x7a, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x1, 0x4, 0x6, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x38, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x3d, 0x5a, 0x54, 0x49, 0x77, 0x4f, 0x44, 0x6b, 0x79, 0x4d, 0x44, 0x51, 0x74, 0x4f, 0x54, 0x45, 0x31, 0x4e, 0x53, 0x30, 0x30, 0x4d, 0x44, 0x68, 0x6c, 0x4c, 0x54, 0x68, 0x68, 0x4d, 0x57, 0x45, 0x74, 0x4d, 0x6a, 0x51, 0x30, 0x4e, 0x32, 0x59, 0x32, 0x4d, 0x7a, 0x6b, 0x32, 0x4f, 0x54, 0x41, 0x7a, 0x7, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6d, 0x2e, 0x6d, 0x75, 0x73, 0x69, 0x63, 0x2e, 0x6d, 0x69, 0x67, 0x75, 0x2e, 0x63, 0x6e, 0x2f, 0x76, 0x34, 0x2f, 0x2, 0x42, 0x79, 0x20, 0x30, 0x34, 0x66, 0x38, 0x31, 0x34, 0x36, 0x31, 0x61, 0x39, 0x38, 0x63, 0x37, 0x61, 0x66, 0x35, 0x35, 0x37, 0x66, 0x65, 0x61, 0x33, 0x63, 0x66, 0x32, 0x38, 0x63, 0x34, 0x65, 0x61, 0x31, 0x35, 0x7, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x7, 0x30, 0x31, 0x34, 0x30, 0x30, 0x30, 0x44, 0x0}
|
data := []byte{0x53, 0x6e, 0x38, 0x44, 0x41, 0x51, 0x4c, 0x2f, 0x67, 0x41, 0x41, 0x42, 0x42, 0x51, 0x45, 0x47, 0x51, 0x58, 0x42, 0x70, 0x58, 0x31, 0x64, 0x35, 0x41, 0x51, 0x77, 0x41, 0x41, 0x51, 0x5a, 0x42, 0x63, 0x47, 0x6c, 0x66, 0x54, 0x57, 0x63, 0x42, 0x44, 0x41, 0x41, 0x42, 0x42, 0x6c, 0x5a, 0x6c, 0x5a, 0x6c, 0x39, 0x58, 0x65, 0x51, 0x45, 0x4d, 0x41, 0x41, 0x45, 0x4a, 0x53, 0x47, 0x56, 0x68, 0x5a, 0x47, 0x56, 0x79, 0x58, 0x31, 0x64, 0x35, 0x41, 0x66, 0x2b, 0x43, 0x41, 0x41, 0x45, 0x4a, 0x53, 0x47, 0x56, 0x68, 0x5a, 0x47, 0x56, 0x79, 0x58, 0x30, 0x31, 0x6e, 0x41, 0x66, 0x2b, 0x43, 0x41, 0x41, 0x41, 0x41, 0x49, 0x66, 0x2b, 0x42, 0x42, 0x41, 0x45, 0x42, 0x45, 0x57, 0x31, 0x68, 0x63, 0x46, 0x74, 0x7a, 0x64, 0x48, 0x4a, 0x70, 0x62, 0x6d, 0x64, 0x64, 0x63, 0x33, 0x52, 0x79, 0x61, 0x57, 0x35, 0x6e, 0x41, 0x66, 0x2b, 0x43, 0x41, 0x41, 0x45, 0x4d, 0x41, 0x51, 0x77, 0x41, 0x41, 0x50, 0x34, 0x42, 0x72, 0x76, 0x2b, 0x41, 0x41, 0x53, 0x52, 0x6a, 0x63, 0x32, 0x30, 0x75, 0x63, 0x32, 0x46, 0x35, 0x63, 0x58, 0x6f, 0x75, 0x59, 0x32, 0x39, 0x74, 0x4c, 0x32, 0x46, 0x77, 0x61, 0x53, 0x38, 0x2f, 0x64, 0x48, 0x6c, 0x77, 0x5a, 0x54, 0x31, 0x68, 0x63, 0x47, 0x6c, 0x54, 0x62, 0x32, 0x35, 0x6e, 0x56, 0x58, 0x4a, 0x73, 0x56, 0x6a, 0x45, 0x42, 0x4e, 0x6d, 0x30, 0x75, 0x62, 0x58, 0x56, 0x7a, 0x61, 0x57, 0x4d, 0x75, 0x62, 0x57, 0x6c, 0x6e, 0x64, 0x53, 0x35, 0x6a, 0x62, 0x69, 0x39, 0x74, 0x61, 0x57, 0x64, 0x31, 0x62, 0x58, 0x56, 0x7a, 0x61, 0x57, 0x4d, 0x76, 0x61, 0x44, 0x55, 0x76, 0x63, 0x47, 0x78, 0x68, 0x65, 0x53, 0x39, 0x68, 0x64, 0x58, 0x52, 0x6f, 0x4c, 0x32, 0x64, 0x6c, 0x64, 0x46, 0x4e, 0x76, 0x62, 0x6d, 0x64, 0x51, 0x62, 0x47, 0x46, 0x35, 0x53, 0x57, 0x35, 0x6d, 0x62, 0x77, 0x45, 0x6c, 0x59, 0x33, 0x4e, 0x74, 0x4c, 0x6e, 0x4e, 0x68, 0x65, 0x58, 0x46, 0x36, 0x4c, 0x6d, 0x4e, 0x76, 0x62, 0x53, 0x39, 0x68, 0x63, 0x47, 0x6b, 0x76, 0x50, 0x33, 0x52, 0x35, 0x63, 0x47, 0x55, 0x39, 0x59, 0x33, 0x4e, 0x74, 0x51, 0x32, 0x68, 0x6c, 0x59, 0x57, 0x74, 0x4e, 0x64, 0x58, 0x4e, 0x70, 0x59, 0x77, 0x45, 0x43, 0x43, 0x6c, 0x56, 0x7a, 0x5a, 0x58, 0x49, 0x74, 0x51, 0x57, 0x64, 0x6c, 0x62, 0x6e, 0x52, 0x65, 0x54, 0x57, 0x39, 0x36, 0x61, 0x57, 0x78, 0x73, 0x59, 0x53, 0x38, 0x31, 0x4c, 0x6a, 0x41, 0x67, 0x4b, 0x46, 0x64, 0x70, 0x62, 0x6d, 0x52, 0x76, 0x64, 0x33, 0x4d, 0x67, 0x54, 0x6c, 0x51, 0x67, 0x4e, 0x69, 0x34, 0x78, 0x4f, 0x79, 0x42, 0x58, 0x54, 0x31, 0x63, 0x32, 0x4e, 0x43, 0x6b, 0x67, 0x51, 0x58, 0x42, 0x77, 0x62, 0x47, 0x56, 0x58, 0x5a, 0x57, 0x4a, 0x4c, 0x61, 0x58, 0x51, 0x76, 0x4e, 0x54, 0x4d, 0x33, 0x4c, 0x6a, 0x4d, 0x32, 0x49, 0x43, 0x68, 0x4c, 0x53, 0x46, 0x52, 0x4e, 0x54, 0x43, 0x77, 0x67, 0x62, 0x47, 0x6c, 0x72, 0x5a, 0x53, 0x42, 0x48, 0x5a, 0x57, 0x4e, 0x72, 0x62, 0x79, 0x6b, 0x67, 0x51, 0x32, 0x68, 0x79, 0x62, 0x32, 0x31, 0x6c, 0x4c, 0x7a, 0x55, 0x77, 0x4c, 0x6a, 0x41, 0x75, 0x4d, 0x6a, 0x59, 0x32, 0x4d, 0x53, 0x34, 0x34, 0x4e, 0x78, 0x42, 0x59, 0x4c, 0x56, 0x4a, 0x6c, 0x63, 0x58, 0x56, 0x6c, 0x63, 0x33, 0x52, 0x6c, 0x5a, 0x43, 0x31, 0x58, 0x61, 0x58, 0x52, 0x6f, 0x44, 0x6c, 0x68, 0x4e, 0x54, 0x45, 0x68, 0x30, 0x64, 0x48, 0x42, 0x53, 0x5a, 0x58, 0x46, 0x31, 0x5a, 0x58, 0x4e, 0x30, 0x41, 0x51, 0x51, 0x48, 0x55, 0x6d, 0x56, 0x6d, 0x5a, 0x58, 0x4a, 0x6c, 0x63, 0x68, 0x74, 0x6f, 0x64, 0x48, 0x52, 0x77, 0x63, 0x7a, 0x6f, 0x76, 0x4c, 0x32, 0x30, 0x75, 0x62, 0x58, 0x56, 0x7a, 0x61, 0x57, 0x4d, 0x75, 0x62, 0x57, 0x6c, 0x6e, 0x64, 0x53, 0x35, 0x6a, 0x62, 0x69, 0x39, 0x32, 0x4e, 0x43, 0x38, 0x43, 0x51, 0x6e, 0x6b, 0x67, 0x4d, 0x44, 0x52, 0x6d, 0x4f, 0x44, 0x45, 0x30, 0x4e, 0x6a, 0x46, 0x68, 0x4f, 0x54, 0x68, 0x6a, 0x4e, 0x32, 0x46, 0x6d, 0x4e, 0x54, 0x55, 0x33, 0x5a, 0x6d, 0x56, 0x68, 0x4d, 0x32, 0x4e, 0x6d, 0x4d, 0x6a, 0x68, 0x6a, 0x4e, 0x47, 0x56, 0x68, 0x4d, 0x54, 0x55, 0x48, 0x59, 0x32, 0x68, 0x68, 0x62, 0x6d, 0x35, 0x6c, 0x62, 0x41, 0x63, 0x77, 0x4d, 0x54, 0x51, 0x77, 0x4d, 0x44, 0x42, 0x45, 0x42, 0x6b, 0x4e, 0x76, 0x62, 0x32, 0x74, 0x70, 0x5a, 0x54, 0x68, 0x54, 0x52, 0x56, 0x4e, 0x54, 0x53, 0x55, 0x39, 0x4f, 0x50, 0x56, 0x70, 0x55, 0x53, 0x58, 0x64, 0x50, 0x52, 0x47, 0x74, 0x35, 0x54, 0x55, 0x52, 0x52, 0x64, 0x45, 0x39, 0x55, 0x52, 0x54, 0x46, 0x4f, 0x55, 0x7a, 0x41, 0x77, 0x54, 0x55, 0x52, 0x6f, 0x62, 0x45, 0x78, 0x55, 0x61, 0x47, 0x68, 0x4e, 0x56, 0x30, 0x56, 0x30, 0x54, 0x57, 0x70, 0x52, 0x4d, 0x45, 0x34, 0x79, 0x57, 0x54, 0x4a, 0x4e, 0x65, 0x6d, 0x73, 0x79, 0x54, 0x31, 0x52, 0x42, 0x65, 0x67, 0x41, 0x3d}
|
||||||
ztool.Val_GobDecode(data, &initdata)
|
dec, _ := zcypt.Base64Decode(base64.StdEncoding, data)
|
||||||
|
ztool.Val_GobDecode(dec, &initdata)
|
||||||
}
|
}
|
||||||
|
@ -60,12 +60,13 @@ func init() {
|
|||||||
default:
|
default:
|
||||||
loger.Fatal(`未定义的接口模式,请检查配置 [Custom].Kw_Mode`)
|
loger.Fatal(`未定义的接口模式,请检查配置 [Custom].Kw_Mode`)
|
||||||
}
|
}
|
||||||
loger = nil
|
loger.Free()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func bdapi(songMid, quality string) (ourl, msg string) {
|
func bdapi(songMid, quality string) (ourl, msg string) {
|
||||||
loger := env.Loger.NewGroup(`Kw`)
|
loger := env.Loger.NewGroup(`Kw`)
|
||||||
|
defer loger.Free()
|
||||||
info, ok := fileInfo[quality]
|
info, ok := fileInfo[quality]
|
||||||
if !ok {
|
if !ok {
|
||||||
msg = sources.E_QNotSupport
|
msg = sources.E_QNotSupport
|
||||||
@ -100,6 +101,7 @@ func bdapi(songMid, quality string) (ourl, msg string) {
|
|||||||
|
|
||||||
func kwdes(songMid, quality string) (ourl, msg string) {
|
func kwdes(songMid, quality string) (ourl, msg string) {
|
||||||
loger := env.Loger.NewGroup(`Kw`)
|
loger := env.Loger.NewGroup(`Kw`)
|
||||||
|
defer loger.Free()
|
||||||
infoFile, ok := fileInfo[quality]
|
infoFile, ok := fileInfo[quality]
|
||||||
if !ok {
|
if !ok {
|
||||||
msg = sources.E_QNotSupport
|
msg = sources.E_QNotSupport
|
||||||
|
@ -91,6 +91,7 @@ type playInfo struct {
|
|||||||
|
|
||||||
func Url(songMid, quality string) (ourl, msg string) {
|
func Url(songMid, quality string) (ourl, msg string) {
|
||||||
loger := env.Loger.NewGroup(`Tx`)
|
loger := env.Loger.NewGroup(`Tx`)
|
||||||
|
defer loger.Free()
|
||||||
infoFile, ok := fileInfo[quality]
|
infoFile, ok := fileInfo[quality]
|
||||||
if !ok || (!env.Config.Custom.Tx_Enable && quality != sources.Q_128k) {
|
if !ok || (!env.Config.Custom.Tx_Enable && quality != sources.Q_128k) {
|
||||||
msg = sources.E_QNotSupport //`不支持的音质`
|
msg = sources.E_QNotSupport //`不支持的音质`
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package wy
|
package wy
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"lx-source/src/env"
|
// "lx-source/src/env"
|
||||||
"maps"
|
// wy "lx-source/src/sources/custom/wy/modules"
|
||||||
"time"
|
// "maps"
|
||||||
|
// "time"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool/logs"
|
// "github.com/ZxwyWebSite/ztool/logs"
|
||||||
"github.com/ZxwyWebSite/ztool/x/cookie"
|
// "github.com/ZxwyWebSite/ztool/x/cookie"
|
||||||
)
|
// )
|
||||||
|
|
||||||
/*
|
/*
|
||||||
刷新登录模块 (来自 NeteaseCloudMusicApi)
|
刷新登录模块 (来自 NeteaseCloudMusicApi)
|
||||||
@ -19,43 +20,43 @@ import (
|
|||||||
原代码未提供详细描述,无法确定有效结果判断条件,暂时先这么写
|
原代码未提供详细描述,无法确定有效结果判断条件,暂时先这么写
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func init() {
|
// func init() {
|
||||||
env.Inits.Add(func() {
|
// env.Inits.Add(func() {
|
||||||
if env.Config.Custom.Wy_Refresh_Enable && env.Config.Custom.Wy_Cookie != `` {
|
// if env.Config.Custom.Wy_Refresh_Enable && env.Config.Custom.Wy_Api_Cookie != `` {
|
||||||
env.Tasker.Add(`wy_refresh`, func(loger *logs.Logger) error {
|
// env.Tasker.Add(`wy_refresh`, func(loger *logs.Logger) error {
|
||||||
// 前置检测
|
// // 前置检测
|
||||||
now := time.Now().Unix()
|
// now := time.Now().Unix()
|
||||||
if now < env.Config.Custom.Wy_Refresh_Interval {
|
// if now < env.Config.Custom.Wy_Refresh_Interval {
|
||||||
loger.Debug(`Key未过期,跳过...`)
|
// loger.Debug(`Key未过期,跳过...`)
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
// 刷新逻辑
|
// // 刷新逻辑
|
||||||
cookies := cookie.ToMap(cookie.Parse(env.Config.Custom.Wy_Cookie))
|
// cookies := cookie.ToMap(cookie.Parse(env.Config.Custom.Wy_Api_Cookie))
|
||||||
res, err := LoginRefresh(ReqQuery{
|
// res, err := wy.LoginRefresh(wy.ReqQuery{
|
||||||
Cookie: cookies,
|
// Cookie: cookies,
|
||||||
})
|
// })
|
||||||
loger.Debug(`Resp: %+v`, res)
|
// loger.Debug(`Resp: %+v`, res)
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
if out, ok := res.Body[`cookie`].(string); ok {
|
// if out, ok := res.Body[`cookie`].(string); ok {
|
||||||
loger.Info(`获取数据成功`)
|
// loger.Info(`获取数据成功`)
|
||||||
cmap := cookie.ToMap(cookie.Parse(out))
|
// cmap := cookie.ToMap(cookie.Parse(out))
|
||||||
maps.Copy(cookies, cmap)
|
// maps.Copy(cookies, cmap)
|
||||||
env.Config.Custom.Wy_Cookie = cookie.Marshal(cookies)
|
// env.Config.Custom.Wy_Api_Cookie = cookie.Marshal(cookies)
|
||||||
loger.Debug(`Cookie: %#v`, cookies)
|
// loger.Debug(`Cookie: %#v`, cookies)
|
||||||
if cmap[`MUSIC_U`] != `` {
|
// if cmap[`MUSIC_U`] != `` {
|
||||||
env.Config.Custom.Wy_Refresh_Interval = now + 2147483647 - 86000
|
// env.Config.Custom.Wy_Refresh_Interval = now + 2147483647 - 86000
|
||||||
} else {
|
// } else {
|
||||||
env.Config.Custom.Wy_Refresh_Interval = now + 86000
|
// env.Config.Custom.Wy_Refresh_Interval = now + 86000
|
||||||
loger.Warn(`未发现有效结果,将在下次检测时再次尝试`)
|
// loger.Warn(`未发现有效结果,将在下次检测时再次尝试`)
|
||||||
}
|
// }
|
||||||
err = env.Cfg.Save(``)
|
// err = env.Cfg.Save(``)
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
loger.Info(`配置更新成功`)
|
// loger.Info(`配置更新成功`)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return err
|
// return err
|
||||||
}, 86000, true)
|
// }, 86000, true)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// crypto.js
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ivKey = bytesconv.StringToBytes(`0102030405060708`)
|
ivKey = bytesconv.StringToBytes(`0102030405060708`)
|
||||||
presetKey = bytesconv.StringToBytes(`0CoJUm6Qyw8W8jud`)
|
presetKey = bytesconv.StringToBytes(`0CoJUm6Qyw8W8jud`)
|
||||||
@ -28,21 +30,6 @@ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7cl
|
|||||||
eapiKey = bytesconv.StringToBytes(`e82ckenh8dichen8`)
|
eapiKey = bytesconv.StringToBytes(`e82ckenh8dichen8`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// func eapiEncrypt(url, text string) map[string][]string {
|
|
||||||
// digest := zcypt.CreateMD5(bytesconv.StringToBytes(ztool.Str_FastConcat(
|
|
||||||
// `nobody`, url, `use`, text, `md5forencrypt`,
|
|
||||||
// )))
|
|
||||||
// data := ztool.Str_FastConcat(
|
|
||||||
// url, `-36cd479b6b5-`, text, `-36cd479b6b5-`, digest,
|
|
||||||
// )
|
|
||||||
// // 注:JSON编码时会自动将[]byte转为string,这里省去一步转换
|
|
||||||
// return map[string][]string{
|
|
||||||
// `params`: {bytesconv.BytesToString(aesEncrypt(bytesconv.StringToBytes(data), eapiKey, false))},
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// crypto.js
|
|
||||||
|
|
||||||
func aesEncrypt(text, key []byte, iv bool) []byte {
|
func aesEncrypt(text, key []byte, iv bool) []byte {
|
||||||
pad := 16 - len(text)%16
|
pad := 16 - len(text)%16
|
||||||
text = append(text, bytes.Repeat([]byte{byte(pad)}, pad)...)
|
text = append(text, bytes.Repeat([]byte{byte(pad)}, pad)...)
|
@ -222,9 +222,9 @@ func createRequest(method, url string, data map[string]any, options reqOptions)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
answer.Status = 502
|
answer.Status = 502
|
||||||
answer.Body = map[string]any{`code`: 502, `msg`: err.Error()}
|
answer.Body = map[string]any{`code`: 502, `msg`: err.Error()}
|
||||||
return err
|
// return err
|
||||||
}
|
}
|
||||||
return nil
|
return err // nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
84
src/sources/custom/wy/modules/core_types.go
Normal file
84
src/sources/custom/wy/modules/core_types.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package wy
|
||||||
|
|
||||||
|
type (
|
||||||
|
// 音乐URL
|
||||||
|
PlayInfo struct {
|
||||||
|
Data []struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Br int `json:"br"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Md5 string `json:"md5"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Expi int `json:"expi"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Gain float64 `json:"gain"`
|
||||||
|
Peak float64 `json:"peak"`
|
||||||
|
Fee int `json:"fee"`
|
||||||
|
Uf interface{} `json:"uf"`
|
||||||
|
Payed int `json:"payed"`
|
||||||
|
Flag int `json:"flag"`
|
||||||
|
CanExtend bool `json:"canExtend"`
|
||||||
|
FreeTrialInfo struct {
|
||||||
|
AlgData interface{} `json:"algData"`
|
||||||
|
End int `json:"end"`
|
||||||
|
FragmentType int `json:"fragmentType"`
|
||||||
|
Start int `json:"start"`
|
||||||
|
} `json:"freeTrialInfo"`
|
||||||
|
Level string `json:"level"`
|
||||||
|
EncodeType string `json:"encodeType"`
|
||||||
|
FreeTrialPrivilege struct {
|
||||||
|
ResConsumable bool `json:"resConsumable"`
|
||||||
|
UserConsumable bool `json:"userConsumable"`
|
||||||
|
ListenType int `json:"listenType"`
|
||||||
|
CannotListenReason int `json:"cannotListenReason"`
|
||||||
|
PlayReason interface{} `json:"playReason"`
|
||||||
|
} `json:"freeTrialPrivilege"`
|
||||||
|
FreeTimeTrialPrivilege struct {
|
||||||
|
ResConsumable bool `json:"resConsumable"`
|
||||||
|
UserConsumable bool `json:"userConsumable"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
RemainTime int `json:"remainTime"`
|
||||||
|
} `json:"freeTimeTrialPrivilege"`
|
||||||
|
URLSource int `json:"urlSource"`
|
||||||
|
RightSource int `json:"rightSource"`
|
||||||
|
PodcastCtrp interface{} `json:"podcastCtrp"`
|
||||||
|
EffectTypes interface{} `json:"effectTypes"`
|
||||||
|
Time int `json:"time"`
|
||||||
|
} `json:"data"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
}
|
||||||
|
// 音乐是否可用
|
||||||
|
VerifyInfo struct {
|
||||||
|
Code int16 `json:"code"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
// 音质数据
|
||||||
|
QualityData struct {
|
||||||
|
Br int `json:"br"` // 比特率 Bit Rate
|
||||||
|
Fid int `json:"fid"` // ?
|
||||||
|
Size int `json:"size"` // 文件大小
|
||||||
|
Vd float64 `json:"vd"` // Volume Delta
|
||||||
|
Sr int `json:"sr"` // 采样率 Sample Rate
|
||||||
|
}
|
||||||
|
// 歌曲音质详情
|
||||||
|
QualityDetail struct {
|
||||||
|
Data struct {
|
||||||
|
SongID int `json:"songId"`
|
||||||
|
H QualityData `json:"h"` // 高质量文件信息
|
||||||
|
M QualityData `json:"m"` // 中质量文件信息
|
||||||
|
L QualityData `json:"l"` // 低质量文件信息
|
||||||
|
Sq QualityData `json:"sq"` // 无损质量文件信息
|
||||||
|
Hr QualityData `json:"hr"` // Hi-Res质量文件信息
|
||||||
|
Db QualityData `json:"db"` // 杜比音质
|
||||||
|
Jm QualityData `json:"jm"` // jymaster(超清母带)
|
||||||
|
Je QualityData `json:"je"` // jyeffect(高清环绕声)
|
||||||
|
Sk QualityData `json:"sk"` // sky(沉浸环绕声)
|
||||||
|
} `json:"data"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Error bool `json:"error"`
|
||||||
|
}
|
||||||
|
)
|
@ -4,73 +4,82 @@ import (
|
|||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
"lx-source/src/sources"
|
"lx-source/src/sources"
|
||||||
"lx-source/src/sources/custom/utils"
|
"lx-source/src/sources/custom/utils"
|
||||||
|
wy "lx-source/src/sources/custom/wy/modules"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
"github.com/ZxwyWebSite/ztool/x/cookie"
|
"github.com/ZxwyWebSite/ztool/x/cookie"
|
||||||
)
|
)
|
||||||
|
|
||||||
type playInfo struct {
|
var (
|
||||||
Data []struct {
|
wy_pool = &sync.Pool{New: func() any { return new(wy.PlayInfo) }}
|
||||||
ID int `json:"id"`
|
// wv_pool *sync.Pool
|
||||||
URL string `json:"url"`
|
|
||||||
Br int `json:"br"`
|
Url func(string, string) (string, string)
|
||||||
Size int `json:"size"`
|
)
|
||||||
Md5 string `json:"md5"`
|
|
||||||
Code int `json:"code"`
|
func init() {
|
||||||
Expi int `json:"expi"`
|
env.Inits.Add(func() {
|
||||||
Type string `json:"type"`
|
loger := env.Loger.NewGroup(`WyInit`)
|
||||||
Gain float64 `json:"gain"`
|
switch env.Config.Custom.Wy_Mode {
|
||||||
Peak float64 `json:"peak"`
|
case `0`, `builtin`:
|
||||||
Fee int `json:"fee"`
|
loger.Debug(`use builtin`)
|
||||||
Uf interface{} `json:"uf"`
|
// if env.Config.Source.MusicIdVerify {
|
||||||
Payed int `json:"payed"`
|
// wv_pool = &sync.Pool{New: func() any { return new(verifyInfo) }}
|
||||||
Flag int `json:"flag"`
|
// }
|
||||||
CanExtend bool `json:"canExtend"`
|
// Url = builtin
|
||||||
FreeTrialInfo struct {
|
case `1`, `163api`:
|
||||||
AlgData interface{} `json:"algData"`
|
if env.Config.Custom.Wy_Api_Cookie == `` {
|
||||||
End int `json:"end"`
|
loger.Fatal(`使用163api且Cookie参数为空`)
|
||||||
FragmentType int `json:"fragmentType"`
|
}
|
||||||
Start int `json:"start"`
|
switch env.Config.Custom.Wy_Api_Type {
|
||||||
} `json:"freeTrialInfo"`
|
case `0`, `native`:
|
||||||
Level string `json:"level"`
|
loger.Debug(`use 163api module`)
|
||||||
EncodeType string `json:"encodeType"`
|
Url = nmModule
|
||||||
FreeTrialPrivilege struct {
|
case `1`, `remote`:
|
||||||
ResConsumable bool `json:"resConsumable"`
|
loger.Debug(`use 163api custom`)
|
||||||
UserConsumable bool `json:"userConsumable"`
|
if env.Config.Custom.Wy_Api_Address == `` {
|
||||||
ListenType int `json:"listenType"`
|
loger.Fatal(`自定义接口地址为空`)
|
||||||
CannotListenReason int `json:"cannotListenReason"`
|
}
|
||||||
PlayReason interface{} `json:"playReason"`
|
if env.Config.Custom.Wy_Api_Address[len(env.Config.Custom.Wy_Api_Address)-1] != '/' {
|
||||||
} `json:"freeTrialPrivilege"`
|
env.Config.Custom.Wy_Api_Address += "/" // 补全尾部斜杠
|
||||||
FreeTimeTrialPrivilege struct {
|
}
|
||||||
ResConsumable bool `json:"resConsumable"`
|
loger.Info(`使用自定义接口: %v`, env.Config.Custom.Wy_Api_Address)
|
||||||
UserConsumable bool `json:"userConsumable"`
|
Url = nmCustom
|
||||||
Type int `json:"type"`
|
default:
|
||||||
RemainTime int `json:"remainTime"`
|
loger.Fatal(`未定义的调用方式,请检查配置 [Custom].Wy_Api_Type`)
|
||||||
} `json:"freeTimeTrialPrivilege"`
|
}
|
||||||
URLSource int `json:"urlSource"`
|
default:
|
||||||
RightSource int `json:"rightSource"`
|
loger.Fatal(`未定义的接口模式,请检查配置 [Custom].Wy_Mode`)
|
||||||
PodcastCtrp interface{} `json:"podcastCtrp"`
|
}
|
||||||
EffectTypes interface{} `json:"effectTypes"`
|
loger.Free()
|
||||||
Time int `json:"time"`
|
})
|
||||||
} `json:"data"`
|
|
||||||
Code int `json:"code"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Url(songMid, quality string) (ourl, msg string) {
|
// func builtin(songMid, quality string) (ourl, msg string) {
|
||||||
|
// loger := env.Loger.NewGroup(`Wy`)
|
||||||
|
// defer loger.Free()
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func nmModule(songMid, quality string) (ourl, msg string) {
|
||||||
loger := env.Loger.NewGroup(`Wy`)
|
loger := env.Loger.NewGroup(`Wy`)
|
||||||
|
defer loger.Free()
|
||||||
rquality, ok := qualityMap[quality]
|
rquality, ok := qualityMap[quality]
|
||||||
if !ok {
|
if !ok {
|
||||||
msg = sources.E_QNotSupport
|
msg = sources.E_QNotSupport
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cookies := cookie.Parse(env.Config.Custom.Wy_Cookie)
|
cookies := cookie.Parse(env.Config.Custom.Wy_Api_Cookie)
|
||||||
answer, err := SongUrlV1(ReqQuery{
|
answer, err := wy.SongUrlV1(wy.ReqQuery{
|
||||||
Cookie: cookie.ToMap(cookies),
|
Cookie: cookie.ToMap(cookies),
|
||||||
Ids: songMid,
|
Ids: songMid,
|
||||||
// Br: rquality,
|
// Br: rquality,
|
||||||
Level: rquality,
|
Level: rquality,
|
||||||
})
|
})
|
||||||
var body playInfo
|
body := wy_pool.Get().(*wy.PlayInfo)
|
||||||
|
defer wy_pool.Put(body)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = ztool.Val_MapToStruct(answer.Body, &body)
|
err = ztool.Val_MapToStruct(answer.Body, &body)
|
||||||
}
|
}
|
||||||
@ -98,56 +107,42 @@ func Url(songMid, quality string) (ourl, msg string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func PyUrl(songMid, quality string) (ourl, msg string) {
|
func nmCustom(songMid, quality string) (ourl, msg string) {
|
||||||
// loger := env.Loger.NewGroup(`Wy`)
|
loger := env.Loger.NewGroup(`Wy`)
|
||||||
// rquality, ok := qualityMap[quality]
|
defer loger.Free()
|
||||||
// if !ok {
|
rquality, ok := qualityMap[quality]
|
||||||
// msg = sources.E_QNotSupport
|
if !ok {
|
||||||
// return
|
msg = sources.E_QNotSupport
|
||||||
// }
|
return
|
||||||
// path := `/api/song/enhance/player/url/v1`
|
}
|
||||||
// requestUrl := `https://interface.music.163.com/eapi/song/enhance/player/url/v1`
|
body := wy_pool.Get().(*wy.PlayInfo)
|
||||||
// var body builtin.WyApi_Song
|
defer wy_pool.Put(body)
|
||||||
// text := ztool.Str_FastConcat(
|
err := ztool.Net_Request(
|
||||||
// `{"encodeType":"flac","ids":["`, songMid, `"],"level":"`, rquality, `"}`,
|
http.MethodGet,
|
||||||
// )
|
ztool.Str_FastConcat(
|
||||||
// var form url.Values = eapiEncrypt(path, text)
|
env.Config.Custom.Wy_Api_Address, `song/url/v1`, `?id=`, songMid, `&level=`, rquality,
|
||||||
// // form, err := json.Marshal(eapiEncrypt(path, text))
|
// `×tamp=`, strconv.FormatInt(time.Now().UnixMilli(), 10),
|
||||||
// // if err == nil {
|
), nil,
|
||||||
// err := ztool.Net_Request(
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
|
||||||
// http.MethodPost, requestUrl,
|
`Cookie`: env.Config.Custom.Wy_Api_Cookie,
|
||||||
// strings.NewReader(form.Encode()), //bytes.NewReader(form),
|
})},
|
||||||
// []ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeader(map[string]string{
|
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&body)},
|
||||||
// `Cookie`: env.Config.Custom.Wy_Cookie,
|
)
|
||||||
// })},
|
if err != nil {
|
||||||
// []ztool.Net_ResHandlerFunc{
|
loger.Error(`SongUrl: %s`, err)
|
||||||
// func(res *http.Response) error {
|
msg = sources.ErrHttpReq
|
||||||
// body, err := io.ReadAll(res.Body)
|
return
|
||||||
// if err != nil {
|
}
|
||||||
// return err
|
loger.Debug(`Resp: %+v`, body)
|
||||||
// }
|
if len(body.Data) == 0 {
|
||||||
// loger.Info(`%s`, body)
|
msg = `No Data:无返回数据`
|
||||||
// return ztool.Err_EsContinue
|
return
|
||||||
// },
|
}
|
||||||
// ztool.Net_ResToStruct(&body),
|
data := body.Data[0]
|
||||||
// },
|
if data.Level != rquality {
|
||||||
// )
|
msg = ztool.Str_FastConcat(`实际音质不匹配: `, rquality, ` <= `, data.Level)
|
||||||
// // }
|
return
|
||||||
// if err != nil {
|
}
|
||||||
// loger.Error(`Request: %s`, err)
|
ourl = utils.DelQuery(data.URL)
|
||||||
// msg = sources.ErrHttpReq
|
return
|
||||||
// return
|
}
|
||||||
// }
|
|
||||||
// loger.Debug(`Resp: %+v`, body)
|
|
||||||
// if len(body.Data) == 0 {
|
|
||||||
// msg = `No Data:无返回数据`
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// data := body.Data[0]
|
|
||||||
// if data.Level != rquality {
|
|
||||||
// msg = sources.E_QNotMatch
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// ourl = utils.DelQuery(data.URL)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
@ -57,10 +57,44 @@ func (*NullSource) GetLink(*caches.Query) (string, string) { return ``, `NullSou
|
|||||||
var UseSource Source = &NullSource{} // = &builtin.Source{}
|
var UseSource Source = &NullSource{} // = &builtin.Source{}
|
||||||
|
|
||||||
// 统一错误
|
// 统一错误
|
||||||
// type Error struct {
|
// type (
|
||||||
// msg string
|
// ErrDef struct {
|
||||||
|
// Typ string
|
||||||
|
// Msg string
|
||||||
|
// }
|
||||||
|
// ErrQul struct {
|
||||||
|
// Need string
|
||||||
|
// Real string
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
|
||||||
|
// func (e *ErrDef) Error() string {
|
||||||
|
// return ztool.Str_FastConcat(e.Typ, `: `, e.Msg)
|
||||||
|
// }
|
||||||
|
// func (e *ErrQul) Error() string {
|
||||||
|
// return ztool.Str_FastConcat(`实际音质不匹配: `, e.Need, ` <= `, e.Real)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (e *Error) Error() string {
|
// 验证失败(Verify Failed)
|
||||||
// return ztool.Str_FastConcat(e.msg)
|
// func ErrVerify(msg string) error {
|
||||||
|
// return &ErrDef{
|
||||||
|
// Typ: Err_Verify,
|
||||||
|
// Msg: msg,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 实际音质不匹配
|
||||||
|
// func ErrQuality(need, real string) error {
|
||||||
|
// return &ErrQul{
|
||||||
|
// Need: need,
|
||||||
|
// Real: real,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 无返回数据(No Data)
|
||||||
|
// func ErrNoData() error {
|
||||||
|
// return &ErrDef{
|
||||||
|
// Typ: `No Data`,
|
||||||
|
// Msg: ``,
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
|
46
src/sources/types.go
Normal file
46
src/sources/types.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package sources
|
||||||
|
|
||||||
|
// MusicFree 数据结构
|
||||||
|
type (
|
||||||
|
// 其他
|
||||||
|
IExtra map[string]interface{}
|
||||||
|
// 音乐
|
||||||
|
IMusicItem struct {
|
||||||
|
Artist string `json:"artist"` // 作者
|
||||||
|
Title string `json:"title"` // 歌曲标题
|
||||||
|
Duration int `json:"duration,omitempty"` // 时长(s)
|
||||||
|
Album string `json:"album,omitempty"` // 专辑名
|
||||||
|
Artwork string `json:"artwork,omitempty"` // 专辑封面图
|
||||||
|
Url string `json:"url,omitempty"` // 默认音源
|
||||||
|
Lrc string `json:"lrc,omitempty"` // 歌词URL
|
||||||
|
RawLrc string `json:"rawLrc,omitempty"` // 歌词文本(lrc格式 带时间戳)
|
||||||
|
Other IExtra `json:"extra,omitempty"` // 其他
|
||||||
|
}
|
||||||
|
// 歌单
|
||||||
|
IMusicSheetItem struct {
|
||||||
|
Artwork string `json:"artwork,omitempty"` // 封面图
|
||||||
|
Title string `json:"title"` // 标题
|
||||||
|
Description string `json:"description,omitempty"` // 描述
|
||||||
|
WorksNum int `json:"worksNum,omitempty"` // 作品总数
|
||||||
|
PlayCount int `json:"playCount,omitempty"` // 播放次数
|
||||||
|
MusicList []IMusicItem `json:"musicList,omitempty"` // 播放列表
|
||||||
|
CreateAt int64 `json:"createAt,omitempty"` // 歌单创建日期
|
||||||
|
Artist string `json:"artist,omitempty"` // 歌单作者
|
||||||
|
Other IExtra `json:"extra,omitempty"` // 其他
|
||||||
|
}
|
||||||
|
// 专辑
|
||||||
|
IAlbumItem IMusicSheetItem
|
||||||
|
// 作者
|
||||||
|
IArtistItem struct {
|
||||||
|
Platform string `json:"platform,omitempty"` // 插件名
|
||||||
|
ID interface{} `json:"id"` // 唯一id
|
||||||
|
Name string `json:"name"` // 姓名
|
||||||
|
Fans int `json:"fans,omitempty"` // 粉丝数
|
||||||
|
Description string `json:"description,omitempty"` // 简介
|
||||||
|
Avatar string `json:"avatar,omitempty"` // 头像
|
||||||
|
WorksNum int `json:"worksNum,omitempty"` // 作品数目
|
||||||
|
MusicList []IMusicItem `json:"musicList,omitempty"` // 作者的音乐列表
|
||||||
|
AlbumList []IAlbumItem `json:"albumList,omitempty"` // 作者的专辑列表
|
||||||
|
Other IExtra `json:"extra,omitempty"` // 其他
|
||||||
|
}
|
||||||
|
)
|
55
update.md
55
update.md
@ -1,5 +1,60 @@
|
|||||||
## Lx-Source/更新日志
|
## Lx-Source/更新日志
|
||||||
|
|
||||||
|
#### \# 2024-01-31 v1.0.2-b12-d3 (dev)
|
||||||
|
+ [重构请求处理逻辑]:
|
||||||
|
```
|
||||||
|
首先拆分为三部分:Before(前置检测), Middle(获取数据), After(验证结果)
|
||||||
|
可解决部分重复代码,但传参难以统一
|
||||||
|
```
|
||||||
|
+ [缓存数据目录]:
|
||||||
|
```
|
||||||
|
128k.mp3 320k.mp3 flac.flac fl64.flac lyric.lrc cover.jpg info.json
|
||||||
|
| 各音质文件缓存 | 歌词 | 封面 | 详情 |
|
||||||
|
```
|
||||||
|
+ [数据库表结构]:
|
||||||
|
```
|
||||||
|
暂未确定
|
||||||
|
```
|
||||||
|
+ 计划:引入SQLite支持,缓存歌曲详情
|
||||||
|
+ 更新go.mod依赖
|
||||||
|
+ 默认文件权限改为0777
|
||||||
|
+ 屏蔽部分未实现功能配置项
|
||||||
|
|
||||||
|
#### \# 2024-01-29 v1.0.2-b12-d3 (dev)
|
||||||
|
+ 增加启动参数 `-p 0777` 设置默认文件权限 解决非root用户下的权限问题 (部分情况0666不管用)
|
||||||
|
+ **(注:为保证正确解析八进制数据,开头的"0"不能去掉!)**
|
||||||
|
+ 低调行事,删除源脚本内Github仓库地址
|
||||||
|
+ 构建时修改版本号?
|
||||||
|
+ 本地缓存:下载出错后删除文件
|
||||||
|
+ 更新wy内置源Api地址
|
||||||
|
+ 优化配置文件保存逻辑
|
||||||
|
+ 支持使用自定义NeteaseCloudMusicApi项目(beta)
|
||||||
|
+ (注:当前测试阶段,强行与旧逻辑兼容,可能无法达到最佳性能)
|
||||||
|
+ 计划:重构源请求逻辑,合并builtin与custom目录?
|
||||||
|
+ 支持wy源检测音质是否可用(需启用qualityMapReverse支持)
|
||||||
|
+ 计划:将参数传入Query,忽略实际音质不匹配强制缓存
|
||||||
|
|
||||||
|
#### \# 2024-01-27 v1.0.2-b12-d3 (dev)
|
||||||
|
+ wy源Cookie长期有效(一个月左右),且刷新逻辑未知,暂时禁用刷新登录功能
|
||||||
|
+ 将router和middleware整合到server文件夹
|
||||||
|
+ 自适应本地缓存绑定地址(beta),多端口建议开启,但缺少灵活性
|
||||||
|
+ windows平台调用loger.Fatal后显示按任意键继续(优化防止闪退)
|
||||||
|
|
||||||
|
#### \# 2024-01-26 v1.0.2-b12-d3 (dev)
|
||||||
|
+ 计划增加命令行模式配置账号登录(测试版)
|
||||||
|
+ 启动命令 `./lx-source-linux-amd64v2 -e menu`
|
||||||
|
+ 清理部分无用注释
|
||||||
|
|
||||||
|
#### \# 2024-01-24 v1.0.2-b12-d3 (dev)
|
||||||
|
<!-- + wyApi项目被警告删库,暂时停止相关更新 -->
|
||||||
|
+ 模块化wyApi部分√
|
||||||
|
+ \[Auth\] 将Key验证模块移至速率限制模块后
|
||||||
|
+ 优化main.go,使用zcypt.RandomBytes函数
|
||||||
|
+ 添加对象池,手动释放Loger,优化创建速度
|
||||||
|
+ 添加多端口监听支持
|
||||||
|
+ 支持配置全局代理、伪装ip
|
||||||
|
+ 支持使用163api模式获取歌曲
|
||||||
|
|
||||||
#### \# 2024-01-21 v1.0.2-b12-d2 (dev)
|
#### \# 2024-01-21 v1.0.2-b12-d2 (dev)
|
||||||
+ 新版api结构设计(暂定)
|
+ 新版api结构设计(暂定)
|
||||||
```
|
```
|
||||||
|
Loading…
x
Reference in New Issue
Block a user