Compare commits
4 Commits
3dd03491ca
...
5be641cbfb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5be641cbfb | ||
|
|
960598971e | ||
|
|
6ce1eb5f92 | ||
|
|
9c7dfd5775 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
test
|
||||||
@@ -65,6 +65,7 @@ func Shell() {
|
|||||||
fmt.Println("== MusicKraken Shell ==")
|
fmt.Println("== MusicKraken Shell ==")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
|
currentMusicObjects := []data.MusicObject{}
|
||||||
for {
|
for {
|
||||||
fmt.Print("> ")
|
fmt.Print("> ")
|
||||||
|
|
||||||
@@ -75,13 +76,35 @@ func Shell() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
searchResults, err := plugin.Search(line, plugin.SearchConfig{IgnoreErrors: false})
|
line = strings.TrimSpace(line)
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
if index, err := strconv.Atoi(line); err == nil {
|
||||||
fmt.Println()
|
if index >= len(currentMusicObjects) {
|
||||||
|
fmt.Println("\n" + line + " is out of bounds")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
current := currentMusicObjects[index]
|
||||||
|
|
||||||
|
if len(current.GetSources()) <= 0 {
|
||||||
|
fmt.Println("\nselected has no sources to download")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMusicObjects, err = plugin.FetchList(current.GetSources()[0])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentMusicObjects, err = plugin.Search(line, plugin.SearchConfig{IgnoreErrors: false})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
printResults(searchResults)
|
printResults(currentMusicObjects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,41 @@ func Fetch(source data.Source) (data.MusicObject, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FetchList(source data.Source) ([]data.MusicObject, error) {
|
||||||
|
res := []data.MusicObject{}
|
||||||
|
|
||||||
|
musicObject, err := Fetch(source)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if a, ok := musicObject.(data.Song); ok {
|
||||||
|
for _, ar := range a.Artists {
|
||||||
|
res = append(res, ar)
|
||||||
|
}
|
||||||
|
if a.Album.Name != "" {
|
||||||
|
res = append(res, a.Album, a)
|
||||||
|
}
|
||||||
|
} else if a, ok := musicObject.(data.Album); ok {
|
||||||
|
for _, ar := range a.Artists {
|
||||||
|
res = append(res, ar)
|
||||||
|
}
|
||||||
|
res = append(res, a)
|
||||||
|
for _, s := range a.Songs {
|
||||||
|
res = append(res, s)
|
||||||
|
}
|
||||||
|
} else if a, ok := musicObject.(data.Artist); ok {
|
||||||
|
res = append(res, a)
|
||||||
|
for _, al := range a.Albums {
|
||||||
|
res = append(res, al)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res = append(res, musicObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
type SearchConfig struct {
|
type SearchConfig struct {
|
||||||
IgnoreErrors bool
|
IgnoreErrors bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package plugin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -250,7 +251,7 @@ func parsePlaylist(playlist *goquery.Selection) []data.MusicObject {
|
|||||||
func (m *Musify) Search(query common.Query) ([]data.MusicObject, error) {
|
func (m *Musify) Search(query common.Query) ([]data.MusicObject, error) {
|
||||||
musicObjects := []data.MusicObject{}
|
musicObjects := []data.MusicObject{}
|
||||||
|
|
||||||
resp, err := m.session.PostMultipartForm("https://musify.club/en/search", map[string]string{
|
resp, err := m.session.PostMultipartForm("https://musify.club/search", map[string]string{
|
||||||
"SearchText": query.Search, // alternatively I could also add year and genre
|
"SearchText": query.Search, // alternatively I could also add year and genre
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -273,16 +274,338 @@ func (m *Musify) Search(query common.Query) ([]data.MusicObject, error) {
|
|||||||
return musicObjects, nil
|
return musicObjects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Musify) FetchSong(source data.Source) (data.Song, error) {
|
func (m *Musify) FetchSong(source data.Source) (data.Song, error) {
|
||||||
return data.Song{
|
song := data.Song{
|
||||||
Name: extractName(source.Url),
|
Sources: []data.Source{
|
||||||
}, nil
|
source,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := m.session.Get(source.Url)
|
||||||
|
if err != nil {
|
||||||
|
return song, err
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := scraper.GetHtml(resp)
|
||||||
|
if err != nil {
|
||||||
|
return song, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download URL
|
||||||
|
/*
|
||||||
|
doc.Find("a[itemprop='audio']").Each(func(i int, anchor *goquery.Selection) {
|
||||||
|
href, exists := anchor.Attr("href")
|
||||||
|
if exists {
|
||||||
|
source.AudioURL = p.host + href
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Song detail
|
||||||
|
var listElement *goquery.Selection
|
||||||
|
doc.Find("ul.album-info").Each(func(i int, albumInfo *goquery.Selection) {
|
||||||
|
listElement = albumInfo.Find("li").First()
|
||||||
|
})
|
||||||
|
|
||||||
|
if listElement != nil {
|
||||||
|
listElement.Find("a").Each(func(i int, artistAnchor *goquery.Selection) {
|
||||||
|
if href, exists := artistAnchor.Attr("href"); exists {
|
||||||
|
song.Artists = append(song.Artists, data.Artist{
|
||||||
|
Name: strings.TrimSpace(artistAnchor.Text()),
|
||||||
|
Sources: []data.Source{
|
||||||
|
{Url: musifyHost + href},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Breadcrumbs
|
||||||
|
if breadcrumbList := doc.Find("ol.breadcrumb"); breadcrumbList.Length() > 0 {
|
||||||
|
listPoints := breadcrumbList.Find("li.breadcrumb-item")
|
||||||
|
if listPoints.Length() != 5 {
|
||||||
|
return song, errors.New("too many breadcrumbs on page")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("found breadcrumbs")
|
||||||
|
|
||||||
|
if artistAnchor := listPoints.Eq(2).Find("a"); artistAnchor != nil && artistAnchor.Length() > 0 {
|
||||||
|
artist := data.Artist{}
|
||||||
|
useArtist := true
|
||||||
|
|
||||||
|
if href, exists := artistAnchor.Attr("href"); exists {
|
||||||
|
hrefParts := strings.Split(href, "/")
|
||||||
|
if len(hrefParts) <= 1 || hrefParts[len(hrefParts)-2] != "artist" {
|
||||||
|
useArtist = false
|
||||||
|
}
|
||||||
|
|
||||||
|
artist.Sources = append(artist.Sources, data.Source{Url: musifyHost + href})
|
||||||
|
} else {
|
||||||
|
useArtist = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if nameElem := artistAnchor.Find("span[itemprop='name']"); nameElem.Length() > 0 {
|
||||||
|
artist.Name = strings.TrimSpace(nameElem.Text())
|
||||||
|
} else {
|
||||||
|
useArtist = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if useArtist {
|
||||||
|
song.Artists = append(song.Artists, artist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if albumAnchor := listPoints.Eq(3).Find("a"); albumAnchor != nil && albumAnchor.Length() > 0 {
|
||||||
|
fmt.Println("found album")
|
||||||
|
|
||||||
|
if href, exists := albumAnchor.Attr("href"); exists {
|
||||||
|
song.Album.Sources = append(song.Album.Sources, data.Source{
|
||||||
|
Url: musifyHost + href,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if nameElem := albumAnchor.Find("span[itemprop='name']"); nameElem.Length() > 0 {
|
||||||
|
song.Album.Name = strings.TrimSpace(nameElem.Text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
song.Name = strings.TrimSpace(listPoints.Eq(4).Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
return song, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSongCard(songCard *goquery.Selection) data.Song {
|
||||||
|
song := data.Song{
|
||||||
|
Artists: []data.Artist{},
|
||||||
|
Sources: []data.Source{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get song name from data attribute
|
||||||
|
songName, _ := songCard.Attr("data-name")
|
||||||
|
song.Name = songName
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Get tracksort
|
||||||
|
tracksortSelection := songCard.Find("div.playlist__position")
|
||||||
|
if tracksortSelection.Length() > 0 {
|
||||||
|
rawTracksort := strings.TrimSpace(tracksortSelection.Text())
|
||||||
|
if parsedTracksort, err := strconv.Atoi(rawTracksort); err == nil {
|
||||||
|
tracksort = parsedTracksort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Playlist details
|
||||||
|
playlistDetails := songCard.Find("div.playlist__details")
|
||||||
|
if playlistDetails.Length() > 0 {
|
||||||
|
// Track anchor
|
||||||
|
anchorList := playlistDetails.Find("a")
|
||||||
|
if anchorList.Length() > 1 {
|
||||||
|
trackAnchor := anchorList.Last()
|
||||||
|
if href, exists := trackAnchor.Attr("href"); exists {
|
||||||
|
song.Sources = append(song.Sources, data.Source{Url: musifyHost + href})
|
||||||
|
}
|
||||||
|
song.Name = strings.TrimSpace(trackAnchor.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artist spans
|
||||||
|
playlistDetails.Find("span[itemprop='byArtist']").Each(func(i int, artistSpan *goquery.Selection) {
|
||||||
|
artist := data.Artist{
|
||||||
|
Sources: []data.Source{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artist URL
|
||||||
|
metaArtistSrc := artistSpan.Find("meta[itemprop='url']")
|
||||||
|
if metaArtistSrc.Length() > 0 {
|
||||||
|
if content, exists := metaArtistSrc.Attr("content"); exists && content != "" {
|
||||||
|
artist.Sources = append(artist.Sources, data.Source{
|
||||||
|
Url: musifyHost + content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artist name
|
||||||
|
metaArtistName := artistSpan.Find("meta[itemprop='name']")
|
||||||
|
if metaArtistName.Length() > 0 {
|
||||||
|
if content, exists := metaArtistName.Attr("content"); exists && content != "" {
|
||||||
|
artist.Name = content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if artist.Name != "" || len(artist.Sources) > 0 {
|
||||||
|
song.Artists = append(song.Artists, artist)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Playlist actions - download link
|
||||||
|
playlistActions := songCard.Find("div.playlist__actions")
|
||||||
|
if playlistActions.Length() > 0 {
|
||||||
|
downloadAnchor := playlistActions.Find("a[itemprop='audio']")
|
||||||
|
if downloadAnchor.Length() > 0 {
|
||||||
|
if href, exists := downloadAnchor.Attr("href"); exists && currentURL != "" {
|
||||||
|
// Add source with audio URL
|
||||||
|
song.Sources = append(song.Sources, data.Source{
|
||||||
|
Url: currentURL,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return song
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseAlbum(doc *goquery.Document) data.Album {
|
||||||
|
album := data.Album{
|
||||||
|
Artists: []data.Artist{},
|
||||||
|
Sources: []data.Source{},
|
||||||
|
Songs: []data.Song{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Breadcrumb
|
||||||
|
breadcrumb := doc.Find("ol.breadcrumb")
|
||||||
|
breadcrumbElements := breadcrumb.Find("li.breadcrumb-item")
|
||||||
|
if breadcrumbElements.Length() == 4 {
|
||||||
|
// Album name from last breadcrumb
|
||||||
|
albumCrumb := breadcrumbElements.Eq(3)
|
||||||
|
album.Name = strings.TrimSpace(albumCrumb.Text())
|
||||||
|
|
||||||
|
// Artist from second last breadcrumb
|
||||||
|
artistCrumb := breadcrumbElements.Eq(2)
|
||||||
|
artistAnchor := artistCrumb.Find("a")
|
||||||
|
if artistAnchor.Length() > 0 {
|
||||||
|
if href, exists := artistAnchor.Attr("href"); exists {
|
||||||
|
hrefParts := strings.Split(href, "/")
|
||||||
|
if len(hrefParts) > 1 && hrefParts[len(hrefParts)-2] == "artist" {
|
||||||
|
artist := data.Artist{
|
||||||
|
Sources: []data.Source{},
|
||||||
|
}
|
||||||
|
|
||||||
|
artist.Sources = append(artist.Sources, data.Source{
|
||||||
|
Url: musifyHost + strings.TrimSpace(href),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Artist name from span
|
||||||
|
if span := artistAnchor.Find("span"); span.Length() > 0 {
|
||||||
|
artist.Name = strings.TrimSpace(span.Text())
|
||||||
|
} else {
|
||||||
|
artist.Name = strings.TrimSpace(artistAnchor.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
album.Artists = append(album.Artists, artist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// m.logger.Debug("there are not 4 breadcrumb items, which shouldn't be the case")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta tags
|
||||||
|
metaURL := doc.Find("meta[itemprop='url']")
|
||||||
|
if metaURL.Length() > 0 {
|
||||||
|
if content, exists := metaURL.Attr("content"); exists {
|
||||||
|
album.Sources = append(album.Sources, data.Source{
|
||||||
|
Url: musifyHost + content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metaName := doc.Find("meta[itemprop='name']")
|
||||||
|
if metaName.Length() > 0 {
|
||||||
|
if content, exists := metaName.Attr("content"); exists {
|
||||||
|
album.Name = content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Album info
|
||||||
|
albumInfo := doc.Find("ul.album-info")
|
||||||
|
if albumInfo.Length() > 0 {
|
||||||
|
// Artists
|
||||||
|
albumInfo.Find("a[itemprop='byArtist']").Each(func(i int, artistAnchor *goquery.Selection) {
|
||||||
|
artist := data.Artist{
|
||||||
|
Sources: []data.Source{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artist URL
|
||||||
|
artistURLMeta := artistAnchor.Find("meta[itemprop='url']")
|
||||||
|
if artistURLMeta.Length() > 0 {
|
||||||
|
if content, exists := artistURLMeta.Attr("content"); exists {
|
||||||
|
artist.Sources = append(artist.Sources, data.Source{
|
||||||
|
Url: musifyHost + content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Artist name
|
||||||
|
artistNameMeta := artistAnchor.Find("meta[itemprop='name']")
|
||||||
|
if artistNameMeta.Length() > 0 {
|
||||||
|
if content, exists := artistNameMeta.Attr("content"); exists {
|
||||||
|
artist.Name = content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if artist.Name != "" {
|
||||||
|
album.Artists = append(album.Artists, artist)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Date published
|
||||||
|
timeSelection := albumInfo.Find("time[itemprop='datePublished']")
|
||||||
|
if timeSelection.Length() > 0 {
|
||||||
|
if datetime, exists := timeSelection.Attr("datetime"); exists {
|
||||||
|
// Note: You'll need to parse the datetime according to your needs
|
||||||
|
// For now, we'll store it as a string or you can parse it to time.Time
|
||||||
|
// album.Date = parsedDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Album artwork would be handled here based on your ArtworkCollection implementation
|
||||||
|
|
||||||
|
return album
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Musify) FetchAlbum(source data.Source) (data.Album, error) {
|
func (m Musify) FetchAlbum(source data.Source) (data.Album, error) {
|
||||||
return data.Album{
|
album := data.Album{
|
||||||
Name: extractName(source.Url),
|
Sources: []data.Source{source},
|
||||||
}, nil
|
Artists: []data.Artist{},
|
||||||
|
Songs: []data.Song{},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := m.session.Get(source.Url)
|
||||||
|
if err != nil {
|
||||||
|
return album, err
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := scraper.GetHtml(resp)
|
||||||
|
if err != nil {
|
||||||
|
return album, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse album metadata
|
||||||
|
parsedAlbum := parseAlbum(doc)
|
||||||
|
album.Name = parsedAlbum.Name
|
||||||
|
album.Artists = parsedAlbum.Artists
|
||||||
|
album.Sources = append(album.Sources, parsedAlbum.Sources...)
|
||||||
|
|
||||||
|
// Parse songs from cards
|
||||||
|
cardBody := doc.Find("div.card-body")
|
||||||
|
if cardBody.Length() > 0 {
|
||||||
|
cardBody.Find("div.playlist__item").Each(func(i int, songCard *goquery.Selection) {
|
||||||
|
song := parseSongCard(songCard)
|
||||||
|
album.Songs = append(album.Songs, song)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tracksort would be handled here based on your implementation
|
||||||
|
|
||||||
|
return album, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Musify) FetchArtist(source data.Source) (data.Artist, error) {
|
func (m Musify) FetchArtist(source data.Source) (data.Artist, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user