始めに
YouTube動画をダウンロードするにあたって、無料のWebサービスはありますが、怪しい広告が表示されたりして危なかったり、煩わしかったりします。
ライブラリを使えば自作も簡単にできますので、実装内容についてまとめました。
ここでは以下のものを使って実装しています。
- Node.js
- TypeScript
- ytdl-core
- Express
- Heroku
ローカルでYouTube動画をダウンロードする
まず始めにローカルでYouTube動画をダウンロードできるようにします。
以下の記事を参考にして、スクリプトを書きました。ただしこちらではTypeScriptを使って実装しています(と言っても型定義全くないですけど。。)。
Node.jsでYoutube動画をDLして保存するytdl-coreを使ったみたメモ
importytdlfrom'ytdl-core';importpathfrom'path';importfsfrom'fs';constBASE_URL='https://www.youtube.com/watch?v=';constYOUTUBE_ID='bnc1NjaXXXX';consturl=`${BASE_URL}${YOUTUBE_ID}`;constvideo=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で変換します。
importpathfrom'path';import{exec}from'child_process';video.on('end',()=>{constinputFilePath=path.resolve(__dirname,`./tmp/${YOUTUBE_ID}.mp4`);constoutputFilePath=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;constfileType=(req.query.fileType||'mp4')as'mp4'|'mp3';constdestFilePath=path.resolve(__dirname,`./tmp/${youtubeId}.mp4`);consturl=`https://www.youtube.com/watch?v=${youtubeId}`;conststream=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.');constmp3FilePath=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){thrownewError('Network error');}returnresponse.blob();}).then((blob)=>{consturl=URL.createObjectURL(blob);consta=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アプリは以下に上がっております。興味がある方は見てください😄