UNPKG

yt-stream

Version:

Create easily readable streams from YouTube video url's

134 lines (118 loc) 5.55 kB
const { validateVideoURL } = require('./validate.js'); const _url = require('./request/url.js'); const request = require('./request/index.js').request; const YouTubeData = require('./classes/ytdata.js'); const userAgent = require('./request/useragent.js').getRandomUserAgent; const { getID } = require('./convert.js'); const genClientInfo = require('./genClient.js'); function getHTML5player(response){ let html5playerRes = /<script\s+src="([^"]+)"(?:\s+type="text\/javascript")?\s+name="player_ias\/base"\s*>|"jsUrl":"([^"]+)"/ .exec(response); return html5playerRes ? html5playerRes[1] || html5playerRes[2] : null; }; function getCver(response){ let startCver = response.split(/["|'|`]key["|'|`]:["|'|`]cver["|'|`],["|'|`]value["|'|`]:["|'|`]/); return startCver[1].split(/["|'|`]/)[0]; } function genNonce(length){ let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'; let nonce = ""; while(nonce.length < length){ nonce += chars[Math.round(Math.random() * (chars.length - 1))]; } return nonce; } function getInfo(ytstream, url, force = false){ return new Promise((resolve, reject) => { if(typeof url !== 'string') return reject(`URL is not a string`); const validation = validateVideoURL(ytstream, url); if(!validation) return reject(`Invalid YouTube video URL`); var ytid = null; let parsed = _url(url); if(['youtube.com', 'music.youtube.com'].includes(parsed.hostname.toLowerCase().split('www.').join(''))) ytid = getID(ytstream, url); else if(parsed.hostname.toLowerCase().split('www.').join('') === `youtu.be`){ if(parsed.pathname.toLowerCase().startsWith('/watch')){ const newurl = 'https://www.youtube.com'+parsed.pathname+parsed.search; ytid = getID(ytstream, newurl); } else { const newurl = `https://www.youtube.com/watch?v=`+parsed.pathname.split('/').join(''); ytid = getID(ytstream, newurl); } } if(ytid === null) return reject(`Invalid YouTube url`); if(ytstream.preference === 'scrape'){ const yturl = `https://www.youtube.com/watch?v=${ytid}&has_verified=1&cbrd=1`; 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'); request(yturl, { headers: headers }, ytstream.agent, 0, force).then(async response => { let res = response.split('var ytInitialPlayerResponse = ')[1]; let html5path = getHTML5player(response); const html5player = typeof html5path === 'string' ? `https://www.youtube.com${html5path}` : null; let cver = getCver(response); if(!res){ return reject(`The YouTube song has no initial player response (1)`); } res = res.split(';</script>')[0]; if(!res){ return reject(`The YouTube song has no initial player response (2)`); } try{ res = decodeURI(res); } catch {} res = res.split(`\\"`).join(""); let seperate = res.split(/}};[a-z]/); let jsonObject = (seperate[0] + "}}").split('\\"').join('\"').split("\\'").join("\'").split("\\`").join("\`"); let splitVars = jsonObject.split(/['|"|`]playerVars['|"|`]:/); while(splitVars.length > 1){ jsonObject = splitVars[0] + splitVars.slice(1).join("\"playerVars\":").split(/}}['|"|`],/).slice(1).join("}}\","); splitVars = jsonObject.split(/['|"|`]playerVars['|"|`]:/); } let data; try{ data = JSON.parse(jsonObject); } catch { return reject(`The YouTube song has no initial player response (3)`); } if(data.playabilityStatus.status !== 'OK'){ let error = data.playabilityStatus.errorScreen.playerErrorMessageRenderer ? data.playabilityStatus.errorScreen.playerErrorMessageRenderer.reason.simpleText : data.playabilityStatus.errorScreen.playerKavRenderer.reason.simpleText; reject(`Error while getting video url\n${error}`); } else resolve(new YouTubeData(data, cver, html5player, headers, null)); }).catch(err => { reject(err); }); } else if(ytstream.preference === 'api'){ const clientInfo = genClientInfo(ytid, ytstream.client); let endPoint = 'https://www.youtube.com/youtubei/v1/player' + (typeof ytstream.apiKey === 'string' ? '?key='+ytstream.apiKey : '?t='+genNonce(12))+'&prettyPrint=false&id='+ytid; request(endPoint, { method: 'POST', body: JSON.stringify(clientInfo.body), 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; } if(data.playabilityStatus.status !== 'OK'){ return reject(data.playabilityStatus.reason); } resolve(new YouTubeData(data, clientInfo.body.context.client.clientVersion, null, clientInfo.headers, clientInfo.body)); }).catch(reject); } }); } module.exports = { getInfo };