ionic-youtube-streams
Advanced tools
Comparing version 1.0.5 to 1.0.6
@@ -51,3 +51,3 @@ "use strict"; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var httpClient, tubeService, ciphService, params, ytApi, response, body, unavailableMsg, jsonStr, config, info_1, html5playerfile, tokens, funcs, url, responseEmbeded, info; | ||
var httpClient, tubeService, ciphService, params, ytApi, response, body, info, playErr, url, responseEmbeded, jsonStr, config, infoResponse, html5playerfile, tokens, funcs; | ||
return __generator(this, function (_a) { | ||
@@ -60,6 +60,8 @@ switch (_a.label) { | ||
params = 'hl=en'; | ||
ytApi = tubeService.VIDEO_URL + youtubeId + '&' + params + | ||
ytApi = tubeService.VIDEO_URL + youtubeId + '&pbj=1&' + params + | ||
'&bpctr=' + Math.ceil(Date.now() / 1000); | ||
return [4 /*yield*/, httpClient.get(ytApi, {}, { | ||
'User-Agent': '' | ||
'User-Agent': '', | ||
'x-youtube-client-name': '1', | ||
'x-youtube-client-version': '2.20191008.04.01', | ||
})]; | ||
@@ -69,52 +71,57 @@ case 1: | ||
body = response.data; | ||
unavailableMsg = tubeService.between(body, '<div id="player-unavailable"', '>'); | ||
if (unavailableMsg && | ||
!/\bhid\b/.test(tubeService.between(unavailableMsg, 'class="', '"'))) { | ||
// Ignore error about age restriction. | ||
if (!body.includes('<div id="watch7-player-age-gate-content"')) { | ||
throw new Error('Video is not supported'); | ||
} | ||
try { | ||
info = JSON.parse(body).reduce(function (part, curr) { return Object.assign(curr, part); }, {}); | ||
} | ||
jsonStr = tubeService.between(body, 'ytplayer.config = ', '</script>'); | ||
if (!jsonStr) return [3 /*break*/, 6]; | ||
config = jsonStr.slice(0, jsonStr.lastIndexOf(';ytplayer.load')); | ||
return [4 /*yield*/, tubeService.gotConfig(youtubeId, null, config, false)]; | ||
catch (err) { | ||
throw Error("Error parsing info: " + err.message); | ||
} | ||
playErr = tubeService.playError(info, 'ERROR'); | ||
if (playErr) { | ||
throw playErr; | ||
} | ||
if (!!info.player) return [3 /*break*/, 3]; | ||
url = tubeService.EMBED_URL + youtubeId + '?hl=en'; | ||
return [4 /*yield*/, httpClient.get(url, {}, {})]; | ||
case 2: | ||
info_1 = _a.sent(); | ||
if (!info_1.formats.length) return [3 /*break*/, 4]; | ||
html5playerfile = urllib.resolve(tubeService.VIDEO_URL, info_1.html5player); | ||
responseEmbeded = _a.sent(); | ||
jsonStr = tubeService.between(responseEmbeded.data, 't.setConfig({\'PLAYER_CONFIG\': ', '</script>'); | ||
config = void 0; | ||
if (!jsonStr) { | ||
throw Error('Could not find player config'); | ||
} | ||
try { | ||
config = JSON.parse(tubeService.cutAfterJSON(jsonStr)); | ||
} | ||
catch (err) { | ||
throw Error("Error parsing config: " + err.message); | ||
} | ||
// @ts-ignore | ||
playErr = tubeService.playError(info, 'LOGIN_REQUIRED'); | ||
if (!config.args.player_response && !config.args.embedded_player_response && playErr) { | ||
throw playErr; | ||
} | ||
// @ts-ignore | ||
info.player = config; | ||
_a.label = 3; | ||
case 3: return [4 /*yield*/, tubeService.gotConfig(youtubeId, null, info, body)]; | ||
case 4: | ||
infoResponse = _a.sent(); | ||
if (!(infoResponse.formats.length > 0)) return [3 /*break*/, 6]; | ||
html5playerfile = urllib.resolve(tubeService.VIDEO_URL, infoResponse.html5player); | ||
return [4 /*yield*/, ciphService.getTokens(html5playerfile)]; | ||
case 3: | ||
case 5: | ||
tokens = _a.sent(); | ||
ciphService.decipherFormats(info_1.formats, tokens, false); | ||
ciphService.decipherFormats(infoResponse.formats, tokens, false); | ||
funcs = []; | ||
tubeService.parallel(funcs, function (err, results) { | ||
if (results[0]) { | ||
mergeFormats(info_1, results[0]); | ||
mergeFormats(infoResponse, results[0]); | ||
} | ||
if (results[1]) { | ||
mergeFormats(info_1, results[1]); | ||
mergeFormats(infoResponse, results[1]); | ||
} | ||
info_1.full = true; | ||
infoResponse.full = true; | ||
}); | ||
return [2 /*return*/, info_1]; | ||
case 4: return [2 /*return*/, null]; | ||
case 5: return [3 /*break*/, 9]; | ||
case 6: | ||
url = tubeService.EMBED_URL + youtubeId + '?hl=en'; | ||
return [4 /*yield*/, httpClient.get(url, {}, {})]; | ||
case 7: | ||
responseEmbeded = _a.sent(); | ||
config = tubeService.between(responseEmbeded.data, 't.setConfig({\'PLAYER_CONFIG\': ', /\}(,'|\}\);)/); | ||
return [4 /*yield*/, tubeService.gotConfig(youtubeId, null, config, true)]; | ||
case 8: | ||
info = _a.sent(); | ||
if (info.formats.length) { | ||
return [2 /*return*/, info]; | ||
} | ||
else { | ||
return [2 /*return*/, null]; | ||
} | ||
_a.label = 9; | ||
case 9: return [2 /*return*/]; | ||
return [2 /*return*/, infoResponse]; | ||
case 6: return [2 /*return*/, null]; | ||
} | ||
@@ -121,0 +128,0 @@ }); |
@@ -10,3 +10,3 @@ import { HTTP } from '@ionic-native/http/ngx'; | ||
constructor(httpClient: HTTP); | ||
gotConfig(id: any, additional: any, config: any, fromEmbed: any): Promise<any>; | ||
gotConfig(id: any, additional: any, info: any, body: any): Promise<any>; | ||
stripHTML(html: any): any; | ||
@@ -16,2 +16,4 @@ parseFormats(info: any): any[]; | ||
parallel(funcs: any, callback: any): void; | ||
cutAfterJSON(mixedJson: any): any; | ||
playError(info: any, status: any): Error | null; | ||
} |
@@ -57,17 +57,10 @@ "use strict"; | ||
} | ||
YTubeService.prototype.gotConfig = function (id, additional, config, fromEmbed) { | ||
YTubeService.prototype.gotConfig = function (id, additional, info, body) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var url, respo, info, playerResponse, playability; | ||
var player_response, url, respo, moreinfo; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!config) { | ||
throw new Error('Could not find player config'); | ||
} | ||
try { | ||
config = JSON.parse(config + (fromEmbed ? '}' : '')); | ||
} | ||
catch (err) { | ||
throw new Error('Error parsing config: ' + err.message); | ||
} | ||
player_response = info.player && info.player.args && info.player.args.player_response; | ||
if (!!player_response) return [3 /*break*/, 2]; | ||
url = urllib.format({ | ||
@@ -83,3 +76,3 @@ protocol: 'https', | ||
hl: 'en', | ||
sts: config.sts, | ||
sts: info.sts, | ||
}, | ||
@@ -90,20 +83,20 @@ }); | ||
respo = _a.sent(); | ||
info = querystring.parse(respo.data); | ||
playerResponse = config.args.player_response || info.player_response; | ||
if (info.status === 'fail') { | ||
throw new Error("Code " + info.errorcode + ": " + this.stripHTML(info.reason)); | ||
moreinfo = querystring.parse(respo.data); | ||
player_response = moreinfo.args.player_response || info.playerResponse; | ||
_a.label = 2; | ||
case 2: | ||
if (typeof player_response === 'object') { | ||
info.player_response = player_response; | ||
} | ||
else { | ||
try { | ||
info.player_response = JSON.parse(playerResponse); | ||
info.player_response = JSON.parse(player_response); | ||
} | ||
catch (err) { | ||
throw new Error('Error parsing `player_response`: ' + err.message); | ||
throw Error("Error parsing `player_response`: " + err.message); | ||
} | ||
} | ||
playability = info.player_response.playabilityStatus; | ||
if (playability && playability.status === 'UNPLAYABLE') { | ||
throw new Error(playability.reason); | ||
} | ||
info.formats = this.parseFormats(info); | ||
info.videoDetails = Object.assign({}, info.player_response.microformat.playerMicroformatRenderer, info.player_response.videoDetails, additional); | ||
info.html5player = info.player && info.player.assets && info.player.assets.js; | ||
// Add additional properties to info. | ||
@@ -119,4 +112,2 @@ Object.assign(info, additional, { | ||
}); | ||
info.age_restricted = fromEmbed; | ||
info.html5player = config.assets.js; | ||
return [2 /*return*/, info]; | ||
@@ -190,4 +181,55 @@ } | ||
}; | ||
YTubeService.prototype.cutAfterJSON = function (mixedJson) { | ||
var open; | ||
var close; | ||
if (mixedJson[0] === '[') { | ||
open = '['; | ||
close = ']'; | ||
} | ||
else if (mixedJson[0] === '{') { | ||
open = '{'; | ||
close = '}'; | ||
} | ||
if (!open) { | ||
throw new Error("Can't cut unsupported JSON (need to begin with [ or { ) but got: " + mixedJson[0]); | ||
} | ||
// States if the loop is currently in a string | ||
var isString = false; | ||
// Current open brackets to be closed | ||
var counter = 0; | ||
var i; | ||
for (i = 0; i < mixedJson.length; i++) { | ||
// Toggle the isString boolean when leaving/entering string | ||
if (mixedJson[i] === '"' && mixedJson[i - 1] !== '\\') { | ||
isString = !isString; | ||
continue; | ||
} | ||
if (isString) | ||
continue; | ||
if (mixedJson[i] === open) { | ||
counter++; | ||
} | ||
else if (mixedJson[i] === close) { | ||
counter--; | ||
} | ||
// All brackets have been closed, thus end of JSON is reached | ||
if (counter === 0) { | ||
// Return the cut JSON | ||
return mixedJson.substr(0, i + 1); | ||
} | ||
} | ||
// We ran through the whole string and ended up with an unclosed bracket | ||
throw Error("Can't cut unsupported JSON (no matching closing bracket found)"); | ||
}; | ||
; | ||
YTubeService.prototype.playError = function (info, status) { | ||
var playability = info.playerResponse.playabilityStatus; | ||
if (playability && playability.status === status) { | ||
return Error(playability.reason || (playability.messages && playability.messages[0])); | ||
} | ||
return null; | ||
}; | ||
; | ||
return YTubeService; | ||
}()); | ||
exports.YTubeService = YTubeService; |
{ | ||
"name": "ionic-youtube-streams", | ||
"version": "1.0.5", | ||
"version": "1.0.6", | ||
"description": "Fetch meta information about YouTube videos including stream urls in ionic apps", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
193394
641