218 lines
8.2 KiB
TypeScript
218 lines
8.2 KiB
TypeScript
import { request } from '../Request';
|
|
import { SpotifyAlbum, SpotifyPlaylist, SpotifyTrack } from './classes';
|
|
import fs from 'fs';
|
|
|
|
let spotifyData: SpotifyDataOptions;
|
|
if (fs.existsSync('.data/spotify.data')) {
|
|
spotifyData = JSON.parse(fs.readFileSync('.data/spotify.data').toString());
|
|
spotifyData.file = true
|
|
}
|
|
/**
|
|
* Spotify Data options that are stored in spotify.data file.
|
|
*/
|
|
export interface SpotifyDataOptions {
|
|
client_id: string;
|
|
client_secret: string;
|
|
redirect_url?: string;
|
|
authorization_code?: string;
|
|
access_token?: string;
|
|
refresh_token?: string;
|
|
token_type?: string;
|
|
expires_in?: number;
|
|
expiry?: number;
|
|
market?: string;
|
|
file? : boolean
|
|
}
|
|
|
|
const pattern = /^((https:)?\/\/)?open.spotify.com\/(track|album|playlist)\//;
|
|
/**
|
|
* Function to get Playlist | Album | Track
|
|
* @param url url of spotify from which you want info
|
|
* @returns Spotify type.
|
|
*/
|
|
export async function spotify(url: string): Promise<Spotify> {
|
|
if (!spotifyData) throw new Error('Spotify Data is missing\nDid you forgot to do authorization ?');
|
|
if (!url.match(pattern)) throw new Error('This is not a Spotify URL');
|
|
if (url.indexOf('track/') !== -1) {
|
|
const trackID = url.split('track/')[1].split('&')[0].split('?')[0];
|
|
const response = await request(`https://api.spotify.com/v1/tracks/${trackID}?market=${spotifyData.market}`, {
|
|
headers: {
|
|
Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`
|
|
}
|
|
}).catch((err: Error) => {
|
|
return err;
|
|
});
|
|
if (response instanceof Error) throw response;
|
|
return new SpotifyTrack(JSON.parse(response));
|
|
} else if (url.indexOf('album/') !== -1) {
|
|
const albumID = url.split('album/')[1].split('&')[0].split('?')[0];
|
|
const response = await request(`https://api.spotify.com/v1/albums/${albumID}?market=${spotifyData.market}`, {
|
|
headers: {
|
|
Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`
|
|
}
|
|
}).catch((err: Error) => {
|
|
return err;
|
|
});
|
|
if (response instanceof Error) throw response;
|
|
return new SpotifyAlbum(JSON.parse(response), spotifyData);
|
|
} else if (url.indexOf('playlist/') !== -1) {
|
|
const playlistID = url.split('playlist/')[1].split('&')[0].split('?')[0];
|
|
const response = await request(
|
|
`https://api.spotify.com/v1/playlists/${playlistID}?market=${spotifyData.market}`,
|
|
{
|
|
headers: {
|
|
Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`
|
|
}
|
|
}
|
|
).catch((err: Error) => {
|
|
return err;
|
|
});
|
|
if (response instanceof Error) throw response;
|
|
return new SpotifyPlaylist(JSON.parse(response), spotifyData);
|
|
} else throw new Error('URL is out of scope for play-dl.');
|
|
}
|
|
/**
|
|
* Function to validate Spotify url
|
|
* @param url url for validation
|
|
* @returns type of url or false.
|
|
*/
|
|
export function sp_validate(url: string): 'track' | 'playlist' | 'album' | 'search' | false {
|
|
if (!url.match(pattern)) return 'search';
|
|
if (url.indexOf('track/') !== -1) {
|
|
return 'track';
|
|
} else if (url.indexOf('album/') !== -1) {
|
|
return 'album';
|
|
} else if (url.indexOf('playlist/') !== -1) {
|
|
return 'playlist';
|
|
} else return false;
|
|
}
|
|
/**
|
|
* Fuction for authorizing for spotify data.
|
|
* @param data Sportify Data options to validate
|
|
* @returns boolean.
|
|
*/
|
|
export async function SpotifyAuthorize(data: SpotifyDataOptions, file : boolean): Promise<boolean> {
|
|
const response = await request(`https://accounts.spotify.com/api/token`, {
|
|
headers: {
|
|
'Authorization': `Basic ${Buffer.from(`${data.client_id}:${data.client_secret}`).toString('base64')}`,
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
},
|
|
body: `grant_type=authorization_code&code=${data.authorization_code}&redirect_uri=${encodeURI(
|
|
data.redirect_url as string
|
|
)}`,
|
|
method: 'POST'
|
|
}).catch((err: Error) => {
|
|
return err;
|
|
});
|
|
if (response instanceof Error) throw response;
|
|
const resp_json = JSON.parse(response);
|
|
spotifyData = {
|
|
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() + (resp_json.expires_in - 1) * 1000,
|
|
token_type: resp_json.token_type,
|
|
market: data.market
|
|
};
|
|
if(file) fs.writeFileSync('.data/spotify.data', JSON.stringify(spotifyData, undefined, 4));
|
|
else {
|
|
console.log(`Client ID : ${spotifyData.client_id}`)
|
|
console.log(`Client Secret : ${spotifyData.client_secret}`)
|
|
console.log(`Refresh Token : ${spotifyData.refresh_token}`)
|
|
console.log(`Market : ${spotifyData.market}`)
|
|
console.log(`\nPaste above info in setToken function.`)
|
|
}
|
|
return true;
|
|
}
|
|
/**
|
|
* Function to check if authorization token is expired or not.
|
|
* @returns boolean
|
|
*/
|
|
export function is_expired(): boolean {
|
|
if (Date.now() >= (spotifyData.expiry as number)) return true;
|
|
else return false;
|
|
}
|
|
/**
|
|
* type for Spotify Class
|
|
*/
|
|
export type Spotify = SpotifyAlbum | SpotifyPlaylist | SpotifyTrack;
|
|
/**
|
|
* Function for searching songs on Spotify
|
|
* @param query searching query
|
|
* @param type "album" | "playlist" | "track"
|
|
* @param limit max no of results
|
|
* @returns Spotify type.
|
|
*/
|
|
export async function sp_search(
|
|
query: string,
|
|
type: 'album' | 'playlist' | 'track',
|
|
limit: number = 10
|
|
): Promise<Spotify[]> {
|
|
const results: Spotify[] = [];
|
|
if (!spotifyData) throw new Error('Spotify Data is missing\nDid you forgot to do authorization ?');
|
|
if (query.length === 0) throw new Error('Pass some query to search.');
|
|
if (limit > 50 || limit < 0) throw new Error(`You crossed limit range of Spotify [ 0 - 50 ]`);
|
|
const response = await request(
|
|
`https://api.spotify.com/v1/search?type=${type}&q=${query.replaceAll(' ', '+')}&limit=${limit}&market=${
|
|
spotifyData.market
|
|
}`,
|
|
{
|
|
headers: {
|
|
Authorization: `${spotifyData.token_type} ${spotifyData.access_token}`
|
|
}
|
|
}
|
|
).catch((err: Error) => {
|
|
return err;
|
|
});
|
|
if (response instanceof Error) throw response;
|
|
const json_data = JSON.parse(response);
|
|
if (type === 'track') {
|
|
json_data.tracks.items.forEach((track: any) => {
|
|
results.push(new SpotifyTrack(track));
|
|
});
|
|
} else if (type === 'album') {
|
|
json_data.albums.items.forEach((album: any) => {
|
|
results.push(new SpotifyAlbum(album, spotifyData));
|
|
});
|
|
} else if (type === 'playlist') {
|
|
json_data.playlists.items.forEach((playlist: any) => {
|
|
results.push(new SpotifyPlaylist(playlist, spotifyData));
|
|
});
|
|
}
|
|
return results;
|
|
}
|
|
/**
|
|
* Function to refresh Token
|
|
* @returns boolean to check whether token is refreshed or not
|
|
*/
|
|
export async function refreshToken(): Promise<boolean> {
|
|
const response = await request(`https://accounts.spotify.com/api/token`, {
|
|
headers: {
|
|
'Authorization': `Basic ${Buffer.from(`${spotifyData.client_id}:${spotifyData.client_secret}`).toString(
|
|
'base64'
|
|
)}`,
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
},
|
|
body: `grant_type=refresh_token&refresh_token=${spotifyData.refresh_token}`,
|
|
method: 'POST'
|
|
}).catch((err: Error) => {
|
|
return err;
|
|
});
|
|
if (response instanceof Error) return false;
|
|
const resp_json = JSON.parse(response);
|
|
spotifyData.access_token = resp_json.access_token;
|
|
spotifyData.expires_in = Number(resp_json.expires_in);
|
|
spotifyData.expiry = Date.now() + (resp_json.expires_in - 1) * 1000;
|
|
spotifyData.token_type = resp_json.token_type;
|
|
if(spotifyData.file) fs.writeFileSync('.data/spotify.data', JSON.stringify(spotifyData, undefined, 4));
|
|
return true;
|
|
}
|
|
|
|
export function setSpotifyToken(options: SpotifyDataOptions) {
|
|
spotifyData = options
|
|
spotifyData.file = false
|
|
refreshToken()
|
|
} |