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 { 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 { 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 { 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 { 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(); }