✨ 音乐下载
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"likedream-goframe/internal/controller/common"
|
||||
"likedream-goframe/internal/controller/cpdaily"
|
||||
"likedream-goframe/internal/controller/menu"
|
||||
"likedream-goframe/internal/controller/music"
|
||||
"likedream-goframe/internal/controller/role"
|
||||
"likedream-goframe/internal/controller/user"
|
||||
"likedream-goframe/internal/controller/user_login"
|
||||
@@ -53,6 +54,8 @@ func StartServer(ctx context.Context) {
|
||||
cpdailyGroup.Bind(cpdaily.NewCpTaskCtrlV1())
|
||||
cpdailyGroup.Bind(cpdaily.NewCpImageCtrlV1())
|
||||
cpdailyGroup.Bind(cpdaily.NewCpTaskLogCtrlV1())
|
||||
musicGroup := group.Group("/music").Middleware(middleware.AuthHandler, middleware.PermissionHandler)
|
||||
musicGroup.Bind(music.NewMusicCtrlV1())
|
||||
})
|
||||
|
||||
s.SetPort(port.Int())
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package v1
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type SearchMusicReq struct {
|
||||
g.Meta `path:"/search" method:"get" tags:"Music" summary:"搜索音乐"`
|
||||
Keyword string `json:"keyword"`
|
||||
}
|
||||
|
||||
type DownloadMusicReq struct {
|
||||
g.Meta `path:"/download" method:"get" tags:"Music" summary:"下载音乐"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"time"
|
||||
|
||||
"resty.dev/v3"
|
||||
)
|
||||
|
||||
var Music = resty.New().
|
||||
SetCookieJar(nil).
|
||||
SetTimeout(3 * time.Minute).
|
||||
SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
@@ -0,0 +1,73 @@
|
||||
package music
|
||||
|
||||
import (
|
||||
"context"
|
||||
"likedream-goframe/internal/application/httpclient"
|
||||
"likedream-goframe/internal/common/errors"
|
||||
"likedream-goframe/internal/utils"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/samber/oops"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var MusicDownloader *Downloader
|
||||
|
||||
type Downloader struct {
|
||||
}
|
||||
|
||||
func (c *Downloader) Search(ctx context.Context, keyword string) (result []MusicSearchResult) {
|
||||
client := httpclient.Music.Clone(ctx)
|
||||
response, err := client.R().Get("https://www.gequbao.net/s/" + url.PathEscape(keyword))
|
||||
if err != nil {
|
||||
utils.OkOrPanic(errors.NewBusinessError(503, "搜索出错1"), oops.WithContext(ctx).Wrap)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
reader, err := goquery.NewDocumentFromReader(response.Body)
|
||||
if err != nil {
|
||||
utils.OkOrPanic(errors.NewBusinessError(503, "搜索出错2"), oops.WithContext(ctx).Wrap)
|
||||
}
|
||||
card := reader.Find(".card")
|
||||
find := card.Find(".card-text > .row:not(:first-child)")
|
||||
find.Each(func(i int, item *goquery.Selection) {
|
||||
selection := item.Find("a")
|
||||
url, _ := selection.Attr("href")
|
||||
name := strings.TrimSpace(selection.Find(".text-primary span").Text())
|
||||
singer := strings.TrimSpace(selection.Find(".text-jade").Text())
|
||||
result = append(result, MusicSearchResult{
|
||||
Name: name,
|
||||
Singer: singer,
|
||||
Path: url,
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Downloader) Download(ctx context.Context, url string) (result []byte) {
|
||||
client := httpclient.Music.Clone(ctx)
|
||||
response, err := client.R().Get("https://www.gequbao.net" + url)
|
||||
if err != nil {
|
||||
utils.OkOrPanic(errors.NewBusinessError(503, "下载出错1"), oops.WithContext(ctx).Wrap)
|
||||
}
|
||||
html := response.String()
|
||||
reg := regexp.MustCompile("window\\.play_id = '(.*?)';")
|
||||
play_id := reg.FindStringSubmatch(html)[1]
|
||||
post, err := client.R().SetBody(map[string]string{
|
||||
"id": play_id,
|
||||
}).Post("https://www.gequbao.net/api/play-url")
|
||||
if err != nil {
|
||||
utils.OkOrPanic(errors.NewBusinessError(503, "下载出错2"), oops.WithContext(ctx).Wrap)
|
||||
}
|
||||
download_url := gjson.Get(post.String(), "data.url").String()
|
||||
|
||||
// 下载音频文件
|
||||
downloadResp, err := client.R().Get(download_url)
|
||||
if err != nil {
|
||||
utils.OkOrPanic(errors.NewBusinessError(503, "下载出错3"), oops.WithContext(ctx).Wrap)
|
||||
}
|
||||
|
||||
return downloadResp.Bytes()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package music
|
||||
|
||||
type MusicSearchResult struct {
|
||||
Name string `json:"name"`
|
||||
Singer string `json:"singer"`
|
||||
Path string `json:"url"`
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package music
|
||||
|
||||
import (
|
||||
"context"
|
||||
v1 "likedream-goframe/internal/api/v1"
|
||||
"likedream-goframe/internal/application/music"
|
||||
"likedream-goframe/internal/utils"
|
||||
)
|
||||
|
||||
type IMusicCtrlV1 interface {
|
||||
Search(ctx context.Context, req *v1.SearchMusicReq) (res *utils.Response[[]music.MusicSearchResult], err error)
|
||||
Download(ctx context.Context, req *v1.DownloadMusicReq) (res *utils.Response[any], err error)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package music
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
v1 "likedream-goframe/internal/api/v1"
|
||||
"likedream-goframe/internal/application/music"
|
||||
"likedream-goframe/internal/utils"
|
||||
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
type MusicCtrlV1 struct {
|
||||
}
|
||||
|
||||
func NewMusicCtrlV1() IMusicCtrlV1 {
|
||||
return &MusicCtrlV1{}
|
||||
}
|
||||
|
||||
func (c *MusicCtrlV1) Search(ctx context.Context, req *v1.SearchMusicReq) (res *utils.Response[[]music.MusicSearchResult], err error) {
|
||||
defer utils.Pass(&err)
|
||||
return utils.SuccessWithData(music.MusicDownloader.Search(ctx, req.Keyword)), nil
|
||||
}
|
||||
|
||||
func (c *MusicCtrlV1) Download(ctx context.Context, req *v1.DownloadMusicReq) (res *utils.Response[any], err error) {
|
||||
defer utils.Pass(&err)
|
||||
|
||||
// 获取音频二进制数据
|
||||
audioData := music.MusicDownloader.Download(ctx, req.Path)
|
||||
|
||||
// 从 context 获取 ghttp.Request
|
||||
r := ghttp.RequestFromCtx(ctx)
|
||||
|
||||
// 构建文件名:Name - Singer.mp3
|
||||
filename := fmt.Sprintf("music.mp3")
|
||||
|
||||
// 设置响应头
|
||||
r.Response.Header().Set("Content-Type", "audio/mpeg")
|
||||
r.Response.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
||||
r.Response.Header().Set("Content-Length", fmt.Sprintf("%d", len(audioData)))
|
||||
|
||||
// 写入二进制数据
|
||||
r.Response.Write(audioData)
|
||||
|
||||
// 标记已处理,防止框架再次处理响应
|
||||
r.ExitAll()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user