Compare commits

...

8 Commits

Author SHA1 Message Date
Hazel Noack
2f6897ceb7 improved shell 2025-10-08 12:31:01 +02:00
Hazel Noack
3c9cef8165 searching for song 2025-10-08 12:26:47 +02:00
Hazel Noack
e5485ea1c3 error handling 2025-10-08 12:09:13 +02:00
Hazel Noack
a1579113dd fetching of albums 2025-10-08 12:05:15 +02:00
Hazel Noack
2edc3a3a44 basic album fetching 2025-10-08 11:54:27 +02:00
Hazel Noack
a60603d54d improved shell 2025-10-08 11:44:17 +02:00
Hazel Noack
1d4b4b069f scraping helper 2025-10-08 11:41:50 +02:00
Hazel Noack
c5e8ee90c7 fetching name 2025-10-08 11:41:38 +02:00
4 changed files with 206 additions and 16 deletions

View File

@@ -5,26 +5,57 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"strings"
"gitea.elara.ws/Hazel/music-kraken/internal/data" "gitea.elara.ws/Hazel/music-kraken/internal/data"
"gitea.elara.ws/Hazel/music-kraken/internal/plugin" "gitea.elara.ws/Hazel/music-kraken/internal/plugin"
) )
func printResults(musicObjects []data.MusicObject) { func printResults(musicObjects []data.MusicObject) {
if len(musicObjects) <= 0 {
return
}
for _, m := range musicObjects { for _, m := range musicObjects {
if a, ok := m.(data.Artist); ok { if a, ok := m.(data.Artist); ok {
fmt.Println("artist: " + a.Name) fmt.Println("artist: " + a.Name)
} else if a, ok := m.(data.Album); ok { } else if a, ok := m.(data.Album); ok {
fmt.Println("release: " + a.Name) fmt.Print("release: " + a.Name)
if len(a.Artists) > 0 {
names := make([]string, len(a.Artists))
for i, artist := range a.Artists {
names[i] = artist.Name
}
fmt.Println(" by " + strings.Join(names, ", "))
} else {
fmt.Println()
}
} else if a, ok := m.(data.Song); ok { } else if a, ok := m.(data.Song); ok {
fmt.Println("track: " + a.Name) fmt.Print("track: " + a.Name)
if len(a.Artists) > 0 {
names := make([]string, len(a.Artists))
for i, artist := range a.Artists {
names[i] = artist.Name
}
fmt.Println(" by " + strings.Join(names, ", "))
} else {
fmt.Println()
} }
} }
} }
fmt.Println()
}
func Shell() { func Shell() {
plugin.RegisterPlugin(&plugin.Musify{}) plugin.RegisterPlugin(&plugin.Musify{})
fmt.Println("== MusicKraken Shell ==")
fmt.Println()
for { for {
fmt.Print("> ") fmt.Print("> ")

View File

@@ -1,7 +1,7 @@
package plugin package plugin
import ( import (
"fmt" "errors"
"regexp" "regexp"
"strings" "strings"
@@ -49,8 +49,9 @@ func (m Musify) RegexSong() *regexp.Regexp {
return regexp.MustCompile(`(?i)https?://musify\.club/track/[a-z\-0-9]+`) return regexp.MustCompile(`(?i)https?://musify\.club/track/[a-z\-0-9]+`)
} }
func parseArtistContact(contact *goquery.Selection) data.Artist { func parseArtistContact(contact *goquery.Selection) (data.Artist, error) {
artist := data.Artist{} artist := data.Artist{}
var err error
anchor := contact.Find("a") anchor := contact.Find("a")
if anchor.Length() > 0 { if anchor.Length() > 0 {
@@ -66,15 +67,104 @@ func parseArtistContact(contact *goquery.Selection) data.Artist {
if name, nameExists := anchor.Attr("title"); nameExists { if name, nameExists := anchor.Attr("title"); nameExists {
artist.Name = name artist.Name = name
} }
} else {
err = errors.New("no anchor found")
} }
return artist /*
# artist image
image_soup = contact.find("img")
if image_soup is not None:
alt = image_soup.get("alt")
if alt is not None:
name = alt
artist_thumbnail = image_soup.get("src")
*/
return artist, err
} }
func parseAlbumContact(contact *goquery.Selection) data.Album { func parseAlbumContact(contact *goquery.Selection) (data.Album, error) {
album := data.Album{} album := data.Album{}
var err error
return album /*
<div class="contacts__item">
<a href="/release/ghost-bath-ghost-bath-2013-602489" title="Ghost Bath - 2013">
<div class="contacts__img release">
<img alt="Ghost Bath" class="lozad" data-src="https://37s.musify.club/img/69/9060265/24178833.jpg"/>
<noscript><img alt="Ghost Bath" src="https://37s.musify.club/img/69/9060265/24178833.jpg"/></noscript>
</div>
<div class="contacts__info">
<strong>Ghost Bath - 2013</strong>
<small>Ghost Bath</small>
<small>Треков: 4</small> <!--tracks-->
<small><i class="zmdi zmdi-star zmdi-hc-fw"></i> 9,04</small>
</div>
</a>
</div>
*/
anchor := contact.Find("a")
if anchor.Length() > 0 {
if url, urlExists := anchor.Attr("href"); urlExists {
album.Sources = append(album.Sources, data.Source{
Url: musifyHost + url,
ObjectType: data.ArtistSource,
})
}
if titleDate, titleExists := anchor.Attr("title"); titleExists {
delimiter := " - "
splitAttr := strings.Split(strings.TrimSpace(titleDate), delimiter)
album.Name = strings.Join(splitAttr[:len(splitAttr)-1], delimiter)
}
} else {
err = errors.New("no anchor found")
}
contactInfo := contact.Find("div.contacts__info")
if contactInfo.Length() > 0 {
/*
<strong>Ghost Bath - 2013</strong>
<small>Ghost Bath</small>
<small>Треков: 4</small> <!--tracks-->
<small><i class="zmdi zmdi-star zmdi-hc-fw"></i> 9,04</small>
*/
// titleSoup := contactInfo.Find("strong")
smallList := contactInfo.Find("small")
if smallList.Length() == 3 {
// artist
rawArtistStr := smallList.First().Text()
for _, artistStr := range strings.Split(rawArtistStr, "&") {
artistStr = strings.TrimRight(artistStr, "& ...\r\n")
artistStr = strings.TrimSpace(artistStr)
if strings.HasSuffix(artistStr, "]") && strings.Contains(artistStr, "[") {
parts := strings.Split(artistStr, "[")
if len(parts) > 1 {
artistStr = strings.TrimSpace(parts[0])
}
}
album.Artists = append(album.Artists, data.Artist{
Name: artistStr,
})
}
// trackCountSoup := smallList[1]
// ratingSoup := smallList[2]
}
}
return album, err
} }
func parseContactContainer(contactContainer *goquery.Selection) []data.MusicObject { func parseContactContainer(contactContainer *goquery.Selection) []data.MusicObject {
@@ -88,9 +178,13 @@ func parseContactContainer(contactContainer *goquery.Selection) []data.MusicObje
if exists { if exists {
if strings.Contains(url, "artist") { if strings.Contains(url, "artist") {
res = append(res, parseArtistContact(contact)) if artist, err := parseArtistContact(contact); err == nil {
res = append(res, artist)
}
} else if strings.Contains(url, "release") { } else if strings.Contains(url, "release") {
res = append(res, parseAlbumContact(contact)) if album, err := parseAlbumContact(contact); err == nil {
res = append(res, album)
}
} }
} }
} }
@@ -99,9 +193,57 @@ func parseContactContainer(contactContainer *goquery.Selection) []data.MusicObje
return res return res
} }
func parsePlaylistItem(playlistItem *goquery.Selection) (data.Song, error) {
song := data.Song{}
var err error
song.Name, _ = playlistItem.Attr("data-name")
playlistDetails := playlistItem.Find("div.playlist__heading")
if playlistDetails.Length() > 0 {
anchorList := playlistDetails.Find("a")
if anchorList.Length() >= 2 {
// artists
anchorList.Each(func(i int, artistAnchor *goquery.Selection) {
if i < anchorList.Length()-1 { // all except the last one
if url, exists := artistAnchor.Attr("href"); exists {
song.Artists = append(song.Artists, data.Artist{
Name: strings.TrimSpace(artistAnchor.Text()),
Sources: []data.Source{
{Url: musifyHost + url, ObjectType: data.ArtistSource},
},
})
}
}
})
// track
trackAnchor := anchorList.Last()
if href, exists := trackAnchor.Attr("href"); exists {
song.Sources = append(song.Sources, data.Source{
Url: musifyHost + href,
ObjectType: data.SongSource,
})
}
} else {
err = errors.New("there are not enough anchors (2) for artist and track")
}
}
return song, err
}
func parsePlaylist(playlist *goquery.Selection) []data.MusicObject { func parsePlaylist(playlist *goquery.Selection) []data.MusicObject {
res := []data.MusicObject{} res := []data.MusicObject{}
playlist.Find("div.playlist__item").Each(func(i int, playlistItem *goquery.Selection) {
if song, err := parsePlaylistItem(playlistItem); err == nil {
res = append(res, song)
}
})
return res return res
} }
@@ -115,9 +257,6 @@ func (m *Musify) Search(query common.Query) ([]data.MusicObject, error) {
return musicObjects, err return musicObjects, err
} }
fmt.Println(resp.Header)
fmt.Println(resp.StatusCode)
doc, err := scraper.GetHtml(resp) doc, err := scraper.GetHtml(resp)
if err != nil { if err != nil {
return musicObjects, err return musicObjects, err

View File

@@ -0,0 +1,24 @@
package scraper
import (
"io"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
)
func GetText(resp *http.Response) (string, error) {
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
return string(body), err
}
func GetHtml(resp *http.Response) (*goquery.Document, error) {
text, err := GetText(resp)
if err != nil {
return nil, err
}
return goquery.NewDocumentFromReader(strings.NewReader(text))
}

View File

@@ -1,13 +1,9 @@
package main package main
import ( import (
"fmt"
"gitea.elara.ws/Hazel/music-kraken/internal/cli" "gitea.elara.ws/Hazel/music-kraken/internal/cli"
) )
func main() { func main() {
fmt.Println("welcome to music kraken")
cli.Shell() cli.Shell()
} }