mirror of
https://github.com/AynaLivePlayer/AynaLivePlayer.git
synced 2025-12-11 20:58:13 +08:00
fix gui freeze bug (work round)
This commit is contained in:
139
adapters/provider/bilibili.go
Normal file
139
adapters/provider/bilibili.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"fmt"
|
||||
"github.com/tidwall/gjson"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type Bilibili struct {
|
||||
InfoApi string
|
||||
FileApi string
|
||||
SearchApi string
|
||||
IdRegex0 *regexp.Regexp
|
||||
IdRegex1 *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewBilibili(config adapter.MediaProviderConfig) adapter.MediaProvider {
|
||||
return &Bilibili{
|
||||
InfoApi: "https://www.bilibili.com/audio/music-service-c/web/song/info?sid=%s",
|
||||
FileApi: "https://api.bilibili.com/audio/music-service-c/url?device=phone&mid=8047632&mobi_app=iphone&platform=ios&privilege=2&songid=%s&quality=2",
|
||||
SearchApi: "https://api.bilibili.com/audio/music-service-c/s?search_type=music&keyword=%s&page=1&pagesize=100",
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^au[0-9]+"),
|
||||
}
|
||||
}
|
||||
|
||||
func _newBilibili() *Bilibili {
|
||||
return &Bilibili{
|
||||
InfoApi: "https://www.bilibili.com/audio/music-service-c/web/song/info?sid=%s",
|
||||
FileApi: "https://api.bilibili.com/audio/music-service-c/url?device=phone&mid=8047632&mobi_app=iphone&platform=ios&privilege=2&songid=%s&quality=2",
|
||||
SearchApi: "https://api.bilibili.com/audio/music-service-c/s?search_type=music&keyword=%s&page=1&pagesize=100",
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^au[0-9]+"),
|
||||
}
|
||||
}
|
||||
|
||||
var BilibiliAPI *Bilibili
|
||||
|
||||
func init() {
|
||||
BilibiliAPI = _newBilibili()
|
||||
Providers[BilibiliAPI.GetName()] = BilibiliAPI
|
||||
}
|
||||
|
||||
func (b *Bilibili) GetName() string {
|
||||
return "bilibili"
|
||||
}
|
||||
|
||||
func (b *Bilibili) MatchMedia(keyword string) *model.Media {
|
||||
if id := b.IdRegex0.FindString(keyword); id != "" {
|
||||
return &model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: b.GetName(),
|
||||
Id: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
if id := b.IdRegex1.FindString(keyword); id != "" {
|
||||
return &model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: b.GetName(),
|
||||
Id: id[2:],
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bilibili) FormatPlaylistUrl(uri string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Bilibili) GetPlaylist(playlist *model.Meta) ([]*model.Media, error) {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
|
||||
func (b *Bilibili) Search(keyword string) ([]*model.Media, error) {
|
||||
resp := httpGetString(fmt.Sprintf(b.SearchApi, url.QueryEscape(keyword)), map[string]string{
|
||||
"user-agent": "BiliMusic/2.233.3",
|
||||
})
|
||||
if resp == "" {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
result := make([]*model.Media, 0)
|
||||
gjson.Get(resp, "data.result").ForEach(func(key, value gjson.Result) bool {
|
||||
result = append(result, &model.Media{
|
||||
Title: value.Get("title").String(),
|
||||
Cover: model.Picture{Url: value.Get("cover").String()},
|
||||
Artist: value.Get("author").String(),
|
||||
Meta: model.Meta{
|
||||
Name: b.GetName(),
|
||||
Id: value.Get("id").String(),
|
||||
},
|
||||
})
|
||||
return true
|
||||
})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *Bilibili) UpdateMedia(media *model.Media) error {
|
||||
resp := httpGetString(fmt.Sprintf(b.InfoApi, media.Meta.(model.Meta).Id), map[string]string{
|
||||
"user-agent": "BiliMusic/2.233.3",
|
||||
})
|
||||
if resp == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
if gjson.Get(resp, "data.title").String() == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Title = gjson.Get(resp, "data.title").String()
|
||||
media.Cover.Url = gjson.Get(resp, "data.cover").String()
|
||||
media.Artist = gjson.Get(resp, "data.author").String()
|
||||
media.Album = media.Title
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bilibili) UpdateMediaUrl(media *model.Media) error {
|
||||
resp := httpGetString(fmt.Sprintf(b.FileApi, media.Meta.(model.Meta).Id), map[string]string{
|
||||
"user-agent": "BiliMusic/2.233.3",
|
||||
})
|
||||
|
||||
if resp == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Header = map[string]string{
|
||||
"user-agent": "BiliMusic/2.233.3",
|
||||
}
|
||||
uri := gjson.Get(resp, "data.cdns.0").String()
|
||||
if uri == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Url = uri
|
||||
return nil
|
||||
}
|
||||
func (k *Bilibili) UpdateMediaLyric(media *model.Media) error {
|
||||
return nil
|
||||
}
|
||||
60
adapters/provider/bilibili_test.go
Normal file
60
adapters/provider/bilibili_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBilibili_Search(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliAPI
|
||||
result, err := api.Search("染 reol")
|
||||
if err != nil {
|
||||
fmt.Println(1, err)
|
||||
return
|
||||
}
|
||||
fmt.Println(result)
|
||||
media := result[0]
|
||||
fmt.Println(*media)
|
||||
err = api.UpdateMediaUrl(media)
|
||||
fmt.Println(err)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestBilibili_GetMusicMeta(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliAPI
|
||||
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "1560601",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
}
|
||||
|
||||
func TestBilibili_GetMusic(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliAPI
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "1560601",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = api.UpdateMediaUrl(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
174
adapters/provider/bilivideo.go
Normal file
174
adapters/provider/bilivideo.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/util"
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"fmt"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/tidwall/gjson"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type BilibiliVideo struct {
|
||||
InfoApi string
|
||||
FileApi string
|
||||
SearchApi string
|
||||
BVRegex *regexp.Regexp
|
||||
IdRegex *regexp.Regexp
|
||||
PageRegex *regexp.Regexp
|
||||
header map[string]string
|
||||
}
|
||||
|
||||
func NewBilibiliVideo(config adapter.MediaProviderConfig) adapter.MediaProvider {
|
||||
return &BilibiliVideo{
|
||||
InfoApi: "https://api.bilibili.com/x/web-interface/view/detail?bvid=%s&aid=&jsonp=jsonp",
|
||||
FileApi: "https://api.bilibili.com/x/player/playurl?type=&otype=json&fourk=1&qn=32&avid=&bvid=%s&cid=%s",
|
||||
SearchApi: "https://api.bilibili.com/x/web-interface/search/type?search_type=video&page=1&keyword=%s",
|
||||
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: map[string]string{
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||
"Referer": "https://www.bilibili.com/",
|
||||
"Origin": "https://www.bilibili.com",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func _newBilibiliVideo() *BilibiliVideo {
|
||||
return &BilibiliVideo{
|
||||
InfoApi: "https://api.bilibili.com/x/web-interface/view/detail?bvid=%s&aid=&jsonp=jsonp",
|
||||
FileApi: "https://api.bilibili.com/x/player/playurl?type=&otype=json&fourk=1&qn=32&avid=&bvid=%s&cid=%s",
|
||||
SearchApi: "https://api.bilibili.com/x/web-interface/search/type?search_type=video&page=1&keyword=%s",
|
||||
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: 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;",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var BilibiliVideoAPI *BilibiliVideo
|
||||
|
||||
func init() {
|
||||
BilibiliVideoAPI = _newBilibiliVideo()
|
||||
Providers[BilibiliVideoAPI.GetName()] = BilibiliVideoAPI
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) getPage(bv string) int {
|
||||
if page := b.PageRegex.FindString(bv); page != "" {
|
||||
return util.Atoi(page[2:])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
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) *model.Media {
|
||||
if id := b.IdRegex.FindString(keyword); id != "" {
|
||||
return &model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: b.GetName(),
|
||||
Id: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) GetPlaylist(playlist *model.Meta) ([]*model.Media, error) {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) FormatPlaylistUrl(uri string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) Search(keyword string) ([]*model.Media, error) {
|
||||
resp := httpGetString(fmt.Sprintf(b.SearchApi, url.QueryEscape(keyword)), b.header)
|
||||
if resp == "" {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
jresp := gjson.Parse(resp)
|
||||
if jresp.Get("code").String() != "0" {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
result := make([]*model.Media, 0)
|
||||
r := regexp.MustCompile("</?em[^>]*>")
|
||||
jresp.Get("data.result").ForEach(func(key, value gjson.Result) bool {
|
||||
result = append(result, &model.Media{
|
||||
Title: r.ReplaceAllString(value.Get("title").String(), ""),
|
||||
Cover: model.Picture{Url: "https:" + value.Get("pic").String()},
|
||||
Artist: value.Get("author").String(),
|
||||
Meta: model.Meta{
|
||||
Name: b.GetName(),
|
||||
Id: value.Get("bvid").String(),
|
||||
},
|
||||
})
|
||||
return true
|
||||
})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) UpdateMedia(media *model.Media) error {
|
||||
resp := httpGetString(fmt.Sprintf(b.InfoApi, b.getBv(media.Meta.(model.Meta).Id)), nil)
|
||||
if resp == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
jresp := gjson.Parse(resp)
|
||||
if jresp.Get("data.View.title").String() == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Title = jresp.Get("data.View.title").String()
|
||||
media.Artist = jresp.Get("data.View.owner.name").String()
|
||||
media.Cover.Url = jresp.Get("data.View.pic").String()
|
||||
media.Album = media.Title
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) UpdateMediaUrl(media *model.Media) error {
|
||||
resp := httpGetString(fmt.Sprintf(b.InfoApi, b.getBv(media.Meta.(model.Meta).Id)), nil)
|
||||
if resp == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
jresp := gjson.Parse(resp)
|
||||
page := b.getPage(media.Meta.(model.Meta).Id) - 1
|
||||
cid := jresp.Get(fmt.Sprintf("data.View.pages.%d.cid", page)).String()
|
||||
if cid == "" {
|
||||
cid = jresp.Get("data.View.cid").String()
|
||||
}
|
||||
if cid == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
resp = httpGetString(fmt.Sprintf(b.FileApi, b.getBv(media.Meta.(model.Meta).Id), cid), b.header)
|
||||
if resp == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
jresp = gjson.Parse(resp)
|
||||
uri := jresp.Get("data.durl.0.url").String()
|
||||
if uri == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Url = uri
|
||||
header := make(map[string]string)
|
||||
_ = copier.Copy(&header, &b.header)
|
||||
header["Referer"] = fmt.Sprintf("https://www.bilibili.com/video/%s", b.getBv(media.Meta.(model.Meta).Id))
|
||||
media.Header = b.header
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BilibiliVideo) UpdateMediaLyric(media *model.Media) error {
|
||||
return nil
|
||||
}
|
||||
100
adapters/provider/bilivideo_test.go
Normal file
100
adapters/provider/bilivideo_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"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)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = api.UpdateMediaUrl(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//fmt.Println(media)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestBV_Regex(t *testing.T) {
|
||||
fmt.Println(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)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
}
|
||||
|
||||
func TestBV_GetMusic2(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)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = api.UpdateMediaUrl(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//fmt.Println(media)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestBV_Search(t *testing.T) {
|
||||
var api adapter.MediaProvider = BilibiliVideoAPI
|
||||
result, err := api.Search("家有女友")
|
||||
if err != nil {
|
||||
fmt.Println(1, err)
|
||||
return
|
||||
}
|
||||
fmt.Println(len(result))
|
||||
for _, r := range result {
|
||||
fmt.Println(r.Artist)
|
||||
}
|
||||
}
|
||||
8
adapters/provider/error.go
Normal file
8
adapters/provider/error.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package provider
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrorExternalApi = errors.New("external api error")
|
||||
ErrorNoSuchProvider = errors.New("not such provider")
|
||||
)
|
||||
29
adapters/provider/http.go
Normal file
29
adapters/provider/http.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
"time"
|
||||
)
|
||||
|
||||
func httpGet(url string, header map[string]string) (*resty.Response, error) {
|
||||
resp, err := resty.New().
|
||||
SetTimeout(time.Second * 3).R().
|
||||
SetHeaders(header).
|
||||
Get(url)
|
||||
return resp, err
|
||||
}
|
||||
func httpGetString(url string, header map[string]string) string {
|
||||
resp, err := httpGet(url, header)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return resp.String()
|
||||
}
|
||||
|
||||
func httpHead(url string, header map[string]string) (*resty.Response, error) {
|
||||
resp, err := resty.New().
|
||||
SetTimeout(time.Second * 3).R().
|
||||
SetHeaders(header).
|
||||
Head(url)
|
||||
return resp, err
|
||||
}
|
||||
230
adapters/provider/kuwo.go
Normal file
230
adapters/provider/kuwo.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"fmt"
|
||||
"github.com/tidwall/gjson"
|
||||
"html"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Kuwo struct {
|
||||
InfoApi string
|
||||
FileApi string
|
||||
SearchCookie string
|
||||
SearchApi string
|
||||
LyricApi string
|
||||
PlaylistApi string
|
||||
PlaylistRegex0 *regexp.Regexp
|
||||
PlaylistRegex1 *regexp.Regexp
|
||||
IdRegex0 *regexp.Regexp
|
||||
IdRegex1 *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewKuwo(config adapter.MediaProviderConfig) adapter.MediaProvider {
|
||||
return &Kuwo{
|
||||
InfoApi: "http://www.kuwo.cn/api/www/music/musicInfo?mid=%s&httpsStatus=1",
|
||||
//FileApi: "http://www.kuwo.cn/api/v1/www/music/playUrl?mid=%d&type=music&httpsStatus=1",
|
||||
FileApi: "http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url&rid=MUSIC_%s",
|
||||
SearchCookie: "http://kuwo.cn/search/list?key=%s",
|
||||
SearchApi: "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=%s&pn=%d&rn=%d",
|
||||
LyricApi: "http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=%s",
|
||||
PlaylistApi: "http://www.kuwo.cn/api/www/playlist/playListInfo?pid=%s&pn=%d&rn=%d&httpsStatus=1",
|
||||
PlaylistRegex0: regexp.MustCompile("[0-9]+"),
|
||||
PlaylistRegex1: regexp.MustCompile("playlist/[0-9]+"),
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^kw[0-9]+"),
|
||||
}
|
||||
}
|
||||
|
||||
func _newKuwo() *Kuwo {
|
||||
return &Kuwo{
|
||||
InfoApi: "http://www.kuwo.cn/api/www/music/musicInfo?mid=%s&httpsStatus=1",
|
||||
//FileApi: "http://www.kuwo.cn/api/v1/www/music/playUrl?mid=%d&type=music&httpsStatus=1",
|
||||
FileApi: "http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url&rid=MUSIC_%s",
|
||||
SearchCookie: "http://kuwo.cn/search/list?key=%s",
|
||||
SearchApi: "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=%s&pn=%d&rn=%d",
|
||||
LyricApi: "http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=%s",
|
||||
PlaylistApi: "http://www.kuwo.cn/api/www/playlist/playListInfo?pid=%s&pn=%d&rn=%d&httpsStatus=1",
|
||||
PlaylistRegex0: regexp.MustCompile("[0-9]+"),
|
||||
PlaylistRegex1: regexp.MustCompile("playlist/[0-9]+"),
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^kw[0-9]+"),
|
||||
}
|
||||
}
|
||||
|
||||
var KuwoAPI *Kuwo
|
||||
|
||||
func init() {
|
||||
KuwoAPI = _newKuwo()
|
||||
Providers[KuwoAPI.GetName()] = KuwoAPI
|
||||
}
|
||||
|
||||
func (k *Kuwo) GetName() string {
|
||||
return "kuwo"
|
||||
}
|
||||
|
||||
func (k *Kuwo) MatchMedia(keyword string) *model.Media {
|
||||
if id := k.IdRegex0.FindString(keyword); id != "" {
|
||||
return &model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: k.GetName(),
|
||||
Id: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
if id := k.IdRegex1.FindString(keyword); id != "" {
|
||||
return &model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: k.GetName(),
|
||||
Id: id[2:],
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kuwo) FormatPlaylistUrl(uri string) string {
|
||||
var id string
|
||||
id = k.PlaylistRegex0.FindString(uri)
|
||||
if id != "" {
|
||||
return id
|
||||
}
|
||||
id = k.PlaylistRegex1.FindString(uri)
|
||||
if id != "" {
|
||||
return id[9:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
//func (k *Kuwo) _kuwoGet(url string) string {
|
||||
// searchCookie, err := httpHead(fmt.Sprintf(k.SearchCookie, "any"), nil)
|
||||
// if err != nil {
|
||||
// return ""
|
||||
// }
|
||||
// kwToken, ok := util.SliceString(regexp.MustCompile("kw_token=([^;])*;").FindString(searchCookie.Header().Get("set-cookie")), 9, -1)
|
||||
// if !ok {
|
||||
// return ""
|
||||
// }
|
||||
// return httpGetString(url, map[string]string{
|
||||
// "cookie": "kw_token=" + kwToken,
|
||||
// "csrf": kwToken,
|
||||
// "referer": "http://www.kuwo.cn/",
|
||||
// })
|
||||
//}
|
||||
|
||||
func (k *Kuwo) _kuwoGet(url string) string {
|
||||
return httpGetString(url, map[string]string{
|
||||
"cookie": "kw_token=" + "95MWTYC4FP",
|
||||
"csrf": "95MWTYC4FP",
|
||||
"referer": "http://www.kuwo.cn/",
|
||||
})
|
||||
}
|
||||
|
||||
func (k *Kuwo) Search(keyword string) ([]*model.Media, error) {
|
||||
resp := k._kuwoGet(fmt.Sprintf(k.SearchApi, url.QueryEscape(keyword), 1, 64))
|
||||
if resp == "" {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
result := make([]*model.Media, 0)
|
||||
gjson.Parse(resp).Get("data.list").ForEach(func(key, value gjson.Result) bool {
|
||||
result = append(result, &model.Media{
|
||||
Title: html.UnescapeString(value.Get("name").String()),
|
||||
Cover: model.Picture{Url: value.Get("pic").String()},
|
||||
Artist: value.Get("artist").String(),
|
||||
Album: value.Get("album").String(),
|
||||
Meta: model.Meta{
|
||||
Name: k.GetName(),
|
||||
Id: value.Get("rid").String(),
|
||||
},
|
||||
})
|
||||
return true
|
||||
})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (k *Kuwo) UpdateMedia(media *model.Media) error {
|
||||
resp := k._kuwoGet(fmt.Sprintf(k.InfoApi, media.Meta.(model.Meta).Id))
|
||||
if resp == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
jresp := gjson.Parse(resp)
|
||||
if jresp.Get("data.musicrid").String() == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Title = html.UnescapeString(jresp.Get("data.name").String())
|
||||
media.Cover.Url = jresp.Get("data.pic").String()
|
||||
media.Artist = jresp.Get("data.artist").String()
|
||||
media.Album = jresp.Get("data.album").String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kuwo) UpdateMediaUrl(media *model.Media) error {
|
||||
result := httpGetString(fmt.Sprintf(k.FileApi, media.Meta.(model.Meta).Id), nil)
|
||||
if result == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Url = result
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kuwo) UpdateMediaLyric(media *model.Media) error {
|
||||
result := httpGetString(fmt.Sprintf(k.LyricApi, media.Meta.(model.Meta).Id), nil)
|
||||
if result == "" {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
lrcs := make([]string, 0)
|
||||
gjson.Parse(result).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) GetPlaylist(playlist *model.Meta) ([]*model.Media, error) {
|
||||
medias := make([]*model.Media, 0)
|
||||
var resp string
|
||||
var jresp gjson.Result
|
||||
for i := 1; i <= 20; i++ {
|
||||
resp = k._kuwoGet(fmt.Sprintf(k.PlaylistApi, playlist.Id, i, 128))
|
||||
if resp == "" {
|
||||
break
|
||||
}
|
||||
//fmt.Println(resp[:100])
|
||||
jresp = gjson.Parse(resp)
|
||||
//fmt.Println(jresp.Get("code").String())
|
||||
if jresp.Get("code").String() != "200" {
|
||||
break
|
||||
}
|
||||
cnt := int(jresp.Get("data.total").Int())
|
||||
//fmt.Println(cnt)
|
||||
//fmt.Println(len(jresp.Get("data.musicList").Array()))
|
||||
jresp.Get("data.musicList").ForEach(func(key, value gjson.Result) bool {
|
||||
medias = append(
|
||||
medias,
|
||||
&model.Media{
|
||||
Title: html.UnescapeString(value.Get("name").String()),
|
||||
Artist: value.Get("artist").String(),
|
||||
Cover: model.Picture{Url: value.Get("pic").String()},
|
||||
Album: value.Get("album").String(),
|
||||
Meta: model.Meta{
|
||||
Name: k.GetName(),
|
||||
Id: value.Get("rid").String(),
|
||||
},
|
||||
})
|
||||
return true
|
||||
})
|
||||
if cnt <= i*100 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(medias) == 0 {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
return medias, nil
|
||||
}
|
||||
89
adapters/provider/kuwo_test.go
Normal file
89
adapters/provider/kuwo_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKuwo_Search(t *testing.T) {
|
||||
var api adapter.MediaProvider = KuwoAPI
|
||||
result, err := api.Search("染 reol")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(result)
|
||||
media := result[0]
|
||||
err = api.UpdateMediaUrl(media)
|
||||
fmt.Println(err)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestKuwo_GetMusicMeta(t *testing.T) {
|
||||
var api adapter.MediaProvider = KuwoAPI
|
||||
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "22804772",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
}
|
||||
|
||||
func TestKuwo_GetMusic(t *testing.T) {
|
||||
var api adapter.MediaProvider = KuwoAPI
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "22804772",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = api.UpdateMediaUrl(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestKuwo_UpdateMediaLyric(t *testing.T) {
|
||||
var api adapter.MediaProvider = 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 = 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)
|
||||
}
|
||||
|
||||
}
|
||||
165
adapters/provider/local.go
Normal file
165
adapters/provider/local.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/util"
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type _LocalPlaylist struct {
|
||||
Name string
|
||||
Medias []*model.Media
|
||||
}
|
||||
|
||||
type Local struct {
|
||||
localDir string
|
||||
Playlists []*_LocalPlaylist
|
||||
}
|
||||
|
||||
func NewLocalCtor(config adapter.MediaProviderConfig) adapter.MediaProvider {
|
||||
localDir, ok := config["local_dir"]
|
||||
if !ok {
|
||||
localDir = "./local"
|
||||
}
|
||||
l := &Local{Playlists: make([]*_LocalPlaylist, 0), localDir: localDir}
|
||||
if err := os.MkdirAll(localDir, 0755); err != nil {
|
||||
return l
|
||||
}
|
||||
for _, n := range getPlaylistNames(localDir) {
|
||||
l.Playlists = append(l.Playlists, &_LocalPlaylist{Name: n})
|
||||
}
|
||||
for i, _ := range l.Playlists {
|
||||
_ = readLocalPlaylist(localDir, l.Playlists[i])
|
||||
}
|
||||
LocalAPI = l
|
||||
Providers[LocalAPI.GetName()] = LocalAPI
|
||||
return l
|
||||
}
|
||||
|
||||
var LocalAPI *Local
|
||||
|
||||
func NewLocal(localdir string) *Local {
|
||||
l := &Local{Playlists: make([]*_LocalPlaylist, 0), localDir: localdir}
|
||||
if err := os.MkdirAll(localdir, 0755); err != nil {
|
||||
return l
|
||||
}
|
||||
for _, n := range getPlaylistNames(localdir) {
|
||||
l.Playlists = append(l.Playlists, &_LocalPlaylist{Name: n})
|
||||
}
|
||||
for i, _ := range l.Playlists {
|
||||
_ = readLocalPlaylist(localdir, l.Playlists[i])
|
||||
}
|
||||
LocalAPI = l
|
||||
Providers[LocalAPI.GetName()] = LocalAPI
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Local) GetName() string {
|
||||
return "local"
|
||||
}
|
||||
|
||||
func (l *Local) MatchMedia(keyword string) *model.Media {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Local) UpdateMediaLyric(media *model.Media) error {
|
||||
// already update in UpdateMedia, do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Local) FormatPlaylistUrl(uri string) string {
|
||||
return uri
|
||||
}
|
||||
|
||||
func (l *Local) GetPlaylist(playlist *model.Meta) ([]*model.Media, error) {
|
||||
var pl *_LocalPlaylist = nil
|
||||
for _, p := range l.Playlists {
|
||||
if p.Name == playlist.Id {
|
||||
pl = p
|
||||
}
|
||||
}
|
||||
if pl == nil {
|
||||
l.Playlists = append(l.Playlists, &_LocalPlaylist{Name: playlist.Id})
|
||||
pl = l.Playlists[len(l.Playlists)-1]
|
||||
}
|
||||
if readLocalPlaylist(l.localDir, pl) != nil {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
return pl.Medias, nil
|
||||
}
|
||||
|
||||
func (l *Local) Search(keyword string) ([]*model.Media, error) {
|
||||
allMedias := make([]*model.Media, 0)
|
||||
for _, p := range l.Playlists {
|
||||
for _, m := range p.Medias {
|
||||
allMedias = append(allMedias, m)
|
||||
}
|
||||
}
|
||||
MediaSort(keyword, allMedias)
|
||||
c := util.Min(len(allMedias), 32)
|
||||
medias := make([]*model.Media, c)
|
||||
for i := 0; i < c; i++ {
|
||||
medias[i] = allMedias[i].Copy()
|
||||
}
|
||||
return medias, nil
|
||||
}
|
||||
|
||||
func (l *Local) SearchV1(keyword string) ([]*model.Media, error) {
|
||||
result := make([]struct {
|
||||
M *model.Media
|
||||
N int
|
||||
}, 0)
|
||||
keywords := strings.Split(keyword, " ")
|
||||
for _, p := range l.Playlists {
|
||||
for _, m := range p.Medias {
|
||||
title := strings.ToLower(m.Title)
|
||||
artist := strings.ToLower(m.Artist)
|
||||
n := 0
|
||||
for _, k := range keywords {
|
||||
kw := strings.ToLower(k)
|
||||
if strings.Contains(title, kw) || strings.Contains(artist, kw) {
|
||||
n++
|
||||
}
|
||||
if kw == title {
|
||||
n += 3
|
||||
}
|
||||
}
|
||||
if n > 0 {
|
||||
result = append(result, struct {
|
||||
M *model.Media
|
||||
N int
|
||||
}{M: m, N: n})
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].N > result[j].N
|
||||
})
|
||||
medias := make([]*model.Media, len(result))
|
||||
for i, r := range result {
|
||||
medias[i] = r.M.Copy()
|
||||
}
|
||||
return medias, nil
|
||||
}
|
||||
|
||||
func (l *Local) UpdateMedia(media *model.Media) error {
|
||||
mediaPath := media.Meta.(model.Meta).Id
|
||||
_, err := os.Stat(mediaPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return readMediaFile(media)
|
||||
}
|
||||
|
||||
func (l *Local) UpdateMediaUrl(media *model.Media) error {
|
||||
mediaPath := media.Meta.(model.Meta).Id
|
||||
_, err := os.Stat(mediaPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
media.Url = mediaPath
|
||||
return nil
|
||||
}
|
||||
72
adapters/provider/local_helper.go
Normal file
72
adapters/provider/local_helper.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/util"
|
||||
"AynaLivePlayer/core/model"
|
||||
"github.com/dhowden/tag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func getPlaylistNames(localdir string) []string {
|
||||
names := make([]string, 0)
|
||||
items, _ := ioutil.ReadDir(localdir)
|
||||
for _, item := range items {
|
||||
if item.IsDir() {
|
||||
names = append(names, item.Name())
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// readLocalPlaylist read files under a directory
|
||||
// and return a _LocalPlaylist object.
|
||||
// This function assume this directory exists
|
||||
func readLocalPlaylist(localdir string, playlist *_LocalPlaylist) error {
|
||||
p1th := playlist.Name
|
||||
playlist.Medias = make([]*model.Media, 0)
|
||||
fullPath := filepath.Join(localdir, p1th)
|
||||
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
items, _ := ioutil.ReadDir(fullPath)
|
||||
for _, item := range items {
|
||||
// if item is a file, read file
|
||||
if !item.IsDir() {
|
||||
fn := item.Name()
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: LocalAPI.GetName(),
|
||||
Id: filepath.Join(fullPath, fn),
|
||||
},
|
||||
}
|
||||
if readMediaFile(&media) != nil {
|
||||
continue
|
||||
}
|
||||
playlist.Medias = append(playlist.Medias, &media)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readMediaFile(media *model.Media) error {
|
||||
p := media.Meta.(model.Meta).Id
|
||||
f, err := os.Open(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
meta, err := tag.ReadFrom(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
media.Title = util.GetOrDefault(meta.Title(), filepath.Base(p))
|
||||
media.Artist = util.GetOrDefault(meta.Artist(), "Unknown")
|
||||
media.Album = util.GetOrDefault(meta.Album(), "Unknown")
|
||||
media.Lyric = meta.Lyrics()
|
||||
if meta.Picture() != nil {
|
||||
media.Cover.Data = meta.Picture().Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
25
adapters/provider/local_test.go
Normal file
25
adapters/provider/local_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLocal_Read(t *testing.T) {
|
||||
items, _ := ioutil.ReadDir(".")
|
||||
for _, item := range items {
|
||||
if item.IsDir() {
|
||||
subitems, _ := ioutil.ReadDir(item.Name())
|
||||
for _, subitem := range subitems {
|
||||
if !subitem.IsDir() {
|
||||
// handle file there
|
||||
fmt.Println(item.Name() + "/" + subitem.Name())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// handle file there
|
||||
fmt.Println(item.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
239
adapters/provider/netease.go
Normal file
239
adapters/provider/netease.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/util"
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
neteaseApi "github.com/XiaoMengXinX/Music163Api-Go/api"
|
||||
neteaseTypes "github.com/XiaoMengXinX/Music163Api-Go/types"
|
||||
neteaseUtil "github.com/XiaoMengXinX/Music163Api-Go/utils"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Netease struct {
|
||||
PlaylistRegex0 *regexp.Regexp
|
||||
PlaylistRegex1 *regexp.Regexp
|
||||
ReqData neteaseUtil.RequestData
|
||||
IdRegex0 *regexp.Regexp
|
||||
IdRegex1 *regexp.Regexp
|
||||
loginStatus neteaseTypes.LoginStatusData
|
||||
}
|
||||
|
||||
func NewNetease(config adapter.MediaProviderConfig) adapter.MediaProvider {
|
||||
return &Netease{
|
||||
PlaylistRegex0: regexp.MustCompile("^[0-9]+$"),
|
||||
// https://music.163.com/playlist?id=2382819181&userid=95906480
|
||||
PlaylistRegex1: regexp.MustCompile("playlist\\?id=[0-9]+"),
|
||||
ReqData: neteaseUtil.RequestData{
|
||||
Headers: neteaseUtil.Headers{
|
||||
{
|
||||
"X-Real-IP",
|
||||
"118.88.88.88",
|
||||
},
|
||||
},
|
||||
},
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^wy[0-9]+"),
|
||||
}
|
||||
}
|
||||
|
||||
func _newNetease() *Netease {
|
||||
return &Netease{
|
||||
PlaylistRegex0: regexp.MustCompile("^[0-9]+$"),
|
||||
// https://music.163.com/playlist?id=2382819181&userid=95906480
|
||||
PlaylistRegex1: regexp.MustCompile("playlist\\?id=[0-9]+"),
|
||||
ReqData: neteaseUtil.RequestData{
|
||||
Headers: neteaseUtil.Headers{
|
||||
{
|
||||
"X-Real-IP",
|
||||
"118.88.88.88",
|
||||
},
|
||||
},
|
||||
},
|
||||
IdRegex0: regexp.MustCompile("^[0-9]+"),
|
||||
IdRegex1: regexp.MustCompile("^wy[0-9]+"),
|
||||
}
|
||||
}
|
||||
|
||||
var NeteaseAPI *Netease
|
||||
|
||||
func init() {
|
||||
NeteaseAPI = _newNetease()
|
||||
Providers[NeteaseAPI.GetName()] = NeteaseAPI
|
||||
}
|
||||
|
||||
// Netease private helper method
|
||||
|
||||
func _neteaseGetArtistNames(data neteaseTypes.SongDetailData) string {
|
||||
artists := make([]string, 0)
|
||||
for _, a := range data.Ar {
|
||||
artists = append(artists, a.Name)
|
||||
}
|
||||
return strings.Join(artists, ",")
|
||||
}
|
||||
|
||||
// MediaProvider implementation
|
||||
|
||||
func (n *Netease) GetName() string {
|
||||
return "netease"
|
||||
}
|
||||
|
||||
func (n *Netease) MatchMedia(keyword string) *model.Media {
|
||||
if id := n.IdRegex0.FindString(keyword); id != "" {
|
||||
return &model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: n.GetName(),
|
||||
Id: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
if id := n.IdRegex1.FindString(keyword); id != "" {
|
||||
return &model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: n.GetName(),
|
||||
Id: id[2:],
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Netease) FormatPlaylistUrl(uri string) string {
|
||||
var id string
|
||||
id = n.PlaylistRegex0.FindString(uri)
|
||||
if id != "" {
|
||||
return id
|
||||
}
|
||||
id = n.PlaylistRegex1.FindString(uri)
|
||||
if id != "" {
|
||||
return id[12:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (n *Netease) GetPlaylist(playlist *model.Meta) ([]*model.Media, error) {
|
||||
result, err := neteaseApi.GetPlaylistDetail(
|
||||
n.ReqData, util.Atoi(playlist.Id))
|
||||
if err != nil || result.Code != 200 {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
cnt := len(result.Playlist.TrackIds)
|
||||
if cnt == 0 {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
|
||||
ids := make([]int, len(result.Playlist.TrackIds))
|
||||
for i := 0; i < cnt; i++ {
|
||||
ids[i] = result.Playlist.TrackIds[i].Id
|
||||
}
|
||||
medias := make([]*model.Media, 0, cnt)
|
||||
for index := 0; index < len(ids); index += 1000 {
|
||||
result2, err := neteaseApi.GetSongDetail(
|
||||
n.ReqData,
|
||||
ids[index:util.IntMin(index+1000, len(ids))])
|
||||
if err != nil || result2.Code != 200 {
|
||||
break
|
||||
}
|
||||
cnt = len(result2.Songs)
|
||||
if cnt == 0 {
|
||||
break
|
||||
}
|
||||
for i := 0; i < cnt; i++ {
|
||||
medias = append(medias, &model.Media{
|
||||
Title: result2.Songs[i].Name,
|
||||
Artist: _neteaseGetArtistNames(result2.Songs[i]),
|
||||
Cover: model.Picture{Url: result2.Songs[i].Al.PicUrl},
|
||||
Album: result2.Songs[i].Al.Name,
|
||||
Url: "",
|
||||
Header: nil,
|
||||
User: nil,
|
||||
Meta: model.Meta{
|
||||
Name: n.GetName(),
|
||||
Id: strconv.Itoa(result2.Songs[i].Id),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(medias) == 0 {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
return medias, nil
|
||||
}
|
||||
|
||||
func (n *Netease) Search(keyword string) ([]*model.Media, error) {
|
||||
rawResult, err := neteaseApi.SearchSong(
|
||||
n.ReqData,
|
||||
neteaseApi.SearchSongConfig{
|
||||
Keyword: keyword,
|
||||
Limit: 30,
|
||||
Offset: 0,
|
||||
})
|
||||
if err != nil || rawResult.Code != 200 {
|
||||
return nil, ErrorExternalApi
|
||||
}
|
||||
medias := make([]*model.Media, 0)
|
||||
for _, song := range rawResult.Result.Songs {
|
||||
artists := make([]string, 0)
|
||||
for _, a := range song.Artists {
|
||||
artists = append(artists, a.Name)
|
||||
}
|
||||
medias = append(medias, &model.Media{
|
||||
Title: song.Name,
|
||||
Artist: strings.Join(artists, ","),
|
||||
Cover: model.Picture{},
|
||||
Album: song.Album.Name,
|
||||
Url: "",
|
||||
Header: nil,
|
||||
Meta: model.Meta{
|
||||
Name: n.GetName(),
|
||||
Id: strconv.Itoa(song.Id),
|
||||
},
|
||||
})
|
||||
}
|
||||
return medias, nil
|
||||
}
|
||||
|
||||
func (n *Netease) UpdateMedia(media *model.Media) error {
|
||||
result, err := neteaseApi.GetSongDetail(
|
||||
n.ReqData,
|
||||
[]int{util.Atoi(media.Meta.(model.Meta).Id)})
|
||||
if err != nil || result.Code != 200 {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
if len(result.Songs) == 0 {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Title = result.Songs[0].Name
|
||||
media.Cover.Url = result.Songs[0].Al.PicUrl
|
||||
media.Album = result.Songs[0].Al.Name
|
||||
media.Artist = _neteaseGetArtistNames(result.Songs[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Netease) UpdateMediaUrl(media *model.Media) error {
|
||||
result, err := neteaseApi.GetSongURL(
|
||||
n.ReqData,
|
||||
neteaseApi.SongURLConfig{Ids: []int{util.Atoi(media.Meta.(model.Meta).Id)}})
|
||||
if err != nil || result.Code != 200 {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
if len(result.Data) == 0 {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
if result.Data[0].Code != 200 {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Url = result.Data[0].Url
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Netease) UpdateMediaLyric(media *model.Media) error {
|
||||
result, err := neteaseApi.GetSongLyric(n.ReqData, util.Atoi(media.Meta.(model.Meta).Id))
|
||||
if err != nil || result.Code != 200 {
|
||||
return ErrorExternalApi
|
||||
}
|
||||
media.Lyric = result.Lrc.Lyric
|
||||
return nil
|
||||
}
|
||||
62
adapters/provider/netease_extra.go
Normal file
62
adapters/provider/netease_extra.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
neteaseApi "github.com/XiaoMengXinX/Music163Api-Go/api"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Netease other method
|
||||
|
||||
func (n *Netease) UpdateStatus() {
|
||||
status, _ := neteaseApi.GetLoginStatus(n.ReqData)
|
||||
n.loginStatus = status
|
||||
}
|
||||
|
||||
// IsLogin check if current cookie is a login user
|
||||
func (n *Netease) IsLogin() bool {
|
||||
return n.loginStatus.Profile.UserId != 0
|
||||
}
|
||||
|
||||
func (n *Netease) Nickname() string {
|
||||
return n.loginStatus.Profile.Nickname
|
||||
}
|
||||
|
||||
func (n *Netease) GetQrLoginKey() string {
|
||||
unikey, err := neteaseApi.GetQrUnikey(n.ReqData)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return unikey.Unikey
|
||||
}
|
||||
|
||||
func (n *Netease) GetQrLoginUrl(key string) string {
|
||||
return fmt.Sprintf("https://music.163.com/login?codekey=%s", key)
|
||||
}
|
||||
|
||||
func (n *Netease) CheckQrLogin(key string) (bool, string) {
|
||||
login, h, err := neteaseApi.CheckQrLogin(n.ReqData, key)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
// if login.Code == 800 || login.Code == 803. login success
|
||||
if login.Code != 800 && login.Code != 803 {
|
||||
return false, login.Message
|
||||
}
|
||||
cookies := make([]*http.Cookie, 0)
|
||||
for _, c := range (&http.Response{Header: h}).Cookies() {
|
||||
if c.Name == "MUSIC_U" || c.Name == "__csrf" {
|
||||
cookies = append(cookies, c)
|
||||
}
|
||||
}
|
||||
n.ReqData.Cookies = cookies
|
||||
return true, login.Message
|
||||
}
|
||||
|
||||
func (n *Netease) Logout() {
|
||||
n.ReqData.Cookies = []*http.Cookie{
|
||||
{Name: "MUSIC_U", Value: ""},
|
||||
{Name: "__csrf", Value: ""},
|
||||
}
|
||||
return
|
||||
}
|
||||
104
adapters/provider/netease_test.go
Normal file
104
adapters/provider/netease_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
"AynaLivePlayer/core/model"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNetease_Search(t *testing.T) {
|
||||
var api adapter.MediaProvider = NeteaseAPI
|
||||
result, err := api.Search("染 reol")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(result)
|
||||
media := result[0]
|
||||
fmt.Println(media)
|
||||
err = api.UpdateMediaUrl(media)
|
||||
fmt.Println(err)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestNetease_Search2(t *testing.T) {
|
||||
var api adapter.MediaProvider = NeteaseAPI
|
||||
result, err := api.Search("出山")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t.Log(result)
|
||||
media := result[0]
|
||||
t.Log(media)
|
||||
err = api.UpdateMediaUrl(media)
|
||||
t.Log(err)
|
||||
t.Log(media.Url)
|
||||
}
|
||||
|
||||
func TestNetease_GetMusicMeta(t *testing.T) {
|
||||
var api adapter.MediaProvider = NeteaseAPI
|
||||
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "33516503",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
fmt.Println(err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
}
|
||||
|
||||
func TestNetease_GetMusic(t *testing.T) {
|
||||
var api adapter.MediaProvider = NeteaseAPI
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "33516503",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMedia(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = api.UpdateMediaUrl(&media)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Println(media)
|
||||
fmt.Println(media.Url)
|
||||
}
|
||||
|
||||
func TestNetease_GetPlaylist(t *testing.T) {
|
||||
var api adapter.MediaProvider = NeteaseAPI
|
||||
playlist, err := api.GetPlaylist(&model.Meta{
|
||||
Name: api.GetName(),
|
||||
//Id: "2520739691",
|
||||
Id: "2382819181",
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(len(playlist))
|
||||
for _, media := range playlist {
|
||||
fmt.Println(media.Title, media.Artist, media.Album)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNetease_UpdateMediaLyric(t *testing.T) {
|
||||
var api adapter.MediaProvider = NeteaseAPI
|
||||
media := model.Media{
|
||||
Meta: model.Meta{
|
||||
Name: api.GetName(),
|
||||
Id: "33516503",
|
||||
},
|
||||
}
|
||||
err := api.UpdateMediaLyric(&media)
|
||||
fmt.Println(err)
|
||||
fmt.Println(media.Lyric)
|
||||
}
|
||||
65
adapters/provider/provider.go
Normal file
65
adapters/provider/provider.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/core/adapter"
|
||||
model "AynaLivePlayer/core/model"
|
||||
)
|
||||
|
||||
var RegisteredProviders = map[string]adapter.MediaProviderCtor{
|
||||
"netease": NewNetease,
|
||||
"local": NewLocalCtor,
|
||||
"kuwo": NewKuwo,
|
||||
"bilibili": NewBilibili,
|
||||
"bilibili-video": NewBilibiliVideo,
|
||||
}
|
||||
|
||||
var Providers map[string]adapter.MediaProvider = make(map[string]adapter.MediaProvider)
|
||||
|
||||
func GetPlaylist(meta *model.Meta) ([]*model.Media, error) {
|
||||
if v, ok := Providers[meta.Name]; ok {
|
||||
return v.GetPlaylist(meta)
|
||||
}
|
||||
return nil, ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func FormatPlaylistUrl(pname, uri string) (string, error) {
|
||||
if v, ok := Providers[pname]; ok {
|
||||
return v.FormatPlaylistUrl(uri), nil
|
||||
}
|
||||
return "", ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func MatchMedia(provider string, keyword string) *model.Media {
|
||||
if v, ok := Providers[provider]; ok {
|
||||
return v.MatchMedia(keyword)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Search(provider string, keyword string) ([]*model.Media, error) {
|
||||
if v, ok := Providers[provider]; ok {
|
||||
return v.Search(keyword)
|
||||
}
|
||||
return nil, ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func UpdateMedia(media *model.Media) error {
|
||||
if v, ok := Providers[media.Meta.(model.Meta).Name]; ok {
|
||||
return v.UpdateMedia(media)
|
||||
}
|
||||
return ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func UpdateMediaUrl(media *model.Media) error {
|
||||
if v, ok := Providers[media.Meta.(model.Meta).Name]; ok {
|
||||
return v.UpdateMediaUrl(media)
|
||||
}
|
||||
return ErrorNoSuchProvider
|
||||
}
|
||||
|
||||
func UpdateMediaLyric(media *model.Media) error {
|
||||
if v, ok := Providers[media.Meta.(model.Meta).Name]; ok {
|
||||
return v.UpdateMediaLyric(media)
|
||||
}
|
||||
return ErrorNoSuchProvider
|
||||
}
|
||||
26
adapters/provider/util.go
Normal file
26
adapters/provider/util.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"AynaLivePlayer/common/util"
|
||||
"AynaLivePlayer/core/model"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func MediaSort(keyword string, medias []*model.Media) {
|
||||
mediaDist := make([]struct {
|
||||
media *model.Media
|
||||
dist int
|
||||
}, len(medias))
|
||||
for i, media := range medias {
|
||||
mediaDist[i].media = media
|
||||
mediaDist[i].dist = util.StrLen(util.LongestCommonString(keyword, media.Title)) +
|
||||
util.StrLen(util.LongestCommonString(keyword, media.Artist))
|
||||
}
|
||||
sort.Slice(mediaDist, func(i, j int) bool {
|
||||
return mediaDist[i].dist > mediaDist[j].dist
|
||||
})
|
||||
for i, media := range mediaDist {
|
||||
medias[i] = media.media
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user