.separate songlink
This commit is contained in:
@@ -0,0 +1,140 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type resolvedTrackLinks struct {
|
||||||
|
TidalURL string
|
||||||
|
AmazonURL string
|
||||||
|
DeezerURL string
|
||||||
|
ISRC string
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
linkResolverProviderSongstats = "songstats"
|
||||||
|
linkResolverProviderDeezerSongLink = "deezer-songlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *SongLinkClient) resolveSpotifyTrackLinks(spotifyTrackID string, region string) (*resolvedTrackLinks, error) {
|
||||||
|
links := &resolvedTrackLinks{}
|
||||||
|
var attempts []string
|
||||||
|
|
||||||
|
isrc, err := s.lookupSpotifyISRC(spotifyTrackID)
|
||||||
|
if err != nil {
|
||||||
|
attempts = append(attempts, fmt.Sprintf("spotify isrc: %v", err))
|
||||||
|
} else {
|
||||||
|
links.ISRC = isrc
|
||||||
|
}
|
||||||
|
|
||||||
|
if links.ISRC != "" {
|
||||||
|
resolvers := prioritizeProviders("link_resolver", []string{
|
||||||
|
linkResolverProviderSongstats,
|
||||||
|
linkResolverProviderDeezerSongLink,
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, resolver := range resolvers {
|
||||||
|
switch resolver {
|
||||||
|
case linkResolverProviderSongstats:
|
||||||
|
addedData, songstatsErr := s.resolveLinksViaSongstats(links)
|
||||||
|
if addedData {
|
||||||
|
recordProviderSuccess("link_resolver", linkResolverProviderSongstats)
|
||||||
|
} else if songstatsErr != nil {
|
||||||
|
recordProviderFailure("link_resolver", linkResolverProviderSongstats)
|
||||||
|
}
|
||||||
|
if songstatsErr != nil {
|
||||||
|
attempts = append(attempts, fmt.Sprintf("songstats: %v", songstatsErr))
|
||||||
|
}
|
||||||
|
case linkResolverProviderDeezerSongLink:
|
||||||
|
addedData, deezerSongLinkErr := s.resolveLinksViaDeezerSongLink(links, region)
|
||||||
|
if addedData {
|
||||||
|
recordProviderSuccess("link_resolver", linkResolverProviderDeezerSongLink)
|
||||||
|
} else if deezerSongLinkErr != nil {
|
||||||
|
recordProviderFailure("link_resolver", linkResolverProviderDeezerSongLink)
|
||||||
|
}
|
||||||
|
if deezerSongLinkErr != nil {
|
||||||
|
attempts = append(attempts, fmt.Sprintf("deezer-songlink: %v", deezerSongLinkErr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if links.TidalURL != "" && links.AmazonURL != "" {
|
||||||
|
return links, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasAnySongLinkData(links) {
|
||||||
|
return links, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(attempts) == 0 {
|
||||||
|
attempts = append(attempts, "no streaming URLs found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return links, errors.New(strings.Join(attempts, " | "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SongLinkClient) resolveLinksViaSongstats(links *resolvedTrackLinks) (bool, error) {
|
||||||
|
if links == nil || links.ISRC == "" {
|
||||||
|
return false, fmt.Errorf("ISRC is required for Songstats resolver")
|
||||||
|
}
|
||||||
|
|
||||||
|
before := *links
|
||||||
|
|
||||||
|
fmt.Printf("Fetching Songstats links for ISRC %s\n", links.ISRC)
|
||||||
|
if err := s.populateLinksFromSongstats(links, links.ISRC); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return *links != before, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SongLinkClient) resolveLinksViaDeezerSongLink(links *resolvedTrackLinks, region string) (bool, error) {
|
||||||
|
if links == nil || links.ISRC == "" {
|
||||||
|
return false, fmt.Errorf("ISRC is required for Deezer song.link resolver")
|
||||||
|
}
|
||||||
|
|
||||||
|
before := *links
|
||||||
|
var attempts []string
|
||||||
|
|
||||||
|
if links.DeezerURL == "" {
|
||||||
|
fmt.Printf("Resolving Deezer track from ISRC %s\n", links.ISRC)
|
||||||
|
deezerURL, err := s.lookupDeezerTrackURLByISRC(links.ISRC)
|
||||||
|
if err != nil {
|
||||||
|
attempts = append(attempts, fmt.Sprintf("deezer isrc: %v", err))
|
||||||
|
} else {
|
||||||
|
links.DeezerURL = deezerURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if links.DeezerURL != "" {
|
||||||
|
fmt.Println("Resolving streaming URLs from song.link via Deezer URL...")
|
||||||
|
deezerResp, err := s.fetchSongLinkLinksByURL(links.DeezerURL, region)
|
||||||
|
if err != nil {
|
||||||
|
attempts = append(attempts, fmt.Sprintf("song.link deezer: %v", err))
|
||||||
|
} else {
|
||||||
|
mergeSongLinkResponse(links, deezerResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if links.ISRC == "" {
|
||||||
|
if resolvedISRC, deezerISRCErr := getDeezerISRC(links.DeezerURL); deezerISRCErr == nil {
|
||||||
|
links.ISRC = resolvedISRC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *links != before {
|
||||||
|
if len(attempts) == 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return true, errors.New(strings.Join(attempts, " | "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(attempts) == 0 {
|
||||||
|
attempts = append(attempts, "no links found via deezer-songlink")
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, errors.New(strings.Join(attempts, " | "))
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package backend
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -48,13 +47,6 @@ type songLinkAPIResponse struct {
|
|||||||
} `json:"linksByPlatform"`
|
} `json:"linksByPlatform"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type resolvedTrackLinks struct {
|
|
||||||
TidalURL string
|
|
||||||
AmazonURL string
|
|
||||||
DeezerURL string
|
|
||||||
ISRC string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSongLinkClient() *SongLinkClient {
|
func NewSongLinkClient() *SongLinkClient {
|
||||||
return &SongLinkClient{
|
return &SongLinkClient{
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
@@ -276,61 +268,6 @@ func (s *SongLinkClient) GetISRCDirect(spotifyID string) (string, error) {
|
|||||||
return s.lookupSpotifyISRC(spotifyID)
|
return s.lookupSpotifyISRC(spotifyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SongLinkClient) resolveSpotifyTrackLinks(spotifyTrackID string, region string) (*resolvedTrackLinks, error) {
|
|
||||||
links := &resolvedTrackLinks{}
|
|
||||||
var attempts []string
|
|
||||||
|
|
||||||
isrc, err := s.lookupSpotifyISRC(spotifyTrackID)
|
|
||||||
if err != nil {
|
|
||||||
attempts = append(attempts, fmt.Sprintf("spotify isrc: %v", err))
|
|
||||||
} else {
|
|
||||||
links.ISRC = isrc
|
|
||||||
}
|
|
||||||
|
|
||||||
if links.ISRC != "" {
|
|
||||||
fmt.Printf("Fetching Songstats links for ISRC %s\n", links.ISRC)
|
|
||||||
if songstatsErr := s.populateLinksFromSongstats(links, links.ISRC); songstatsErr != nil {
|
|
||||||
attempts = append(attempts, fmt.Sprintf("songstats: %v", songstatsErr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if links.ISRC != "" && links.DeezerURL == "" {
|
|
||||||
fmt.Printf("Resolving Deezer track from ISRC %s\n", links.ISRC)
|
|
||||||
deezerURL, deezerErr := s.lookupDeezerTrackURLByISRC(links.ISRC)
|
|
||||||
if deezerErr != nil {
|
|
||||||
attempts = append(attempts, fmt.Sprintf("deezer isrc: %v", deezerErr))
|
|
||||||
} else {
|
|
||||||
links.DeezerURL = deezerURL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if links.DeezerURL != "" {
|
|
||||||
fmt.Println("Resolving streaming URLs from song.link via Deezer URL...")
|
|
||||||
deezerResp, deezerSongLinkErr := s.fetchSongLinkLinksByURL(links.DeezerURL, region)
|
|
||||||
if deezerSongLinkErr != nil {
|
|
||||||
attempts = append(attempts, fmt.Sprintf("song.link deezer: %v", deezerSongLinkErr))
|
|
||||||
} else {
|
|
||||||
mergeSongLinkResponse(links, deezerResp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if links.ISRC == "" {
|
|
||||||
if resolvedISRC, deezerISRCErr := getDeezerISRC(links.DeezerURL); deezerISRCErr == nil {
|
|
||||||
links.ISRC = resolvedISRC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasAnySongLinkData(links) {
|
|
||||||
return links, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(attempts) == 0 {
|
|
||||||
attempts = append(attempts, "no streaming URLs found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return links, errors.New(strings.Join(attempts, " | "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SongLinkClient) fetchSongLinkLinksByURL(rawURL string, region string) (*songLinkAPIResponse, error) {
|
func (s *SongLinkClient) fetchSongLinkLinksByURL(rawURL string, region string) (*songLinkAPIResponse, error) {
|
||||||
apiURL := fmt.Sprintf("https://api.song.link/v1-alpha.1/links?url=%s", url.QueryEscape(rawURL))
|
apiURL := fmt.Sprintf("https://api.song.link/v1-alpha.1/links?url=%s", url.QueryEscape(rawURL))
|
||||||
if region != "" {
|
if region != "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user