This commit is contained in:
Shiny Nematoda
2022-06-15 15:38:11 +00:00
parent cab2a893e9
commit bb70f30f17
7 changed files with 353 additions and 115 deletions
+104
View File
@@ -0,0 +1,104 @@
package main
import (
"sync"
"github.com/tidwall/gjson"
)
func TwoRowItemRenderer(t string, a gjson.Result) []Item {
r := []Item{}
var id string
if t == "album" || t == "singles" {
id = "menu.menuRenderer.items.#(menuNavigationItemRenderer.text.runs.0.text == Shuffle play).menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId"
} else {
id = "navigationEndpoint.browseEndpoint.browseId"
}
wg := sync.WaitGroup{}
v := a.Get("#.musicTwoRowItemRenderer")
v.ForEach(
func(_, j gjson.Result) bool {
wg.Add(1)
go func() {
defer wg.Done()
r = append(r, Item{
Id: j.Get(id).String(),
Title: RunsText(j.Get("title")),
Sub: RunsText(j.Get("subtitle")),
Thumbnails: GetThumbnails(j.Get("thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails")),
})
}()
wg.Wait()
return true
},
)
return r
}
func CarouselShelfRenderer(i gjson.Result) map[string]interface{} {
shelf := make(map[string]interface{})
shelf["title"] = RunsText(i.Get("header.musicCarouselShelfBasicHeaderRenderer.title"))
ct := i.Get("contents")
if ct.Get("#(musicTwoRowItemRenderer)").Exists() {
shelf["contents"] = TwoRowItemRenderer("", ct)
} else if ct.Get("#(musicResponsiveListItemRenderer)").Exists() {
shelf["contents"] = ResponsiveListItemRenderer(ct)
}
shelf["raw"] = ct.Value()
return shelf
}
func ResponsiveListItemRenderer(s gjson.Result) []Item {
r := []Item{}
wg := sync.WaitGroup{}
s.ForEach(
func(_, v gjson.Result) bool {
wg.Add(1)
go func() {
defer wg.Done()
j := v.Get("musicResponsiveListItemRenderer")
flex := j.Get("flexColumns.#.musicResponsiveListItemFlexColumnRenderer.text.runs.0")
r = append(r, Item{
Id: j.Get("playlistItemData.videoId").String(),
Title: flex.Get("#(navigationEndpoint.watchEndpoint.videoId).text").String(),
SubId: flex.Get("#.navigationEndpoint.browseEndpoint").Get("#(browseId).browseId").String(),
Sub: flex.Get("#(navigationEndpoint.browseEndpoint.browseId).text").String(),
Thumbnails: GetThumbnails(j.Get("thumbnail.musicThumbnailRenderer.thumbnail.thumbnails")),
})
}()
wg.Wait()
return true
},
)
return r
}
+104 -10
View File
@@ -6,11 +6,24 @@ import (
"io"
"log"
"net/http"
"strings"
"github.com/tidwall/gjson"
)
func Fetch(path string, data []byte) (string, int, error) {
func Fetch(path string, data []byte, c string) (string, int, error) {
url := "https://music.youtube.com/youtubei/v1/" + path + "?key=AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
var query string
if c != "" {
query = "&ctoken=" + c + "&continuation=" + c + "&type=next"
} else {
query = ""
}
url := "https://music.youtube.com/youtubei/v1/" + path + "?key=AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30" + query + "&prettyPrint=false"
log.Println(url)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
@@ -41,14 +54,14 @@ func Fetch(path string, data []byte) (string, int, error) {
return string(body), resp.StatusCode, nil
}
func FetchBrowse(id string, browse BrowseData) (string, int) {
func FetchBrowse(id string, c string, browse BrowseData) (string, int) {
data, err := json.Marshal(browse)
if err != nil {
return ErrorMessage(err), 500
}
raw, status, err := Fetch("browse", data)
raw, status, err := Fetch("browse", data, c)
if err != nil {
return ErrorMessage(err), 500
}
@@ -57,11 +70,68 @@ func FetchBrowse(id string, browse BrowseData) (string, int) {
}
func FetchHome(c string) (string, int) {
/* WIP */
id := "FEmusic_home"
if c != "" {
id = ""
}
log.Println(id)
context := GetTypeBrowse("home", id)
raw, status := FetchBrowse(id, c, context)
/*res, err := ParseHome(raw)
if err != nil {
return ErrorMessage(err), 500
}*/
return raw, status
}
func FetchExplore() (string, int) {
id := "FEmusic_explore"
log.Println(id)
context := GetTypeBrowse("", id)
raw, status := FetchBrowse(id, "", context)
res, err := ParseExplore(raw)
if err != nil {
return ErrorMessage(err), 500
}
return res, status
}
func FetchMoods() (string, int) {
/* WIP */
id := "FEmusic_moods_and_genres"
log.Println(id)
context := GetTypeBrowse("", id)
raw, status := FetchBrowse(id, "", context)
return raw, status
}
func FetchArtist(id string) (string, int) {
browse := GetTypeBrowse("artist", id)
context := GetTypeBrowse("artist", id)
raw, status := FetchBrowse(id, browse)
raw, status := FetchBrowse(id, "", context)
res, err := ParseArtist(raw)
if err != nil {
@@ -73,9 +143,9 @@ func FetchArtist(id string) (string, int) {
func FetchLyrics(id string) (string, int) {
browse := GetTypeBrowse("lyrics", id)
context := GetTypeBrowse("lyrics", id)
raw, status := FetchBrowse(id, browse)
raw, status := FetchBrowse(id, "", context)
res, err := ParseLyrics(raw)
if err != nil {
@@ -85,6 +155,30 @@ func FetchLyrics(id string) (string, int) {
return res, status
}
func FetchPlaylist(id string) (string, int) {
context := GetTypeBrowse("playlist", id)
raw, status := FetchBrowse(id, "", context)
return raw, status
}
func FetchAlbum(id string) (string, int) {
context := GetTypeBrowse("album", id)
raw, status := FetchBrowse(id, "", context)
url := gjson.Parse(raw).Get("microformat.microformatDataRenderer.urlCanonical").String()
data := "{\"canonicalUrl\": \"" +
strings.Replace(url, "https://music.youtube.com", "", -1) +
"\"}"
return data, status
}
func FetchNext(id string) (string, int) {
data, err := json.Marshal(GetTypeNext(id))
@@ -92,7 +186,7 @@ func FetchNext(id string) (string, int) {
return ErrorMessage(err), 500
}
raw, status, err := Fetch("next", data)
raw, status, err := Fetch("next", data, "")
if err != nil {
return ErrorMessage(err), 500
}
@@ -103,4 +197,4 @@ func FetchNext(id string) (string, int) {
}
return res, status
}
}
+38 -10
View File
@@ -1,10 +1,10 @@
package main
import (
"os"
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/recover"
"os"
)
func SetHeaders(c *fiber.Ctx) error {
@@ -17,21 +17,39 @@ func SetHeaders(c *fiber.Ctx) error {
}
func HandleHealth(c *fiber.Ctx) error {
defer calc()()
defer calc(c.OriginalURL())()
fmt.Println("Health Check!!")
return c.SendStatus(204)
}
func HandleNext(c *fiber.Ctx) error {
defer calc()()
func HandleHome(c *fiber.Ctx) error {
defer calc(c.OriginalURL())()
res, status := FetchHome(c.Params("id"))
res, status := FetchNext(c.Params("id"))
return c.Status(status).SendString(res)
}
func HandleExplore(c *fiber.Ctx) error {
defer calc(c.OriginalURL())()
res, status := FetchExplore()
return c.Status(status).SendString(res)
}
func HandleNext(c *fiber.Ctx) error {
defer calc(c.OriginalURL())()
res, status := FetchNext(c.Params("id"))
return c.Status(status).SendString(res)
}
func HandleBrowse(c *fiber.Ctx) error {
defer calc()()
defer calc(c.OriginalURL())()
id := c.Params("id")
@@ -42,15 +60,22 @@ func HandleBrowse(c *fiber.Ctx) error {
case id[:4] == "MPLY":
res, status := FetchLyrics(id)
return c.Status(status).SendString(res)
case id[:4] == "MPRE":
res, status := FetchAlbum(id)
return c.Status(status).SendString(res)
case id[:4] == "VLRD" || id[:2] == "RD":
res, status := FetchPlaylist(id)
return c.Status(status).SendString(res)
default:
return c.SendString("{error: \"Invalid URL\"}")
return c.SendString("{\"error\": \"Invalid Browse URL\"}")
}
}
func HandleArtist(c *fiber.Ctx) error {
defer calc()()
defer calc(c.OriginalURL())()
res, status := FetchArtist(c.Params("id"))
return c.Status(status).SendString(res)
}
@@ -61,6 +86,9 @@ func main() {
app.Use(recover.New())
app.Get("/healthz", HandleHealth)
app.Get("/home", HandleHome)
app.Get("/home/:id", HandleHome)
app.Get("/explore", HandleExplore)
app.Get("/next/:id", HandleNext)
app.Get("/browse/:id", HandleBrowse)
app.Get("/channel/:id", HandleArtist)
+77 -85
View File
@@ -57,98 +57,20 @@ func GetThumbnails(j gjson.Result) []Thumbnail {
return t
}
func GetSongs(s gjson.Result) []Item {
r := []Item{}
wg := sync.WaitGroup{}
s.ForEach(
func(_, v gjson.Result) bool {
wg.Add(1)
go func() {
defer wg.Done()
j := v.Get("musicResponsiveListItemRenderer")
flex := j.Get("flexColumns.#.musicResponsiveListItemFlexColumnRenderer.text.runs.0")
r = append(r, Item{
Id: j.Get("playlistItemData.videoId").String(),
Title: flex.Get("#(navigationEndpoint.watchEndpoint.videoId).text").String(),
Thumbnails: GetThumbnails(j.Get("thumbnail.musicThumbnailRenderer.thumbnail.thumbnails")),
})
}()
wg.Wait()
return true
},
)
return r
}
func GetTwoRowItem(t string, a gjson.Result) []Item {
r := []Item{}
var id string
if t == "album" || t == "singles" {
id = "menu.menuRenderer.items.#(menuNavigationItemRenderer.text.runs.0.text == Shuffle play).menuNavigationItemRenderer.navigationEndpoint.watchPlaylistEndpoint.playlistId"
} else {
id = "navigationEndpoint.browseEndpoint.browseId"
}
wg := sync.WaitGroup{}
a.ForEach(
func(_, v gjson.Result) bool {
wg.Add(1)
go func() {
defer wg.Done()
j := v.Get("musicTwoRowItemRenderer")
r = append(r, Item{
Id: j.Get(id).String(),
Title: RunsText(j.Get("title")),
Sub: RunsText(j.Get("subtitle")),
Thumbnails: GetThumbnails(j.Get("thumbnailRenderer.musicThumbnailRenderer.thumbnail.thumbnails")),
})
}()
wg.Wait()
return true
},
)
return r
}
func GetNextSongs(n gjson.Result) []Item {
r := []Item{}
wg := sync.WaitGroup{}
n.ForEach(
func(_, v gjson.Result) bool {
n.Get("#.playlistPanelRenderer").ForEach(
func(_, j 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")),
@@ -166,6 +88,76 @@ func GetNextSongs(n gjson.Result) []Item {
return r
}
func ParseHome(raw string) (string, error) {
j := gjson.Parse(raw)
c := j.Get("contents.singleColumnBrowseResultsRenderer.tabs.0.tabRenderer.content.sectionListRenderer")
m := c.Get("contents")
n := c.Get("continuations.0.nextContinuationData.continuation").String()
var body []map[string]interface{}
m.ForEach(
func(key, value gjson.Result) bool {
var v map[string]interface{}
icsr := value.Get("musicImmersiveCarouselShelfRenderer")
csr := value.Get("musicCarouselShelfRenderer")
switch {
case icsr.Exists():
v = CarouselShelfRenderer(icsr)
case csr.Exists():
v = CarouselShelfRenderer(csr)
default:
v = make(map[string]interface{})
v["raw"] = value.Value()
}
body = append(body, v)
return true
},
)
data := Home{
Contents: body,
Continue: n,
}
res, err := json.Marshal(data)
if err != nil {
return "", err
}
return string(res), nil
}
func ParseExplore(raw string) (string, error) {
j := gjson.Parse(raw)
c := j.Get("contents.singleColumnBrowseResultsRenderer.tabs.0.tabRenderer.content.sectionListRenderer.contents")
a := c.Get("#(musicCarouselShelfRenderer.header.musicCarouselShelfBasicHeaderRenderer.title.runs.0.text == New albums & singles).musicCarouselShelfRenderer")
t := c.Get("#(musicCarouselShelfRenderer.header.musicCarouselShelfBasicHeaderRenderer.title.runs.0.text == Trending).musicCarouselShelfRenderer")
val := Explore{
Albums: TwoRowItemRenderer("album", a.Get("contents")),
Trending: ResponsiveListItemRenderer(t.Get("contents")),
}
res, err := json.Marshal(val)
if err != nil {
return "", err
}
return string(res), nil
}
func ParseArtist(raw string) (string, error) {
j := gjson.Parse(raw)
@@ -186,10 +178,10 @@ func ParseArtist(raw string) (string, error) {
BrowsePlaylistId: h.Get("playButton.buttonRenderer.navigationEndpoint.watchEndpoint.playlistId").String(),
PlaylistId: s.Get("contents.0.musicResponsiveListItemRenderer.flexColumns.0.musicResponsiveListItemFlexColumnRenderer.text.runs.0.navigationEndpoint.watchEndpoint.playlistId").String(),
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")),
Songs: ResponsiveListItemRenderer(s.Get("contents")),
Albums: TwoRowItemRenderer("album", a.Get("contents")),
Singles: TwoRowItemRenderer("singles", m.Get("contents")),
Artists: TwoRowItemRenderer("artist", u.Get("contents")),
},
}
@@ -229,6 +221,7 @@ func ParseNext(raw string) (string, error) {
c := j.Get("contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs")
m := j.Get("playerOverlays.playerOverlayRenderer.browserMediaSession.browserMediaSessionRenderer")
n := c.Get("#(tabRenderer.title == Up next).tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer")
l := c.Get("#(tabRenderer.title == Lyrics).tabRenderer.endpoint.browseEndpoint.browseId")
@@ -237,7 +230,6 @@ func ParseNext(raw string) (string, error) {
MediaSession: MediaSession{
Album: RunsText(m.Get("album")),
Thumbnails: GetThumbnails(m.Get("thumbnailDetails.thumbnails")),
},
Songs: GetNextSongs(n.Get("contents")),
}
+12 -1
View File
@@ -11,6 +11,7 @@ type Item struct {
Id string `json:"id"`
Title string `json:"title"`
Sub string `json:"subtitle"`
SubId string `json:"subId"`
Thumbnails []Thumbnail `json:"thumbnails"`
}
@@ -21,6 +22,16 @@ type Items struct {
Artists []Item `json:"recommendedArtists"`
}
type Home struct {
Contents []map[string]interface{} `json:"contents"`
Continue string `json:"continue"`
}
type Explore struct {
Albums []Item `json:"albums_and_singles"`
Trending []Item `json:"trending"`
}
type Artist struct {
Title string `json:"title"`
Description string `json:"description"`
@@ -37,7 +48,7 @@ type MediaSession struct {
}
type Next struct {
LyricsId string `json:"lyricsId"`
LyricsId string `json:"lyricsId"`
Songs []Item `json:"songs"`
MediaSession MediaSession `json:"mediaSession"`
}
+16 -7
View File
@@ -10,15 +10,24 @@ var BaseContext = Context{
}
func GetTypeBrowse(t, id string) BrowseData {
return BrowseData{
Context: BaseContext,
MusicConfig: BrowseMusicConfig{
MusicConfig: PageType{
PageType: "MUSIC_PAGE_TYPE_" + strings.ToUpper(t),
if t != "" {
return BrowseData{
Context: BaseContext,
MusicConfig: BrowseMusicConfig{
MusicConfig: PageType{
PageType: "MUSIC_PAGE_TYPE_" + strings.ToUpper(t),
},
},
},
BrowseId: id,
BrowseId: id,
}
} else {
return BrowseData{
Context: BaseContext,
BrowseId: id,
}
}
}
func GetTypeNext(id string) NextData {
+2 -2
View File
@@ -27,10 +27,10 @@ func ErrorMessage(err error) string {
return fmt.Sprintf("{\"error\": \"%s\", \"message\": \"Please Report this error\"}", err)
}
func calc() func() {
func calc(url string) func() {
start := time.Now()
return func() {
msg := fmt.Sprintf("Responded in %v\n", time.Since(start))
msg := fmt.Sprintf("Responded %v in %v\n", url, time.Since(start))
fmt.Println(msg)
}
}