chore: simplify format selection

This commit is contained in:
LuanRT
2022-02-13 19:06:56 -03:00
parent 2b5027eb06
commit cd64e30b69

View File

@@ -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();
}
});