File: token.go

package info (click to toggle)
golang-github-raitonoberu-lyricsapi 0.0~git20250321.2e8c45d-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 108 kB
  • sloc: makefile: 4
file content (117 lines) | stat: -rw-r--r-- 2,923 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package spotify

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"

	"github.com/pquerna/otp/totp"
)

const (
	serverTimeUrl = "https://open.spotify.com/server-time"
	tokenUrl      = "https://open.spotify.com/get_access_token"
)

// let's hope this lasts
const secret = "GU2TANZRGQ2TQNJTGQ4DONBZHE2TSMRSGQ4DMMZQGMZDSMZUG4"

func (c *Client) refreshToken() error {
	totp, totpTime, err := c.getTotp()
	if err != nil {
		return fmt.Errorf("failed to get totp: %w", err)
	}
	timeStr := fmt.Sprint(totpTime.Unix())

	url := tokenUrl + "?" + url.Values{
		"reason":      {"transport"},
		"productType": {"web-player"},
		"totp":        {totp},
		"totpServer":  {totp},
		"totpVer":     {"5"},
		"sTime":       {timeStr},
		"cTime":       {timeStr + "420"},
	}.Encode()

	req, _ := http.NewRequest("GET", url, nil)
	req.Header = http.Header{
		"referer":             {"https://open.spotify.com/"},
		"origin":              {"https://open.spotify.com/"},
		"accept":              {"application/json"},
		"app-platform":        {"WebPlayer"},
		"spotify-app-version": {"1.2.61.20.g3b4cd5b2"},
		"user-agent":          {userAgent},
		"cookie":              {c.cookie},
	}
	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	type responseType struct {
		AccessToken string `json:"accessToken"`
		ExpiresIn   int64  `json:"accessTokenExpirationTimestampMs"`
		IsAnonymous bool   `json:"isAnonymous"`
	}

	var response responseType
	err = json.NewDecoder(resp.Body).Decode(&response)
	if err != nil {
		return err
	}

	if c.cookie != "" && response.IsAnonymous {
		return ErrInvalidCookie
	}
	if response.AccessToken == "" {
		return ErrNoToken
	}

	c.token = response.AccessToken
	c.tokenExp = time.Unix(0, response.ExpiresIn*int64(time.Millisecond))
	return nil
}

func (c *Client) getTotp() (string, time.Time, error) {
	serverTime, err := c.getServerTime()
	if err != nil {
		// fuck it we ball
		serverTime = time.Now()
	}
	totp, err := totp.GenerateCode(secret, serverTime)
	if err != nil {
		return "", time.Time{}, err
	}
	return totp, serverTime, nil
}

func (c *Client) getServerTime() (time.Time, error) {
	req, _ := http.NewRequest("GET", serverTimeUrl, nil)
	req.Header = http.Header{
		"referer":             {"https://open.spotify.com/"},
		"origin":              {"https://open.spotify.com/"},
		"accept":              {"application/json"},
		"app-platform":        {"WebPlayer"},
		"spotify-app-version": {"1.2.61.20.g3b4cd5b2"},
		"user-agent":          {userAgent},
		"cookie":              {c.cookie},
	}
	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return time.Time{}, err
	}
	defer resp.Body.Close()

	type responseType struct {
		ServerTime int64 `json:"serverTime"`
	}

	var response responseType
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return time.Time{}, err
	}
	return time.Unix(response.ServerTime, 0), nil
}