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
|
||||
|
||||
require (
|
||||
github.com/ZxwyWebSite/cr-go-sdk v0.0.2
|
||||
github.com/ZxwyWebSite/ztool v0.0.1
|
||||
github.com/gin-contrib/gzip v1.0.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
)
|
||||
|
||||
@ -43,4 +45,7 @@ require (
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace github.com/ZxwyWebSite/ztool v0.0.1 => ./pkg/ztool // ../ztool
|
||||
replace (
|
||||
github.com/ZxwyWebSite/cr-go-sdk v0.0.2 => ../cr-go-sdk
|
||||
github.com/ZxwyWebSite/ztool v0.0.1 => ./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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
|
43
init.go
43
init.go
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"encoding/base64"
|
||||
"lx-source/src/caches"
|
||||
"lx-source/src/caches/cloudcache"
|
||||
"lx-source/src/caches/localcache"
|
||||
"lx-source/src/env"
|
||||
|
||||
@ -12,6 +13,7 @@ import (
|
||||
stdurl "net/url"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ZxwyWebSite/cr-go-sdk"
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
"github.com/ZxwyWebSite/ztool/logs"
|
||||
"github.com/ZxwyWebSite/ztool/zcypt"
|
||||
@ -169,22 +171,31 @@ func initMain() {
|
||||
// }
|
||||
// icl.Info(`使用本地缓存,文件路径 %q,绑定地址 %v`, LocalCachePath, env.Config.Apis.BindAddr)
|
||||
case `2`, `cloudreve`:
|
||||
icl.Fatal(`Cloudreve驱动暂未完善,未兼容新版调用方式,当前版本禁用`)
|
||||
// icl.Warn(`Cloudreve驱动暂未完善,使用非本机存储时存在兼容性问题,请谨慎使用`)
|
||||
// cs, err := cloudreve.NewSite(&cloudreve.Config{
|
||||
// SiteUrl: env.Config.Cache.Cloud_Site,
|
||||
// Username: env.Config.Cache.Cloud_User,
|
||||
// Password: env.Config.Cache.Cloud_Pass,
|
||||
// Session: env.Config.Cache.Cloud_Sess,
|
||||
// })
|
||||
// if err != nil {
|
||||
// icl.Error(`驱动["cloudreve"]初始化失败: %v, 将禁用缓存功能`, err)
|
||||
// }
|
||||
// UseCache = &crcache.Cache{
|
||||
// Cs: cs,
|
||||
// Path: env.Config.Cache.Cloud_Path,
|
||||
// IsOk: err == nil,
|
||||
// }
|
||||
icl.Warn(`欢迎使用新版 Cloudreve 驱动, 由 cr-go-sdk 提供强力支持`)
|
||||
site := &cr.SiteObj{
|
||||
Addr: env.Config.Cache.Cloud_Site,
|
||||
ApiVer: cr.ApiV383,
|
||||
Users: &cr.UserObj{
|
||||
Mail: env.Config.Cache.Cloud_User,
|
||||
Pass: env.Config.Cache.Cloud_Pass,
|
||||
Cookie: cr.ParseCookie(env.Config.Cache.Cloud_Sess),
|
||||
},
|
||||
}
|
||||
cache, err := caches.New(&cloudcache.Cache{
|
||||
Site: site,
|
||||
Path: env.Config.Cache.Cloud_Path,
|
||||
})
|
||||
if err != nil {
|
||||
icl.Error(`驱动["cloudreve"]初始化失败: %v, 将禁用缓存功能`, err)
|
||||
} else {
|
||||
env.Tasker.Add(`cloud_sess`, func(l *logs.Logger, i int64) error {
|
||||
if sess := site.Users.Cookie.String(); sess != env.Config.Cache.Cloud_Sess {
|
||||
env.Config.Cache.Cloud_Sess = sess
|
||||
}
|
||||
return env.Cfg.Save(``)
|
||||
}, 3600, true)
|
||||
}
|
||||
caches.UseCache = cache
|
||||
default:
|
||||
icl.Error(`未定义的缓存模式,请检查配置 [Cache].Mode,本次启动禁用缓存`)
|
||||
}
|
||||
|
42
menu.go
42
menu.go
@ -1,8 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"lx-source/src/env"
|
||||
"lx-source/src/sources/custom/tx"
|
||||
wm "lx-source/src/sources/custom/wy/modules"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -22,6 +25,8 @@ func parseEtag(etag *string) {
|
||||
// menuMian()
|
||||
case `wyqr`:
|
||||
wyQrLogin()
|
||||
case `txqq`:
|
||||
txQqLogin()
|
||||
default:
|
||||
loger.Fatal(`未知参数:%q`, *etag)
|
||||
}
|
||||
@ -33,6 +38,17 @@ func wyQrLogin() {
|
||||
loger := env.Loger.NewGroup(`WyQrLogin`)
|
||||
defer loger.Free()
|
||||
loger.Info(`执行模块: 网易云扫码登录`)
|
||||
|
||||
if env.Config.Custom.Wy_Api_Cookie != `` {
|
||||
loger.Warn("已存在账号数据, 继续操作可能导致数据覆盖丢失!")
|
||||
fmt.Print(`输入'y'继续: `)
|
||||
var input string
|
||||
fmt.Scanln(&input)
|
||||
if input != `y` {
|
||||
loger.Fatal(`用户取消操作`)
|
||||
}
|
||||
}
|
||||
|
||||
res, err := wm.LoginQrKey()
|
||||
if err != nil {
|
||||
loger.Fatal(`无法创建请求: %s`, err)
|
||||
@ -80,6 +96,32 @@ func wyQrLogin() {
|
||||
}
|
||||
}
|
||||
|
||||
// QQ快速登录
|
||||
func txQqLogin() {
|
||||
loger := env.Loger.NewGroup(`TxQqLogin`)
|
||||
defer loger.Free()
|
||||
loger.Info(`执行模块: QQ快速登录`)
|
||||
|
||||
if runtime.GOOS != `windows` {
|
||||
loger.Fatal(`该模块仅支持在windows环境下使用`)
|
||||
return
|
||||
}
|
||||
|
||||
if env.Config.Custom.Tx_Ukey != `` {
|
||||
loger.Warn("已存在账号数据, 继续操作可能导致数据覆盖丢失!")
|
||||
fmt.Print(`输入'y'继续: `)
|
||||
var input string
|
||||
fmt.Scanln(&input)
|
||||
if input != `y` {
|
||||
loger.Fatal(`用户取消操作`)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Qlogin_graph(loger); err != nil {
|
||||
loger.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// func menuMian() {
|
||||
// app := menu.NewApp(`Lx-Source`)
|
||||
// app.Data = menu.Data{
|
||||
|
@ -1 +1,121 @@
|
||||
package cloudcache
|
||||
|
||||
import (
|
||||
"lx-source/src/caches"
|
||||
"lx-source/src/env"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
cr "github.com/ZxwyWebSite/cr-go-sdk"
|
||||
"github.com/ZxwyWebSite/cr-go-sdk/service/explorer"
|
||||
"github.com/ZxwyWebSite/ztool"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
Site *cr.SiteObj
|
||||
Path string
|
||||
state bool
|
||||
}
|
||||
|
||||
func (c *Cache) Get(q *caches.Query) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(c.Path)
|
||||
b.WriteByte('/')
|
||||
b.WriteString(q.Source)
|
||||
b.WriteByte('/')
|
||||
b.WriteString(q.MusicID)
|
||||
list, err := c.Site.Directory(b.String())
|
||||
if err != nil {
|
||||
caches.Loger.Debug(`列出目录: %v`, err)
|
||||
return ``
|
||||
}
|
||||
name := q.Quality + `.` + q.Extname
|
||||
var id string
|
||||
for _, v := range list.Objects {
|
||||
if v.Name == name && v.Type == `file` {
|
||||
id = v.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
if id == `` {
|
||||
caches.Loger.Debug(`文件不存在`)
|
||||
return ``
|
||||
}
|
||||
srcs, err := c.Site.FileSource(cr.GenerateSrc(false, id))
|
||||
if err != nil {
|
||||
caches.Loger.Debug(`生成外链: %v`, err)
|
||||
return ``
|
||||
}
|
||||
return (*srcs)[0].URL
|
||||
/*link, err := c.Site.FileDownload(id)
|
||||
if err != nil {
|
||||
caches.Loger.Debug(`下载文件: %v`, err)
|
||||
return ``
|
||||
}
|
||||
if (*link)[0] == '/' {
|
||||
return c.Site.Addr + (*link)[1:]
|
||||
}
|
||||
return *link*/
|
||||
}
|
||||
|
||||
func (c *Cache) Set(q *caches.Query, l string) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(c.Path)
|
||||
b.WriteByte('/')
|
||||
b.WriteString(q.Source)
|
||||
b.WriteByte('/')
|
||||
b.WriteString(q.MusicID)
|
||||
dir := b.String()
|
||||
err := c.Site.DirectoryNew(&explorer.DirectoryService{
|
||||
Path: dir,
|
||||
})
|
||||
if err != nil {
|
||||
caches.Loger.Debug(`创建目录: %v`, err)
|
||||
return ``
|
||||
}
|
||||
/*var buf bytes.Buffer
|
||||
err = ztool.Net_Download(l, &buf, nil)
|
||||
if err != nil {
|
||||
caches.Loger.Debug(`下载文件: %v`, err)
|
||||
return ``
|
||||
}*/
|
||||
name := q.Quality + `.` + q.Extname
|
||||
err = ztool.Net_Request(
|
||||
http.MethodGet, l, nil,
|
||||
[]ztool.Net_ReqHandlerFunc{ztool.Net_ReqAddHeaders()},
|
||||
[]ztool.Net_ResHandlerFunc{func(res *http.Response) error {
|
||||
return (&cr.UploadTask{
|
||||
Site: c.Site,
|
||||
File: res.Body,
|
||||
Size: uint64(res.ContentLength),
|
||||
Name: name,
|
||||
Mime: `audio/mpeg`,
|
||||
}).Do(dir)
|
||||
}},
|
||||
)
|
||||
if err != nil {
|
||||
caches.Loger.Debug(`上传文件: %v`, err)
|
||||
return ``
|
||||
}
|
||||
return c.Get(q)
|
||||
}
|
||||
|
||||
func (c *Cache) Stat() bool {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *Cache) Init() error {
|
||||
cr.Cr_Debug = env.Config.Main.Debug
|
||||
err := c.Site.SdkInit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Site.Users.Cookie == nil || c.Site.Config.User.Anonymous {
|
||||
err = c.Site.SdkLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.state = true
|
||||
return nil
|
||||
}
|
||||
|
31
src/env/env.go
vendored
31
src/env/env.go
vendored
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Version = `1.0.3.0430`
|
||||
Version = `1.0.3.0518`
|
||||
)
|
||||
|
||||
var (
|
||||
@ -36,10 +36,10 @@ type (
|
||||
Print bool `comment:"控制台输出 (影响io性能,后台使用建议关闭)"`
|
||||
SysLev bool `comment:"(实验性) 设置进程高优先级"`
|
||||
// FFConv bool `comment:"(实验性) 使用FFMpeg修复音频(本地缓存)"`
|
||||
NgProxy bool `comment:"兼容反向代理(beta)"`
|
||||
Timeout int64 `comment:"网络请求超时(单位:秒,海外服务器可适当调大)"`
|
||||
|
||||
Store string `comment:"内存缓存持久化文件地址"`
|
||||
NgProxy bool `comment:"兼容反向代理(beta)"`
|
||||
Timeout int64 `comment:"网络请求超时(单位:秒,海外服务器可适当调大)"`
|
||||
Store string `comment:"内存缓存持久化文件地址"`
|
||||
ErrMp3 string `comment:"获取失败默认音频"`
|
||||
}
|
||||
// 接口
|
||||
Conf_Apis struct {
|
||||
@ -174,11 +174,11 @@ type (
|
||||
Local_Bind string `comment:"本地缓存外部访问地址"`
|
||||
Local_Auto bool `comment:"自适应缓存访问地址(beta)"`
|
||||
// 云盘
|
||||
// Cloud_Site string `comment:"Cloudreve站点地址"`
|
||||
// Cloud_User string `comment:"Cloudreve用户名"`
|
||||
// Cloud_Pass string `comment:"Cloudreve密码"`
|
||||
// Cloud_Sess string `comment:"Cloudreve会话"`
|
||||
// Cloud_Path string `comment:"Cloudreve存储路径"`
|
||||
Cloud_Site string `comment:"Cloudreve站点地址"`
|
||||
Cloud_User string `comment:"Cloudreve用户名"`
|
||||
Cloud_Pass string `comment:"Cloudreve密码"`
|
||||
Cloud_Sess string `comment:"Cloudreve会话"`
|
||||
Cloud_Path string `comment:"Cloudreve存储路径"`
|
||||
}
|
||||
// 结构
|
||||
Conf struct {
|
||||
@ -204,6 +204,7 @@ var (
|
||||
SysLev: false,
|
||||
Timeout: 30,
|
||||
Store: `/data/memo.bin`,
|
||||
ErrMp3: `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`,
|
||||
},
|
||||
Apis: Conf_Apis{
|
||||
// BindAddr: `http://192.168.10.22:1011/`,
|
||||
@ -276,11 +277,11 @@ var (
|
||||
LinkMode: `1`,
|
||||
Local_Path: `data/cache`,
|
||||
Local_Bind: `http://127.0.0.1:1011/`,
|
||||
// Cloud_Site: `https://cloudreveplus-demo.onrender.com/`,
|
||||
// Cloud_User: `admin@cloudreve.org`,
|
||||
// Cloud_Pass: `CloudrevePlusDemo`,
|
||||
// Cloud_Sess: ``,
|
||||
// Cloud_Path: `/Lx-Source/cache`,
|
||||
Cloud_Site: `https://cloudreveplus-demo.onrender.com/`,
|
||||
Cloud_User: `admin@cloudreve.org`,
|
||||
Cloud_Pass: `CloudrevePlusDemo`,
|
||||
Cloud_Sess: ``,
|
||||
Cloud_Path: `/Lx-Source/cache`,
|
||||
},
|
||||
}
|
||||
Config = DefCfg
|
||||
|
@ -2,6 +2,7 @@
|
||||
package resp
|
||||
|
||||
import (
|
||||
"lx-source/src/env"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -26,7 +27,7 @@ type Resp struct {
|
||||
}
|
||||
|
||||
// 获取失败默认音频
|
||||
var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
|
||||
// var ErrMp3 = `https://r2eu.zxwy.link/gh/lx-source/static/error.mp3`
|
||||
|
||||
// 返回请求
|
||||
/*
|
||||
@ -43,7 +44,7 @@ func (o *Resp) Execute(c *gin.Context) {
|
||||
case 2:
|
||||
status = http.StatusServiceUnavailable
|
||||
if o.Data == nil || o.Data == `` {
|
||||
o.Data = ErrMp3
|
||||
o.Data = env.Config.Main.ErrMp3 //ErrMp3
|
||||
}
|
||||
case 3:
|
||||
status = http.StatusUnauthorized
|
||||
|
@ -103,6 +103,7 @@ func musicHandler(c *gin.Context) {
|
||||
cstat = caches.UseCache.Stat()
|
||||
}
|
||||
uquery := caches.NewQuery(ps, pid, pq)
|
||||
uquery.Request = c.Request
|
||||
defer uquery.Free()
|
||||
if cstat {
|
||||
loger.Debug(`FileGet: %v`, uquery.Query())
|
||||
|
@ -16,7 +16,7 @@
|
||||
const api = './';
|
||||
function l(s, id, q) {
|
||||
let url = `${api}link/${s}/${id[Math.floor(Math.random() * id.length)]}/${q}`;
|
||||
const key = localStorage.getItem('apipass'); if (key) url += `?key=${key}`;
|
||||
const key = localStorage.getItem('apipass'); if (key) url += `?key=${encodeURIComponent(key)}`;
|
||||
fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((res) => {
|
||||
|
@ -110,12 +110,12 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
||||
target_url := ztool.Str_FastConcat(
|
||||
`https://mobi.kuwo.cn/mobi.s?f=kuwo&q=`,
|
||||
base64_encrypt(ztool.Str_FastConcat(
|
||||
`user=0&android_id=0&prod=kwplayer_ar_8.5.5.0&corp=kuwo&newver=3&vipver=8.5.5.0&source=kwplayer_ar_8.5.5.0_apk_keluze.apk&p2p=1¬race=0`,
|
||||
`corp=kuwo&source=kwplayer_ar_1.1.9_oppo_118980_320.apk&p2p=1&sig=0`,
|
||||
`&type=`, convtype,
|
||||
`&br=`, infoFile.H, infoFile.E,
|
||||
`&format=`, infoFile.E,
|
||||
`&rid=`, songMid,
|
||||
`&priority=bitrate&loginUid=0&network=WIFI&loginSid=0&mode=down`,
|
||||
`¬race=0&priority=bitrate&network=WIFI&mode=down`,
|
||||
)),
|
||||
)
|
||||
if parsemod {
|
||||
@ -138,7 +138,7 @@ func kwdes(songMid, quality string) (ourl, msg string) {
|
||||
return
|
||||
}
|
||||
realQuality := strconv.Itoa(resp.Data.Bitrate)
|
||||
if realQuality != infoFile.H[:len(infoFile.H)-1] {
|
||||
if realQuality != infoFile.H[:len(infoFile.H)-1] /*&& resp.Data.Bitrate != 1*/ {
|
||||
msg = sources.E_QNotMatch
|
||||
if !env.Config.Source.ForceFallback {
|
||||
return
|
||||
|
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 {
|
||||
msg = `Fallback to 128k`
|
||||
infoFile = fileInfo[sources.Q_128k]
|
||||
quality = sources.Q_128k
|
||||
} else {
|
||||
msg = `Fallbacked`
|
||||
tryLink = true
|
||||
|
15
update.md
15
update.md
@ -3,6 +3,21 @@
|
||||
<!-- #### \# 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)
|
||||
+ Tx源支持自定义CDN链接地址
|
||||
+ Wy源支持扫码登录(beta), 启动参数 `-e wyqr`
|
||||
|
Loading…
x
Reference in New Issue
Block a user