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[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 }