UNPKG

yt-stream

Version:

Create easily readable streams from YouTube video url's

133 lines (110 loc) 5.01 kB
const { validatePlaylistURL } = require("./validate.js"); const _url = require('./request/url.js'); const { requestCallback, request } = require("./request/index.js"); const userAgent = require("./request/useragent.js").getRandomUserAgent; const { getID } = require("./convert.js"); const Playlist = require("./classes/playlist.js"); const genClientInfo = require('./genClient.js'); function genNonce(length){ let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'; let nonce = ""; while(nonce.length < length){ nonce += chars[Math.round(Math.random() * (chars.length - 1))]; } return nonce; } function getPlaylist(ytstream, url){ return new Promise(async (resolve, reject) => { if(!validatePlaylistURL(ytstream, url)) return reject(`Invalid YouTube playlist url`); let listId = null; const parsed = _url(url); if(['youtube.com', 'music.youtube.com'].includes(parsed.hostname.toLowerCase().split('www.').join(''))) listId = getID(ytstream, url); else if(parsed.hostname.toLowerCase().split('www.').join('') === `youtu.be`){ if(parsed.pathname.toLowerCase().startsWith('/playlist')){ const newurl = 'https://www.youtube.com'+parsed.pathname+parsed.search; listId = getID(ytstream, newurl); } } if(listId === null) return reject(`Invalid YouTube url`); if(ytstream.preference === 'scrape'){ const userA = typeof ytstream.userAgent === 'string' ? ytstream.userAgent : userAgent(); let headers = { 'accept-language': 'en-US,en-IN;q=0.9,en;q=0.8,hi;q=0.7', 'user-agent' : userA, }; for(const header in ytstream.headers){ headers[header] = ytstream.headers[header]; } headers['cookie'] = ytstream.agent.jar.getCookieStringSync('https://www.youtube.com'); var request_url = `https://www.youtube.com/playlist?list=${listId}&has_verified=1`; var response; try{ response = await requestPlayList(request_url, headers, ytstream.agent); } catch (err){ reject(err); } response = response.split('var ytInitialData = ')[1]; if(!response) return reject(`The YouTube playlist has no initial data response`); response = response.split(';</script>')[0]; if(!response) return reject(`The YouTube playlist has no initial data response`); var json = response; try{ json = JSON.parse(json); } catch {} var alerts = (json.alerts || []); alerts.push({alertRenderer: {}}); var errors = alerts.filter(a => { return (a.alertRenderer || a.alertWithButtonRenderer).type === 'ERROR'; }); if(errors.length > 0) return reject(errors[0].alertRenderer.text.runs[0].text); resolve(new Playlist(json, headers, listId)); } else if(ytstream.preference === 'api'){ const clientInfo = genClientInfo(undefined, ytstream.client); let endPoint = 'https://www.youtube.com/youtubei/v1/browse' + (typeof ytstream.apiKey === 'string' ? '?key='+ytstream.apiKey : '?t='+genNonce(12))+'&prettyPrint=false'; request(endPoint, { method: 'POST', body: JSON.stringify({...clientInfo.body, browseId: !listId.startsWith("VL") ? `VL${listId}` : listId}), headers: clientInfo.headers }, ytstream.agent, 0, false).then(res => { let data; try{ data = JSON.parse(res); } catch { reject(`Invalid response from the YouTube API`); return; } resolve(new Playlist(data, clientInfo.headers, listId)); }).catch(reject); } }); } function requestPlayList(url, headers, agent){ return new Promise(async (resolve, reject) => { let options = { headers: headers, method: 'GET' }; var res; try{ res = await requestCallback(url, options, agent, false); } catch(err) { return reject(err); } while(_url((res.stream.headers.location ?? '')) !== false){ res = await requestCallback(res.stream.headers.location, options, agent, false); } var response = ''; res.stream.on('data', d => { response += d; }); res.stream.on('end', () => { resolve(response); }); res.stream.on('error', err => { reject(err); }); }); } module.exports = { getPlaylist };