Skip to content

Commit 444ee52

Browse files
committed
fix: update Spotify API anonymous token generation
Closes #12
1 parent 9e7f260 commit 444ee52

File tree

3 files changed

+231
-105
lines changed

3 files changed

+231
-105
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
"author": "Sayem Chowdhury",
1818
"license": "MIT",
1919
"dependencies": {
20+
"@scure/base": "^1.2.4",
2021
"axios": "^0.26.1",
2122
"browser-id3-writer": "^4.4.0",
2223
"delay": "^5.0.0",
2324
"node-html-parser": "^5.3.3",
25+
"otpauth": "^9.3.6",
2426
"p-queue": "^6.6.2",
2527
"spotify-uri": "^2.2.0",
2628
"spotify-web-api-node": "^5.0.2"

src/converter/spotify.ts

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import axios from 'axios';
22
import SpotifyWebApi from 'spotify-web-api-node';
33
import PQueue from 'p-queue';
4+
import {TOTP} from 'otpauth';
5+
import {base32} from '@scure/base';
46
import {isrc2deezer, upc2deezer} from './deezer';
57
import type {playlistInfo, trackType} from '../types';
68

@@ -35,18 +37,59 @@ const queue = new PQueue({concurrency: 25});
3537
/**
3638
* Export core spotify module
3739
*/
38-
export const spotifyApi = new SpotifyWebApi();
40+
export let spotifyApi = new SpotifyWebApi();
41+
42+
/**
43+
* Generate Spotify auth TOTP
44+
*/
45+
const generateTotp = async (): Promise<string> => {
46+
const secretSauce = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
47+
const secretCipherBytes = [12, 56, 76, 33, 88, 44, 88, 33, 78, 78, 11, 66, 22, 22, 55, 69, 54].map(
48+
(e, t) => e ^ ((t % 33) + 9),
49+
);
50+
51+
const secretBytes = new TextEncoder().encode(secretCipherBytes.join(''));
52+
53+
const secret = base32.encode(secretBytes);
54+
55+
const response = await axios.get('https://open.spotify.com/server-time');
56+
const serverTimeSeconds = response.data.serverTime;
57+
58+
const totp = new TOTP({
59+
algorithm: 'SHA1',
60+
digits: 6,
61+
period: 30,
62+
secret: secret,
63+
});
64+
65+
const otp = totp.generate(serverTimeSeconds);
66+
67+
return otp;
68+
};
3969

4070
/**
4171
* Set spotify tokens anonymously. This is required to bypass api limits.
4272
* @returns {tokensType}
4373
*/
4474
export const setSpotifyAnonymousToken = async () => {
45-
const {data} = await axios.get<tokensType>(
46-
'https://open.spotify.com/get_access_token?reason=transport&productType=embed',
47-
);
48-
spotifyApi.setAccessToken(data.accessToken);
49-
return data;
75+
const timestamp = Math.floor(Date.now() / 1000);
76+
const totp = await generateTotp();
77+
78+
for (let i = 0; i <= 10; i++) {
79+
const {data} = await axios.get<tokensType>(
80+
`https://open.spotify.com/get_access_token?reason=transport&productType=web_player&totp=${totp}&totpVer=5&ts=${timestamp}`,
81+
);
82+
83+
if (data.accessToken.includes('_') || data.accessToken.includes('-')) {
84+
spotifyApi = new SpotifyWebApi({
85+
clientId: data.clientId,
86+
});
87+
88+
spotifyApi.setAccessToken(data.accessToken);
89+
return data;
90+
}
91+
}
92+
throw new Error('Unable to find a valid Spotify access token');
5093
};
5194

5295
/**

0 commit comments

Comments
 (0)