From 61e736ce972f3cd6af8e4be28cd5e72f1921ab86 Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Thu, 10 Mar 2022 19:30:06 +0100 Subject: [PATCH] Trim string args in external facing functions as people are not cleaning up their user input --- play-dl/Deezer/index.ts | 12 +++--- play-dl/SoundCloud/index.ts | 12 +++--- play-dl/Spotify/index.ts | 22 ++++++----- play-dl/YouTube/utils/extractor.ts | 60 ++++++++++++++++-------------- play-dl/index.ts | 40 ++++++++++---------- 5 files changed, 78 insertions(+), 68 deletions(-) diff --git a/play-dl/Deezer/index.ts b/play-dl/Deezer/index.ts index 745d5eb..fde755d 100644 --- a/play-dl/Deezer/index.ts +++ b/play-dl/Deezer/index.ts @@ -145,7 +145,7 @@ export type Deezer = DeezerTrack | DeezerPlaylist | DeezerAlbum; * object depending on the provided URL. */ export async function deezer(url: string): Promise { - const typeData = await internalValidate(url); + const typeData = await internalValidate(url.trim()); if (typeData.error) { throw new Error(`This is not a Deezer track, playlist or album URL:\n${typeData.error}`); @@ -179,7 +179,7 @@ export async function deezer(url: string): Promise { * `false` means that the provided URL was a wrongly formatted or an unsupported Deezer URL. */ export async function dz_validate(url: string): Promise<'track' | 'playlist' | 'album' | 'search' | false> { - const typeData = await internalValidate(url); + const typeData = await internalValidate(url.trim()); return typeData.type; } @@ -257,13 +257,13 @@ export async function dz_advanced_track_search(options: DeezerAdvancedSearchOpti if (limit < 1) throw new Error('The minimum search limit for Deezer is 1'); const metadata: string[] = []; - if (options.artist) metadata.push(`artist:"${encodeURIComponent(options.artist)}"`); + if (options.artist) metadata.push(`artist:"${encodeURIComponent(options.artist.trim())}"`); - if (options.album) metadata.push(`album:"${encodeURIComponent(options.album)}"`); + if (options.album) metadata.push(`album:"${encodeURIComponent(options.album.trim())}"`); - if (options.title) metadata.push(`track:"${encodeURIComponent(options.title)}"`); + if (options.title) metadata.push(`track:"${encodeURIComponent(options.title.trim())}"`); - if (options.label) metadata.push(`label:"${encodeURIComponent(options.label)}"`); + if (options.label) metadata.push(`label:"${encodeURIComponent(options.label.trim())}"`); if (!isNaN(Number(options.minDurationInSec))) metadata.push(`dur_min:${options.minDurationInSec}`); diff --git a/play-dl/SoundCloud/index.ts b/play-dl/SoundCloud/index.ts index 50a17ef..e46a13c 100644 --- a/play-dl/SoundCloud/index.ts +++ b/play-dl/SoundCloud/index.ts @@ -30,10 +30,11 @@ const pattern = /^(?:(https?):\/\/)?(?:(?:www|m)\.)?(api\.soundcloud\.com|soundc */ export async function soundcloud(url: string): Promise { if (!soundData) throw new Error('SoundCloud Data is missing\nDid you forget to do authorization ?'); - if (!url.match(pattern)) throw new Error('This is not a SoundCloud URL'); + const url_ = url.trim(); + if (!url_.match(pattern)) throw new Error('This is not a SoundCloud URL'); const data = await request( - `https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${soundData.client_id}` + `https://api-v2.soundcloud.com/resolve?url=${url_}&client_id=${soundData.client_id}` ).catch((err: Error) => err); if (data instanceof Error) throw data; @@ -162,10 +163,11 @@ export async function check_id(id: string): Promise { * ``` */ export async function so_validate(url: string): Promise { - if (!url.startsWith('https')) return 'search'; - if (!url.match(pattern)) return false; + const url_ = url.trim(); + if (!url_.startsWith('https')) return 'search'; + if (!url_.match(pattern)) return false; const data = await request( - `https://api-v2.soundcloud.com/resolve?url=${url}&client_id=${soundData.client_id}` + `https://api-v2.soundcloud.com/resolve?url=${url_}&client_id=${soundData.client_id}` ).catch((err: Error) => err); if (data instanceof Error) return false; diff --git a/play-dl/Spotify/index.ts b/play-dl/Spotify/index.ts index 16b29fd..f3e6f5c 100644 --- a/play-dl/Spotify/index.ts +++ b/play-dl/Spotify/index.ts @@ -43,9 +43,10 @@ const pattern = /^((https:)?\/\/)?open.spotify.com\/(track|album|playlist)\//; */ 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 url_ = url.trim(); + 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}` @@ -55,7 +56,7 @@ export async function spotify(url: string): Promise { }); if (response instanceof Error) throw response; return new SpotifyTrack(JSON.parse(response)); - } else if (url.indexOf('album/') !== -1) { + } 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: { @@ -66,7 +67,7 @@ export async function spotify(url: string): Promise { }); if (response instanceof Error) throw response; return new SpotifyAlbum(JSON.parse(response), spotifyData, false); - } else if (url.indexOf('playlist/') !== -1) { + } 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}`, @@ -91,13 +92,14 @@ export async function spotify(url: string): Promise { * ``` */ export function sp_validate(url: string): 'track' | 'playlist' | 'album' | 'search' | false { - if (!url.startsWith('https')) return 'search'; - if (!url.match(pattern)) return false; - if (url.indexOf('track/') !== -1) { + const url_ = url.trim(); + if (!url_.startsWith('https')) return 'search'; + if (!url_.match(pattern)) return false; + if (url_.indexOf('track/') !== -1) { return 'track'; - } else if (url.indexOf('album/') !== -1) { + } else if (url_.indexOf('album/') !== -1) { return 'album'; - } else if (url.indexOf('playlist/') !== -1) { + } else if (url_.indexOf('playlist/') !== -1) { return 'playlist'; } else return false; } diff --git a/play-dl/YouTube/utils/extractor.ts b/play-dl/YouTube/utils/extractor.ts index 9dc6453..cfa21dd 100644 --- a/play-dl/YouTube/utils/extractor.ts +++ b/play-dl/YouTube/utils/extractor.ts @@ -41,26 +41,27 @@ const playlist_pattern = * ``` */ export function yt_validate(url: string): 'playlist' | 'video' | 'search' | false { - if (url.indexOf('list=') === -1) { - if (url.startsWith('https')) { - if (url.match(video_pattern)) { + const url_ = url.trim(); + if (url_.indexOf('list=') === -1) { + if (url_.startsWith('https')) { + if (url_.match(video_pattern)) { let id: string; - if (url.includes('youtu.be/')) id = url.split('youtu.be/')[1].split(/(\?|\/|&)/)[0]; - else if (url.includes('youtube.com/embed/')) - id = url.split('youtube.com/embed/')[1].split(/(\?|\/|&)/)[0]; - else if (url.includes('youtube.com/shorts/')) - id = url.split('youtube.com/shorts/')[1].split(/(\?|\/|&)/)[0]; - else id = url.split('watch?v=')[1].split(/(\?|\/|&)/)[0]; + if (url_.includes('youtu.be/')) id = url_.split('youtu.be/')[1].split(/(\?|\/|&)/)[0]; + else if (url_.includes('youtube.com/embed/')) + id = url_.split('youtube.com/embed/')[1].split(/(\?|\/|&)/)[0]; + else if (url_.includes('youtube.com/shorts/')) + id = url_.split('youtube.com/shorts/')[1].split(/(\?|\/|&)/)[0]; + else id = url_.split('watch?v=')[1].split(/(\?|\/|&)/)[0]; if (id.match(video_id_pattern)) return 'video'; else return false; } else return false; } else { - if (url.match(video_id_pattern)) return 'video'; - else if (url.match(playlist_id_pattern)) return 'playlist'; + if (url_.match(video_id_pattern)) return 'video'; + else if (url_.match(playlist_id_pattern)) return 'playlist'; else return 'search'; } } else { - if (!url.match(playlist_pattern)) return yt_validate(url.replace(/(\?|\&)list=[^&]+/, '')); + if (!url_.match(playlist_pattern)) return yt_validate(url_.replace(/(\?|\&)list=[^&]+/, '')); else return 'playlist'; } } @@ -99,15 +100,16 @@ function extractVideoId(urlOrId: string): string | false { export function extractID(url: string): string { const check = yt_validate(url); if (!check || check === 'search') throw new Error('This is not a YouTube url or videoId or PlaylistID'); - if (url.startsWith('https')) { - if (url.indexOf('list=') === -1) { - const video_id = extractVideoId(url); + const url_ = url.trim(); + if (url_.startsWith('https')) { + if (url_.indexOf('list=') === -1) { + const video_id = extractVideoId(url_); if (!video_id) throw new Error('This is not a YouTube url or videoId or PlaylistID'); return video_id; } else { - return url.split('list=')[1].split('&')[0]; + return url_.split('list=')[1].split('&')[0]; } - } else return url; + } else return url_; } /** * Basic function to get data from a YouTube url or ID. @@ -127,12 +129,13 @@ export function extractID(url: string): string { */ export async function video_basic_info(url: string, options: InfoOptions = {}): Promise { if (typeof url !== 'string') throw new Error('url parameter is not a URL string or a string of HTML'); + const url_ = url.trim(); let body: string; const cookieJar = {}; if (options.htmldata) { - body = url; + body = url_; } else { - const video_id = extractVideoId(url); + const video_id = extractVideoId(url_); if (!video_id) throw new Error('This is not a YouTube Watch URL'); const new_url = `https://www.youtube.com/watch?v=${video_id}&has_verified=1`; body = await request(new_url, { @@ -440,7 +443,7 @@ function parseSeconds(seconds: number): string { * @returns Deciphered Video Info {@link InfoData}. */ export async function video_info(url: string, options: InfoOptions = {}): Promise { - const data = await video_basic_info(url, options); + const data = await video_basic_info(url.trim(), options); return await decipher_info(data); } /** @@ -485,16 +488,17 @@ export async function decipher_info( */ export async function playlist_info(url: string, options: PlaylistOptions = {}): Promise { if (!url || typeof url !== 'string') throw new Error(`Expected playlist url, received ${typeof url}!`); - if (!url.startsWith('https')) url = `https://www.youtube.com/playlist?list=${url}`; - if (url.indexOf('list=') === -1) throw new Error('This is not a Playlist URL'); + let url_ = url.trim(); + if (!url_.startsWith('https')) url_ = `https://www.youtube.com/playlist?list=${url_}`; + if (url_.indexOf('list=') === -1) throw new Error('This is not a Playlist URL'); - if (url.includes('music.youtube.com')) { - const urlObj = new URL(url); + if (url_.includes('music.youtube.com')) { + const urlObj = new URL(url_); urlObj.hostname = 'www.youtube.com'; - url = urlObj.toString(); + url_ = urlObj.toString(); } - const body = await request(url, { + const body = await request(url_, { headers: { 'accept-language': options.language || 'en-US;q=0.9' } @@ -517,8 +521,8 @@ export async function playlist_info(url: string, options: PlaylistOptions = {}): throw new Error(`While parsing playlist url\n${response.alerts[0].alertRenderer.text.runs[0].text}`); else throw new Error('While parsing playlist url\nUnknown Playlist Error'); } - if (url.indexOf('watch?v=') !== -1) { - return getWatchPlaylist(response, body, url); + if (url_.indexOf('watch?v=') !== -1) { + return getWatchPlaylist(response, body, url_); } else return getNormalPlaylist(response, body); } /** diff --git a/play-dl/index.ts b/play-dl/index.ts index 82e4e8e..fe18bc7 100644 --- a/play-dl/index.ts +++ b/play-dl/index.ts @@ -109,20 +109,21 @@ async function stream(url: string, options?: StreamOptions): Promise { - if (url.length === 0) throw new Error('Stream URL has a length of 0. Check your url again.'); - if (options.htmldata) return await yt_stream(url, options); - if (url.indexOf('spotify') !== -1) { + const url_ = url.trim(); + if (url_.length === 0) throw new Error('Stream URL has a length of 0. Check your url again.'); + if (options.htmldata) return await yt_stream(url_, options); + if (url_.indexOf('spotify') !== -1) { throw new Error( 'Streaming from Spotify is not supported. Please use search() to find a similar track on YouTube or SoundCloud instead.' ); } - if (url.indexOf('deezer') !== -1) { + if (url_.indexOf('deezer') !== -1) { throw new Error( 'Streaming from Deezer is not supported. Please use search() to find a similar track on YouTube or SoundCloud instead.' ); } - if (url.indexOf('soundcloud') !== -1) return await so_stream(url, options.quality); - else return await yt_stream(url, options); + if (url_.indexOf('soundcloud') !== -1) return await so_stream(url_, options.quality); + else return await yt_stream(url_, options); } async function search(query: string, options: { source: { deezer: 'album' } } & SearchOptions): Promise; @@ -207,18 +208,18 @@ async function search( options: SearchOptions = {} ): Promise { if (!options.source) options.source = { youtube: 'video' }; - query = encodeURIComponent(query); + const query_ = encodeURIComponent(query.trim()); if (options.source.youtube) - return await yt_search(query, { + return await yt_search(query_, { limit: options.limit, type: options.source.youtube, language: options.language, unblurNSFWThumbnails: options.unblurNSFWThumbnails }); - else if (options.source.spotify) return await sp_search(query, options.source.spotify, options.limit); - else if (options.source.soundcloud) return await so_search(query, options.source.soundcloud, options.limit); + else if (options.source.spotify) return await sp_search(query_, options.source.spotify, options.limit); + else if (options.source.soundcloud) return await so_search(query_, options.source.soundcloud, options.limit); else if (options.source.deezer) - return await dz_search(query, { limit: options.limit, type: options.source.deezer, fuzzy: options.fuzzy }); + return await dz_search(query_, { limit: options.limit, type: options.source.deezer, fuzzy: options.fuzzy }); else throw new Error('Not possible to reach Here LOL. Easter Egg of play-dl if someone get this.'); } @@ -289,18 +290,19 @@ async function validate( | false > { let check; - if (!url.startsWith('https')) return 'search'; - if (url.indexOf('spotify') !== -1) { - check = sp_validate(url); + const url_ = url.trim(); + if (!url_.startsWith('https')) return 'search'; + if (url_.indexOf('spotify') !== -1) { + check = sp_validate(url_); return check !== false ? (('sp_' + check) as 'sp_track' | 'sp_album' | 'sp_playlist') : false; - } else if (url.indexOf('soundcloud') !== -1) { - check = await so_validate(url); + } else if (url_.indexOf('soundcloud') !== -1) { + check = await so_validate(url_); return check !== false ? (('so_' + check) as 'so_playlist' | 'so_track') : false; - } else if (url.indexOf('deezer') !== -1) { - check = await dz_validate(url); + } else if (url_.indexOf('deezer') !== -1) { + check = await dz_validate(url_); return check !== false ? (('dz_' + check) as 'dz_track' | 'dz_playlist' | 'dz_album') : false; } else { - check = yt_validate(url); + check = yt_validate(url_); return check !== false ? (('yt_' + check) as 'yt_video' | 'yt_playlist') : false; } }