130 lines
3.2 KiB
Go
130 lines
3.2 KiB
Go
|
package main
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"github.com/gofiber/fiber/v3/client"
|
|||
|
"net/url"
|
|||
|
"regexp"
|
|||
|
"strconv"
|
|||
|
"strings"
|
|||
|
)
|
|||
|
|
|||
|
var headers = map[string][]string{
|
|||
|
"Accept": {"application/json"},
|
|||
|
"Content-Type": {"application/json"},
|
|||
|
"User-Agent": {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"},
|
|||
|
"Host": {"api.bilibili.com"},
|
|||
|
}
|
|||
|
|
|||
|
type Client struct {
|
|||
|
cli *client.Client
|
|||
|
}
|
|||
|
|
|||
|
func NewClient(sessData string) *Client {
|
|||
|
return &Client{
|
|||
|
cli: client.New().AddHeaders(headers).SetCookie("SESSDATA", sessData),
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (c *Client) GetSubtitles(url string) (string, error) {
|
|||
|
vid, page, err := parseBilibiliUrl(url)
|
|||
|
if err != nil {
|
|||
|
return "", err
|
|||
|
}
|
|||
|
data, err := c.callView(vid)
|
|||
|
if err != nil {
|
|||
|
return "", err
|
|||
|
}
|
|||
|
var aid, cid = data.Data.Stat.Aid, 0
|
|||
|
for _, p := range data.Data.Pages {
|
|||
|
if p.Page == page {
|
|||
|
cid = p.Cid
|
|||
|
}
|
|||
|
}
|
|||
|
player, err := c.callPlayer(aid, cid)
|
|||
|
if err != nil {
|
|||
|
return "", err
|
|||
|
}
|
|||
|
subTitlesUrl := ""
|
|||
|
for _, sub := range player.Data.Subtitle.Subtitles {
|
|||
|
if sub.SubtitleUrl != "" {
|
|||
|
subTitlesUrl = fmt.Sprintf("https:%s", sub.SubtitleUrl)
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
subtitles, err := c.callSubtitles(subTitlesUrl)
|
|||
|
if err != nil {
|
|||
|
return "", err
|
|||
|
}
|
|||
|
var subtitlesList = make([]string, 0, len(subtitles.Body))
|
|||
|
for _, sub := range subtitles.Body {
|
|||
|
subtitlesList = append(subtitlesList, sub.Content)
|
|||
|
}
|
|||
|
return strings.Join(subtitlesList, "\n"), nil
|
|||
|
}
|
|||
|
|
|||
|
func (c *Client) callView(vid string) (data Response[ViewResponseData], err error) {
|
|||
|
var r = c.cli.R()
|
|||
|
if strings.HasPrefix(vid, "av") {
|
|||
|
r.AddParam("aid", vid[2:])
|
|||
|
} else {
|
|||
|
r.AddParam("bvid", vid)
|
|||
|
}
|
|||
|
resp, err := r.Get("https://api.bilibili.com/x/web-interface/view")
|
|||
|
if err != nil {
|
|||
|
return data, fmt.Errorf("请求 %s 出错:%v", r.URL(), err)
|
|||
|
}
|
|||
|
err = resp.JSON(&data)
|
|||
|
return data, err
|
|||
|
}
|
|||
|
|
|||
|
func (c *Client) callPlayer(aid, cid int) (data Response[PlayerResponseData], err error) {
|
|||
|
var r = c.cli.R()
|
|||
|
r.AddParam("aid", strconv.Itoa(aid))
|
|||
|
r.AddParam("cid", strconv.Itoa(cid))
|
|||
|
|
|||
|
resp, err := r.Get("https://api.bilibili.com/x/player/v2")
|
|||
|
if err != nil {
|
|||
|
return data, fmt.Errorf("请求 %s 出错:%v", r.URL(), err)
|
|||
|
}
|
|||
|
err = resp.JSON(&data)
|
|||
|
return data, err
|
|||
|
}
|
|||
|
|
|||
|
func (c *Client) callSubtitles(url string) (data SubtitlesResponse, err error) {
|
|||
|
var r = c.cli.R()
|
|||
|
resp, err := r.Get(url)
|
|||
|
if err != nil {
|
|||
|
return data, fmt.Errorf("请求 %s 出错:%v", r.URL(), err)
|
|||
|
}
|
|||
|
err = resp.JSON(&data)
|
|||
|
return data, err
|
|||
|
}
|
|||
|
|
|||
|
func parseBilibiliUrl(bu string) (vid string, page int, err error) {
|
|||
|
u, err := url.Parse(bu)
|
|||
|
if err != nil {
|
|||
|
return "", 0, err
|
|||
|
}
|
|||
|
if u.Host != "www.bilibili.com" && u.Host != "m.bilibili.com" {
|
|||
|
return "", 0, fmt.Errorf("链接似乎不是bilibili的视频链接")
|
|||
|
}
|
|||
|
re := regexp.MustCompile("/video/(?P<vid>[a-zA-Z0-9]+)")
|
|||
|
parts := re.FindStringSubmatch(u.Path)
|
|||
|
names := re.SubexpNames()
|
|||
|
for _, name := range names {
|
|||
|
if name == "vid" {
|
|||
|
vid = parts[re.SubexpIndex(name)]
|
|||
|
}
|
|||
|
}
|
|||
|
if vid == "" {
|
|||
|
return "", 0, fmt.Errorf("链接似乎不是bilibili的视频链接,未获取到视频id")
|
|||
|
}
|
|||
|
query := u.Query()
|
|||
|
if query.Get("p") != "" {
|
|||
|
pageStr := query.Get("p")
|
|||
|
page, _ = strconv.Atoi(pageStr)
|
|||
|
}
|
|||
|
return vid, page, nil
|
|||
|
}
|