mirror of
https://github.com/AynaLivePlayer/miaosic.git
synced 2025-12-14 00:38:14 +08:00
init
This commit is contained in:
125
providers/bilibili/bilibili.go
Normal file
125
providers/bilibili/bilibili.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"github.com/aynakeya/deepcolor"
|
||||
"github.com/aynakeya/deepcolor/dphttp"
|
||||
"github.com/tidwall/gjson"
|
||||
"miaosic"
|
||||
"miaosic/providers"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Bilibili struct {
|
||||
providers.DeepcolorProvider
|
||||
requester dphttp.IRequester
|
||||
IdRegex0 *regexp.Regexp
|
||||
IdRegex1 *regexp.Regexp
|
||||
header map[string]string
|
||||
}
|
||||
|
||||
func NewBilibili(requester dphttp.IRequester) *Bilibili {
|
||||
bili := &Bilibili{
|
||||
requester: requester,
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^au[0-9]+"),
|
||||
}
|
||||
bili.initHeader()
|
||||
bili.InfoFunc = bili.buildInfoApi()
|
||||
bili.FileFunc = bili.buildFileApi()
|
||||
bili.SearchFunc = bili.buildSearchApi()
|
||||
return bili
|
||||
}
|
||||
|
||||
func (b *Bilibili) GetName() string {
|
||||
return "bilibili"
|
||||
}
|
||||
|
||||
func (b *Bilibili) MatchMedia(keyword string) *miaosic.Media {
|
||||
if id := b.IdRegex0.FindString(keyword); id != "" {
|
||||
return &miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: b.GetName(),
|
||||
Identifier: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
if id := b.IdRegex1.FindString(keyword); id != "" {
|
||||
return &miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: b.GetName(),
|
||||
Identifier: id[2:],
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bilibili) MatchPlaylist(keyword string) *miaosic.Playlist {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bilibili) initHeader() {
|
||||
b.header = map[string]string{
|
||||
"user-agent": "BiliMusic/2.233.3",
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bilibili) buildInfoApi() dphttp.ApiFunc[*miaosic.Media, *miaosic.Media] {
|
||||
return deepcolor.CreateApiFunc(
|
||||
b.requester,
|
||||
func(media *miaosic.Media) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithSingleQuery("https://www.bilibili.com/audio/music-service-c/web/song/info", "sid", b.header)(media.Meta.Identifier)
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, media *miaosic.Media) error {
|
||||
if resp.Get("data.title").String() == "" {
|
||||
return miaosic.ErrorExternalApi
|
||||
}
|
||||
media.Title = resp.Get("data.title").String()
|
||||
media.Cover.Url = resp.Get("data.cover").String()
|
||||
media.Artist = resp.Get("data.author").String()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Bilibili) buildFileApi() dphttp.ApiFunc[*miaosic.Media, *miaosic.Media] {
|
||||
return deepcolor.CreateApiFunc(
|
||||
b.requester,
|
||||
func(media *miaosic.Media) (*dphttp.Request, error) {
|
||||
// Assuming the endpoint and query are similar for file details
|
||||
return deepcolor.NewGetRequestWithSingleQuery("https://api.bilibili.com/audio/music-service-c/url?device=phone&mid=8047632&mobi_app=iphone&platform=ios&privilege=2&quality=2", "songid", b.header)(media.Meta.Identifier)
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, media *miaosic.Media) error {
|
||||
if resp.Get("data.cdns.0").String() == "" {
|
||||
return miaosic.ErrorExternalApi
|
||||
}
|
||||
media.Url = resp.Get("data.cdns.0").String()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Bilibili) buildSearchApi() dphttp.ApiFuncResult[string, []*miaosic.Media] {
|
||||
return deepcolor.CreateApiResultFunc(
|
||||
b.requester,
|
||||
func(query string) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithSingleQuery("https://api.bilibili.com/audio/music-service-c/s?search_type=music&page=1&pagesize=100", "keyword", b.header)(query)
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, result *[]*miaosic.Media) error {
|
||||
// Assuming data contains a list of search results
|
||||
for _, r := range resp.Get("data.result").Array() {
|
||||
media := &miaosic.Media{
|
||||
Title: r.Get("title").String(),
|
||||
Cover: miaosic.Picture{Url: r.Get("cover").String()},
|
||||
Artist: r.Get("author").String(),
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: b.GetName(),
|
||||
Identifier: r.Get("id").String(),
|
||||
},
|
||||
}
|
||||
*result = append(*result, media)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
28
providers/bilibili/bilibili_test.go
Normal file
28
providers/bilibili/bilibili_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"miaosic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var api miaosic.MediaProvider = NewBilibili(miaosic.Requester)
|
||||
|
||||
func TestBilibili_Search(t *testing.T) {
|
||||
result, err := api.Search("染 reol")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, result)
|
||||
}
|
||||
|
||||
func TestBilibili_GetMusic(t *testing.T) {
|
||||
media := miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: api.GetName(),
|
||||
Identifier: "1560601",
|
||||
},
|
||||
}
|
||||
require.NoError(t, api.UpdateMedia(&media))
|
||||
require.NoError(t, api.UpdateMediaUrl(&media))
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
9
providers/bilibili/init.go
Normal file
9
providers/bilibili/init.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"miaosic"
|
||||
)
|
||||
|
||||
func init() {
|
||||
miaosic.RegisterProvider(NewBilibili(miaosic.Requester))
|
||||
}
|
||||
228
providers/bilivideo/bilivideo.go
Normal file
228
providers/bilivideo/bilivideo.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package bilivideo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/aynakeya/deepcolor"
|
||||
"github.com/aynakeya/deepcolor/dphttp"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/tidwall/gjson"
|
||||
"miaosic"
|
||||
"miaosic/providers"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var _ = (miaosic.MediaProvider)(&BilibiliVideo{})
|
||||
|
||||
type BilibiliVideo struct {
|
||||
providers.DeepcolorProvider
|
||||
requester dphttp.IRequester
|
||||
BVRegex *regexp.Regexp
|
||||
IdRegex *regexp.Regexp
|
||||
PageRegex *regexp.Regexp
|
||||
header map[string]string
|
||||
}
|
||||
|
||||
func NewBilibiliViedo(requester dphttp.IRequester) *BilibiliVideo {
|
||||
headers := map[string]string{
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0",
|
||||
"Referer": "https://www.bilibili.com/",
|
||||
"Origin": "https://www.bilibili.com",
|
||||
"Cookie": "buvid3=9A8B3564-BDA9-407F-B45F-D5C40786CA49167618infoc;",
|
||||
}
|
||||
pvdr := &BilibiliVideo{
|
||||
BVRegex: regexp.MustCompile("^BV[0-9A-Za-z]+"),
|
||||
IdRegex: regexp.MustCompile("^BV[0-9A-Za-z]+(\\?p=[0-9]+)?"),
|
||||
PageRegex: regexp.MustCompile("p=[0-9]+"),
|
||||
header: headers,
|
||||
}
|
||||
pvdr.InfoFunc = pvdr.buildInfoApi()
|
||||
//pvdr.FileFunc = buildFileApi(requester, headers)
|
||||
//pvdr.LyricFunc = buildLyricApi(requester, headers)
|
||||
//pvdr.PlaylistFunc = buildPlaylistApi(requester, headers)
|
||||
pvdr.SearchFunc = pvdr.buildSearchApi()
|
||||
return pvdr
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) getPage(bv string) int {
|
||||
if page := b.PageRegex.FindString(bv); page != "" {
|
||||
return cast.ToInt(page[2:])
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) getBv(bv string) string {
|
||||
return b.BVRegex.FindString(bv)
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) GetName() string {
|
||||
return "bilibili-video"
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) MatchMedia(keyword string) *miaosic.Media {
|
||||
if id := b.IdRegex.FindString(keyword); id != "" {
|
||||
return &miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: b.GetName(),
|
||||
Identifier: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) MatchPlaylist(keyword string) *miaosic.Playlist {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) UpdateMediaLyric(media *miaosic.Media) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) buildInfoApi() dphttp.ApiFunc[*miaosic.Media, *miaosic.Media] {
|
||||
return deepcolor.CreateApiFunc(
|
||||
b.requester,
|
||||
func(params *miaosic.Media) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithSingleQuery(
|
||||
"https://api.bilibili.com/x/web-interface/view/detail?&aid=&jsonp=jsonp",
|
||||
"bvid", b.header,
|
||||
)(params.Meta.Identifier)
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(result *gjson.Result, media *miaosic.Media) error {
|
||||
if result.Get("data.View.title").String() == "" {
|
||||
return errors.New("failed to find required data")
|
||||
}
|
||||
media.Title = result.Get("data.View.title").String()
|
||||
media.Artist = result.Get("data.View.owner.name").String()
|
||||
media.Cover.Url = result.Get("data.View.pic").String()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
//func buildFileApi(requester dphttp.IRequester, headers map[string]string) dphttp.ApiFunc[*miaosic.Media, *miaosic.Media] {
|
||||
// return deepcolor.CreateApiFunc(
|
||||
// requester,
|
||||
// func(params *miaosic.Media) (*dphttp.Request, error) {
|
||||
// return deepcolor.NewGetRequestWithSingleQuery(
|
||||
// "https://api.bilibili.com/x/player/playurl?&avid=&cid=&qn=80&type=&otype=json",
|
||||
// "bvid", headers,
|
||||
// )(params.Meta.Identifier)
|
||||
// }
|
||||
//}
|
||||
|
||||
func (b *BilibiliVideo) buildSearchApi() dphttp.ApiFuncResult[string, []*miaosic.Media] {
|
||||
return deepcolor.CreateApiResultFunc(
|
||||
b.requester,
|
||||
deepcolor.NewGetRequestWithSingleQuery(
|
||||
"https://api.bilibili.com/x/web-interface/search/type?search_type=video&page=1",
|
||||
"keyword", b.header),
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, result *[]*miaosic.Media) error {
|
||||
if resp.Get("code").String() != "0" {
|
||||
return errors.New("failed to find required data")
|
||||
}
|
||||
r := regexp.MustCompile("</?em[^>]*>")
|
||||
resp.Get("data.result").ForEach(func(key, value gjson.Result) bool {
|
||||
*result = append(*result, &miaosic.Media{
|
||||
Title: r.ReplaceAllString(value.Get("title").String(), ""),
|
||||
Cover: miaosic.Picture{Url: "https:" + value.Get("pic").String()},
|
||||
Artist: value.Get("author").String(),
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: b.GetName(),
|
||||
Identifier: value.Get("bvid").String(),
|
||||
},
|
||||
})
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
//pvdr.cidApi = dphttp.CreateResultAPI(
|
||||
//requester,
|
||||
//&dphttp.ApiInfo[string, *gjson.Result, []string]{
|
||||
//Request: deepcolor.NewGetRequestWithSingleQuery(
|
||||
//"https://api.bilibili.com/x/web-interface/view/detail?&aid=&jsonp=jsonp",
|
||||
//"bvid", pvdr.header,
|
||||
//),
|
||||
//Parser: deepcolor.ParserGJson,
|
||||
//Selector: func (result *gjson.Result) ([]string, error) {
|
||||
//rcids := result.Get("data.View.pages.#.cid").Array()
|
||||
//cids := make([]string, 0)
|
||||
//if len(cids) == 0 {
|
||||
//cid := result.Get("data.View.cid").String()
|
||||
//if cid == "" {
|
||||
//return nil, providers.ErrorExternalApi
|
||||
//}
|
||||
//cids = append(cids, cid)
|
||||
//} else {
|
||||
//for _, r := range rcids {
|
||||
//cids = append(cids, r.String())
|
||||
//}
|
||||
//}
|
||||
//return cids, nil
|
||||
//},
|
||||
//},
|
||||
//)
|
||||
|
||||
//var fileApi = dphttp.CreateReceiverAPI(
|
||||
// requester,
|
||||
// &dphttp.ApiInfo[[]string, *gjson.Result, *model.Media]{
|
||||
// Request: deepcolor.NewGetRequestWithQuery(
|
||||
// "https://api.bilibili.com/x/player/playurl?type=&otype=json&fourk=1&qn=32&avid=",
|
||||
// []string{"bvid", "cid"}, pvdr.header),
|
||||
// Parser: deepcolor.ParserGJson,
|
||||
// Receiver: func(result *gjson.Result, container *model.Media) error {
|
||||
// uri := result.Get("data.durl.0.url").String()
|
||||
// if uri == "" {
|
||||
// return providers.ErrorExternalApi
|
||||
// }
|
||||
// container.Url = uri
|
||||
// header := make(map[string]string)
|
||||
// _ = copier.Copy(&header, &pvdr.header)
|
||||
// header["Referer"] = fmt.Sprintf("https://www.bilibili.com/video/%s", pvdr.getBv(container.Meta.(model.Meta).Id))
|
||||
// container.Header = header
|
||||
// return nil
|
||||
// },
|
||||
// })
|
||||
//return pvdr
|
||||
//}
|
||||
|
||||
//
|
||||
//var BilibiliVideoAPI *BilibiliVideo
|
||||
//
|
||||
//func init() {
|
||||
// BilibiliVideoAPI = _newBilibiliVideo()
|
||||
// Providers[BilibiliVideoAPI.GetName()] = BilibiliVideoAPI
|
||||
//}
|
||||
//
|
||||
|
||||
//
|
||||
//func (b *BilibiliVideo) GetPlaylist(playlist *model.Meta) ([]*model.Media, error) {
|
||||
// return nil, providers.ErrorExternalApi
|
||||
//}
|
||||
//
|
||||
//func (b *BilibiliVideo) FormatPlaylistUrl(uri string) string {
|
||||
// return ""
|
||||
//}
|
||||
//
|
||||
//func (b *BilibiliVideo) Search(keyword string) ([]*model.Media, error) {
|
||||
// return b.searchApi(keyword)
|
||||
//}
|
||||
//
|
||||
//func (b *BilibiliVideo) UpdateMedia(media *model.Media) error {
|
||||
// err := b.infoApi(b.getBv(media.Meta.(model.Meta).Id), media)
|
||||
// if err != nil {
|
||||
// return providers.ErrorExternalApi
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func (b *BilibiliVideo) UpdateMediaUrl(media *model.Media) error {
|
||||
// page := b.getPage(media.Meta.(model.Meta).Id) - 1
|
||||
// cids, err := b.cidApi(b.getBv(media.Meta.(model.Meta).Id))
|
||||
// if err != nil || page > len(cids) {
|
||||
// return providers.ErrorExternalApi
|
||||
// }
|
||||
// return b.fileApi([]string{b.getBv(media.Meta.(model.Meta).Id), cids[page]}, media)
|
||||
//}
|
||||
83
providers/bilivideo/bilivideo_test.go
Normal file
83
providers/bilivideo/bilivideo_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package bilivideo
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBV_GetMusicMeta(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliVideoAPI
|
||||
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "BV1434y1q71P",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "卦者那啥子靈風", media.Artist)
|
||||
}
|
||||
|
||||
func TestBV_GetMusic(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliVideoAPI
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "BV1434y1q71P",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
assert.Nil(t, err)
|
||||
err = api.UpdateMediaUrl(&media)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, strings.Contains(media.Url, "bilivideo"), media.Url)
|
||||
}
|
||||
|
||||
func TestBV_Regex(t *testing.T) {
|
||||
assert.Equal(t, "BV1gA411P7ir?p=3", regexp.MustCompile("^BV[0-9A-Za-z]+(\\?p=[0-9]+)?").FindString("BV1gA411P7ir?p=3"))
|
||||
}
|
||||
|
||||
func TestBV_GetMusicMeta2(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliVideoAPI
|
||||
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "BV1gA411P7ir?p=3",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
assert.Nil(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, "沈默沈默", media.Artist)
|
||||
}
|
||||
|
||||
func TestBV_GetMusic2(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliVideoAPI
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "BV1gA411P7ir?p=1",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
assert.Nil(t, err)
|
||||
err = api.UpdateMediaUrl(&media)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "沈默沈默", media.Artist)
|
||||
}
|
||||
|
||||
func TestBV_Search(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliVideoAPI
|
||||
result, err := api.Search("家有女友")
|
||||
assert.Nil(t, err, "Search Error")
|
||||
assert.Truef(t, len(result) > 0, "Search Result Empty")
|
||||
t.Log(result[0])
|
||||
}
|
||||
7
providers/kuwo/init.go
Normal file
7
providers/kuwo/init.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package kuwo
|
||||
|
||||
import "miaosic"
|
||||
|
||||
func init() {
|
||||
miaosic.RegisterProvider(NewKuwo(miaosic.Requester))
|
||||
}
|
||||
288
providers/kuwo/kuwo.go
Normal file
288
providers/kuwo/kuwo.go
Normal file
@@ -0,0 +1,288 @@
|
||||
package kuwo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aynakeya/deepcolor"
|
||||
"github.com/aynakeya/deepcolor/dphttp"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/tidwall/gjson"
|
||||
"html"
|
||||
"math"
|
||||
"math/rand"
|
||||
"miaosic"
|
||||
"miaosic/providers"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Kuwo struct {
|
||||
providers.DeepcolorProvider
|
||||
requester dphttp.IRequester
|
||||
PlaylistRegex0 *regexp.Regexp
|
||||
PlaylistRegex1 *regexp.Regexp
|
||||
IdRegex0 *regexp.Regexp
|
||||
IdRegex1 *regexp.Regexp
|
||||
header map[string]string
|
||||
}
|
||||
|
||||
func NewKuwo(requester dphttp.IRequester) *Kuwo {
|
||||
kw := &Kuwo{
|
||||
requester: requester,
|
||||
PlaylistRegex0: regexp.MustCompile("[0-9]+"),
|
||||
PlaylistRegex1: regexp.MustCompile("playlist/[0-9]+"),
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^kw[0-9]+"),
|
||||
}
|
||||
kw.initToken()
|
||||
kw.InfoFunc = kw.buildInfoApi()
|
||||
kw.FileFunc = kw.buildFileApi()
|
||||
kw.LyricFunc = kw.buildLyricApi()
|
||||
kw.PlaylistFunc = kw.playlistApi
|
||||
kw.SearchFunc = kw.buildSearchApi()
|
||||
return kw
|
||||
}
|
||||
|
||||
func (k *Kuwo) GetName() string {
|
||||
return "kuwo"
|
||||
}
|
||||
|
||||
func (k *Kuwo) MatchMedia(keyword string) *miaosic.Media {
|
||||
if id := k.IdRegex0.FindString(keyword); id != "" {
|
||||
return &miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: k.GetName(),
|
||||
Identifier: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
if id := k.IdRegex1.FindString(keyword); id != "" {
|
||||
return &miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: k.GetName(),
|
||||
Identifier: id[2:],
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kuwo) MatchPlaylist(uri string) *miaosic.Playlist {
|
||||
var id string
|
||||
id = k.PlaylistRegex0.FindString(uri)
|
||||
if id != "" {
|
||||
return &miaosic.Playlist{
|
||||
Meta: miaosic.MediaMeta{k.GetName(), id},
|
||||
}
|
||||
}
|
||||
id = k.PlaylistRegex1.FindString(uri)
|
||||
if id != "" {
|
||||
return &miaosic.Playlist{
|
||||
Meta: miaosic.MediaMeta{k.GetName(), id[9:]},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kuwo) generateSecret(t, e string) string {
|
||||
if e == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var n string
|
||||
for i := 0; i < len(e); i++ {
|
||||
n += strconv.Itoa(int(e[i]))
|
||||
}
|
||||
r := len(n) / 5
|
||||
o_0 := string(n[r]) + string(n[2*r]) + string(n[3*r]) + string(n[4*r])
|
||||
if 5*r < len(n) {
|
||||
o_0 += string(n[5*r])
|
||||
}
|
||||
o, _ := strconv.Atoi(o_0)
|
||||
l := int(math.Ceil(float64(len(e)) / 2.0))
|
||||
c := int(math.Pow(2, 31)) - 1
|
||||
|
||||
if o < 2 {
|
||||
return ""
|
||||
}
|
||||
|
||||
d := rand.Intn(100000000)
|
||||
//d := 80378195
|
||||
n += strconv.Itoa(d)
|
||||
|
||||
var num1, num2 int64
|
||||
for len(n) > 10 {
|
||||
// stupid javascript
|
||||
if len(n[10:]) > 19 {
|
||||
num1 = 0
|
||||
num2 = cast.ToInt64(n[19 : 19+8])
|
||||
} else {
|
||||
num1 = cast.ToInt64(n[:10])
|
||||
num2 = cast.ToInt64(n[10:])
|
||||
}
|
||||
n = cast.ToString(num1 + num2)
|
||||
}
|
||||
|
||||
nValue, _ := strconv.Atoi(n)
|
||||
nValue = (o*nValue + l) % c
|
||||
|
||||
var h int
|
||||
var f string
|
||||
|
||||
for i := 0; i < len(t); i++ {
|
||||
h = int(t[i]) ^ int(math.Floor(float64(nValue)/float64(c)*255))
|
||||
hexValue := strconv.FormatInt(int64(h), 16)
|
||||
|
||||
if h < 16 {
|
||||
f += "0" + hexValue
|
||||
} else {
|
||||
f += hexValue
|
||||
}
|
||||
|
||||
nValue = (o*nValue + l) % c
|
||||
}
|
||||
|
||||
// d to hex string, if d length < 8, add 0 to head
|
||||
dHex := fmt.Sprintf("%x", d)
|
||||
if len(dHex) < 8 {
|
||||
dHex = strings.Repeat("0", 8-len(dHex)) + dHex
|
||||
}
|
||||
|
||||
return f + dHex
|
||||
}
|
||||
|
||||
func (k *Kuwo) initToken() {
|
||||
k.header = map[string]string{
|
||||
"cookie": "Hm_Iuvt_cdb524f42f0cer9b268e4v7y734w5esq24=TN7FsbxFGt8y2sTb4tGnzhpD7StNfiRM",
|
||||
"secret": k.generateSecret("TN7FsbxFGt8y2sTb4tGnzhpD7StNfiRM", "Hm_Iuvt_cdb524f42f0cer9b268e4v7y734w5esq24"),
|
||||
"referer": "http://www.kuwo.cn/",
|
||||
}
|
||||
//searchCookie, err := k.requester.Get("http://kuwo.cn/search/list?key=any", nil)
|
||||
//fmt.Println(searchCookie.Header(), err)
|
||||
}
|
||||
|
||||
func (k *Kuwo) buildInfoApi() dphttp.ApiFunc[*miaosic.Media, *miaosic.Media] {
|
||||
return deepcolor.CreateApiFunc(
|
||||
k.requester,
|
||||
func(media *miaosic.Media) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithQuery(
|
||||
"http://www.kuwo.cn/api/www/music/musicInfo",
|
||||
[]string{"mid"}, k.header)([]string{media.Meta.Identifier})
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, media *miaosic.Media) error {
|
||||
if resp.Get("data.musicrid").String() == "" {
|
||||
return miaosic.ErrorExternalApi
|
||||
}
|
||||
media.Title = html.UnescapeString(resp.Get("data.name").String())
|
||||
media.Cover.Url = resp.Get("data.pic").String()
|
||||
media.Artist = resp.Get("data.artist").String()
|
||||
media.Album = resp.Get("data.album").String()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kuwo) buildLyricApi() dphttp.ApiFunc[*miaosic.Media, *miaosic.Media] {
|
||||
return deepcolor.CreateApiFunc(
|
||||
k.requester,
|
||||
func(media *miaosic.Media) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithQuery(
|
||||
"http://m.kuwo.cn/newh5/singles/songinfoandlrc",
|
||||
[]string{"musicId"}, k.header)([]string{media.Meta.Identifier})
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, media *miaosic.Media) error {
|
||||
lrcs := make([]string, 0)
|
||||
resp.Get("data.lrclist").ForEach(func(key, value gjson.Result) bool {
|
||||
lrcs = append(lrcs, fmt.Sprintf("[00:%s]%s", value.Get("time").String(), value.Get("lineLyric").String()))
|
||||
|
||||
return true
|
||||
})
|
||||
media.Lyric = strings.Join(lrcs, "\n")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kuwo) buildSearchApi() dphttp.ApiFuncResult[string, []*miaosic.Media] {
|
||||
return deepcolor.CreateApiResultFunc(
|
||||
k.requester,
|
||||
func(keyword string) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithQuery(
|
||||
"http://www.kuwo.cn/api/www/search/searchMusicBykeyWord",
|
||||
[]string{"key", "pn", "rn"}, k.header)([]string{keyword, "1", "64"})
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, result *[]*miaosic.Media) error {
|
||||
resp.Get("data.list").ForEach(func(key, value gjson.Result) bool {
|
||||
*result = append(*result, &miaosic.Media{
|
||||
Title: html.UnescapeString(value.Get("name").String()),
|
||||
Cover: miaosic.Picture{Url: value.Get("pic").String()},
|
||||
Artist: value.Get("artist").String(),
|
||||
Album: value.Get("album").String(),
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: k.GetName(),
|
||||
Identifier: value.Get("rid").String(),
|
||||
},
|
||||
})
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kuwo) buildFileApi() dphttp.ApiFunc[*miaosic.Media, *miaosic.Media] {
|
||||
return deepcolor.CreateApiFunc(
|
||||
k.requester,
|
||||
func(media *miaosic.Media) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithSingleQuery(
|
||||
"http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url",
|
||||
"rid", k.header)("MUSIC_" + media.Meta.Identifier)
|
||||
},
|
||||
deepcolor.ParserText,
|
||||
func(resp string, media *miaosic.Media) error {
|
||||
media.Url = resp
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kuwo) playlistApi(src *miaosic.Playlist, dst *miaosic.Playlist) error {
|
||||
dst.Medias = make([]*miaosic.Media, 0)
|
||||
api := deepcolor.CreateChainApiFunc(
|
||||
k.requester,
|
||||
func(page int) (*dphttp.Request, error) {
|
||||
return deepcolor.NewGetRequestWithQuery(
|
||||
"http://www.kuwo.cn/api/www/playlist/playListInfo",
|
||||
[]string{"pid", "pn", "rn"}, k.header)([]string{src.Meta.Identifier, cast.ToString(page), "100"})
|
||||
},
|
||||
deepcolor.ParserGJson,
|
||||
func(resp *gjson.Result, playlist *miaosic.Playlist) error {
|
||||
resp.Get("data.musicList").ForEach(func(key, value gjson.Result) bool {
|
||||
playlist.Medias = append(
|
||||
playlist.Medias,
|
||||
&miaosic.Media{
|
||||
Title: html.UnescapeString(value.Get("name").String()),
|
||||
Artist: value.Get("artist").String(),
|
||||
Cover: miaosic.Picture{Url: value.Get("pic").String()},
|
||||
Album: value.Get("album").String(),
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: k.GetName(),
|
||||
Identifier: value.Get("rid").String(),
|
||||
},
|
||||
})
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
},
|
||||
func(page int, resp *gjson.Result, playlist *miaosic.Playlist) (int, bool) {
|
||||
if resp.Get("code").String() != "200" {
|
||||
return page, false
|
||||
}
|
||||
cnt := int(resp.Get("data.total").Int())
|
||||
if cnt <= page*100 {
|
||||
return page, false
|
||||
}
|
||||
return page + 1, true
|
||||
},
|
||||
)
|
||||
return api(1, dst)
|
||||
}
|
||||
84
providers/kuwo/kuwo_test.go
Normal file
84
providers/kuwo/kuwo_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package kuwo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aynakeya/deepcolor"
|
||||
"github.com/stretchr/testify/require"
|
||||
"miaosic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var api miaosic.MediaProvider = NewKuwo(deepcolor.NewRestyRequester())
|
||||
|
||||
func TestKuwo_Secret(t *testing.T) {
|
||||
// using 80378195 as d
|
||||
require.Equal(t, "5add7ba59bc95d3d38a8983a82af6efc78d3484c6d253f29a2154dd042b3383604ca7953",
|
||||
api.(*Kuwo).generateSecret("zddnb2yWCXJjk6aWb2tSZBNeaPBChEPY", "Hm_Iuvt_cdb524f42f0cer9b268e4v7y734w5esq24"))
|
||||
|
||||
}
|
||||
|
||||
func TestKuwo_Search(t *testing.T) {
|
||||
result, err := api.Search("周杰伦")
|
||||
require.NoError(t, err)
|
||||
fmt.Println(result)
|
||||
media := result[0]
|
||||
err = api.UpdateMediaUrl(media)
|
||||
fmt.Println(err)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestKuwo_GetMusicMeta(t *testing.T) {
|
||||
media := miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: api.GetName(),
|
||||
Identifier: "22804772",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "霜雪千年", media.Title)
|
||||
}
|
||||
|
||||
func TestKuwo_GetMusic(t *testing.T) {
|
||||
media := miaosic.Media{
|
||||
Meta: miaosic.MediaMeta{
|
||||
Provider: api.GetName(),
|
||||
Identifier: "22804772",
|
||||
},
|
||||
}
|
||||
require.NoError(t, api.UpdateMedia(&media))
|
||||
require.NoError(t, api.UpdateMediaUrl(&media))
|
||||
require.Equal(t, "霜雪千年", media.Title)
|
||||
require.True(t, len(media.Url) > 0)
|
||||
}
|
||||
|
||||
//func TestKuwo_UpdateMediaLyric(t *testing.T) {
|
||||
// var api adapter.MediaProvider = providers.KuwoAPI
|
||||
// media := model.Media{
|
||||
// Meta: model.Meta{
|
||||
// Name: api.GetName(),
|
||||
// Id: "22804772",
|
||||
// },
|
||||
// }
|
||||
// err := api.UpdateMediaLyric(&media)
|
||||
// fmt.Println(err)
|
||||
// fmt.Println(media.Lyric)
|
||||
//}
|
||||
//
|
||||
//func TestKuwo_GetPlaylist(t *testing.T) {
|
||||
// var api adapter.MediaProvider = providers.KuwoAPI
|
||||
// playlist, err := api.GetPlaylist(&model.Meta{
|
||||
// Name: api.GetName(),
|
||||
// //Id: "1082685104",
|
||||
// Id: "2959147566",
|
||||
// })
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// return
|
||||
// }
|
||||
// fmt.Println(len(playlist))
|
||||
// for _, media := range playlist {
|
||||
// fmt.Println(media.Title, media.Artist, media.Album)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
53
providers/providers.go
Normal file
53
providers/providers.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"github.com/aynakeya/deepcolor/dphttp"
|
||||
"miaosic"
|
||||
)
|
||||
|
||||
type DeepcolorProvider struct {
|
||||
InfoFunc dphttp.ApiFunc[*miaosic.Media, *miaosic.Media]
|
||||
FileFunc dphttp.ApiFunc[*miaosic.Media, *miaosic.Media]
|
||||
LyricFunc dphttp.ApiFunc[*miaosic.Media, *miaosic.Media]
|
||||
PlaylistFunc dphttp.ApiFunc[*miaosic.Playlist, *miaosic.Playlist]
|
||||
SearchFunc dphttp.ApiFuncResult[string, []*miaosic.Media]
|
||||
}
|
||||
|
||||
func (d *DeepcolorProvider) UpdatePlaylist(playlist *miaosic.Playlist) error {
|
||||
if d.PlaylistFunc == nil {
|
||||
return miaosic.ErrNotImplemented
|
||||
}
|
||||
return d.PlaylistFunc(playlist, playlist)
|
||||
}
|
||||
|
||||
func (d *DeepcolorProvider) Search(keyword string) ([]*miaosic.Media, error) {
|
||||
if d.SearchFunc == nil {
|
||||
return nil, miaosic.ErrNotImplemented
|
||||
}
|
||||
//result := make([]*miaosic.Media, 0)
|
||||
//err :=
|
||||
//fmt.Println(result)
|
||||
return d.SearchFunc(keyword)
|
||||
}
|
||||
|
||||
func (d *DeepcolorProvider) UpdateMedia(media *miaosic.Media) error {
|
||||
if d.InfoFunc == nil {
|
||||
return miaosic.ErrNotImplemented
|
||||
}
|
||||
return d.InfoFunc(media, media)
|
||||
}
|
||||
|
||||
func (d DeepcolorProvider) UpdateMediaUrl(media *miaosic.Media) error {
|
||||
if d.FileFunc == nil {
|
||||
return miaosic.ErrNotImplemented
|
||||
}
|
||||
return d.FileFunc(media, media)
|
||||
}
|
||||
|
||||
func (d DeepcolorProvider) UpdateMediaLyric(media *miaosic.Media) error {
|
||||
if d.LyricFunc == nil {
|
||||
// if no lyric func, return nil
|
||||
return nil
|
||||
}
|
||||
return d.LyricFunc(media, media)
|
||||
}
|
||||
Reference in New Issue
Block a user