Edited at

Node.jsでYoutube動画をDLして保存するytdl-coreを使ったみたメモ

Node.jsでYoutubeの動画ファイルをごにょごにょって話で調べるとけっこう色々なライブラリが出てきます。


https://www.npmjs.com/search?q=youtube-dl


今回は、更新が比較的新しいのでメンテナンスされていることに期待出来そうな、ytdl-coreというモジュールを使ってみます。使ったみた雰囲気だとffmpegを利用しないでもある程度利用できそうでした。


https://github.com/fent/node-ytdl-core


ちなみにコマンドラインツール版のytdlもあります。

あと、自分の動画で試してます。


環境


  • Node.js 10.11.0

  • npm 6.4.1

  • macOS Mojave 10.4


インストール

npm i ytdl-core


DLのみ

const fs = require('fs');

const ytdl = require('ytdl-core');
const BASE_PATH = `https://www.youtube.com/watch?v=`;

const youtubeId = `9CHiwJhx3qw`; //DLするYoutube動画のID(urlのv=の後ろの部分11桁)
const url = BASE_PATH+youtubeId;

ytdl(url).pipe(fs.createWriteStream(`${youtubeId}.mp4`));


プログレス表示と動画の情報取得

DLが完了した際のイベントを取得したい時は以下のように'end'のイベントでハンドリングできます。


こちらのサンプルコード二つを参考にしています。

https://github.com/fent/node-ytdl-core/blob/master/example/progress.js

https://github.com/fent/node-ytdl-core/blob/master/example/get_info.js


今回はreadlineも表示用に使います。

npm i readline


app.js

const fs = require('fs');

const ytdl = require('ytdl-core');
const readline = require('readline'); //表示用途

const BASE_PATH = `https://www.youtube.com/watch?v=`;

const youtubeId = `9CHiwJhx3qw`; //DLするYoutube動画のID(urlのv=の後ろの部分11桁)
const url = BASE_PATH+youtubeId;

const video = ytdl(url,{filter: (format) => format.container === 'mp4' });

let starttime;

video.pipe(fs.createWriteStream(`${youtubeId}.mp4`));

video.once('response', () => starttime = Date.now());

video.on('progress', (chunkLength, downloaded, total) => {
const floatDownloaded = downloaded / total;
const downloadedMinutes = (Date.now() - starttime) / 1000 / 60;
readline.cursorTo(process.stdout, 0);
process.stdout.write(`${(floatDownloaded * 100).toFixed(2)}% downloaded`);
process.stdout.write(`(${(downloaded / 1024 / 1024).toFixed(2)}MB of ${(total / 1024 / 1024).toFixed(2)}MB)\n`);
process.stdout.write(`running for: ${downloadedMinutes.toFixed(2)}minutes`);
process.stdout.write(`, estimated time left: ${(downloadedMinutes / floatDownloaded - downloadedMinutes).toFixed(2)}minutes `);
readline.moveCursor(process.stdout, 0, -1);
});

video.on('end', () => {
process.stdout.write('\n\n');

//DLしたYoutube動画の情報
ytdl.getInfo(youtubeId, (err, info) => {
if (err) throw err;

console.log('\n動画情報もろもろ');
console.log(info.player_response.videoDetails);

console.log('\n動画タイトル');
console.log(info.player_response.videoDetails.title);

console.log('\n秒数');
console.log(info.player_response.videoDetails.lengthSeconds);

console.log('\nサムネイル');
console.log(info.player_response.videoDetails.thumbnail);
});
});


結果はこんな感じ

$ node app.js

動画情報もろもろ
{ videoId: '9CHiwJhx3qw',
title: '【検証】うんこボタンであのイタズラは成功するか!?【予想外の展開】',
lengthSeconds: '288',
keywords: [ 'iot', 'iotlt', 'うんこボタン', 'ドッキリ', 'イタズラ', '電子工作' ],
channelId: 'UC42TOX3Ff7H0iORoPoBkAcg',
isCrawlable: true,
thumbnail: { thumbnails: [ [Object], [Object], [Object], [Object] ] },
viewCount: '298',
author: 'どっとすたぢお',
isLiveContent: false }

動画タイトル
【検証】うんこボタンであのイタズラは成功するか!?【予想外の展開】

秒数
288

サムネイル
{ thumbnails:
[ { url:
'https://i.ytimg.com/vi/9CHiwJhx3qw/hqdefault.jpg?sqp=-oaymwEiCKgBEF5IWvKriqkDFQgBFQAAAAAYASUAAMhCPQCAokN4AQ==&rs=AOn4CLAS-n8-TmsSRbr_gbKFLAuBY22Rkg',
width: 168,
height: 94 },
{ url:
'https://i.ytimg.com/vi/9CHiwJhx3qw/hqdefault.jpg?sqp=-oaymwEiCMQBEG5IWvKriqkDFQgBFQAAAAAYASUAAMhCPQCAokN4AQ==&rs=AOn4CLA0WQFxu5lN1nHd3S4qYabeCvioKA',
width: 196,
height: 110 },
{ url:
'https://i.ytimg.com/vi/9CHiwJhx3qw/hqdefault.jpg?sqp=-oaymwEjCPYBEIoBSFryq4qpAxUIARUAAAAAGAElAADIQj0AgKJDeAE=&rs=AOn4CLA3j-N0ahl91bywiKcv7vGOiD_3Pw',
width: 246,
height: 138 },
{ url:
'https://i.ytimg.com/vi/9CHiwJhx3qw/hqdefault.jpg?sqp=-oaymwEjCNACELwBSFryq4qpAxUIARUAAAAAGAElAADIQj0AgKJDeAE=&rs=AOn4CLBL4Tit-bNE3WbNMitKo2TdGrMu-A',
width: 336,
height: 188 } ] }

サムネイルが取れるの面白いですよね。




その他

ほんとはmp4じゃなくて音源だけのmp3を撮りたかったけどffmpegが必要っぽいとのことと、READMEを見る限りhighestaudioaudioonlyなども指定できるっぽいけど指定してDLされたファイルが壊れてしまう現象になった(謎)のでとりあえずmp4で試してみました。

バイナリが壊れる現象はオプションの指定の仕方が良くないような気がするので時間があれば再チャレンジしてみたい気持ち