始めに
YouTube動画をダウンロードするにあたって、無料のWebサービスはありますが、怪しい広告が表示されたりして危なかったり、煩わしかったりします。
ライブラリを使えば自作も簡単にできますので、実装内容についてまとめました。
ここでは以下のものを使って実装しています。
- Node.js
- TypeScript
- ytdl-core
- Express
- Heroku
ローカルでYouTube動画をダウンロードする
まず始めにローカルでYouTube動画をダウンロードできるようにします。
以下の記事を参考にして、スクリプトを書きました。ただしこちらではTypeScriptを使って実装しています(と言っても型定義全くないですけど。。)。
Node.jsでYoutube動画をDLして保存するytdl-coreを使ったみたメモ
import ytdl from 'ytdl-core';
import path from 'path';
import fs from 'fs';
const BASE_URL = 'https://www.youtube.com/watch?v=';
const YOUTUBE_ID = 'bnc1NjaXXXX';
const url = `${BASE_URL}${YOUTUBE_ID}`;
const video = ytdl(url);
video.pipe(fs.createWriteStream(path.resolve(__dirname, `./tmp/${YOUTUBE_ID}.mp4`)));
video.on('end', () => {
console.log('file downloaded.');
});
これでmp4ファイルがダウンロードされました。mp3はffmpegで変換する必要があるようなのでchild_processで変換します。
import path from 'path';
import { exec } from 'child_process';
video.on('end', () => {
const inputFilePath = path.resolve(__dirname, `./tmp/${YOUTUBE_ID}.mp4`);
const outputFilePath = path.resolve(__dirname, `./tmp/${YOUTUBE_ID}.mp3`);
// ffmpegでmp3に変換する。yオプションで上書きができる(これがないと、出力先にファイルが存在している場合は止まってしまう)
exec(`ffmpeg -y -i ${inputFilePath} ${outputFilePath}`, (error, stdout, stderr) => {
if (error) {
console.error(error);
return;
}
console.log(stdout);
console.log(stderr);
});
});
ExpressでYouTube動画をダウンロードするAPIを作る
ローカルでダウンロードできるようになったので、次はExpressでサーバーを立てて、API経由でダウンロードして動画または音声を返すAPIを作ります。
/api/youtube/bnc1NjaXXXX?fileType=mp4
みたいに送ったら対象のYouTubeIdをmp4またはmp3でダウンロードするようにします。
// Youtubeのダウンロード
app.get('/api/youtube/:youtubeId', (req, res) => {
const { youtubeId } = req.params;
const fileType = (req.query.fileType || 'mp4') as 'mp4' | 'mp3';
const destFilePath = path.resolve(__dirname, `./tmp/${youtubeId}.mp4`);
const url = `https://www.youtube.com/watch?v=${youtubeId}`;
const stream = ytdl(url, { quality: 'highest' });
stream.pipe(fs.createWriteStream(destFilePath));
stream.on('error', (err) => {
console.error(err);
res.status(400).send('download error!');
});
stream.on('end', () => {
console.log(`youtube file (${youtubeId}.mp4) downloaded.`);
// mp4の場合はそのまま返す
if (fileType === 'mp4') {
res.download(destFilePath);
return;
}
// mp3の場合は変換してから返す
console.log('transform mp4 -> mp3.');
const mp3FilePath = path.resolve(__dirname, `./tmp/${youtubeId}.mp3`);
exec(`ffmpeg -y -i ${destFilePath} ${mp3FilePath}`, (err, stdout, stderr) => {
if (err) {
console.error(err);
res.status(500).send('movie translation error!');
return;
}
console.log(stdout);
console.log(stderr);
res.download(mp3FilePath);
});
});
});
フロント側のリクエストは以下のようにしました。ファイルのダウンロードは直接URL遷移してもできますが、エラーの場合だとエラーページが出てしまうので、一度blobで受け取るようにしました。(どうでも良いですが、fetch APIのエラーハンドリングは一々response.ok
をチェックしないといけないのは面倒ですね。。)
console.log('downloading...');
// ファイルダウンロード
fetch(`/api/youtube/${youtubeId}?fileType=${fileType}`)
.then((response) => {
if (!response.ok) {
throw new Error('Network error');
}
return response.blob();
})
.then((blob) => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${youtubeId}.${fileType}`;
document.body.appendChild(a);
a.click();
a.remove();
})
.finally(() => {
console.log('finished.');
});
});
Herokuにデプロイする
最後にHerokuにデプロイします。ExpressをHerokuにデプロイするやり方とほぼ同じですが、今回はffmpegを入れる必要があります。こちらに書かれていたbuildpackを使用しましたが、heroku-cliを使うのが面倒だったのでGUIで入れました。
Herokuのアプリ画面に入り、SettingsのBuildpacksというセクションでbuildpackを2つ入れます。今までは自動でheroku/nodejsが使われていたと思いますが、2つ入れる必要があるので明記する必要があります。
これで無事デプロイされて動作すれば完了です。
終わりに
以上がYouTube動画をダウンロードするWebアプリを作る流れでした。今回Node.jsでYouTube動画をダウンロードしましたが、音声付きだと画質は360pしか選べなくて少し残念でした。昔PythonでダウンロードしたときはフルHDでも普通にダウンロードできた気がしたんですけどね。。
今回作成したソースとHerokuアプリは以下に上がっております。興味がある方は見てください😄