Skip to content

Commit

Permalink
fix: check for unavailable video after every endpoint request
Browse files Browse the repository at this point in the history
closes #804
  • Loading branch information
fent committed Dec 3, 2020
1 parent e619e22 commit 7f0ab71
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 44 deletions.
36 changes: 22 additions & 14 deletions lib/info.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,17 @@ const AGE_RESTRICTED_URLS = [
*/
exports.getBasicInfo = async(id, options) => {
const retryOptions = Object.assign({}, miniget.defaultOptions, options.requestOptions);
const isValid = info =>
info.player_response && (info.player_response.streamingData || isRental(info.player_response));
let info = await pipeline([id, options], retryOptions, isValid, [
const validate = info => {
let playErr = utils.playError(info.player_response, ['ERROR'], UnrecoverableError);
let privateErr = privateVideoError(info.player_response);
if (playErr || privateErr) {
throw playErr || privateErr;
}
return info && (
info.player_response.streamingData || isRental(info.player_response) || isNotYetBroadcasted(info.player_response)
);
};
let info = await pipeline([id, options], validate, retryOptions, [
getJSONWatchPage,
getEmbedPage,
getVideoInfoPage,
Expand Down Expand Up @@ -75,8 +83,8 @@ exports.getBasicInfo = async(id, options) => {
};

const privateVideoError = player_response => {
let playability = player_response.playabilityStatus;
if (playability.status === 'LOGIN_REQUIRED' && playability.messages &&
let playability = player_response && player_response.playabilityStatus;
if (playability && playability.status === 'LOGIN_REQUIRED' && playability.messages &&
playability.messages.filter(m => /This is a private video/.test(m)).length) {
return new UnrecoverableError(playability.reason || (playability.messages && playability.messages[0]));
} else {
Expand All @@ -91,6 +99,12 @@ const isRental = player_response => {
};


const isNotYetBroadcasted = player_response => {
let playability = player_response.playabilityStatus;
return playability && playability.status === 'LIVE_STREAM_OFFLINE';
};


const getHTMLWatchURL = (id, options) => `${VIDEO_URL + id}&hl=${options.lang || 'en'}`;
const getHTMLWatchPageBody = (id, options) => {
const url = getHTMLWatchURL(id, options);
Expand Down Expand Up @@ -122,12 +136,12 @@ const getIdentityToken = (id, options, key, throwIfNotFound) =>
* If unable to succeed with one endpoint, moves onto the next one.
*
* @param {Array.<Object>} args
* @param {Function} validate
* @param {Object} retryOptions
* @param {Function} isValid
* @param {Array.<Function>} endpoints
* @returns {[Object, Object, Object]}
*/
const pipeline = async(args, retryOptions, isValid, endpoints) => {
const pipeline = async(args, validate, retryOptions, endpoints) => {
let info;
for (let func of endpoints) {
try {
Expand All @@ -137,7 +151,7 @@ const pipeline = async(args, retryOptions, isValid, endpoints) => {
newInfo.player_response.videoDetails);
newInfo.player_response = assign(info && info.player_response, newInfo.player_response);
info = assign(info, newInfo);
if (isValid(info)) {
if (validate(info, false)) {
break;
}
} catch (err) {
Expand Down Expand Up @@ -257,12 +271,6 @@ const getJSONWatchPage = async(id, options) => {
info.player_response = findPlayerResponse('watch.json `player_response`', info);
info.html5player = info.player && info.player.assets && info.player.assets.js;

let playErr = utils.playError(info.player_response, ['ERROR'], UnrecoverableError);
let privateErr = privateVideoError(info.player_response);
if (playErr || privateErr) {
throw playErr || privateErr;
}

return info;
};

Expand Down
2 changes: 1 addition & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ exports.cutAfterJSON = mixedJson => {
* @returns {!Error}
*/
exports.playError = (player_response, statuses, ErrorType = Error) => {
let playability = player_response.playabilityStatus;
let playability = player_response && player_response.playabilityStatus;
if (playability && statuses.includes(playability.status)) {
return new ErrorType(playability.reason || (playability.messages && playability.messages[0]));
}
Expand Down
4 changes: 2 additions & 2 deletions test/files/refresh.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const videos = [
{
id: '_HSylqgVYQI',
type: 'regular',
keep: ['video.flv', 'watch-reload-now.json', 'watch-reload-now-2.json', 'watch-empty.json'],
keep: ['video.flv', 'watch-reload-now.json', 'watch-reload-now-2.json'],
saveInfo: true,
transform: [
{
Expand Down Expand Up @@ -132,7 +132,7 @@ const videos = [
'x-youtube-client-version': '0',
},
} },
keep: ['embed-player-vars.html', 'watch-backup.html', 'watch-reload-now.json'],
keep: ['embed-player-vars.html', 'watch-backup.html', 'watch-reload-now.json', 'watch-empty.json'],
transform: [
{
page: 'embed.html',
Expand Down
File renamed without changes.
16 changes: 0 additions & 16 deletions test/files/videos/live-future/embed.html

This file was deleted.

1 change: 0 additions & 1 deletion test/files/videos/live-future/get_video_info

This file was deleted.

15 changes: 5 additions & 10 deletions test/info-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ describe('ytdl.getInfo()', () => {
const scope = nock(id, 'embed-backup', {
embed: [true, 200, 'player-vars'],
});
let info = await ytdl.getInfo(id);
let info = await ytdl.getInfo(id, { requestOptions: { maxRetries: 0 } });
scope.done();
assert.strictEqual(info.formats.length, expected.formats.length);
});
Expand Down Expand Up @@ -403,18 +403,13 @@ describe('ytdl.getInfo()', () => {
});

describe('When watch.json page gives back an empty response', () => {
it('Retries the request', async() => {
const id = '_HSylqgVYQI';
const scope1 = nock(id, 'regular', {
it('Uses next endpoint as backup', async() => {
const id = 'LuZu9N53Vd0';
const scope1 = nock(id, 'embed-backup', {
watchJson: [true, 200, 'empty'],
});
const scope2 = nock(id, 'regular', {
watchHtml: false,
player: false,
});
let info = await ytdl.getInfo(id, { requestOptions: { maxRetries: 1 } });
let info = await ytdl.getInfo(id, { requestOptions: { maxRetries: 0 } });
scope1.done();
scope2.done();
assert.ok(info.formats.length);
assert.ok(info.formats[0].url);
});
Expand Down

0 comments on commit 7f0ab71

Please sign in to comment.