diff --git a/examples/download/README.md b/examples/download/README.md new file mode 100644 index 00000000..1a3b1a3e --- /dev/null +++ b/examples/download/README.md @@ -0,0 +1,76 @@ +# Downloading Videos + +With YouTube.js you can consume videos in your applications as Node Streams. + +```js +import Innertube from 'youtubei.js'; + +const yt = await new Innertube(); + +// Downloading videos returns a Node Stream which you can consume +const stream = yt.download('VIDEO_ID'); + +// Alternatively you may want more info before downloading +const info = await yt.getInfo('VIDEO_ID'); + +// Then you may request a download stream +const stream_from_info = yt.download(); +``` + +## Download Methods + +There are two options for downloading a video: +- `Innertube.download(video_id: string, options?: DownloadOptions)` +- `VideoInfo.download(options?: DownloadOptions)` + +The download methods are identical, but the first one is a convenience method for the second one. + +## Download Options + +The `DownloadOptions` object is used to choose the format for download + +- `format`: The format of the video to download. Use `'any'` to pick any format available. (default: `'mp4'`) +- `quality`: The quality of the video to download. (default: `'360p'`) There's some options: + - `'best'`: The best quality available. Picks the highest dimensions and highest bitrate stream. + - `'bestefficiency'`: The best quality available. Picks the highest dimensions and lowest bitrate stream. + - You may also filter via quality labels such as `'720p'` or `'1080p'`. +- `type`: The type of the video to download. (default: `'videoandaudio'`) + - `'videoandaudio'`: Download both video and audio. + - `'video'`: Download only video. + - `'audio'`: Download only audio. +- `range`: The range of the video to download. (default: `undefined`) + - `range.start`: First byte to download. + - `range.end`: Last byte to download. + +## Download Events + +In addition to the normal events emitted by Node Streams, YouTube.js also emits the following events on the stream: + +- `info`: Emitted when the video metadata is available. (only emiited with `Innertube.download` not with `VideoInfo.download`) +- `progress`: Emitted when the video is downloading. +- `start`: Emitted when the video starts downloading. + +```js +stream.on('info', (info) => { + // `info` is an instance of VideoDetails as returned by Innertube.getBasicInfo() and is thus not complete with all the details. +}); + +stream.on('progress', (progress) => { + // The progress object has the following properties + // progress.size - size of the video in megabytes as string + // progress.percent - percentage of the video downloaded as string + // progress.chunk_size - size of the last chunk downloaded as bytes + // progress.downloaded_size - size of the video downloaded in megabytes as string + // progress.raw_data.chunk_size - size of the last chunk downloaded as bytes + // progress.raw_data.downloaded_size - size of the video downloaded as bytes + // progress.raw_data.size - size of the response range in bytes +}); + +stream.on('start', () => { + // start does not have any data +}); +``` + +## Aborting Downloads + +The download stream may be abotted by calling `stream.cancel()` diff --git a/examples/download/basic/README.md b/examples/download/basic/README.md new file mode 100644 index 00000000..43b332ea --- /dev/null +++ b/examples/download/basic/README.md @@ -0,0 +1,3 @@ +# Basic Video Download Example + +Donwload video in 360p quality with both audio and video. \ No newline at end of file diff --git a/examples/download/basic/index.js b/examples/download/basic/index.js new file mode 100644 index 00000000..7f8f8635 --- /dev/null +++ b/examples/download/basic/index.js @@ -0,0 +1,20 @@ +// import Innertube from 'youtubei.js'; +const { createWriteStream } = require('fs'); +const Innertube = require('../../../lib/Innertube'); + +(async () => { + // instantiate the library + const yt = await new Innertube(); + + // download the video + // the default options are to download with 360p quality + // with both audio and video in an mp4 container + yt.download('bUHZ2k9DYHY') + .pipe(createWriteStream('./stream.mp4')) + .on('progress', progress => { + console.log(`Downloaded ${progress.percent}%`); + }) + .on('finish', () => { + console.log('Download finished'); + }); +})(); diff --git a/examples/download/ffmpeg/README.md b/examples/download/ffmpeg/README.md new file mode 100644 index 00000000..27001c8c --- /dev/null +++ b/examples/download/ffmpeg/README.md @@ -0,0 +1,5 @@ +# FFMPEG Download Example + +If you want to download vidoes in their best quality, you're going to have to download the audio and video separately. We can use `ffmpeg` to mux the audio and video streams back together. + +This example uses `ffmpeg-static` package to provide a static binary of `ffmpeg`. \ No newline at end of file diff --git a/examples/download/ffmpeg/index.js b/examples/download/ffmpeg/index.js new file mode 100644 index 00000000..665bb038 --- /dev/null +++ b/examples/download/ffmpeg/index.js @@ -0,0 +1,66 @@ +/** + * Mux audio and video into a single stream. + * + * This example requires ffmpeg-static package or some other ffmpeg binary installed on your system. + */ + +// import Innertube from 'youtubei.js'; +const Innertube = require('../../../lib/Innertube'); +const ffmpeg = require('ffmpeg-static'); + +const fs = require('fs'); +const cp = require('child_process'); +const readline = require('readline'); + + +(async () => { + // instantiate the library + const yt = await new Innertube(); + + // get video info + const info = await yt.getInfo('bUHZ2k9DYHY'); + + // get the best video stream + const video = info.download({ + quality: 'bestefficiency', + format: 'any', + type: 'video', + }); + + // get the best audio stream + const audio = info.download({ + quality: 'bestefficiency', + format: 'any', + type: 'audio', + }); + + // create a ffmpeg instance + const ffmpeg_process = cp.spawn(ffmpeg, [ + // remove ffmpeg's banner spam + '-hide_banner', + // inputs + '-i', 'pipe:3', + '-i', 'pipe:4', + // map the streams + '-map', '0:a', + '-map', '1:v', + // keep the original encodings + '-c', 'copy', + // output format + '-f', 'matroska', + // force write to file + '-y', 'stream.mkv' + ], { + windowsHide: true, + stdio: [ + // inherit stdio, stdin and stdout + 'inherit', 'inherit', 'inherit', + // pipe:3, pipe:4 + 'pipe', 'pipe' + ] + }); + + // pipe the streams to ffmpeg + audio.pipe(ffmpeg_process.stdio[3]); + video.pipe(ffmpeg_process.stdio[4]); +})(); \ No newline at end of file diff --git a/examples/download/ffmpeg/package.json b/examples/download/ffmpeg/package.json new file mode 100644 index 00000000..2fd551c2 --- /dev/null +++ b/examples/download/ffmpeg/package.json @@ -0,0 +1,6 @@ +{ + "license": "MIT", + "dependencies": { + "ffmpeg-static": "^5.0.0" + } +}