yellowcab/scrape/listenbrainz.go
2023-10-24 17:38:03 -04:00

131 lines
3.4 KiB
Go

package scrape
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/dustin/go-humanize"
)
type listenbrainz struct {
Name string
}
type listenbrainzMbidMapping struct {
RecordingMbid string `json:"recording_mbid"`
ReleaseMbid string `json:"release_mbid"`
Artists []struct {
ArtistMbid string `json:"artist_mbid"`
Name string `json:"artist_credit_name"`
} `json:"artists"`
CoverArtArchiveID int64 `json:"caa_id"`
}
type listenbrainzTrackAdditionalInfo struct {
URL string `json:"origin_url"`
}
type listenbrainzTrackMetadata struct {
Name string `json:"track_name"`
Artist string `json:"artist_name"`
ReleaseName string `json:"release_name"`
AdditionalInfo listenbrainzTrackAdditionalInfo `json:"additional_info"`
MbidMapping *listenbrainzMbidMapping `json:"mbid_mapping"`
}
type listenbrainzListen struct {
TrackMetadata listenbrainzTrackMetadata `json:"track_metadata"`
Timestamp int64 `json:"listened_at"`
}
type listenbrainzReactProps struct {
Listens []listenbrainzListen `json:"listens"`
}
func (s *listenbrainz) GetNowPlaying(username string) (response *ScrobblerResponse, err error) {
encodedUsername := url.PathEscape(username)
resp, err := http.Get(fmt.Sprintf("https://listenbrainz.org/user/%s/", encodedUsername))
if err != nil {
return
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return
}
reactPropsJson := doc.Find("#page-react-props").Text()
if err != nil {
return
}
var reactProps listenbrainzReactProps
json.Unmarshal([]byte(reactPropsJson), &reactProps)
if len(reactProps.Listens) < 1 {
err = errors.New("no listens recorded")
return
}
lastPlayed := reactProps.Listens[0]
track := Track{
Name: lastPlayed.TrackMetadata.Name,
StreamURL: &lastPlayed.TrackMetadata.AdditionalInfo.URL,
}
artist := &Artist{
Name: lastPlayed.TrackMetadata.Artist,
}
album := &Album{
Name: lastPlayed.TrackMetadata.ReleaseName,
}
var coverURL *string
if lastPlayed.TrackMetadata.MbidMapping != nil {
trackUrl := fmt.Sprintf("https://musicbrainz.org/recording/%s", lastPlayed.TrackMetadata.MbidMapping.RecordingMbid)
track.URL = &trackUrl
albumUrl := fmt.Sprintf("https://musicbrainz.org/release/%s", lastPlayed.TrackMetadata.MbidMapping.ReleaseMbid)
album.URL = &albumUrl
coverURLstr := fmt.Sprintf("https://coverartarchive.org/release/%s/%d.jpg", lastPlayed.TrackMetadata.MbidMapping.ReleaseMbid, lastPlayed.TrackMetadata.MbidMapping.CoverArtArchiveID)
coverURL = &coverURLstr
artistURL := fmt.Sprintf("https://musicbrainz.org/artist/%s", lastPlayed.TrackMetadata.MbidMapping.Artists[0].ArtistMbid)
artist.URL = &artistURL
}
var timestamp time.Time
if lastPlayed.Timestamp == 0 {
timestamp = time.Now()
} else {
timestamp = time.Unix(lastPlayed.Timestamp, 0)
}
response = &ScrobblerResponse{
FromScrobbler: FromScrobbler{
Name: s.Name,
UserURL: fmt.Sprintf("https://listenbrainz.org/user/%s/", encodedUsername),
},
Cover: coverURL,
Artist: artist,
Track: track,
Album: album,
TimestampMs: timestamp.UnixMilli(),
EstTimestamp: humanize.Time(timestamp),
}
return
}
var Listenbrainz = listenbrainz{
Name: "ListenBrainz",
}