mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
harden deezer auth and lyrics tagging behavior
This commit is contained in:
@@ -2,6 +2,7 @@ package deezer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -57,14 +58,18 @@ func TestGetDownloadableNativeCipher(t *testing.T) {
|
||||
c.loggedIn = true
|
||||
c.arl = "arl"
|
||||
c.license = "license"
|
||||
c.jwt = "jwt"
|
||||
|
||||
origBase := baseURL
|
||||
origMedia := mediaURL
|
||||
origPipe := pipeURL
|
||||
baseURL = ts.URL
|
||||
mediaURL = ts.URL + "/media"
|
||||
pipeURL = ts.URL + "/pipe"
|
||||
defer func() {
|
||||
baseURL = origBase
|
||||
mediaURL = origMedia
|
||||
pipeURL = origPipe
|
||||
}()
|
||||
|
||||
d, err := c.GetDownloadable(context.Background(), "42", 2)
|
||||
@@ -106,14 +111,18 @@ func TestGetDownloadableDRMError(t *testing.T) {
|
||||
c.loggedIn = true
|
||||
c.arl = "arl"
|
||||
c.license = "license"
|
||||
c.jwt = "jwt"
|
||||
|
||||
origBase := baseURL
|
||||
origMedia := mediaURL
|
||||
origPipe := pipeURL
|
||||
baseURL = ts.URL
|
||||
mediaURL = ts.URL + "/media"
|
||||
pipeURL = ts.URL + "/pipe"
|
||||
defer func() {
|
||||
baseURL = origBase
|
||||
mediaURL = origMedia
|
||||
pipeURL = origPipe
|
||||
}()
|
||||
|
||||
_, err := c.GetDownloadable(context.Background(), "42", 2)
|
||||
@@ -121,3 +130,154 @@ func TestGetDownloadableDRMError(t *testing.T) {
|
||||
t.Fatalf("expected drm error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMetadataAddsLyricsFromPipe(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/track/1141668":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"id": 1141668, "title": "In Da Club", "artist": map[string]any{"name": "50 Cent"}})
|
||||
case "/pipe":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"data": map[string]any{"track": map[string]any{"lyrics": map[string]any{"text": "Go, go, go\nGo shawty", "synchronizedLines": []any{map[string]any{"line": "Go, go, go", "milliseconds": 0}, map[string]any{"line": "Go shawty", "milliseconds": 4280}}}}}})
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
cfgData := config.DefaultConfigData()
|
||||
c := New(&config.Config{File: cfgData, Session: cfgData})
|
||||
c.loggedIn = true
|
||||
c.jwt = "jwt"
|
||||
|
||||
origBase := baseURL
|
||||
origPipe := pipeURL
|
||||
baseURL = ts.URL
|
||||
pipeURL = ts.URL + "/pipe"
|
||||
defer func() {
|
||||
baseURL = origBase
|
||||
pipeURL = origPipe
|
||||
}()
|
||||
|
||||
meta, err := c.GetMetadata(context.Background(), "1141668", "track")
|
||||
if err != nil {
|
||||
t.Fatalf("GetMetadata() error = %v", err)
|
||||
}
|
||||
if !strings.Contains(stringFromAny(meta["lyrics"]), "Go shawty") {
|
||||
t.Fatalf("expected lyrics text, got %q", stringFromAny(meta["lyrics"]))
|
||||
}
|
||||
if !strings.Contains(stringFromAny(meta["lyrics_synced"]), "[00:00.00]Go, go, go") {
|
||||
t.Fatalf("expected synced lyrics, got %q", stringFromAny(meta["lyrics_synced"]))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginWithCredentials(t *testing.T) {
|
||||
mobileToken := testMobileToken(t)
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/gateway" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
switch r.URL.Query().Get("method") {
|
||||
case "mobile_auth":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"results": map[string]any{"TOKEN": mobileToken}})
|
||||
case "api_checkToken":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"results": "sid123"})
|
||||
case "mobile_userAuth":
|
||||
var payload map[string]any
|
||||
_ = json.NewDecoder(r.Body).Decode(&payload)
|
||||
if strings.TrimSpace(stringFromAny(payload["mail"])) == "" || strings.TrimSpace(stringFromAny(payload["password"])) == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"error": map[string]any{"message": "missing creds"}})
|
||||
return
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"results": map[string]any{"ARL": "arl-token", "JWT": "jwt-token", "refresh_token": "refresh-token", "license_token": "license-token", "USER_ID": "42"}})
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
cfgData := config.DefaultConfigData()
|
||||
cfgData.Deezer.Email = "tidal1@alpin.sbs"
|
||||
cfgData.Deezer.Password = "tidal1@alpin.sbs"
|
||||
c := New(&config.Config{File: cfgData, Session: cfgData})
|
||||
|
||||
origGateway := gatewayURL
|
||||
gatewayURL = ts.URL + "/gateway"
|
||||
defer func() { gatewayURL = origGateway }()
|
||||
|
||||
if err := c.Login(context.Background()); err != nil {
|
||||
t.Fatalf("Login() error = %v", err)
|
||||
}
|
||||
if !c.loggedIn {
|
||||
t.Fatalf("expected logged in client")
|
||||
}
|
||||
if c.arl != "arl-token" {
|
||||
t.Fatalf("arl = %q, want arl-token", c.arl)
|
||||
}
|
||||
if c.jwt != "jwt-token" {
|
||||
t.Fatalf("jwt = %q, want jwt-token", c.jwt)
|
||||
}
|
||||
if c.refresh != "refresh-token" {
|
||||
t.Fatalf("refresh = %q, want refresh-token", c.refresh)
|
||||
}
|
||||
if c.license != "license-token" {
|
||||
t.Fatalf("license = %q, want license-token", c.license)
|
||||
}
|
||||
if c.cfg.Session.Deezer.RefreshToken != "refresh-token" {
|
||||
t.Fatalf("session refresh token = %q", c.cfg.Session.Deezer.RefreshToken)
|
||||
}
|
||||
if c.cfg.File.Deezer.RefreshToken != "refresh-token" {
|
||||
t.Fatalf("file refresh token = %q", c.cfg.File.Deezer.RefreshToken)
|
||||
}
|
||||
}
|
||||
|
||||
func testMobileToken(t *testing.T) string {
|
||||
t.Helper()
|
||||
plain := []byte(strings.Repeat("A", 64) + strings.Repeat("B", 16) + strings.Repeat("C", 16))
|
||||
enc, err := aesECBEncrypt([]byte(gatewayDec), plain)
|
||||
if err != nil {
|
||||
t.Fatalf("aesECBEncrypt() error = %v", err)
|
||||
}
|
||||
return hex.EncodeToString(enc)
|
||||
}
|
||||
|
||||
func TestLoginWithRefreshToken(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/renew":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"jwt": "jwt-token", "refresh_token": "refresh-token-2"})
|
||||
case "/pipe":
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{"data": map[string]any{"tokens": map[string]any{"mediaServiceLicenseToken": map[string]any{"token": "license-token"}}}})
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
cfgData := config.DefaultConfigData()
|
||||
cfgData.Deezer.RefreshToken = "refresh-token"
|
||||
c := New(&config.Config{File: cfgData, Session: cfgData})
|
||||
|
||||
origAuth := authURL
|
||||
origPipe := pipeURL
|
||||
authURL = ts.URL + "/renew"
|
||||
pipeURL = ts.URL + "/pipe"
|
||||
defer func() {
|
||||
authURL = origAuth
|
||||
pipeURL = origPipe
|
||||
}()
|
||||
|
||||
if err := c.Login(context.Background()); err != nil {
|
||||
t.Fatalf("Login() error = %v", err)
|
||||
}
|
||||
if !c.loggedIn {
|
||||
t.Fatalf("expected logged in client")
|
||||
}
|
||||
if c.jwt != "jwt-token" || c.license != "license-token" {
|
||||
t.Fatalf("unexpected jwt/license: jwt=%q license=%q", c.jwt, c.license)
|
||||
}
|
||||
if c.cfg.Session.Deezer.RefreshToken != "refresh-token-2" {
|
||||
t.Fatalf("session refresh token = %q", c.cfg.Session.Deezer.RefreshToken)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user