mirror of
https://github.com/LuanRT/YouTube.js.git
synced 2026-07-02 21:52:48 +00:00
chore: simplify format selection
This commit is contained in:
109
lib/Innertube.js
109
lib/Innertube.js
@@ -348,13 +348,18 @@ class Innertube {
|
||||
let cancelled = false;
|
||||
|
||||
const stream = new Stream.PassThrough();
|
||||
Actions.getVideoInfo(this, { id, is_desktop: true }).then(async (video_data) => {
|
||||
Actions.getVideoInfo(this, { id, desktop: true }).then(async (video_data) => {
|
||||
let formats = [];
|
||||
|
||||
if (video_data.playabilityStatus.status === 'LOGIN_REQUIRED') return stream.emit('error', { message: 'You must login to download age-restricted videos.', error_type: 'LOGIN_REQUIRED', playability_status: video_data.playabilityStatus.status });
|
||||
if (!video_data.streamingData) return stream.emit('error', { message: 'Streaming data not available.', error_type: 'NO_STREAMING_DATA', playability_status: video_data.playabilityStatus.status });
|
||||
if (video_data.playabilityStatus.status === 'LOGIN_REQUIRED')
|
||||
return stream.emit('error', { message: 'You must login to download age-restricted videos.', error_type: 'LOGIN_REQUIRED', playability_status: video_data.playabilityStatus.status });
|
||||
if (!video_data.streamingData)
|
||||
return stream.emit('error', { message: 'Streaming data not available.', error_type: 'NO_STREAMING_DATA', playability_status: video_data.playabilityStatus.status });
|
||||
|
||||
formats = formats
|
||||
.concat(video_data.streamingData.formats || [])
|
||||
.concat(video_data.streamingData.adaptiveFormats || []);
|
||||
|
||||
formats = formats.concat(video_data.streamingData.formats || []).concat(video_data.streamingData.adaptiveFormats || []);
|
||||
formats.forEach((format) => {
|
||||
format.url = format.url || format.signatureCipher || format.cipher;
|
||||
|
||||
@@ -378,47 +383,40 @@ class Innertube {
|
||||
formats.hls_manifest_url = video_data.streamingData.hlsManifestUrl || undefined;
|
||||
formats.dash_manifest_url = video_data.streamingData.dashManifestUrl || undefined;
|
||||
|
||||
let url;
|
||||
let format;
|
||||
let bitrates;
|
||||
let filtered_streams;
|
||||
let filtered_formats;
|
||||
|
||||
switch (options.type) {
|
||||
case 'video':
|
||||
filtered_streams = formats.filter((format) => format.has_video && !format.has_audio);
|
||||
break;
|
||||
case 'audio':
|
||||
filtered_streams = formats.filter((format) => format.has_audio && !format.has_video);
|
||||
break;
|
||||
case 'videoandaudio':
|
||||
filtered_streams = formats.filter((format) => format.has_video && format.has_audio);
|
||||
break;
|
||||
default:
|
||||
filtered_streams = formats.filter((format) => format.has_video && format.has_audio);
|
||||
break;
|
||||
}
|
||||
filtered_formats = ({
|
||||
'video': formats.filter((format) => format.has_video && !format.has_audio),
|
||||
'audio': formats.filter((format) => format.has_audio && !format.has_video),
|
||||
'videoandaudio': formats.filter((format) => format.has_video && format.has_audio)
|
||||
})[options.type] || formats.filter((format) => format.has_video && format.has_audio);
|
||||
|
||||
if (options.type != 'videoandaudio') {
|
||||
let streams;
|
||||
|
||||
options.type != 'audio' && (streams = filtered_streams.filter((format) => format.mimeType.includes(options.format || 'mp4') && format.qualityLabel == options.quality)) ||
|
||||
(streams = filtered_streams.filter((format) => format.mimeType.includes(options.format || 'mp4')));
|
||||
options.type != 'audio' &&
|
||||
(streams = filtered_formats.filter((format) => format.mimeType.includes(options.format || 'mp4') && format.qualityLabel == options.quality)) ||
|
||||
(streams = filtered_formats.filter((format) => format.mimeType.includes(options.format || 'mp4')));
|
||||
|
||||
streams == undefined || streams.length == 0 && (streams = filtered_streams.filter((format) => format.quality == 'medium'));
|
||||
streams == undefined || streams.length == 0 &&
|
||||
(streams = filtered_formats.filter((format) => format.quality == 'medium'));
|
||||
|
||||
bitrates = streams.map((format) => format.bitrate);
|
||||
url = streams.filter((format) => format.bitrate === Math.max(...bitrates))[0];
|
||||
format = streams.filter((format) => format.bitrate === Math.max(...bitrates))[0];
|
||||
} else {
|
||||
format = filtered_formats[0];
|
||||
}
|
||||
|
||||
const selected_format = options.type == 'videoandaudio' ? filtered_streams[0] : url;
|
||||
if (!selected_format) {
|
||||
if (!format)
|
||||
return stream.emit('error', { message: 'Could not find any suitable format.', type: 'FORMAT_UNAVAILABLE' });
|
||||
} else {
|
||||
const refined_data = new Parser(this, video_data, { client: 'YOUTUBE', data_type: 'VIDEO_INFO', desktop_v: true }).parse();
|
||||
stream.emit('info', { video_details: refined_data, selected_format, formats });
|
||||
}
|
||||
|
||||
const refined_data = new Parser(this, video_data, { client: 'YOUTUBE', data_type: 'VIDEO_INFO', desktop_v: true }).parse();
|
||||
stream.emit('info', { video_details: refined_data, selected_format: format, formats });
|
||||
|
||||
if (options.type == 'videoandaudio' && !options.range) {
|
||||
const response = await Axios.get(selected_format.url, {
|
||||
const response = await Axios.get(format.url, {
|
||||
responseType: 'stream',
|
||||
cancelToken: new CancelToken(function executor(c) { cancel = c; }),
|
||||
headers: Constants.STREAM_HEADERS
|
||||
@@ -437,12 +435,24 @@ class Innertube {
|
||||
downloaded_size += chunk.length;
|
||||
let size = (response.headers['content-length'] / 1024 / 1024).toFixed(2);
|
||||
let percentage = Math.floor((downloaded_size / response.headers['content-length']) * 100);
|
||||
stream.emit('progress', { chunk_size: chunk.length, downloaded_size: (downloaded_size / 1024 / 1024).toFixed(2), percentage, size, raw_data: { chunk_size: chunk.length, downloaded: downloaded_size, size: response.headers['content-length'] } });
|
||||
|
||||
stream.emit('progress', {
|
||||
size,
|
||||
percentage,
|
||||
chunk_size: chunk.length,
|
||||
downloaded_size: (downloaded_size / 1024 / 1024).toFixed(2),
|
||||
raw_data: {
|
||||
chunk_size: chunk.length,
|
||||
downloaded: downloaded_size,
|
||||
size: response.headers['content-length']
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
response.data.on('error', (err) => {
|
||||
cancelled && stream.emit('error', { message: 'The download was cancelled.', type: 'DOWNLOAD_CANCELLED' })
|
||||
|| stream.emit('error', { message: err.message, type: 'DOWNLOAD_ABORTED' });
|
||||
cancelled &&
|
||||
stream.emit('error', { message: 'The download was cancelled.', type: 'DOWNLOAD_CANCELLED' }) ||
|
||||
stream.emit('error', { message: err.message, type: 'DOWNLOAD_ABORTED' });
|
||||
});
|
||||
|
||||
response.data.pipe(stream, { end: true });
|
||||
@@ -457,10 +467,10 @@ class Innertube {
|
||||
stream.emit('start');
|
||||
|
||||
const downloadChunk = async () => {
|
||||
(chunk_end >= selected_format.contentLength || options.range) && (must_end = true);
|
||||
options.range && (selected_format.contentLength = options.range.end);
|
||||
(chunk_end >= format.contentLength || options.range) && (must_end = true);
|
||||
options.range && (format.contentLength = options.range.end);
|
||||
|
||||
const response = await Axios.get(`${selected_format.url}&range=${chunk_start}-${chunk_end || ''}`, {
|
||||
const response = await Axios.get(`${format.url}&range=${chunk_start}-${chunk_end || ''}`, {
|
||||
responseType: 'stream',
|
||||
cancelToken: new CancelToken(function executor(c) { cancel = c; }),
|
||||
headers: Constants.STREAM_HEADERS
|
||||
@@ -473,17 +483,27 @@ class Innertube {
|
||||
|
||||
response.data.on('data', (chunk) => {
|
||||
downloaded_size += chunk.length;
|
||||
let size = (selected_format.contentLength / 1024 / 1024).toFixed(2);
|
||||
let percentage = Math.floor((downloaded_size / selected_format.contentLength) * 100);
|
||||
stream.emit('progress', { chunk_size: chunk.length, downloaded_size: (downloaded_size / 1024 / 1024).toFixed(2), percentage, size, raw_data: { chunk_size: chunk.length, downloaded: downloaded_size, size: response.headers['content-length'] } });
|
||||
|
||||
let size = (format.contentLength / 1024 / 1024).toFixed(2);
|
||||
let percentage = Math.floor((downloaded_size / format.contentLength) * 100);
|
||||
|
||||
stream.emit('progress', {
|
||||
size,
|
||||
percentage,
|
||||
chunk_size: chunk.length,
|
||||
downloaded_size: (downloaded_size / 1024 / 1024).toFixed(2),
|
||||
raw_data: {
|
||||
chunk_size: chunk.length,
|
||||
downloaded: downloaded_size,
|
||||
size: response.headers['content-length']
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
response.data.on('error', (err) => {
|
||||
if (cancelled) {
|
||||
stream.emit('error', { message: 'The download was cancelled.', type: 'DOWNLOAD_CANCELLED' });
|
||||
} else {
|
||||
cancelled &&
|
||||
stream.emit('error', { message: 'The download was cancelled.', type: 'DOWNLOAD_CANCELLED' }) ||
|
||||
stream.emit('error', { message: err.message, type: 'DOWNLOAD_ABORTED' });
|
||||
}
|
||||
});
|
||||
|
||||
response.data.on('end', () => {
|
||||
@@ -496,6 +516,7 @@ class Innertube {
|
||||
|
||||
response.data.pipe(stream, { end: must_end });
|
||||
};
|
||||
|
||||
downloadChunk();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user