2023-10-23 18:24:52 +00:00
|
|
|
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
|
|
|
|
}
|
2023-11-02 20:30:29 +00:00
|
|
|
func (v Video) GetUrlByItag(itagNo int64) string {
|
|
|
|
for _, format := range v.StreamingData.AdaptiveFormats {
|
|
|
|
if format.Itag == itagNo {
|
|
|
|
return format.URL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2023-10-23 18:24:52 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|