From 4d1e04a1ecd31df306fc2c99c38c275e652c97f0 Mon Sep 17 00:00:00 2001 From: Shiny Nematoda Date: Thu, 23 Mar 2023 18:01:10 +0000 Subject: [PATCH] Support for `moreButton` in artist page (Hyperpipe/Hyperpipe#34) --- lib/artist.go | 76 +++++++++++++++++++++++++++++++++++++++++- lib/charts.go | 2 +- lib/common.go | 7 ---- lib/explore.go | 2 +- lib/genre.go | 2 +- lib/genres.go | 2 +- lib/lyrics.go | 2 +- main.go | 22 +++++++++---- utils/types.go | 89 +++++++++++++++++++++++++++++++------------------- 9 files changed, 152 insertions(+), 52 deletions(-) diff --git a/lib/artist.go b/lib/artist.go index 67b9f76..6364db3 100644 --- a/lib/artist.go +++ b/lib/artist.go @@ -7,6 +7,29 @@ import ( "github.com/tidwall/gjson" ) +type Items struct { + Songs []Item `json:"songs"` + Albums []Item `json:"albums"` + Singles []Item `json:"singles"` + Artists []Item `json:"recommendedArtists"` +} + +type MoreItem struct { + Params string `json:"params"` + Click string `json:"click"` + Visit string `json:"visit"` +} + +type ArtistMore struct { + Album MoreItem `json:"album"` + Singles MoreItem `json:"singles"` +} + +type ArtistNext struct { + Title string `json:"title"` + Items []Item `json:"items"` +} + type Artist struct { Title string `json:"title"` Description string `json:"description,omitempty"` @@ -15,6 +38,17 @@ type Artist struct { SubscriberCount string `json:"subscriberCount,omitempty"` Thumbnails []Thumbnail `json:"thumbnails"` Items Items `json:"items"` + More ArtistMore `json:"more"` +} + +func parseMoreButton(raw, v gjson.Result) MoreItem { + nav := raw.Get("header.musicCarouselShelfBasicHeaderRenderer.moreContentButton.buttonRenderer.navigationEndpoint") + + return MoreItem{ + Params: nav.Get("browseEndpoint.params").String(), + Click: nav.Get("clickTrackingParams").String(), + Visit: v.String(), + } } func parseArtist(raw string) (string, error) { @@ -61,6 +95,32 @@ func parseArtist(raw string) (string, error) { Singles: TwoRowItemRenderer(m.Get("contents"), true), Artists: TwoRowItemRenderer(u.Get("contents"), false), }, + More: ArtistMore{ + Album: parseMoreButton(a, j.Get("responseContext.visitorData")), + Singles: parseMoreButton(m, j.Get("responseContext.visitorData")), + }, + } + + res, err := json.Marshal(val) + if err != nil { + return "", err + } + + return string(res), nil +} + +func parseArtistNext(raw string) (string, error) { + + j := gjson.Parse(raw) + + val := ArtistNext{ + Title: RunsText(j.Get("header.musicHeaderRenderer.title")), + Items: TwoRowItemRenderer( + j.Get( + "contents.singleColumnBrowseResultsRenderer.tabs.0.tabRenderer.content.sectionListRenderer.contents.0.gridRenderer.items", + ), + true, + ), } res, err := json.Marshal(val) @@ -73,7 +133,7 @@ func parseArtist(raw string) (string, error) { func GetArtist(id string) (string, int) { - context := utils.TypeBrowse("artist", id, "", "") + context := utils.TypeBrowsePage(id, "artist") raw, status := utils.FetchBrowse(context) @@ -84,3 +144,17 @@ func GetArtist(id string) (string, int) { return res, status } + +func GetArtistNext(id, params, ct, v string) (string, int) { + + context := utils.TypeBrowse(id, params, []string{ct, v}) + + raw, status := utils.FetchBrowse(context) + + res, err := parseArtistNext(raw) + if err != nil { + return utils.ErrMsg(err), 500 + } + + return res, status +} diff --git a/lib/charts.go b/lib/charts.go index e456bf7..9c16701 100644 --- a/lib/charts.go +++ b/lib/charts.go @@ -62,7 +62,7 @@ func parseCharts(raw string) (string, error) { func GetCharts(params, code string) (string, int) { - context := utils.TypeBrowse("", "FEmusic_charts", params, code) + context := utils.TypeBrowseForm("FEmusic_charts", params, code) raw, status := utils.FetchBrowse(context) diff --git a/lib/common.go b/lib/common.go index 228990a..d55eabe 100644 --- a/lib/common.go +++ b/lib/common.go @@ -25,13 +25,6 @@ type Item struct { Thumbnails []Thumbnail `json:"thumbnails,omitempty"` } -type Items struct { - Songs []Item `json:"songs"` - Albums []Item `json:"albums"` - Singles []Item `json:"singles"` - Artists []Item `json:"recommendedArtists"` -} - func RunsText(j gjson.Result) string { var s []string diff --git a/lib/explore.go b/lib/explore.go index 2d1a88c..9b2a16c 100644 --- a/lib/explore.go +++ b/lib/explore.go @@ -52,7 +52,7 @@ func parseExplore(raw string) (string, error) { func GetExplore() (string, int) { - context := utils.TypeBrowse("", "FEmusic_explore", "", "") + context := utils.TypeBrowse("FEmusic_explore", "", []string{}) raw, status := utils.FetchBrowse(context) diff --git a/lib/genre.go b/lib/genre.go index 6e381d1..e973f8f 100644 --- a/lib/genre.go +++ b/lib/genre.go @@ -50,7 +50,7 @@ func parseGenre(raw string) (string, error) { func GetGenre(param string) (string, int) { - context := utils.TypeBrowse("", "FEmusic_moods_and_genres_category", param, "") + context := utils.TypeBrowse("FEmusic_moods_and_genres_category", param, []string{}) raw, status := utils.FetchBrowse(context) diff --git a/lib/genres.go b/lib/genres.go index 41ac4b4..d919ef0 100644 --- a/lib/genres.go +++ b/lib/genres.go @@ -40,7 +40,7 @@ func parseGenres(raw string) (string, error) { func GetGenres() (string, int) { - context := utils.TypeBrowse("", "FEmusic_moods_and_genres", "", "") + context := utils.TypeBrowse("FEmusic_moods_and_genres", "", []string{}) raw, status := utils.FetchBrowse(context) diff --git a/lib/lyrics.go b/lib/lyrics.go index b9ad93e..6090811 100644 --- a/lib/lyrics.go +++ b/lib/lyrics.go @@ -36,7 +36,7 @@ func parseLyrics(raw string) (string, error) { func GetLyrics(id string) (string, int) { - context := utils.TypeBrowse("lyrics", id, "", "") + context := utils.TypeBrowsePage(id, "lyrics") raw, status := utils.FetchBrowse(context) diff --git a/main.go b/main.go index e45ac79..e2b7bf8 100644 --- a/main.go +++ b/main.go @@ -61,6 +61,21 @@ func HandleLyrics(c *fiber.Ctx) error { return c.Status(status).SendString(res) } +func HandleArtist(c *fiber.Ctx) error { + res, status := lib.GetArtist(c.Params("id")) + + return c.Status(status).SendString(res) +} + +func HandleArtistNext(c *fiber.Ctx) error { + res, status := lib.GetArtistNext(c.Params("id"), + c.Params("params"), + c.Query("ct"), + c.Query("v")) + + return c.Status(status).SendString(res) +} + func HandleBrowse(c *fiber.Ctx) error { // Will be removed, Do not use @@ -83,12 +98,6 @@ func HandleBrowse(c *fiber.Ctx) error { } } -func HandleArtist(c *fiber.Ctx) error { - res, status := lib.GetArtist(c.Params("id")) - - return c.Status(status).SendString(res) -} - func main() { if os.Getenv("HYP_PROXY") == "" { fmt.Println("HYP_PROXY is empty!") @@ -109,6 +118,7 @@ func main() { app.Get("/lyrics/:id", HandleLyrics) app.Get("/browse/:id", HandleBrowse) app.Get("/channel/:id", HandleArtist) + app.Get("/next/channel/:id/:params", HandleArtistNext) port := os.Getenv("PORT") if port == "" { diff --git a/utils/types.go b/utils/types.go index db4642d..095641e 100644 --- a/utils/types.go +++ b/utils/types.go @@ -6,10 +6,17 @@ type Client struct { Name string `json:"clientName,omitempty"` Version string `json:"clientVersion,omitempty"` Hl string `json:"hl",omitempty` + Url string `json:"originalUrl",omitempty` + Visit string `json:"visitorData",omitempty` +} + +type Click struct { + Param string `json:"clickTrackingParams",omitempty` } type Context struct { Client Client `json:"client",omitempty` + Click Click `json:"clickTracking",omitempty` } type PageType struct { @@ -21,7 +28,7 @@ type BrowseMusicConfig struct { } type Form struct { - Values []string `json:"selectedValues"` + Values []string `json:"selectedValues",omitempty` } type WatchMusicConfig struct { @@ -30,11 +37,11 @@ type WatchMusicConfig struct { } type BrowseData struct { - Context Context `json:"context,omitempty"` - MusicConfig BrowseMusicConfig `json:"browseEndpointContextMusicConfig,omitempty"` - BrowseId string `json:"browseId,omitempty"` - Params string `json:"params,omitempty"` - Form Form `json:"formData,omitempty"` + Context *Context `json:"context,omitempty"` + MusicConfig *BrowseMusicConfig `json:"browseEndpointContextMusicConfig,omitempty"` + BrowseId string `json:"browseId,omitempty"` + Params *string `json:"params,omitempty"` + Form *Form `json:"formData,omitempty"` } type NextData struct { @@ -50,38 +57,54 @@ type NextData struct { var BaseContext Context = Context{ Client: Client{ Name: "WEB_REMIX", - Version: "1.20220926.01.00", - Hl: "en-US", + Version: "1.20230320.01.00", + Hl: "en", }, } -func TypeBrowse(t, id, params, form string) BrowseData { - if t != "" { - return BrowseData{ - Context: BaseContext, - MusicConfig: BrowseMusicConfig{ - MusicConfig: PageType{ - PageType: "MUSIC_PAGE_TYPE_" + strings.ToUpper(t), - }, - }, - BrowseId: id, - } - } else if form != "" { - return BrowseData{ - Context: BaseContext, - BrowseId: id, - Params: params, - Form: Form{ - Values: []string{form}, - }, - } - } else { - return BrowseData{ - Context: BaseContext, - BrowseId: id, - Params: params, +func TypeBrowsePage(id, t string) BrowseData { + config := BrowseMusicConfig{ + MusicConfig: PageType{ + PageType: "MUSIC_PAGE_TYPE_" + strings.ToUpper(t), + }, + } + + return BrowseData{ + Context: &BaseContext, + MusicConfig: &config, + BrowseId: id, + } +} + +func TypeBrowseForm(id, params, form string) BrowseData { + forms := Form{ + Values: []string{form}, + } + + return BrowseData{ + Context: &BaseContext, + BrowseId: id, + Params: ¶ms, + Form: &forms, + } +} + +func TypeBrowse(id, params string, ct []string) BrowseData { + data := BrowseData{ + Context: &BaseContext, + BrowseId: id, + Params: ¶ms, + } + + if len(ct) > 0 { + data.Context.Client.Url = "https://music.youtube.com/channel/" + id + data.Context.Client.Visit = ct[1] + data.Context.Click = Click{ + Param: ct[0], } } + + return data } func TypeNext(id, pid string) NextData {