.isrc finder fallback

This commit is contained in:
afkarxyz
2026-04-02 12:11:56 +07:00
parent 4b3bf1cf48
commit 460405a437
2 changed files with 130 additions and 17 deletions
+35 -17
View File
@@ -87,14 +87,7 @@ func (s *SongLinkClient) lookupSpotifyISRC(spotifyTrackID string) (string, error
isrc, resolvedTrackID, err := s.lookupSpotifyISRCViaSpotFetchAPI(normalizedTrackID, spotFetchAPIURL)
if err == nil && isrc != "" {
fmt.Printf("Found ISRC via SpotFetch API: %s\n", isrc)
if err := PutCachedISRC(normalizedTrackID, isrc); err != nil {
fmt.Printf("Warning: failed to write ISRC cache: %v\n", err)
}
if resolvedTrackID != "" && resolvedTrackID != normalizedTrackID {
if err := PutCachedISRC(resolvedTrackID, isrc); err != nil {
fmt.Printf("Warning: failed to write ISRC cache for resolved track ID: %v\n", err)
}
}
cacheResolvedSpotifyTrackISRC(normalizedTrackID, resolvedTrackID, isrc)
return isrc, nil
}
if err != nil {
@@ -102,21 +95,46 @@ func (s *SongLinkClient) lookupSpotifyISRC(spotifyTrackID string) (string, error
}
}
payload, err := fetchSpotifyTrackRawData(s.client, normalizedTrackID)
if err != nil {
return "", err
payload, metadataErr := fetchSpotifyTrackRawData(s.client, normalizedTrackID)
if metadataErr == nil {
isrc, extractErr := extractSpotifyTrackISRC(payload)
if extractErr == nil {
fmt.Printf("Found ISRC via Spotify metadata: %s\n", isrc)
cacheResolvedSpotifyTrackISRC(normalizedTrackID, "", isrc)
return isrc, nil
}
metadataErr = extractErr
}
isrc, err := extractSpotifyTrackISRC(payload)
if err != nil {
return "", err
if metadataErr != nil {
fmt.Printf("Warning: Spotify metadata ISRC lookup failed, falling back to Soundplate: %v\n", metadataErr)
}
fmt.Printf("Found ISRC via Spotify metadata: %s\n", isrc)
if err := PutCachedISRC(normalizedTrackID, isrc); err != nil {
isrc, resolvedTrackID, soundplateErr := s.lookupSpotifyISRCViaSoundplate(normalizedTrackID)
if soundplateErr == nil && isrc != "" {
fmt.Printf("Found ISRC via Soundplate: %s\n", isrc)
cacheResolvedSpotifyTrackISRC(normalizedTrackID, resolvedTrackID, isrc)
return isrc, nil
}
if metadataErr != nil && soundplateErr != nil {
return "", fmt.Errorf("spotify metadata lookup failed: %v | soundplate lookup failed: %w", metadataErr, soundplateErr)
}
if soundplateErr != nil {
return "", soundplateErr
}
return "", metadataErr
}
func cacheResolvedSpotifyTrackISRC(trackID string, resolvedTrackID string, isrc string) {
if err := PutCachedISRC(trackID, isrc); err != nil {
fmt.Printf("Warning: failed to write ISRC cache: %v\n", err)
}
return isrc, nil
if resolvedTrackID != "" && resolvedTrackID != trackID {
if err := PutCachedISRC(resolvedTrackID, isrc); err != nil {
fmt.Printf("Warning: failed to write ISRC cache for resolved track ID: %v\n", err)
}
}
}
func (s *SongLinkClient) lookupSpotifyISRCViaSpotFetchAPI(spotifyTrackID string, apiBaseURL string) (string, string, error) {
+95
View File
@@ -0,0 +1,95 @@
package backend
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
const (
soundplateSpotifyAPIURL = "https://phpstack-822472-6184058.cloudwaysapps.com/api/spotify.php"
soundplateRefererURL = "https://phpstack-822472-6184058.cloudwaysapps.com/?"
soundplateUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"
)
type soundplateSpotifyResponse struct {
Name string `json:"name"`
Artist string `json:"artist"`
Album string `json:"album"`
AlbumType string `json:"album_type"`
ArtworkURL string `json:"artwork_url"`
ISRC string `json:"isrc"`
Year string `json:"year"`
SpotifyURL string `json:"spotify_url"`
}
func (s *SongLinkClient) lookupSpotifyISRCViaSoundplate(spotifyTrackID string) (string, string, error) {
normalizedTrackID, err := extractSpotifyTrackID(spotifyTrackID)
if err != nil {
return "", "", err
}
spotifyTrackURL := fmt.Sprintf("https://open.spotify.com/track/%s", normalizedTrackID)
query := url.Values{}
query.Set("q", spotifyTrackURL)
req, err := http.NewRequest(http.MethodGet, soundplateSpotifyAPIURL+"?"+query.Encode(), nil)
if err != nil {
return "", "", fmt.Errorf("failed to create Soundplate ISRC request: %w", err)
}
req.Header.Set("User-Agent", soundplateUserAgent)
req.Header.Set("Accept", "*/*")
req.Header.Set("Referer", soundplateRefererURL)
req.Header.Set("Accept-Language", "en-US,en;q=0.9,id;q=0.8")
req.Header.Set("Sec-CH-UA", "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\"")
req.Header.Set("Sec-CH-UA-Mobile", "?0")
req.Header.Set("Sec-CH-UA-Platform", "\"Windows\"")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-origin")
req.Header.Set("Priority", "u=1, i")
resp, err := s.client.Do(req)
if err != nil {
return "", "", fmt.Errorf("Soundplate ISRC request failed: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", "", fmt.Errorf("failed to read Soundplate ISRC response: %w", err)
}
if resp.StatusCode != http.StatusOK {
bodyPreview := strings.TrimSpace(string(body))
if len(bodyPreview) > 256 {
bodyPreview = bodyPreview[:256]
}
return "", "", fmt.Errorf("Soundplate ISRC returned status %d (%s)", resp.StatusCode, bodyPreview)
}
var payload soundplateSpotifyResponse
if err := json.Unmarshal(body, &payload); err != nil {
return "", "", fmt.Errorf("failed to decode Soundplate ISRC response: %w", err)
}
isrc := firstISRCMatch(payload.ISRC)
if isrc == "" {
isrc = firstISRCMatch(string(body))
}
if isrc == "" {
return "", "", fmt.Errorf("ISRC missing in Soundplate response")
}
resolvedTrackID := ""
if payload.SpotifyURL != "" {
if trackID, err := extractSpotifyTrackID(payload.SpotifyURL); err == nil {
resolvedTrackID = trackID
}
}
return isrc, resolvedTrackID, nil
}