From b55f0ad3d33f0bdc3a664b82de5c80c6e84e62b9 Mon Sep 17 00:00:00 2001 From: killer069 <65385476+killer069@users.noreply.github.com> Date: Wed, 8 Sep 2021 23:37:24 +0530 Subject: [PATCH] spotify revamped --- play-dl/Spotify/index.ts | 173 +++++++++++++++++++++++++-------------- play-dl/index.ts | 2 +- 2 files changed, 111 insertions(+), 64 deletions(-) diff --git a/play-dl/Spotify/index.ts b/play-dl/Spotify/index.ts index 8c23f08..613dae1 100644 --- a/play-dl/Spotify/index.ts +++ b/play-dl/Spotify/index.ts @@ -3,6 +3,8 @@ import { SpotifyAlbum, SpotifyPlaylist, SpotifyVideo } from "./classes" import readline from 'readline' import fs from 'fs' +var spotifyData : SpotifyDataOptions; + interface SpotifyDataOptions{ client_id : string; client_secret : string; @@ -12,50 +14,45 @@ interface SpotifyDataOptions{ refresh_token? : string; token_type? : string; expires_in? : number; + expiry? : number; + market? : string; } -const ask = readline.createInterface({ - input : process.stdin, - output : process.stdout -}) - const pattern = /^((https:)?\/\/)?open.spotify.com\/(track|album|playlist)\// export async function spotify(url : string): Promise{ if(!url.match(pattern)) throw new Error('This is not a Spotify URL') - let embed = embed_url(url) - let response = await got(embed) - return parse_json(embed, response.body) -} - -function parse_json(url : string, data : string): SpotifyAlbum | SpotifyPlaylist | SpotifyVideo{ - let json_data = JSON.parse(decodeURIComponent(data.split('')[0])) - if(url.indexOf('track') !== -1){ - return new SpotifyVideo(json_data) - } - else if(url.indexOf('album') !== -1){ - return new SpotifyAlbum(json_data) - } - else if(url.indexOf('playlist') !== -1){ - return new SpotifyPlaylist(json_data) - } - else throw new Error('Failed to parse data') -} - -function embed_url(url : string): string{ if(url.indexOf('track/') !== -1){ - let trackID = url.split('track/')[1].split('?')[0].split('/')[0].split('&')[0] - return `https://open.spotify.com/embed/track/${trackID}` + let trackID = url.split('track/')[1].split('&')[0].split('?')[0] + let response = await got(`https://api.spotify.com/v1/tracks/${trackID}?market=${spotifyData.market}`, { + headers : { + "Authorization" : `${spotifyData.token_type} ${spotifyData.access_token}` + } + }).catch((err) => {return 0}) + if(typeof response !== 'number') return new SpotifyVideo(JSON.parse(response.body)) + else throw new Error('Failed to get spotify Track Data') } else if(url.indexOf('album/') !== -1){ - let albumID = url.split('album/')[1].split('?')[0].split('/')[0].split('&')[0] - return `https://open.spotify.com/embed/album/${albumID}` + let albumID = url.split('album/')[1].split('&')[0].split('?')[0] + let response = await got(`https://api.spotify.com/v1/albums/${albumID}?market=${spotifyData.market}`, { + headers : { + "Authorization" : `${spotifyData.token_type} ${spotifyData.access_token}` + } + }).catch((err) => {return 0}) + if(typeof response !== 'number') return new SpotifyAlbum(JSON.parse(response.body)) + else throw new Error('Failed to get spotify Album Data') } else if(url.indexOf('playlist/') !== -1){ - let playlistID = url.split('playlist/')[1].split('?')[0].split('/')[0].split('&')[0] - return `https://open.spotify.com/embed/playlist/${playlistID}` + let playlistID = url.split('playlist/')[1].split('&')[0].split('?')[0] + let response = await got(`https://api.spotify.com/v1/playlists/${playlistID}?market=${spotifyData.market}`, { + headers : { + "Authorization" : `${spotifyData.token_type} ${spotifyData.access_token}` + } + }).catch((err) => {return 0}) + if(typeof response !== 'number') return new SpotifyAlbum(JSON.parse(response.body)) + else throw new Error('Failed to get spotify Playlist Data') } - else throw new Error('Unable to generate embed url for given spotify url.') + else throw new Error('URL is out of scope for play-dl.') } export function sp_validate(url : string): "track" | "playlist" | "album" | boolean{ @@ -73,26 +70,39 @@ export function sp_validate(url : string): "track" | "playlist" | "album" | bool } export function Authorization(){ - let client_id : string, client_secret : string, redirect_url : string; - let code : string; + let ask = readline.createInterface({ + input : process.stdin, + output : process.stdout + }) + + let client_id : string, client_secret : string, redirect_url : string, market : string; ask.question('Client ID : ', (id) => { client_id = id ask.question('Client Secret : ', (secret) => { client_secret = secret ask.question('Redirect URL : ', (url) => { redirect_url = url - console.log('Now Go to this url in your browser and Paste this url. Answer the next question \n') - console.log(`https://accounts.spotify.com/authorize?client_id=${client_id}&response_type=code&redirect_uri=${encodeURI(redirect_url)} \n`) - ask.question('Redirected URL : ', (url) => { - code = url.split('code=')[1] - if (!fs.existsSync('.data')) fs.mkdirSync('.data') - fs.writeFileSync('.data/spotify.data', JSON.stringify({ - client_id, - client_secret, - redirect_url, - authorization_code : code - })) - ask.close() + console.log('\nMarket Selection URL : \nhttps://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements \n') + ask.question('Market : ', (mar) => { + if(mar.length === 2) market = mar + else { + console.log('Invalid Market, Selecting IN as market') + market = 'IN' + } + console.log('\nNow Go to your browser and Paste this url. Authroize it and paste the redirected url here. \n') + console.log(`https://accounts.spotify.com/authorize?client_id=${client_id}&response_type=code&redirect_uri=${encodeURI(redirect_url)} \n`) + ask.question('Redirected URL : ', (url) => { + if (!fs.existsSync('.data')) fs.mkdirSync('.data') + spotifyData = { + client_id, + client_secret, + redirect_url, + authorization_code : url.split('code=')[1], + market + } + fs.writeFileSync('.data/spotify.data', JSON.stringify(spotifyData, undefined, 4)) + ask.close() + }) }) }) }) @@ -101,32 +111,69 @@ export function Authorization(){ export async function StartSpotify(){ if(!fs.existsSync('.data/spotify.data')) throw new Error('Spotify Data is Missing\nDid you forgot to do authorization ?') - - let data: SpotifyDataOptions = JSON.parse(fs.readFileSync('.data/spotify.data').toString()) - - if(data.authorization_code) data = await SpotifyAuthorize(data) + if(!spotifyData) spotifyData = JSON.parse(fs.readFileSync('.data/spotify.data').toString()) + + if(spotifyData.authorization_code) { + let check = await SpotifyAuthorize(spotifyData) + if(check !== false) spotifyData = check + fs.writeFileSync('.data/spotify.data', JSON.stringify(spotifyData, undefined, 4)) + } } -async function SpotifyAuthorize(data : SpotifyDataOptions): Promise{ +async function SpotifyAuthorize(data : SpotifyDataOptions): Promise{ let response = await got.post(`https://accounts.spotify.com/api/token?grant_type=authorization_code&code=${data.authorization_code}&redirect_uri=${encodeURI(data.redirect_url)}`, { headers : { "Authorization" : `Basic ${Buffer.from(`${data.client_id}:${data.client_secret}`).toString('base64')}`, "Content-Type" : "application/x-www-form-urlencoded" } + }).catch(() => { + return 0 }) - if(response.statusCode === 200) { - let resp_json = JSON.parse(response.body) - return{ - client_id : data.client_id, - client_secret : data.client_secret, - redirect_url : data.redirect_url, - access_token : resp_json.access_token, - refresh_token : resp_json.refresh_token, - expires_in : Number(resp_json.expires_in), - token_type : resp_json.token_type - } + if(typeof response === 'number') return false + let resp_json = JSON.parse(response.body) + return{ + client_id : data.client_id, + client_secret : data.client_secret, + redirect_url : data.redirect_url, + access_token : resp_json.access_token, + refresh_token : resp_json.refresh_token, + expires_in : Number(resp_json.expires_in), + expiry : Date.now() + (Number(resp_json.expires_in) * 1000), + token_type : resp_json.token_type, + market : data.market } - else throw new Error(`Got ${response.statusCode} while getting spotify access token\n${response.body}`) +} + +export function is_expired(){ + if(Date.now() >= (spotifyData.expiry as number)) return true + else return false +} + +export async function RefreshToken(): Promise{ + let response = await got.post(`https://accounts.spotify.com/api/token?grant_type=refresh_token&refresh_token=${spotifyData.refresh_token}`, { + headers : { + "Authorization" : `Basic ${Buffer.from(`${spotifyData.client_id}:${spotifyData.client_secret}`).toString('base64')}`, + "Content-Type" : "application/x-www-form-urlencoded" + } + }).catch(() => { + return 0 + }) + + if(typeof response === 'number') return false + let resp_json = JSON.parse(response.body) + spotifyData = { + client_id : spotifyData.client_id, + client_secret : spotifyData.client_secret, + redirect_url : spotifyData.redirect_url, + access_token : resp_json.access_token, + refresh_token : spotifyData.refresh_token, + expires_in : Number(resp_json.expires_in), + expiry : Date.now() + (Number(resp_json.expires_in) * 1000), + token_type : resp_json.token_type, + market : spotifyData.market + } + fs.writeFileSync('.data/spotify.data', JSON.stringify(spotifyData, undefined, 4)) + return true } \ No newline at end of file diff --git a/play-dl/index.ts b/play-dl/index.ts index b974ce4..0f2f82d 100644 --- a/play-dl/index.ts +++ b/play-dl/index.ts @@ -1,6 +1,6 @@ export { playlist_info, video_basic_info, video_info, search, stream, stream_from_info, yt_validate, extractID } from "./YouTube"; -export { spotify, sp_validate, Authorization, StartSpotify } from './Spotify' +export { spotify, sp_validate, Authorization, StartSpotify, RefreshToken, is_expired } from './Spotify' import { sp_validate, yt_validate } from ".";