mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
fix deezer track pagination and decryption track id resolution
This commit is contained in:
@@ -176,6 +176,9 @@ func (c *Client) GetMetadata(ctx context.Context, item, mediaType string) (map[s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if tracks, pageErr := c.getCollectionPageItems(ctx, "/album/"+strings.TrimSpace(item)+"/tracks"); pageErr == nil {
|
||||||
|
resp["tracks"] = map[string]any{"data": tracks}
|
||||||
|
}
|
||||||
items := make([]any, 0)
|
items := make([]any, 0)
|
||||||
if tracks, ok := resp["tracks"].(map[string]any); ok {
|
if tracks, ok := resp["tracks"].(map[string]any); ok {
|
||||||
if data, ok := tracks["data"].([]any); ok {
|
if data, ok := tracks["data"].([]any); ok {
|
||||||
@@ -197,6 +200,9 @@ func (c *Client) GetMetadata(ctx context.Context, item, mediaType string) (map[s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if tracks, pageErr := c.getCollectionPageItems(ctx, "/playlist/"+strings.TrimSpace(item)+"/tracks"); pageErr == nil {
|
||||||
|
resp["tracks"] = map[string]any{"data": tracks}
|
||||||
|
}
|
||||||
items := make([]any, 0)
|
items := make([]any, 0)
|
||||||
if tracks, ok := resp["tracks"].(map[string]any); ok {
|
if tracks, ok := resp["tracks"].(map[string]any); ok {
|
||||||
if data, ok := tracks["data"].([]any); ok {
|
if data, ok := tracks["data"].([]any); ok {
|
||||||
@@ -269,6 +275,35 @@ func (c *Client) getArtistAlbums(ctx context.Context, artistID string) (map[stri
|
|||||||
return map[string]any{"data": all, "total": total}, nil
|
return map[string]any{"data": all, "total": total}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) getCollectionPageItems(ctx context.Context, path string) ([]any, error) {
|
||||||
|
const pageSize = 100
|
||||||
|
index := 0
|
||||||
|
total := -1
|
||||||
|
all := make([]any, 0)
|
||||||
|
for {
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("limit", strconv.Itoa(pageSize))
|
||||||
|
params.Set("index", strconv.Itoa(index))
|
||||||
|
resp, err := c.apiGet(ctx, path, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, _ := resp["data"].([]any)
|
||||||
|
all = append(all, data...)
|
||||||
|
if total < 0 {
|
||||||
|
total = jsonutil.IntFromAny(resp["total"])
|
||||||
|
}
|
||||||
|
if len(data) < pageSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index += len(data)
|
||||||
|
if total > 0 && index >= total {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) GetDownloadable(ctx context.Context, item string, _ int) (*provider.Downloadable, error) {
|
func (c *Client) GetDownloadable(ctx context.Context, item string, _ int) (*provider.Downloadable, error) {
|
||||||
if strings.TrimSpace(c.license) == "" {
|
if strings.TrimSpace(c.license) == "" {
|
||||||
if err := c.ensureLaunchSession(ctx); err != nil {
|
if err := c.ensureLaunchSession(ctx); err != nil {
|
||||||
@@ -282,7 +317,7 @@ func (c *Client) GetDownloadable(ctx context.Context, item string, _ int) (*prov
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
trackToken, err := c.getTrackToken(ctx, item)
|
trackToken, mediaTrackID, err := c.getTrackToken(ctx, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -294,7 +329,13 @@ func (c *Client) GetDownloadable(ctx context.Context, item string, _ int) (*prov
|
|||||||
if ext == "" {
|
if ext == "" {
|
||||||
ext = "mp3"
|
ext = "mp3"
|
||||||
}
|
}
|
||||||
trackID := strings.TrimSpace(jsonutil.StringFromAny(meta["id"]))
|
trackID := strings.TrimSpace(media.TrackID)
|
||||||
|
if trackID == "" {
|
||||||
|
trackID = strings.TrimSpace(mediaTrackID)
|
||||||
|
}
|
||||||
|
if trackID == "" {
|
||||||
|
trackID = strings.TrimSpace(jsonutil.StringFromAny(meta["id"]))
|
||||||
|
}
|
||||||
if trackID == "" {
|
if trackID == "" {
|
||||||
trackID = strings.TrimSpace(item)
|
trackID = strings.TrimSpace(item)
|
||||||
}
|
}
|
||||||
@@ -549,32 +590,33 @@ func (c *Client) loginWithCredentials(ctx context.Context, email, password strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getTrackToken(ctx context.Context, trackID string) (string, error) {
|
func (c *Client) getTrackToken(ctx context.Context, trackID string) (string, string, error) {
|
||||||
if token, err := c.getTrackTokenFromPipe(ctx, trackID); err == nil && strings.TrimSpace(token) != "" {
|
if token, mediaID, err := c.getTrackTokenFromPipe(ctx, trackID); err == nil && strings.TrimSpace(token) != "" {
|
||||||
return token, nil
|
return token, mediaID, nil
|
||||||
} else if errors.Is(err, errDeezerJWTExpired) {
|
} else if errors.Is(err, errDeezerJWTExpired) {
|
||||||
c.refreshJWTFromAvailableState(ctx)
|
c.refreshJWTFromAvailableState(ctx)
|
||||||
if token, retryErr := c.getTrackTokenFromPipe(ctx, trackID); retryErr == nil && strings.TrimSpace(token) != "" {
|
if token, mediaID, retryErr := c.getTrackTokenFromPipe(ctx, trackID); retryErr == nil && strings.TrimSpace(token) != "" {
|
||||||
return token, nil
|
return token, mediaID, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := c.ensureJWT(ctx, "deezer jwt unavailable for track media token"); err == nil {
|
if err := c.ensureJWT(ctx, "deezer jwt unavailable for track media token"); err == nil {
|
||||||
if token, retryErr := c.getTrackTokenFromPipe(ctx, trackID); retryErr == nil && strings.TrimSpace(token) != "" {
|
if token, mediaID, retryErr := c.getTrackTokenFromPipe(ctx, trackID); retryErr == nil && strings.TrimSpace(token) != "" {
|
||||||
return token, nil
|
return token, mediaID, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resp, err := c.apiGet(ctx, "/track/"+url.PathEscape(strings.TrimSpace(trackID)), nil)
|
resp, err := c.apiGet(ctx, "/track/"+url.PathEscape(strings.TrimSpace(trackID)), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
token := strings.TrimSpace(jsonutil.StringFromAny(resp["track_token"]))
|
token := strings.TrimSpace(jsonutil.StringFromAny(resp["track_token"]))
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return "", errors.New("deezer track metadata missing track_token")
|
return "", "", errors.New("deezer track metadata missing track_token")
|
||||||
}
|
}
|
||||||
return token, nil
|
mediaID := strings.TrimSpace(jsonutil.StringFromAny(resp["id"]))
|
||||||
|
return token, mediaID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getTrackTokenFromPipe(ctx context.Context, trackID string) (string, error) {
|
func (c *Client) getTrackTokenFromPipe(ctx context.Context, trackID string) (string, string, error) {
|
||||||
query := `query KmpMpTrackMedia($trackId: String!) { track(trackId: $trackId) { media { __typename ...TrackMediaFields } } } fragment TrackMediaFields on TrackMedia { id version token { payload expiresAt version } estimatedSizes { flac: FLAC mp3_320: MP3_320 mp3_128: MP3_128 mp3_misc: MP3_MISC opus_std: OPUS_STD opus_high: OPUS_HIGH sbc_256: SBC_256 aac_96: AAC_96 aac_64: AAC_64 ac4_ims: AC4_IMS dd_joc: DD_JOC mp4_ra1: MP4_RA1 mp4_ra2: MP4_RA2 mp4_ra3: MP4_RA3 } gain rights { sub { available } ads { available } } }`
|
query := `query KmpMpTrackMedia($trackId: String!) { track(trackId: $trackId) { media { __typename ...TrackMediaFields } } } fragment TrackMediaFields on TrackMedia { id version token { payload expiresAt version } estimatedSizes { flac: FLAC mp3_320: MP3_320 mp3_128: MP3_128 mp3_misc: MP3_MISC opus_std: OPUS_STD opus_high: OPUS_HIGH sbc_256: SBC_256 aac_96: AAC_96 aac_64: AAC_64 ac4_ims: AC4_IMS dd_joc: DD_JOC mp4_ra1: MP4_RA1 mp4_ra2: MP4_RA2 mp4_ra3: MP4_RA3 } gain rights { sub { available } ads { available } } }`
|
||||||
body := map[string]any{
|
body := map[string]any{
|
||||||
"operationName": "KmpMpTrackMedia",
|
"operationName": "KmpMpTrackMedia",
|
||||||
@@ -589,13 +631,15 @@ func (c *Client) getTrackTokenFromPipe(ctx context.Context, trackID string) (str
|
|||||||
}
|
}
|
||||||
out, err := c.pipeGraphQL(ctx, body, "deezer track media query")
|
out, err := c.pipeGraphQL(ctx, body, "deezer track media query")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
payload := strings.TrimSpace(jsonutil.StringFromAny(jsonutil.NestedMap(jsonutil.NestedMap(jsonutil.NestedMap(jsonutil.NestedMap(out, "data"), "track"), "media"), "token")["payload"]))
|
media := jsonutil.NestedMap(jsonutil.NestedMap(jsonutil.NestedMap(out, "data"), "track"), "media")
|
||||||
|
payload := strings.TrimSpace(jsonutil.StringFromAny(jsonutil.NestedMap(media, "token")["payload"]))
|
||||||
if payload == "" {
|
if payload == "" {
|
||||||
return "", errors.New("deezer track media response missing token payload")
|
return "", "", errors.New("deezer track media response missing token payload")
|
||||||
}
|
}
|
||||||
return payload, nil
|
mediaID := strings.TrimSpace(jsonutil.StringFromAny(media["id"]))
|
||||||
|
return payload, mediaID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type lyricsResult struct {
|
type lyricsResult struct {
|
||||||
@@ -1148,6 +1192,7 @@ type mediaResult struct {
|
|||||||
URL string
|
URL string
|
||||||
Format string
|
Format string
|
||||||
Cipher string
|
Cipher string
|
||||||
|
TrackID string
|
||||||
}
|
}
|
||||||
|
|
||||||
type deezerMediaError struct {
|
type deezerMediaError struct {
|
||||||
@@ -1270,19 +1315,50 @@ func (c *Client) getMediaURLWithRequest(ctx context.Context, trackToken string,
|
|||||||
return nil, &deezerMediaError{Code: e.Code, Message: e.Message}
|
return nil, &deezerMediaError{Code: e.Code, Message: e.Message}
|
||||||
}
|
}
|
||||||
for _, want := range requestedFormats {
|
for _, want := range requestedFormats {
|
||||||
|
for _, preferredCipher := range []string{"NONE", "BF_CBC_STRIPE"} {
|
||||||
for _, m := range parsed.Data[0].Media {
|
for _, m := range parsed.Data[0].Media {
|
||||||
if !strings.EqualFold(strings.TrimSpace(m.Format), want) {
|
if !strings.EqualFold(strings.TrimSpace(m.Format), want) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !strings.EqualFold(strings.TrimSpace(m.Cipher.Type), preferredCipher) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if len(m.Sources) == 0 || strings.TrimSpace(m.Sources[0].URL) == "" {
|
if len(m.Sources) == 0 || strings.TrimSpace(m.Sources[0].URL) == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return &mediaResult{URL: m.Sources[0].URL, Format: m.Format, Cipher: m.Cipher.Type}, nil
|
sourceURL := strings.TrimSpace(m.Sources[0].URL)
|
||||||
|
return &mediaResult{URL: sourceURL, Format: m.Format, Cipher: m.Cipher.Type, TrackID: extractTrackIDFromMediaURL(sourceURL)}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, errors.New("deezer media response contains no sources")
|
return nil, errors.New("deezer media response contains no sources")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractTrackIDFromMediaURL(rawURL string) string {
|
||||||
|
u, err := url.Parse(strings.TrimSpace(rawURL))
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
parts := strings.Split(strings.TrimSpace(strings.Trim(u.Path, "/")), "/")
|
||||||
|
for i := len(parts) - 1; i >= 0; i-- {
|
||||||
|
p := strings.TrimSpace(parts[i])
|
||||||
|
if p == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
digitsOnly := true
|
||||||
|
for _, r := range p {
|
||||||
|
if r < '0' || r > '9' {
|
||||||
|
digitsOnly = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if digitsOnly {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func buildFormatPriority(quality int, allowFallback bool) []string {
|
func buildFormatPriority(quality int, allowFallback bool) []string {
|
||||||
want := "FLAC"
|
want := "FLAC"
|
||||||
if quality <= 0 {
|
if quality <= 0 {
|
||||||
|
|||||||
@@ -99,6 +99,118 @@ func TestGetMetadataArtistPaginatesAlbums(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetMetadataAlbumPaginatesTracks(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/album/46514392":
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": 46514392, "title": "Clouseau30", "tracks": map[string]any{"data": []any{}}})
|
||||||
|
case "/album/46514392/tracks":
|
||||||
|
callCount++
|
||||||
|
index := r.URL.Query().Get("index")
|
||||||
|
limit := r.URL.Query().Get("limit")
|
||||||
|
if limit != "100" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch index {
|
||||||
|
case "0":
|
||||||
|
items := make([]any, 0, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
items = append(items, map[string]any{"id": i + 1, "title": "T"})
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"data": items, "total": 105})
|
||||||
|
case "100":
|
||||||
|
items := make([]any, 0, 5)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
items = append(items, map[string]any{"id": 101 + i, "title": "T"})
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"data": items, "total": 105})
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
cfgData := config.DefaultConfigData()
|
||||||
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
||||||
|
c.loggedIn = true
|
||||||
|
|
||||||
|
origBase := baseURL
|
||||||
|
baseURL = ts.URL
|
||||||
|
defer func() { baseURL = origBase }()
|
||||||
|
|
||||||
|
meta, err := c.GetMetadata(context.Background(), "46514392", "album")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetMetadata() error = %v", err)
|
||||||
|
}
|
||||||
|
tracksObj, _ := meta["tracks"].(map[string]any)
|
||||||
|
items, _ := tracksObj["items"].([]any)
|
||||||
|
if len(items) != 105 {
|
||||||
|
t.Fatalf("tracks len = %d, want 105", len(items))
|
||||||
|
}
|
||||||
|
if callCount != 2 {
|
||||||
|
t.Fatalf("track page call count = %d, want 2", callCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMetadataPlaylistPaginatesTracks(t *testing.T) {
|
||||||
|
callCount := 0
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/playlist/123":
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": 123, "title": "Mix", "tracks": map[string]any{"data": []any{}}})
|
||||||
|
case "/playlist/123/tracks":
|
||||||
|
callCount++
|
||||||
|
index := r.URL.Query().Get("index")
|
||||||
|
limit := r.URL.Query().Get("limit")
|
||||||
|
if limit != "100" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch index {
|
||||||
|
case "0":
|
||||||
|
items := make([]any, 0, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
items = append(items, map[string]any{"id": i + 1, "title": "T"})
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"data": items, "total": 101})
|
||||||
|
case "100":
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"data": []any{map[string]any{"id": 101, "title": "T"}}, "total": 101})
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
cfgData := config.DefaultConfigData()
|
||||||
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
||||||
|
c.loggedIn = true
|
||||||
|
|
||||||
|
origBase := baseURL
|
||||||
|
baseURL = ts.URL
|
||||||
|
defer func() { baseURL = origBase }()
|
||||||
|
|
||||||
|
meta, err := c.GetMetadata(context.Background(), "123", "playlist")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetMetadata() error = %v", err)
|
||||||
|
}
|
||||||
|
tracksObj, _ := meta["tracks"].(map[string]any)
|
||||||
|
items, _ := tracksObj["items"].([]any)
|
||||||
|
if len(items) != 101 {
|
||||||
|
t.Fatalf("tracks len = %d, want 101", len(items))
|
||||||
|
}
|
||||||
|
if callCount != 2 {
|
||||||
|
t.Fatalf("track page call count = %d, want 2", callCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetDownloadableNativeCipher(t *testing.T) {
|
func TestGetDownloadableNativeCipher(t *testing.T) {
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
@@ -161,6 +273,58 @@ func TestGetDownloadableNativeCipher(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetDownloadablePrefersNoneCipherWhenAvailable(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.URL.Path {
|
||||||
|
case "/track/42":
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"id": 42, "title": "X", "track_token": "tt"})
|
||||||
|
case "/media":
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"data": []any{map[string]any{"errors": []any{}, "media": []any{
|
||||||
|
map[string]any{"cipher": map[string]any{"type": "BF_CBC_STRIPE"}, "format": "FLAC", "sources": []any{map[string]any{"url": "https://cdn.example/bf"}}},
|
||||||
|
map[string]any{"cipher": map[string]any{"type": "NONE"}, "format": "FLAC", "sources": []any{map[string]any{"url": "https://cdn.example/plain"}}},
|
||||||
|
}}}})
|
||||||
|
default:
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
cfgData := config.DefaultConfigData()
|
||||||
|
cfgData.Deezer.ARL = "arl"
|
||||||
|
c := New(&config.Config{File: cfgData, Session: cfgData})
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetDownloadable() error = %v", err)
|
||||||
|
}
|
||||||
|
if d.Cipher != "NONE" || d.URL != "https://cdn.example/plain" {
|
||||||
|
t.Fatalf("expected NONE cipher source, got %+v", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractTrackIDFromMediaURL(t *testing.T) {
|
||||||
|
url := "https://f-cdnt-stream.dzcdn.net/media/1/9/6/4/8/2552667002/64821d6a2007e90768fa0300b508fcf4.flac?hdnea=x"
|
||||||
|
if got := extractTrackIDFromMediaURL(url); got != "2552667002" {
|
||||||
|
t.Fatalf("extractTrackIDFromMediaURL() = %q, want 2552667002", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoginPrefersARLFlowOverRefreshShortcut(t *testing.T) {
|
func TestLoginPrefersARLFlowOverRefreshShortcut(t *testing.T) {
|
||||||
mobileToken := testMobileToken(t)
|
mobileToken := testMobileToken(t)
|
||||||
refreshCalled := false
|
refreshCalled := false
|
||||||
@@ -298,13 +462,16 @@ func TestGetTrackTokenPrefersPipeToken(t *testing.T) {
|
|||||||
pipeURL = origPipe
|
pipeURL = origPipe
|
||||||
}()
|
}()
|
||||||
|
|
||||||
token, err := c.getTrackToken(context.Background(), "42")
|
token, mediaID, err := c.getTrackToken(context.Background(), "42")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("getTrackToken() error = %v", err)
|
t.Fatalf("getTrackToken() error = %v", err)
|
||||||
}
|
}
|
||||||
if token != "pipe-track-token" {
|
if token != "pipe-track-token" {
|
||||||
t.Fatalf("token = %q, want pipe-track-token", token)
|
t.Fatalf("token = %q, want pipe-track-token", token)
|
||||||
}
|
}
|
||||||
|
if mediaID != "" {
|
||||||
|
t.Fatalf("mediaID = %q, want empty when pipe media id missing", mediaID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDownloadableUsesPipeTrackToken(t *testing.T) {
|
func TestGetDownloadableUsesPipeTrackToken(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user