dndmusicbot/youtube/youtube.go

151 lines
3.6 KiB
Go

package youtube
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/grafov/m3u8"
)
type Client struct {
InnertubeClient *InnertubeClient
HTTPClient *http.Client
}
type InnertubeClient struct {
Key string `json:"-"`
HDRClientName string `json:"-"`
HL string `json:"hl,omitempty"`
GL string `json:"gl,omitempty"`
ClientName string `json:"clientName,omitempty"`
ClientVersion string `json:"clientVersion,omitempty"`
DeviceModel string `json:"deviceModel,omitempty"`
UserAgent string `json:"userAgent,omitempty"`
TimeZone string `json:"timeZone,omitempty"`
UTCOffsetMins int64 `json:"utcOffsetMinutes,omitempty"`
}
type innertubeRequest struct {
VideoID string `json:"videoId,omitempty"`
BrowseID string `json:"browseId,omitempty"`
Continuation string `json:"continuation,omitempty"`
Context struct {
Client InnertubeClient `json:"client"`
} `json:"context"`
PlaybackContext struct {
ContentPlaybackContext struct {
HTML5Preference string `json:"html5Preference"`
} `json:"contentPlaybackContext"`
} `json:"playbackContext,omitempty"`
ContentCheckOK bool `json:"contentCheckOk,omitempty"`
RacyCheckOk bool `json:"racyCheckOk,omitempty"`
Params string `json:"params"`
}
var (
IOSClient = InnertubeClient{
Key: "AIzaSyAOghZGza2MQSZkY_zfZ370N-PUdXEo8AI",
HDRClientName: "5",
HL: "en",
GL: "US",
ClientName: "IOS",
ClientVersion: "17.33.2",
DeviceModel: "iPhone14,3",
UserAgent: "com.google.ios.youtube/17.33.2 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)",
TimeZone: "UTC",
UTCOffsetMins: 0,
}
)
var DefaultClient = IOSClient
func New() *Client {
client := new(Client)
client.HTTPClient = &http.Client{}
client.InnertubeClient = &DefaultClient
return client
}
func (c Client) GetVideoFromID(id string) (out Video, err error) {
uri, _ := url.Parse("https://www.youtube.com/youtubei/v1/player")
q := uri.Query()
q.Add("key", c.InnertubeClient.Key)
uri.RawQuery = q.Encode()
reqData := new(innertubeRequest)
reqData.Context.Client = *c.InnertubeClient
reqData.VideoID = id
reqData.ContentCheckOK = true
reqData.RacyCheckOk = true
reqData.Params = "CgIQBg=="
reqData.PlaybackContext.ContentPlaybackContext.HTML5Preference = "HTML5_PREF_WANTS"
reqBody, err := json.Marshal(reqData)
if err != nil {
return
}
payload := bytes.NewReader(reqBody)
req, err := http.NewRequest("POST", uri.String(), payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("User-Agent", c.InnertubeClient.UserAgent)
req.Header.Add("X-YouTube-Client-Name", c.InnertubeClient.HDRClientName)
req.Header.Add("X-YouTube-Client-Version", c.InnertubeClient.ClientVersion)
req.Header.Add("Content-Type", "application/json")
res, err := c.HTTPClient.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
json.NewDecoder(res.Body).Decode(&out)
return
}
func (v Video) GetUrlByItag(itagNo int64) string {
for _, format := range v.StreamingData.AdaptiveFormats {
if format.Itag == itagNo {
return format.URL
}
}
return ""
}
func (v Video) GetHLSPlaylist(itag string) (out string) {
resp, err := http.Get(v.StreamingData.HlsManifestURL)
if err != nil {
return
}
defer resp.Body.Close()
p, _, err := m3u8.DecodeFrom(resp.Body, true)
if err != nil {
return
}
pl := p.(*m3u8.MasterPlaylist)
for _, variant := range pl.Variants {
if len(variant.Alternatives) > 0 {
for _, a := range variant.Alternatives {
if a.GroupId == itag {
return a.URI
}
}
}
}
return
}