mirror of
https://github.com/Dvorinka/SpotifyRecAlg.git
synced 2026-06-03 20:13:03 +00:00
first commit
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
package unlocker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotConfigured = errors.New("unlocker service not configured")
|
||||
ErrTrackNotFound = errors.New("track not found")
|
||||
)
|
||||
|
||||
// Client for the Python unlocker service (auth-free Spotify access)
|
||||
type Client struct {
|
||||
baseURL string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewClient(baseURL string) *Client {
|
||||
if baseURL == "" {
|
||||
return nil
|
||||
}
|
||||
return &Client{
|
||||
baseURL: baseURL,
|
||||
client: &http.Client{Timeout: 30 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Configured() bool {
|
||||
return c != nil && c.baseURL != ""
|
||||
}
|
||||
|
||||
type TrackResponse struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Artist string `json:"artist"`
|
||||
Artists []string `json:"artists"`
|
||||
Album string `json:"album"`
|
||||
DurationMS int `json:"duration_ms"`
|
||||
Explicit bool `json:"explicit"`
|
||||
ExternalURLs map[string]string `json:"external_urls"`
|
||||
}
|
||||
|
||||
type ImportRequest struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type ImportResponse struct {
|
||||
Track *TrackResponse `json:"track"`
|
||||
Links map[string]string `json:"links"`
|
||||
Parsed *ParsedInfo `json:"parsed,omitempty"`
|
||||
Note string `json:"note,omitempty"`
|
||||
}
|
||||
|
||||
type ParsedInfo struct {
|
||||
Service string `json:"service"`
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type LinksResponse struct {
|
||||
SpotifyID string `json:"spotify_id"`
|
||||
ISRC string `json:"isrc"`
|
||||
Links map[string]LinkDetails `json:"links"`
|
||||
}
|
||||
|
||||
type LinkDetails struct {
|
||||
URL string `json:"url"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// ImportFromURL imports a track from any streaming service URL (auth-free)
|
||||
func (c *Client) ImportFromURL(ctx context.Context, url string) (*ImportResponse, error) {
|
||||
if !c.Configured() {
|
||||
return nil, ErrNotConfigured
|
||||
}
|
||||
|
||||
reqBody, _ := json.Marshal(ImportRequest{URL: url})
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/import", bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unlocker request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unlocker returned %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var result ImportResponse
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse unlocker response: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetTrack gets a track by Spotify ID (auth-free)
|
||||
func (c *Client) GetTrack(ctx context.Context, trackID string) (*TrackResponse, error) {
|
||||
if !c.Configured() {
|
||||
return nil, ErrNotConfigured
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/spotify/track/"+trackID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unlocker request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, ErrTrackNotFound
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unlocker returned %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var result TrackResponse
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse unlocker response: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GetLinks gets cross-platform links for a Spotify track
|
||||
func (c *Client) GetLinks(ctx context.Context, spotifyID string) (*LinksResponse, error) {
|
||||
if !c.Configured() {
|
||||
return nil, ErrNotConfigured
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/links/"+spotifyID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unlocker request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unlocker returned %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var result LinksResponse
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse unlocker response: %w", err)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user