From 5be641cbfb13ee5e902d31fe9919f7cff2a74a3e Mon Sep 17 00:00:00 2001 From: Hazel Noack Date: Wed, 8 Oct 2025 14:43:47 +0200 Subject: [PATCH] implemented fetch album --- internal/plugin/musify.go | 232 +++++++++++++++++++++++++++++++++++++- 1 file changed, 229 insertions(+), 3 deletions(-) diff --git a/internal/plugin/musify.go b/internal/plugin/musify.go index 88a8a44..52eb745 100644 --- a/internal/plugin/musify.go +++ b/internal/plugin/musify.go @@ -376,10 +376,236 @@ func (m *Musify) FetchSong(source data.Source) (data.Song, error) { 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) { - return data.Album{ - Name: extractName(source.Url), - }, nil + album := data.Album{ + Sources: []data.Source{source}, + 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) {