ytdl-core
Advanced tools
Comparing version 4.0.2 to 4.0.3
@@ -34,3 +34,4 @@ const urllib = require('url'); | ||
exports.getBasicInfo = async(id, options) => { | ||
let info = await getJSONWatchPage(id, options); | ||
const retryOptions = Object.assign({}, miniget.Defaults, options.requestOptions); | ||
let info = await retryFn(getJSONWatchPage, [id, options], retryOptions); | ||
let player_response = | ||
@@ -42,5 +43,5 @@ (info.player && info.player.args && info.player.args.player_response) || | ||
let playErr = utils.playError(player_response, ['ERROR']); | ||
let playErr = utils.playError(player_response, ['ERROR', 'UNPLAYABLE']); | ||
let privateErr = privateVideoError(player_response); | ||
if (playErr) { | ||
if (playErr || privateErr) { | ||
throw playErr || privateErr; | ||
@@ -50,11 +51,7 @@ } | ||
let age_restricted = false; | ||
if (!player_response || (!player_response.streamingData && | ||
!utils.playError(player_response, ['UNPLAYABLE']) && !isRental(player_response))) { | ||
if (!player_response || (!player_response.streamingData && !isRental(player_response))) { | ||
// If the video page doesn't work, maybe because it has mature content. | ||
// and requires an account logged in to view, try the embed page. | ||
let [embedded_player_response, embedbody] = await getEmbedPage(id, options); | ||
let [embedded_player_response, embedbody] = await retryFn(getEmbedPage, [id, options], retryOptions); | ||
playErr = utils.playError(player_response, ['LOGIN_REQUIRED']); | ||
if (!embedded_player_response && playErr) { | ||
throw playErr; | ||
} | ||
player_response = embedded_player_response; | ||
@@ -66,3 +63,3 @@ html5player = html5player || getHTML5player(embedbody); | ||
if (!player_response || (!player_response.streamingData && !isRental(player_response))) { | ||
player_response = await getVideoInfoPage(id, options, info); | ||
player_response = await retryFn(getVideoInfoPage, [id, options, info], retryOptions); | ||
} | ||
@@ -141,2 +138,21 @@ | ||
const retryFn = async(fn, args, options) => { | ||
let currentTry = 0, result; | ||
while (currentTry <= options.maxRetries) { | ||
try { | ||
result = await fn(...args); | ||
break; | ||
} catch (err) { | ||
if (err instanceof miniget.MinigetError || currentTry >= options.maxRetries) { | ||
throw err; | ||
} | ||
let wait = Math.min(++currentTry * options.backoff.inc, options.backoff.max); | ||
await new Promise(resolve => setTimeout(resolve, wait)); | ||
} | ||
} | ||
return result; | ||
}; | ||
const jsonClosingChars = /^[)\]}'\s]+/; | ||
const parseJSON = (source, json) => { | ||
@@ -149,2 +165,3 @@ if (!json) { | ||
try { | ||
json = json.replace(jsonClosingChars, ''); | ||
return JSON.parse(json); | ||
@@ -159,3 +176,3 @@ } catch (err) { | ||
const getWatchJSONURL = (id, options) => `${getWatchURL(id, options)}&pbj=1`; | ||
const getJSONWatchPage = async(id, options, maxRetries = 1) => { | ||
const getJSONWatchPage = async(id, options) => { | ||
const reqOptions = Object.assign({ headers: {} }, options.requestOptions); | ||
@@ -181,12 +198,7 @@ let cookie = reqOptions.headers.Cookie || reqOptions.headers.cookie; | ||
let parsedBody; | ||
let jsonClosingChars = /^[)\]}'\s]+/; | ||
if (jsonClosingChars.test(body)) { | ||
body = body.replace(jsonClosingChars, ''); | ||
} | ||
parsedBody = parseJSON('watch.json', body); | ||
if (parsedBody.reload === 'now' && maxRetries > 0) { | ||
if (parsedBody.reload === 'now') { | ||
await setIdentityToken('browser', false); | ||
return getJSONWatchPage(id, options, maxRetries - 1); | ||
} | ||
if (!Array.isArray(parsedBody)) { | ||
if (parsedBody.reload === 'now' || !Array.isArray(parsedBody)) { | ||
throw Error('Unable to retrieve video metadata'); | ||
@@ -241,8 +253,5 @@ } | ||
if (player_response.streamingData) { | ||
if (player_response.streamingData.formats) { | ||
formats = formats.concat(player_response.streamingData.formats); | ||
} | ||
if (player_response.streamingData.adaptiveFormats) { | ||
formats = formats.concat(player_response.streamingData.adaptiveFormats); | ||
} | ||
formats = formats | ||
.concat(player_response.streamingData.formats || []) | ||
.concat(player_response.streamingData.adaptiveFormats || []); | ||
} | ||
@@ -249,0 +258,0 @@ return formats; |
@@ -9,3 +9,3 @@ { | ||
], | ||
"version": "4.0.2", | ||
"version": "4.0.3", | ||
"repository": { | ||
@@ -41,3 +41,3 @@ "type": "git", | ||
"m3u8stream": "^0.8.0", | ||
"miniget": "^2.0.1", | ||
"miniget": "^2.1.0", | ||
"sax": "^1.1.3" | ||
@@ -44,0 +44,0 @@ }, |
@@ -28,26 +28,4 @@ # node-ytdl-core | ||
Attempts to download a video from the given url. Returns a [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable). `options` can have the following | ||
Attempts to download a video from the given url. Returns a [readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable). `options` can have the following, in addition to any [`getInfo()` option](#async-ytdl.getinfo(url%2C-%5Boptions%5D)) and [`chooseFormat()` option](#ytdl.downloadfrominfo(info%2C-options)). | ||
* `quality` - Video quality to download. Can be an [itag value](http://en.wikipedia.org/wiki/YouTube#Quality_and_formats), a list of itag values, or `highest`/`lowest`/`highestaudio`/`lowestaudio`/`highestvideo`/`lowestvideo`. `highestaudio`/`lowestaudio`/`highestvideo`/`lowestvideo` all prefer audio/video only respectively. Defaults to `highest`, which prefers formats with both video and audio. | ||
A typical video's formats will be sorted in the following way using `quality: 'highest'` | ||
``` | ||
itag container quality codecs bitrate audio bitrate | ||
18 mp4 360p avc1.42001E, mp4a.40.2 696.66KB 96KB | ||
137 mp4 1080p avc1.640028 4.53MB | ||
248 webm 1080p vp9 2.52MB | ||
136 mp4 720p avc1.4d4016 2.2MB | ||
247 webm 720p vp9 1.44MB | ||
135 mp4 480p avc1.4d4014 1.1MB | ||
134 mp4 360p avc1.4d401e 593.26KB | ||
140 mp4 mp4a.40.2 128KB | ||
``` | ||
format 18 at 360p will be chosen first since it's the highest quality format with both video and audio. If you'd like a higher quality format with both video and audio, see the section on [handling separate streams](#handling-separate-streams). | ||
* `filter` - Used to filter the list of formats to choose from. Can be `audioandvideo` or `videoandaudio` to filter formats that contain both video and audio, `video` to filter for formats that contain video, or `videoonly` for formats that contain video and no additional audio track. Can also be `audio` or `audioonly`. You can give a filtering function that gets called with each format available. This function is given the `format` object as its first argument, and should return true if the format is preferable. | ||
```js | ||
// Example with custom function. | ||
ytdl(url, { filter: format => format.container === 'mp4' }) | ||
``` | ||
* `format` - Primarily used to download specific video or audio streams. This can be a specific `format` object returned from `getInfo`. | ||
* Supplying this option will ignore the `filter` and `quality` options since the format is explicitly provided. | ||
* `range` - A byte range in the form `{start: INT, end: INT}` that specifies part of the file to download, ie {start: 10355705, end: 12452856}. Not supported on segmented (DASH MPD, m3u8) formats. | ||
@@ -60,6 +38,4 @@ * This downloads a portion of the file, and not a separately spliced video. | ||
* `highWaterMark` - How much of the video download to buffer into memory. See [node's docs](https://nodejs.org/api/stream.html#stream_constructor_new_stream_writable_options) for more. Defaults to 512KB. | ||
* `dlChunkSize` - The size of the download chunk in bytes. When the chosen format is video only or audio only, the download in this case is separated into multiple chunks to avoid throttling. Setting it to 0 disables chunking. Defaults to 10MB. | ||
* `dlChunkSize` - When the chosen format is video only or audio only, the download is separated into multiple chunks to avoid throttling. This option specifies the size of each chunk in bytes. Setting it to 0 disables chunking. Defaults to 10MB. | ||
In addition, any [`getInfo()` option](#async-ytdl.getinfo(url%2C-%5Boptions%5D)) can be given. | ||
#### Event: info | ||
@@ -105,5 +81,29 @@ * [`ytdl.videoInfo`](typings/index.d.ts#L194) - Info. | ||
Can be used if you'd like to choose a format yourself with the [options above](#ytdlurl-options). | ||
Throws an Error if it fails to find any matching format. | ||
Can be used if you'd like to choose a format yourself. Throws an Error if it fails to find any matching format. | ||
`options` can have the following | ||
* `quality` - Video quality to download. Can be an [itag value](http://en.wikipedia.org/wiki/YouTube#Quality_and_formats), a list of itag values, or `highest`/`lowest`/`highestaudio`/`lowestaudio`/`highestvideo`/`lowestvideo`. `highestaudio`/`lowestaudio`/`highestvideo`/`lowestvideo` all prefer audio/video only respectively. Defaults to `highest`, which prefers formats with both video and audio. | ||
A typical video's formats will be sorted in the following way using `quality: 'highest'` | ||
``` | ||
itag container quality codecs bitrate audio bitrate | ||
18 mp4 360p avc1.42001E, mp4a.40.2 696.66KB 96KB | ||
137 mp4 1080p avc1.640028 4.53MB | ||
248 webm 1080p vp9 2.52MB | ||
136 mp4 720p avc1.4d4016 2.2MB | ||
247 webm 720p vp9 1.44MB | ||
135 mp4 480p avc1.4d4014 1.1MB | ||
134 mp4 360p avc1.4d401e 593.26KB | ||
140 mp4 mp4a.40.2 128KB | ||
``` | ||
format 18 at 360p will be chosen first since it's the highest quality format with both video and audio. If you'd like a higher quality format with both video and audio, see the section on [handling separate streams](#handling-separate-streams). | ||
* `filter` - Used to filter the list of formats to choose from. Can be `audioandvideo` or `videoandaudio` to filter formats that contain both video and audio, `video` to filter for formats that contain video, or `videoonly` for formats that contain video and no additional audio track. Can also be `audio` or `audioonly`. You can give a filtering function that gets called with each format available. This function is given the `format` object as its first argument, and should return true if the format is preferable. | ||
```js | ||
// Example with custom function. | ||
ytdl(url, { filter: format => format.container === 'mp4' }) | ||
``` | ||
* `format` - Primarily used to download specific video or audio streams. This can be a specific `format` object returned from `getInfo`. | ||
* Supplying this option will ignore the `filter` and `quality` options since the format is explicitly provided. | ||
```js | ||
@@ -110,0 +110,0 @@ // Example of choosing a video format. |
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
80000
2160
Updatedminiget@^2.1.0