mirror of
https://github.com/ZxwyWebSite/lx-source.git
synced 2025-05-23 21:37:42 +08:00
2024-05-18 v1.0.3.0518
This commit is contained in:
parent
e402c10381
commit
30b3260617
7
go.mod
7
go.mod
@ -3,9 +3,11 @@ module lx-source
|
|||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/ZxwyWebSite/cr-go-sdk v0.0.2
|
||||||
github.com/ZxwyWebSite/ztool v0.0.1
|
github.com/ZxwyWebSite/ztool v0.0.1
|
||||||
github.com/gin-contrib/gzip v1.0.0
|
github.com/gin-contrib/gzip v1.0.0
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,4 +45,7 @@ require (
|
|||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/ZxwyWebSite/ztool v0.0.1 => ./pkg/ztool // ../ztool
|
replace (
|
||||||
|
github.com/ZxwyWebSite/cr-go-sdk v0.0.2 => ../cr-go-sdk
|
||||||
|
github.com/ZxwyWebSite/ztool v0.0.1 => ./pkg/ztool // ../ztool
|
||||||
|
)
|
||||||
|
2
go.sum
2
go.sum
@ -36,6 +36,8 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
|
|||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 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=
|
||||||
|
43
init.go
43
init.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"lx-source/src/caches"
|
"lx-source/src/caches"
|
||||||
|
"lx-source/src/caches/cloudcache"
|
||||||
"lx-source/src/caches/localcache"
|
"lx-source/src/caches/localcache"
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
stdurl "net/url"
|
stdurl "net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/cr-go-sdk"
|
||||||
"github.com/ZxwyWebSite/ztool"
|
"github.com/ZxwyWebSite/ztool"
|
||||||
"github.com/ZxwyWebSite/ztool/logs"
|
"github.com/ZxwyWebSite/ztool/logs"
|
||||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||||
@ -169,22 +171,31 @@ func initMain() {
|
|||||||
// }
|
// }
|
||||||
// icl.Info(`使用本地缓存,文件路径 %q,绑定地址 %v`, LocalCachePath, env.Config.Apis.BindAddr)
|
// icl.Info(`使用本地缓存,文件路径 %q,绑定地址 %v`, LocalCachePath, env.Config.Apis.BindAddr)
|
||||||
case `2`, `cloudreve`:
|
case `2`, `cloudreve`:
|
||||||
icl.Fatal(`Cloudreve驱动暂未完善,未兼容新版调用方式,当前版本禁用`)
|
icl.Warn(`欢迎使用新版 Cloudreve 驱动, 由 cr-go-sdk 提供强力支持`)
|
||||||
// icl.Warn(`Cloudreve驱动暂未完善,使用非本机存储时存在兼容性问题,请谨慎使用`)
|
site := &cr.SiteObj{
|
||||||
// cs, err := cloudreve.NewSite(&cloudreve.Config{
|
Addr: env.Config.Cache.Cloud_Site,
|
||||||
// SiteUrl: env.Config.Cache.Cloud_Site,
|
ApiVer: cr.ApiV383,
|
||||||
// Username: env.Config.Cache.Cloud_User,
|
Users: &cr.UserObj{
|
||||||
// Password: env.Config.Cache.Cloud_Pass,
|
Mail: env.Config.Cache.Cloud_User,
|
||||||
// Session: env.Config.Cache.Cloud_Sess,
|
Pass: env.Config.Cache.Cloud_Pass,
|
||||||
// })
|
Cookie: cr.ParseCookie(env.Config.Cache.Cloud_Sess),
|
||||||
// if err != nil {
|
},
|
||||||
// icl.Error(`驱动["cloudreve"]初始化失败: %v, 将禁用缓存功能`, err)
|
}
|
||||||
// }
|
cache, err := caches.New(&cloudcache.Cache{
|
||||||
// UseCache = &crcache.Cache{
|
Site: site,
|
||||||
// Cs: cs,
|
Path: env.Config.Cache.Cloud_Path,
|
||||||
// Path: env.Config.Cache.Cloud_Path,
|
})
|
||||||
// IsOk: err == nil,
|
if err != nil {
|
||||||
// }
|
icl.Error(`驱动["cloudreve"]初始化失败: %v, 将禁用缓存功能`, err)
|
||||||
|
} else {
|
||||||
|
env.Tasker.Add(`cloud_sess`, func(l *logs.Logger, i int64) error {
|
||||||
|
if sess := site.Users.Cookie.String(); sess != env.Config.Cache.Cloud_Sess {
|
||||||
|
env.Config.Cache.Cloud_Sess = sess
|
||||||
|
}
|
||||||
|
return env.Cfg.Save(``)
|
||||||
|
}, 3600, true)
|
||||||
|
}
|
||||||
|
caches.UseCache = cache
|
||||||
default:
|
default:
|
||||||
icl.Error(`未定义的缓存模式,请检查配置 [Cache].Mode,本次启动禁用缓存`)
|
icl.Error(`未定义的缓存模式,请检查配置 [Cache].Mode,本次启动禁用缓存`)
|
||||||
}
|
}
|
||||||
|
42
menu.go
42
menu.go
@ -1,8 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"lx-source/src/env"
|
"lx-source/src/env"
|
||||||
|
"lx-source/src/sources/custom/tx"
|
||||||
wm "lx-source/src/sources/custom/wy/modules"
|
wm "lx-source/src/sources/custom/wy/modules"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -22,6 +25,8 @@ func parseEtag(etag *string) {
|
|||||||
// menuMian()
|
// menuMian()
|
||||||
case `wyqr`:
|
case `wyqr`:
|
||||||
wyQrLogin()
|
wyQrLogin()
|
||||||
|
case `txqq`:
|
||||||
|
txQqLogin()
|
||||||
default:
|
default:
|
||||||
loger.Fatal(`未知参数:%q`, *etag)
|
loger.Fatal(`未知参数:%q`, *etag)
|
||||||
}
|
}
|
||||||
@ -33,6 +38,17 @@ func wyQrLogin() {
|
|||||||
loger := env.Loger.NewGroup(`WyQrLogin`)
|
loger := env.Loger.NewGroup(`WyQrLogin`)
|
||||||
defer loger.Free()
|
defer loger.Free()
|
||||||
loger.Info(`执行模块: 网易云扫码登录`)
|
loger.Info(`执行模块: 网易云扫码登录`)
|
||||||
|
|
||||||
|
if env.Config.Custom.Wy_Api_Cookie != `` {
|
||||||
|
loger.Warn("已存在账号数据, 继续操作可能导致数据覆盖丢失!")
|
||||||
|
fmt.Print(`输入'y'继续: `)
|
||||||
|
var input string
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
if input != `y` {
|
||||||
|
loger.Fatal(`用户取消操作`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res, err := wm.LoginQrKey()
|
res, err := wm.LoginQrKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
loger.Fatal(`无法创建请求: %s`, err)
|
loger.Fatal(`无法创建请求: %s`, err)
|
||||||
@ -80,6 +96,32 @@ func wyQrLogin() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QQ快速登录
|
||||||
|
func txQqLogin() {
|
||||||
|
loger := env.Loger.NewGroup(`TxQqLogin`)
|
||||||
|
defer loger.Free()
|
||||||
|
loger.Info(`执行模块: QQ快速登录`)
|
||||||
|
|
||||||
|
if runtime.GOOS != `windows` {
|
||||||
|
loger.Fatal(`该模块仅支持在windows环境下使用`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if env.Config.Custom.Tx_Ukey != `` {
|
||||||
|
loger.Warn("已存在账号数据, 继续操作可能导致数据覆盖丢失!")
|
||||||
|
fmt.Print(`输入'y'继续: `)
|
||||||
|
var input string
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
if input != `y` {
|
||||||
|
loger.Fatal(`用户取消操作`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Qlogin_graph(loger); err != nil {
|
||||||
|
loger.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// func menuMian() {
|
// func menuMian() {
|
||||||
// app := menu.NewApp(`Lx-Source`)
|
// app := menu.NewApp(`Lx-Source`)
|
||||||
// app.Data = menu.Data{
|
// app.Data = menu.Data{
|
||||||
|
@ -1 +1,121 @@
|
|||||||
package cloudcache
|
package cloudcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"lx-source/src/caches"
|
||||||
|
"lx-source/src/env"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
cr "github.com/ZxwyWebSite/cr-go-sdk"
|
||||||
|
"github.com/ZxwyWebSite/cr-go-sdk/service/explorer"
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
Site *cr.SiteObj
|
||||||
|
Path string
|
||||||
|
state bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Get(q *caches.Query) string {
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString(c.Path)
|
||||||
|
b.WriteByte('/')
|
||||||
|
b.WriteString(q.Source)
|
||||||
|
b.WriteByte('/')
|
||||||
|
b.WriteString(q.MusicID)
|
||||||
|
list, err := c.Site.Directory(b.String())
|
||||||
|
if err != nil {
|
||||||
|
caches.Loger.Debug(`列出目录: %v`, err)
|
||||||
|
return ``
|
||||||
|
}
|
||||||
|
name := q.Quality + `.` + q.Extname
|
||||||
|
var id string
|
||||||
|
for _, v := range list.Objects {
|
||||||
|
if v.Name == name && v.Type == `file` {
|
||||||
|
id = v.ID
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if id == `` {
|
||||||
|
caches.Loger.Debug(`文件不存在`)
|
||||||
|
return ``
|
||||||
|
}
|
||||||
|
srcs, err := c.Site.FileSource(cr.GenerateSrc(false, id))
|
||||||
|
if err != nil {
|
||||||
|
caches.Loger.Debug(`生成外链: %v`, err)
|
||||||
|
return ``
|
||||||
|
}
|
||||||
|
return (*srcs)[0].URL
|
||||||
|
/*link, err := c.Site.FileDownload(id)
|
||||||
|
if err != nil {
|
||||||
|
caches.Loger.Debug(`下载文件: %v`, err)
|
||||||
|
return ``
|
||||||
|
}
|
||||||
|
if (*link)[0] == '/' {
|
||||||
|
return c.Site.Addr + (*link)[1:]
|
||||||
|
}
|
||||||
|
return *link*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Set(q *caches.Query, l string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString(c.Path)
|
||||||
|
b.WriteByte('/')
|
||||||
|
b.WriteString(q.Source)
|
||||||
|
b.WriteByte('/')
|
||||||
|
b.WriteString(q.MusicID)
|
||||||
|
dir := b.String()
|
||||||
|
err := c.Site.DirectoryNew(&explorer.DirectoryService{
|
||||||
|
Path: dir,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
caches.Loger.Debug(`创建目录: %v`, err)
|
||||||
|
return ``
|
||||||
|
}
|
||||||
|
/*var buf bytes.Buffer
|
||||||
|
err = ztool.Net_Download(l, &buf, nil)
|
||||||
|
if err != nil {
|
||||||
|
caches.Loger.Debug(`下载文件: %v`, err)
|
||||||
|
return ``
|
||||||
|
}*/
|
||||||
|
name := q.Quality + `.` + q.Extname
|
||||||
|
err = ztool.Net_Request(
|
||||||
|
http.MethodGet, l, nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
return (&cr.UploadTask{
|
||||||
|
Site: c.Site,
|
||||||
|
File: res.Body,
|
||||||
|
Size: uint64(res.ContentLength),
|
||||||
|
Name: name,
|
||||||
|
Mime: `audio/mpeg`,
|
||||||
|
}).Do(dir)
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
caches.Loger.Debug(`上传文件: %v`, err)
|
||||||
|
return ``
|
||||||
|
}
|
||||||
|
return c.Get(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Stat() bool {
|
||||||
|
return c.state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Init() error {
|
||||||
|
cr.Cr_Debug = env.Config.Main.Debug
|
||||||
|
err := c.Site.SdkInit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.Site.Users.Cookie == nil || c.Site.Config.User.Anonymous {
|
||||||
|
err = c.Site.SdkLogin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.state = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
25
src/env/env.go
vendored
25
src/env/env.go
vendored
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = `1.0.3.0430`
|
Version = `1.0.3.0518`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -38,8 +38,8 @@ type (
|
|||||||
// FFConv bool `comment:"(实验性) 使用FFMpeg修复音频(本地缓存)"`
|
// FFConv bool `comment:"(实验性) 使用FFMpeg修复音频(本地缓存)"`
|
||||||
NgProxy bool `comment:"兼容反向代理(beta)"`
|
NgProxy bool `comment:"兼容反向代理(beta)"`
|
||||||
Timeout int64 `comment:"网络请求超时(单位:秒,海外服务器可适当调大)"`
|
Timeout int64 `comment:"网络请求超时(单位:秒,海外服务器可适当调大)"`
|
||||||
|
|
||||||
Store string `comment:"内存缓存持久化文件地址"`
|
Store string `comment:"内存缓存持久化文件地址"`
|
||||||
|
ErrMp3 string `comment:"获取失败默认音频"`
|
||||||
}
|
}
|
||||||
// 接口
|
// 接口
|
||||||
Conf_Apis struct {
|
Conf_Apis struct {
|
||||||
@ -174,11 +174,11 @@ type (
|
|||||||
Local_Bind string `comment:"本地缓存外部访问地址"`
|
Local_Bind string `comment:"本地缓存外部访问地址"`
|
||||||
Local_Auto bool `comment:"自适应缓存访问地址(beta)"`
|
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 {
|
||||||
@ -204,6 +204,7 @@ var (
|
|||||||
SysLev: false,
|
SysLev: false,
|
||||||
Timeout: 30,
|
Timeout: 30,
|
||||||
Store: `/data/memo.bin`,
|
Store: `/data/memo.bin`,
|
||||||
|
ErrMp3: `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`,
|
||||||
},
|
},
|
||||||
Apis: Conf_Apis{
|
Apis: Conf_Apis{
|
||||||
// BindAddr: `http://192.168.10.22:1011/`,
|
// BindAddr: `http://192.168.10.22:1011/`,
|
||||||
@ -276,11 +277,11 @@ var (
|
|||||||
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
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
package resp
|
package resp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"lx-source/src/env"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -26,7 +27,7 @@ type Resp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取失败默认音频
|
// 获取失败默认音频
|
||||||
var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
|
// var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
|
||||||
|
|
||||||
// 返回请求
|
// 返回请求
|
||||||
/*
|
/*
|
||||||
@ -43,7 +44,7 @@ func (o *Resp) Execute(c *gin.Context) {
|
|||||||
case 2:
|
case 2:
|
||||||
status = http.StatusServiceUnavailable
|
status = http.StatusServiceUnavailable
|
||||||
if o.Data == nil || o.Data == `` {
|
if o.Data == nil || o.Data == `` {
|
||||||
o.Data = ErrMp3
|
o.Data = env.Config.Main.ErrMp3 //ErrMp3
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
status = http.StatusUnauthorized
|
status = http.StatusUnauthorized
|
||||||
|
@ -103,6 +103,7 @@ func musicHandler(c *gin.Context) {
|
|||||||
cstat = caches.UseCache.Stat()
|
cstat = caches.UseCache.Stat()
|
||||||
}
|
}
|
||||||
uquery := caches.NewQuery(ps, pid, pq)
|
uquery := caches.NewQuery(ps, pid, pq)
|
||||||
|
uquery.Request = c.Request
|
||||||
defer uquery.Free()
|
defer uquery.Free()
|
||||||
if cstat {
|
if cstat {
|
||||||
loger.Debug(`FileGet: %v`, uquery.Query())
|
loger.Debug(`FileGet: %v`, uquery.Query())
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
const api = './';
|
const api = './';
|
||||||
function l(s, id, q) {
|
function l(s, id, q) {
|
||||||
let url = `${api}link/${s}/${id[Math.floor(Math.random() * id.length)]}/${q}`;
|
let url = `${api}link/${s}/${id[Math.floor(Math.random() * id.length)]}/${q}`;
|
||||||
const key = localStorage.getItem('apipass'); if (key) url += `?key=${key}`;
|
const key = localStorage.getItem('apipass'); if (key) url += `?key=${encodeURIComponent(key)}`;
|
||||||
fetch(url)
|
fetch(url)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -110,12 +110,12 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
|||||||
target_url := ztool.Str_FastConcat(
|
target_url := ztool.Str_FastConcat(
|
||||||
`https://mobi.kuwo.cn/mobi.s?f=kuwo&q=`,
|
`https://mobi.kuwo.cn/mobi.s?f=kuwo&q=`,
|
||||||
base64_encrypt(ztool.Str_FastConcat(
|
base64_encrypt(ztool.Str_FastConcat(
|
||||||
`user=0&android_id=0&prod=kwplayer_ar_8.5.5.0&corp=kuwo&newver=3&vipver=8.5.5.0&source=kwplayer_ar_8.5.5.0_apk_keluze.apk&p2p=1¬race=0`,
|
`corp=kuwo&source=kwplayer_ar_1.1.9_oppo_118980_320.apk&p2p=1&sig=0`,
|
||||||
`&type=`, convtype,
|
`&type=`, convtype,
|
||||||
`&br=`, infoFile.H, infoFile.E,
|
`&br=`, infoFile.H, infoFile.E,
|
||||||
`&format=`, infoFile.E,
|
`&format=`, infoFile.E,
|
||||||
`&rid=`, songMid,
|
`&rid=`, songMid,
|
||||||
`&priority=bitrate&loginUid=0&network=WIFI&loginSid=0&mode=down`,
|
`¬race=0&priority=bitrate&network=WIFI&mode=down`,
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
if parsemod {
|
if parsemod {
|
||||||
@ -138,7 +138,7 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
realQuality := strconv.Itoa(resp.Data.Bitrate)
|
realQuality := strconv.Itoa(resp.Data.Bitrate)
|
||||||
if realQuality != infoFile.H[:len(infoFile.H)-1] {
|
if realQuality != infoFile.H[:len(infoFile.H)-1] /*&& resp.Data.Bitrate != 1*/ {
|
||||||
msg = sources.E_QNotMatch
|
msg = sources.E_QNotMatch
|
||||||
if !env.Config.Source.ForceFallback {
|
if !env.Config.Source.ForceFallback {
|
||||||
return
|
return
|
||||||
|
355
src/sources/custom/tx/login.go
Normal file
355
src/sources/custom/tx/login.go
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
package tx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"lx-source/src/env"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/ZxwyWebSite/ztool"
|
||||||
|
"github.com/ZxwyWebSite/ztool/logs"
|
||||||
|
"github.com/ZxwyWebSite/ztool/x/json"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname request github.com/ZxwyWebSite/ztool.request
|
||||||
|
func request(client *http.Client, method, url string, body io.Reader, reqh []ztool.Net_ReqHandlerFunc, resh []ztool.Net_ResHandlerFunc) error
|
||||||
|
|
||||||
|
// QQ快速登录 - 直接使用本机已登录账号
|
||||||
|
func Qlogin_graph(l *logs.Logger) error {
|
||||||
|
// 参考文章: https://learnku.com/articles/33970
|
||||||
|
|
||||||
|
jar, _ := cookiejar.New(nil)
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: time.Second * 10,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
Jar: jar,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step0 - 获取本机QQ服务地址
|
||||||
|
// (应该没有人会占用4301端口导致qq切换备用端口吧,先不写了...懒)
|
||||||
|
err := request(
|
||||||
|
client, http.MethodGet, `https://localhost.ptlogin2.qq.com:4301/pc_querystatus`, nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
_, err := io.Copy(io.Discard, res.Body)
|
||||||
|
return err
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(`step0: 无法连接本机QQ服务`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step1 - 获取 pt_local_token
|
||||||
|
var pt_local_token string
|
||||||
|
err = request(
|
||||||
|
client, http.MethodGet,
|
||||||
|
`https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=716027609&daid=383&style=33&login_text=%E7%99%BB%E5%BD%95&hide_title_bar=1&hide_border=1&target=self&s_url=https://graph.qq.com/oauth2.0/login_jump&pt_3rd_aid=100497308&pt_feedback_link=https://support.qq.com/products/77942?customInfo=.appid100497308&theme=2&verify_theme=`,
|
||||||
|
nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
|
||||||
|
`Referer`: `https://graph.qq.com/`,
|
||||||
|
})},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
for _, v := range res.Cookies() {
|
||||||
|
if v.Name == `pt_local_token` {
|
||||||
|
pt_local_token = v.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := io.Copy(io.Discard, res.Body)
|
||||||
|
return err
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l.Info(`pt_local_token: %v`, pt_local_token)
|
||||||
|
|
||||||
|
// Step2 - 获取本机登录的 QQ 号
|
||||||
|
var url2 strings.Builder
|
||||||
|
url2.WriteString(`https://localhost.ptlogin2.qq.com:4301/pt_get_uins?callback=ptui_getuins_CB&r=`)
|
||||||
|
url2.WriteString(strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
|
||||||
|
url2.WriteString(`&pt_local_tk=`)
|
||||||
|
url2.WriteString(pt_local_token)
|
||||||
|
var out2 []struct {
|
||||||
|
Uin int `json:"uin"`
|
||||||
|
// FaceIndex int `json:"face_index"`
|
||||||
|
// Gender int `json:"gender"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
// ClientType int `json:"client_type"`
|
||||||
|
// UinFlag int `json:"uin_flag"`
|
||||||
|
// Account int `json:"account"`
|
||||||
|
}
|
||||||
|
var header2 = map[string]string{
|
||||||
|
`Referer`: `https://xui.ptlogin2.qq.com/`,
|
||||||
|
// `Cookie`: `pt_local_token=` + pt_local_token,
|
||||||
|
}
|
||||||
|
err = request(
|
||||||
|
client, http.MethodGet, url2.String(), nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(header2)},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
data, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sep_b := bytes.IndexByte(data, '[')
|
||||||
|
sep_e := bytes.LastIndexByte(data, ']')
|
||||||
|
if sep_b == -1 || sep_e == -1 {
|
||||||
|
return errors.New(`step2: 无法解析返回数据`)
|
||||||
|
}
|
||||||
|
sep := data[sep_b : sep_e+1]
|
||||||
|
return json.Unmarshal(sep, &out2)
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var uin string
|
||||||
|
length := len(out2)
|
||||||
|
switch length {
|
||||||
|
case 0:
|
||||||
|
return errors.New(`step2: 无可用账号`)
|
||||||
|
case 1:
|
||||||
|
uin = strconv.Itoa(out2[0].Uin)
|
||||||
|
default:
|
||||||
|
fmt.Println(`请选择要登录的账号:`)
|
||||||
|
for i, v := range out2 {
|
||||||
|
fmt.Println(i, v.Nickname, v.Uin)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
fmt.Print(`输入序号: `)
|
||||||
|
var input string
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
i, err := strconv.Atoi(input)
|
||||||
|
if err != nil {
|
||||||
|
l.Error(`err: %v`, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= length {
|
||||||
|
l.Error(`err: 下标越界`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uin = strconv.Itoa(out2[i].Uin)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.Info(`uin: %v`, uin)
|
||||||
|
|
||||||
|
// Step3 - 获取 clientkey
|
||||||
|
var url3 strings.Builder
|
||||||
|
url3.WriteString(`https://localhost.ptlogin2.qq.com:4301/pt_get_st?clientuin=`)
|
||||||
|
url3.WriteString(uin)
|
||||||
|
url3.WriteString(`&r=`)
|
||||||
|
url3.WriteString(strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
|
||||||
|
url3.WriteString(`&pt_local_tk=`)
|
||||||
|
url3.WriteString(pt_local_token)
|
||||||
|
url3.WriteString(`&callback=__jp0`)
|
||||||
|
// var clientkey string
|
||||||
|
err = request(
|
||||||
|
client, http.MethodGet, url3.String(), nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(header2)},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
/*for _, v := range res.Cookies() {
|
||||||
|
if v.Name == `clientkey` {
|
||||||
|
clientkey = v.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
_, err := io.Copy(io.Discard, res.Body)
|
||||||
|
return err
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step4 - 获取 skey
|
||||||
|
var url4 strings.Builder
|
||||||
|
url4.WriteString(`https://ssl.ptlogin2.qq.com/jump?clientuin=`)
|
||||||
|
url4.WriteString(uin)
|
||||||
|
url4.WriteString(`&keyindex=9&pt_aid=716027609&daid=383&u1=https://graph.qq.com/oauth2.0/login_jump&pt_local_tk=`)
|
||||||
|
url4.WriteString(pt_local_token)
|
||||||
|
url4.WriteString(`&pt_3rd_aid=100497308&ptopt=1&style=40`)
|
||||||
|
/*var cookie4 strings.Builder
|
||||||
|
cookie4.WriteString(`pt_local_token=`)
|
||||||
|
cookie4.WriteString(pt_local_token)
|
||||||
|
cookie4.WriteByte(';')
|
||||||
|
cookie4.WriteString(`clientuin=`)
|
||||||
|
cookie4.WriteString(uin)
|
||||||
|
cookie4.WriteByte(';')
|
||||||
|
cookie4.WriteString(`clientkey=`)
|
||||||
|
cookie4.WriteString(clientkey)*/
|
||||||
|
var jurl string
|
||||||
|
err = request(
|
||||||
|
client, http.MethodGet, url4.String(), nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders( /*map[string]string{
|
||||||
|
`Referer`: `https://xui.ptlogin2.qq.com/`,
|
||||||
|
`Cookie`: cookie4.String(),
|
||||||
|
}*/header2)},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
data, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sep_b := bytes.IndexByte(data, ',')
|
||||||
|
sep_e := bytes.LastIndexByte(data, ' ')
|
||||||
|
if sep_b == -1 || sep_e == -1 {
|
||||||
|
return errors.New(`step4: 无法解析返回数据`)
|
||||||
|
}
|
||||||
|
jurl = string(data[sep_b+3 : sep_e-2])
|
||||||
|
return nil
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step5 - 获取 p_skey
|
||||||
|
var p_skey string
|
||||||
|
err = request(
|
||||||
|
client, http.MethodGet, jurl, nil,
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(header2)},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
for _, v := range res.Cookies() {
|
||||||
|
if v.Name == `p_skey` && v.Value != `` {
|
||||||
|
p_skey = v.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := io.Copy(io.Discard, res.Body)
|
||||||
|
return err
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step6 - 登录账号
|
||||||
|
getGtk := func(skey string) string {
|
||||||
|
var hash = 5381
|
||||||
|
for _, v := range skey {
|
||||||
|
hash += (hash << 5) + int(v)
|
||||||
|
}
|
||||||
|
return strconv.Itoa(hash & 0x7fffffff)
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
var authcode string
|
||||||
|
err = request(
|
||||||
|
client, http.MethodPost,
|
||||||
|
`https://graph.qq.com/oauth2.0/authorize`,
|
||||||
|
strings.NewReader(ztool.Str_FastConcat(
|
||||||
|
`response_type=code&client_id=100497308&redirect_uri=https%3A%2F%2Fy.qq.com%2Fportal%2Fwx_redirect.html%3Flogin_type%3D1%26surl%3Dhttps%3A%2F%2Fy.qq.com%2F&scope=get_user_info%2Cget_app_friends&state=state&switch=&from_ptlogin=1&src=1&update_auth=1&openapi=1010_1030`,
|
||||||
|
`&g_tk=`, getGtk(p_skey),
|
||||||
|
`&auth_time=`, strconv.FormatInt(now.UnixMilli(), 10),
|
||||||
|
`&ui=`, strings.ToUpper(uuid.NewString()),
|
||||||
|
)),
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
|
||||||
|
`Referer`: `https://graph.qq.com/oauth2.0/show?which=Login&display=pc&response_type=code&client_id=100497308&redirect_uri=https%3A%2F%2Fy.qq.com%2Fportal%2Fwx_redirect.html%3Flogin_type%3D1%26surl%3Dhttps%3A%2F%2Fy.qq.com%2F&state=state&display=pc&scope=get_user_info%2Cget_app_friends`,
|
||||||
|
`Content-Type`: `application/x-www-form-urlencoded`,
|
||||||
|
})},
|
||||||
|
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||||
|
/*if res.StatusCode != 302 {
|
||||||
|
return errors.New(`step6: not redirect`)
|
||||||
|
}*/
|
||||||
|
location := res.Header[`Location`][0]
|
||||||
|
l.Info(`loc: %v`, location)
|
||||||
|
loc, err := url.Parse(location)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
authcode = loc.Query()[`code`][0]
|
||||||
|
return nil
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l.Info(`authcode: %v`, authcode)
|
||||||
|
var out6 struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
// Ts int64 `json:"ts"`
|
||||||
|
// StartTs int64 `json:"start_ts"`
|
||||||
|
// Traceid string `json:"traceid"`
|
||||||
|
Req struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Data struct {
|
||||||
|
// Openid string `json:"openid"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
ExpiredAt int `json:"expired_at"`
|
||||||
|
// Musicid int `json:"musicid"`
|
||||||
|
Musickey string `json:"musickey"`
|
||||||
|
// MusickeyCreateTime int `json:"musickeyCreateTime"`
|
||||||
|
// FirstLogin int `json:"first_login"`
|
||||||
|
// ErrMsg string `json:"errMsg"`
|
||||||
|
// SessionKey string `json:"sessionKey"`
|
||||||
|
// Unionid string `json:"unionid"`
|
||||||
|
StrMusicid string `json:"str_musicid"`
|
||||||
|
// Errtip string `json:"errtip"`
|
||||||
|
// Nick string `json:"nick"`
|
||||||
|
// Logo string `json:"logo"`
|
||||||
|
// FeedbackURL string `json:"feedbackURL"`
|
||||||
|
// EncryptUin string `json:"encryptUin"`
|
||||||
|
// Userip string `json:"userip"`
|
||||||
|
// LastLoginTime int `json:"lastLoginTime"`
|
||||||
|
// KeyExpiresIn int `json:"keyExpiresIn"`
|
||||||
|
// RefreshKey string `json:"refresh_key"`
|
||||||
|
// LoginType int `json:"loginType"`
|
||||||
|
// Prompt2Bind int `json:"prompt2bind"`
|
||||||
|
// LogoffStatus int `json:"logoffStatus"`
|
||||||
|
// OtherAccounts []interface{} `json:"otherAccounts"`
|
||||||
|
// OtherPhoneNo string `json:"otherPhoneNo"`
|
||||||
|
// Token string `json:"token"`
|
||||||
|
// IsPrized int `json:"isPrized"`
|
||||||
|
// IsShowDevManage int `json:"isShowDevManage"`
|
||||||
|
// ErrTip2 string `json:"errTip2"`
|
||||||
|
// Tip3 string `json:"tip3"`
|
||||||
|
// EncryptedPhoneNo string `json:"encryptedPhoneNo"`
|
||||||
|
// PhoneNo string `json:"phoneNo"`
|
||||||
|
// BindAccountType int `json:"bindAccountType"`
|
||||||
|
// NeedRefreshKeyIn int `json:"needRefreshKeyIn"`
|
||||||
|
} `json:"data"`
|
||||||
|
} `json:"req"`
|
||||||
|
}
|
||||||
|
err = request(
|
||||||
|
client, http.MethodPost,
|
||||||
|
`https://u.y.qq.com/cgi-bin/musicu.fcg`,
|
||||||
|
strings.NewReader(ztool.Str_FastConcat(
|
||||||
|
`{"comm":{"g_tk":5381,"platform":"yqq","ct":24,"cv":0},"req":{"module":"QQConnectLogin.LoginServer","method":"QQLogin","param":{"code":"`, authcode, `"}}}`,
|
||||||
|
)),
|
||||||
|
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders(map[string]string{
|
||||||
|
`Referer`: `https://y.qq.com/`,
|
||||||
|
`Content-Type`: `application/x-www-form-urlencoded`,
|
||||||
|
})},
|
||||||
|
[]ztool.Net_ResHandlerFunc{ztool.Net_ResToStruct(&out6)},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l.Info(`res: %+v`, out6)
|
||||||
|
l.Info(`登录成功`)
|
||||||
|
|
||||||
|
env.Config.Custom.Tx_Enable = true
|
||||||
|
env.Config.Custom.Tx_Uuin = out6.Req.Data.StrMusicid
|
||||||
|
env.Config.Custom.Tx_Ukey = out6.Req.Data.Musickey
|
||||||
|
env.Config.Custom.Tx_Refresh_Enable = true
|
||||||
|
env.Config.Custom.Tx_Refresh_Interval = time.Date(now.Year(), now.Month(), now.Day()+5, 0, 0, 0, 0, now.Location()).Unix()
|
||||||
|
// env.Config.Custom.Tx_RefreshToken = out6.Req.Data.RefreshToken
|
||||||
|
// env.Config.Custom.Tx_AccessToken = out6.Req.Data.AccessToken
|
||||||
|
|
||||||
|
return env.Cfg.Save(``)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QQ扫码登录(todo)
|
||||||
|
// func qlogin_qr_()
|
@ -103,6 +103,7 @@ Loop:
|
|||||||
if quality != sources.Q_128k && infoBody.TrackInfo.Pay.PayPlay == 0 {
|
if quality != sources.Q_128k && infoBody.TrackInfo.Pay.PayPlay == 0 {
|
||||||
msg = `Fallback to 128k`
|
msg = `Fallback to 128k`
|
||||||
infoFile = fileInfo[sources.Q_128k]
|
infoFile = fileInfo[sources.Q_128k]
|
||||||
|
quality = sources.Q_128k
|
||||||
} else {
|
} else {
|
||||||
msg = `Fallbacked`
|
msg = `Fallbacked`
|
||||||
tryLink = true
|
tryLink = true
|
||||||
|
15
update.md
15
update.md
@ -3,6 +3,21 @@
|
|||||||
<!-- #### \# 2024-02-14 v1.0.3-rel (release)
|
<!-- #### \# 2024-02-14 v1.0.3-rel (release)
|
||||||
+ **停止更新:感谢这三个月的陪伴,现因无力维护,停止后续更新,发布最后版本,大家有缘再见** -->
|
+ **停止更新:感谢这三个月的陪伴,现因无力维护,停止后续更新,发布最后版本,大家有缘再见** -->
|
||||||
|
|
||||||
|
#### \# 2024-05-18 v1.0.3.0518 (dev)
|
||||||
|
+ (据用户反馈,当前账号添加方式过于复杂,很多参数不知道怎么填,故增加部分平台简化登录方式)
|
||||||
|
+ Tx源支持QQ快速登录(beta), 启动参数 `-e txqq`
|
||||||
|
+ 支持自定义错误音频地址(不填禁用), 位置 [Main].ErrMp3
|
||||||
|
+ ~~修复若干已知Bug~~
|
||||||
|
|
||||||
|
<!-- #### \# 2024-05-12 v1.0.3.0503 (dev)
|
||||||
|
+ 支持对接Python版ApiServer(二次分销?)
|
||||||
|
+ 同时开发了一套数据共享接口
|
||||||
|
(太过逆天,先不发了,容易导致滥用问题) -->
|
||||||
|
|
||||||
|
#### \# 2024-05-03 v1.0.3.0503 (dev)
|
||||||
|
+ 基于 cr-go-sdk 重新支持 Cloudreve 缓存
|
||||||
|
+ 解决Tx源一处Fallback死循环问题
|
||||||
|
|
||||||
#### \# 2024-04-30 v1.0.3.0430 (beta)
|
#### \# 2024-04-30 v1.0.3.0430 (beta)
|
||||||
+ Tx源支持自定义CDN链接地址
|
+ Tx源支持自定义CDN链接地址
|
||||||
+ Wy源支持扫码登录(beta), 启动参数 `-e wyqr`
|
+ Wy源支持扫码登录(beta), 启动参数 `-e wyqr`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user