From d7a23bf4d56243d8631b5d1400f1922708453af9 Mon Sep 17 00:00:00 2001 From: Shiny Nematoda Date: Sat, 16 Apr 2022 22:42:41 +0530 Subject: [PATCH] Added 'next' endpoint and 'singles' for artist --- fetch.go | 42 ++++++++++++++++++++++++++++------ main.go | 16 ++++++++++--- parser.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++---- structs.go | 39 +++++++++++++++++++++++++++----- types.go | 30 +++++++++++++++++-------- 5 files changed, 165 insertions(+), 28 deletions(-) diff --git a/fetch.go b/fetch.go index b1b85e4..c970c4e 100644 --- a/fetch.go +++ b/fetch.go @@ -1,16 +1,16 @@ package main import ( - "bytes" - "encoding/json" "io" "log" + "bytes" "net/http" + "encoding/json" ) -func Fetch(data []byte) (string, int, error) { +func Fetch(path string, data []byte) (string, int, error) { - url := "https://music.youtube.com/youtubei/v1/browse?key=AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30" + url := "https://music.youtube.com/youtubei/v1/" + path + "?key=AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30&prettyPrint=false" req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) if err != nil { @@ -19,7 +19,8 @@ func Fetch(data []byte) (string, int, error) { req.Header.Set("Content-Type", "application/json; charset=utf-8") req.Header.Set("Origin", "https://music.youtube.com") - req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") + req.Header.Set("x-origin", "https://music.youtube.com") + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36") client := &http.Client{} @@ -47,7 +48,7 @@ func FetchArtist(id string) (string, int) { return ErrorMessage(err), 500 } - raw, status, err := Fetch(data) + raw, status, err := Fetch("browse", data) if err != nil { return ErrorMessage(err), 500 } else if status > 300 { @@ -59,5 +60,32 @@ func FetchArtist(id string) (string, int) { return ErrorMessage(err), 500 } - return res, 200 + return res, status } + +func FetchNext(id string) (string, int) { + + data, err := json.Marshal(GetTypeNext(id)) + if err != nil { + return ErrorMessage(err), 500 + } + + raw, status, err := Fetch("next", data) + if err != nil { + return ErrorMessage(err), 500 + } else if status > 300 { + return raw, status + } + + res, err := ParseNext(raw) + + return res, status +} + +/*func main() { + + res, status := FetchNext("KWLGyeg74es") + + log.Println(status) + log.Println(res) +}*/ \ No newline at end of file diff --git a/main.go b/main.go index 4ffb52e..5d1de8d 100644 --- a/main.go +++ b/main.go @@ -8,16 +8,25 @@ import ( ) func SetHeaders(c *fiber.Ctx) error { - c.Vary("Origin") c.Type("json", "utf-8") - c.Set("Access-Control-Allow-Origin", "https://hyperpipe.surge.sh") + c.Set("Access-Control-Allow-Origin", "*") + c.Set("Access-Control-Allow-Headers", "*") + c.Set("Access-Control-Max-Age", "1728000") + return c.Next() } func HandleHealth(c *fiber.Ctx) error { defer calc()() - return c.Status(204).SendString("") + return c.SendStatus(204) +} + +func HandleNext(c *fiber.Ctx) error { + defer calc()() + + res, status := FetchNext(c.Params("id")) + return c.Status(status).SendString(res) } func HandleBrowse(c *fiber.Ctx) error { @@ -47,6 +56,7 @@ func main() { app.Use(recover.New()) app.Get("/healthz", HandleHealth) + app.Get("/next/:id", HandleNext) app.Get("/browse/:id", HandleBrowse) app.Get("/channel/:id", HandleArtist) diff --git a/parser.go b/parser.go index 7f9d86c..6fa3f5a 100644 --- a/parser.go +++ b/parser.go @@ -72,11 +72,11 @@ func GetSongs(s gjson.Result) []Item { defer wg.Done() j := v.Get("musicResponsiveListItemRenderer") - f := j.Get("flexColumns.#.musicResponsiveListItemFlexColumnRenderer.text.runs.0") + flex := j.Get("flexColumns.#.musicResponsiveListItemFlexColumnRenderer.text.runs.0") r = append(r, Item{ Id: j.Get("playlistItemData.videoId").String(), - Title: f.Get("#(navigationEndpoint.watchEndpoint.videoId).text").String(), + Title: flex.Get("#(navigationEndpoint.watchEndpoint.videoId).text").String(), Thumbnails: GetThumbnails(j.Get("thumbnail.musicThumbnailRenderer.thumbnail.thumbnails")), }) @@ -97,9 +97,9 @@ func GetTwoRowItem(t string, a gjson.Result) []Item { var id string - if t == "album" { + if t == "album" || t == "singles" { id = "menu.menuRenderer.items.#(menuNavigationItemRenderer.text.runs.0.text == Shuffle play).menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId" - } else if t == "artist" { + } else { id = "navigationEndpoint.browseEndpoint.browseId" } @@ -133,6 +133,39 @@ func GetTwoRowItem(t string, a gjson.Result) []Item { return r } +func GetNextSongs(n gjson.Result) []Item { + + r := []Item{} + + wg := sync.WaitGroup{} + + n.ForEach( + func(_, v gjson.Result) bool { + + wg.Add(1) + + go func() { + defer wg.Done() + + j := v.Get("playlistPanelVideoRenderer") + + r = append(r, Item{ + Id: j.Get("navigationEndpoint.watchEndpoint.videoId").String(), + Title: RunsText(j.Get("title")), + Sub: j.Get("longBylineText.runs.2.text").String(), + Thumbnails: GetThumbnails(j.Get("thumbnail.thumbnails")), + }) + }() + + wg.Wait() + + return true + }, + ) + + return r +} + func ParseArtist(raw string) (string, error) { j := gjson.Parse(raw) @@ -142,6 +175,7 @@ func ParseArtist(raw string) (string, error) { s := c.Get("#(musicShelfRenderer.title.runs.0.text == Songs).musicShelfRenderer") a := c.Get("#(musicCarouselShelfRenderer.header.musicCarouselShelfBasicHeaderRenderer.title.runs.0.text == Albums).musicCarouselShelfRenderer") + m := c.Get("#(musicCarouselShelfRenderer.header.musicCarouselShelfBasicHeaderRenderer.title.runs.0.text == Singles).musicCarouselShelfRenderer") u := c.Get("#(musicCarouselShelfRenderer.header.musicCarouselShelfBasicHeaderRenderer.title.runs.0.text == Fans might also like).musicCarouselShelfRenderer") val := Artist{ @@ -154,6 +188,7 @@ func ParseArtist(raw string) (string, error) { Items: Items{ Songs: GetSongs(s.Get("contents")), Albums: GetTwoRowItem("album", a.Get("contents")), + Singles: GetTwoRowItem("singles", m.Get("contents")), Artists: GetTwoRowItem("artist", u.Get("contents")), }, } @@ -165,3 +200,26 @@ func ParseArtist(raw string) (string, error) { return string(res), nil } + +func ParseNext(raw string) (string, error) { + + j := gjson.Parse(raw) + + m := j.Get("playerOverlays.playerOverlayRenderer.browserMediaSession.browserMediaSessionRenderer") + n := j.Get("contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs.#(tabRenderer.title == Up next).tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer") + + val := Next{ + MediaSession: MediaSession{ + Album: RunsText(m.Get("album")), + Thumbnails: GetThumbnails(m.Get("thumbnailDetails.thumbnails")), + }, + Songs: GetNextSongs(n.Get("contents")), + } + + res, err := json.Marshal(val) + if err != nil { + return "", err + } + + return string(res), nil +} \ No newline at end of file diff --git a/structs.go b/structs.go index 90c43e7..2085e04 100644 --- a/structs.go +++ b/structs.go @@ -1,5 +1,7 @@ package main + +/* Parsers */ type Thumbnail struct { Height int64 `json:"height"` Width int64 `json:"width"` @@ -16,6 +18,7 @@ type Item struct { type Items struct { Songs []Item `json:"songs"` Albums []Item `json:"albums"` + Singles []Item `json:"singles"` Artists []Item `json:"recommendedArtists"` } @@ -29,6 +32,18 @@ type Artist struct { Items Items `json:"items"` } +type MediaSession struct { + Thumbnails []Thumbnail `json:"thumbnails"` + Album string `json:"album"` +} + +type Next struct { + Songs []Item `json:"songs"` + MediaSession MediaSession `json:"mediaSession"` +} + + +/* Structs and Types */ type Client struct { Name string `json:"clientName"` Version string `json:"clientVersion"` @@ -42,12 +57,26 @@ type PageType struct { PageType string `json:"pageType"` } -type MusicConfig struct { +type BrowseMusicConfig struct { MusicConfig PageType `json:"browseEndpointContextMusicConfig"` } -type BrowseData struct { - Context Context `json:"context"` - MusicConfig MusicConfig `json:"browseEndpointContextMusicConfig"` - BrowseId string `json:"browseId"` +type WatchMusicConfig struct { + Panel bool `json:"hasPersistentPlaylistPanel"` + Type string `json:"musicVideoType"` +} + +type BrowseData struct { + Context Context `json:"context"` + MusicConfig BrowseMusicConfig `json:"browseEndpointContextMusicConfig"` + BrowseId string `json:"browseId"` +} + +type NextData struct { + Id string `json:"videoId"` + Context Context `json:"context"` + Audio bool `json:"isAudioOnly"` + Tuner string `json:"tunerSettingValue"` + Panel bool `json:"enablePersistentPlaylistPanel"` + MusicConfig WatchMusicConfig `json:"watchEndpointMusicConfig"` } diff --git a/types.go b/types.go index 54eb615..42f8834 100644 --- a/types.go +++ b/types.go @@ -2,19 +2,17 @@ package main import "strings" -func GetContext() Context { - return Context{ - Client: Client{ - Name: "WEB_REMIX", - Version: "1.20211213.00.00", - }, - } +var BaseContext = Context{ + Client: Client{ + Name: "WEB_REMIX", + Version: "1.20211213.00.00", + }, } func GetTypeBrowse(t, id string) BrowseData { return BrowseData{ - Context: GetContext(), - MusicConfig: MusicConfig{ + Context: BaseContext, + MusicConfig: BrowseMusicConfig{ MusicConfig: PageType{ PageType: "MUSIC_PAGE_TYPE_" + strings.ToUpper(t), }, @@ -22,3 +20,17 @@ func GetTypeBrowse(t, id string) BrowseData { BrowseId: id, } } + +func GetTypeNext(id string) NextData { + return NextData{ + Id: id, + Context: BaseContext, + Panel: true, + Audio: true, + Tuner: "AUTOMIX_SETTING_NORMAL", + MusicConfig: WatchMusicConfig{ + Panel: true, + Type: "MUSIC_VIDEO_TYPE_ATV", + }, + } +}