Last active
February 26, 2022 03:25
-
-
Save bobbicodes/9b3e6dfb6e268c84f5058a8e0f02179a to your computer and use it in GitHub Desktop.
Download and process YouTube videos with nbb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns ytdl | |
(:require ["ytdl-core$default" :as ytdl] | |
["fluent-ffmpeg$default" :as FFmpeg] | |
["fs" :as fs] | |
[promesa.core :as p])) | |
(defn save | |
"Fetches a YouTube url and writes it to a file." | |
[url name] | |
(p/let [stream (ytdl url)] | |
(.pipe stream (fs/createWriteStream name)))) | |
(defn ffmpeg | |
"Creates ffmpeg command to extract audio and apply dynamic compression." | |
[url threshold ratio makeup] | |
(.audioFilters (FFmpeg url) | |
(js->clj | |
{:filter "acompressor" | |
:options {:threshold (str threshold "dB") | |
:ratio ratio | |
:makeup makeup}}))) | |
(comment | |
(ffmpeg "https://www.youtube.com/watch?v=x24fLxur-2o" -40 20 10) | |
(save "https://www.youtube.com/watch?v=x24fLxur-2o" "maps.mp4") | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const http = require('http') | |
const fs = require('fs') | |
const { PassThrough } = require('stream') | |
const path = require('path') | |
const ytdl = require('ytdl-core') | |
const FFmpeg = require('fluent-ffmpeg') | |
const stream = function streamify (uri, opt) { | |
opt = { | |
...opt, | |
videoFormat: 'mp4', | |
quality: 'lowest', | |
audioFormat: 'mp3', | |
filter (format) { | |
return format.container === opt.videoFormat && format.audioBitrate | |
} | |
} | |
const video = ytdl(uri, opt) | |
const { file, audioFormat } = opt | |
const stream = file ? fs.createWriteStream(file) : new PassThrough() | |
const ffmpeg = FFmpeg(video).audioFilters( | |
{ | |
filter: 'acompressor', | |
options: { threshold: '-40dB', ratio: 20, makeup: 8 } | |
} | |
) | |
process.nextTick(() => { | |
const output = ffmpeg.format(audioFormat).pipe(stream) | |
ffmpeg.once('error', error => stream.emit('error', error)) | |
output.once('error', error => { | |
video.end() | |
stream.emit('error', error) | |
}) | |
}) | |
stream.video = video | |
stream.ffmpeg = ffmpeg | |
return stream | |
} | |
http.createServer(demo).listen(3000) | |
async function demo (req, res) { | |
try { | |
for await (const chunk of stream(`https://www.youtube.com/watch?v=BKHImVMWq3o`)) { | |
res.write(chunk) | |
} | |
res.end() | |
} catch (err) { | |
console.error(err) | |
if (!res.headersSent) { | |
res.writeHead(500) | |
res.end('internal system error') | |
} | |
} | |
} | |
console.log('open http://localhost:3000 for audio stream') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment