Last active
January 12, 2021 15:59
-
-
Save colevandersWands/f8b7f636bc8f7b64f88139d6c7d8b302 to your computer and use it in GitHub Desktop.
BBB Screenshare extractor CLI
This file contains 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
'use strict'; | |
/* generate mp4's of the screenshare + audio from a BBB hosted recording | |
$ node this-file.js meeting-id name-of-output-file keep | |
- meeting-id (required) a big blue button meeting id, you can find this in the URL for recorded sessions. | |
the script will exit early if this is not provided | |
- name-of-output-file (optional) the output file name should not include an extension, just the name. | |
defaults to class-recording.mp4 | |
- keep (optional) can be anything, the script just checks for a 4th cli arg | |
if present, the original videos will not be removed | |
test it out with this short video: | |
- video link: https://meet.openknowledge.be/playback/presentation/2.0/playback.html?meetingId=05594ce10542676cd7f00b5d118cb2f367054196-1606646031832 | |
- cli input: $ node this-file.js 05594ce10542676cd7f00b5d118cb2f367054196-1606646031832 test-download | |
heads-up: you will need https://www.ffmpeg.org/ installed on your computer for this to work | |
possible improvement: do everything in memory without writing temp files? | |
credits: adapted from https://github.com/bermarte/HYF-small-video-utility | |
*/ | |
const fs = require('fs'); | |
const path = require('path'); | |
const util = require('util'); | |
const writeFilePromise = util.promisify(fs.writeFile); | |
const unlinkPromise = util.promisify(fs.unlink); | |
const ffmpeg = require('fluent-ffmpeg'); | |
const fetch = require('node-fetch'); | |
// exit early if there is no meeting id | |
const meetingId = process.argv[2]; | |
if (!meetingId) { | |
console.log('no meeting id provided'); | |
process.exit(0); | |
} | |
// configurations | |
const mediaExtension = '.mp4'; | |
const domain = 'https://meet.openknowledge.be/'; | |
const uniquifier = Math.random() | |
.toString(16) | |
.substr(2, 12); | |
// read in the file name and generate a temporary output path | |
const outputFileName = `${process.argv[3] || | |
`class-recording-${uniquifier}`}${mediaExtension}`; | |
const outputPath = path.join(__dirname, outputFileName); | |
// did the user ask to keep the temp files? | |
const keepTemps = process.argv[4]; | |
// generate fetch URL and temporary output path for the silent screenshare video | |
const deskshareUrl = `${domain}presentation/${meetingId}/deskshare/deskshare${mediaExtension}`; | |
const deskshareFileName = 'temp-deskshare-' + uniquifier; | |
const desksharePath = path.join( | |
__dirname, | |
`${deskshareFileName}${mediaExtension}` | |
); | |
// generate fetch URL and temporary output path for the webcams with audio | |
const webcamsUrl = `${domain}presentation/${meetingId}/video/webcams${mediaExtension}`; | |
const webcamsFileName = 'temp-webcams-' + uniquifier; | |
const webcamsPath = path.join(__dirname, `${webcamsFileName}${mediaExtension}`); | |
// generate temporary audio output path | |
const audioFileName = `temp-audio-${uniquifier}.aac`; | |
const audioPath = path.join(__dirname, audioFileName); | |
// main script | |
(async () => { | |
try { | |
console.log('--- downloading videos ---'); | |
await Promise.all([ | |
// downloadVideo is hoisted from the bottom of the file | |
downloadVideo(deskshareUrl, desksharePath), | |
downloadVideo(webcamsUrl, webcamsPath), | |
]); | |
console.log('--- extracting audio from webcams ---'); | |
await new Promise((res, rej) => { | |
ffmpeg(webcamsPath) | |
.outputOptions(['-vn', '-acodec copy']) | |
.save(audioPath) | |
.on('end', () => (console.log('mixing done'), res())) | |
.on('error', err => (console.error(err), rej())); | |
}); | |
console.log('--- combining audio with deskshare video ---'); | |
await new Promise((res, rej) => { | |
ffmpeg() | |
.input(audioPath) | |
.input(desksharePath) | |
.output(outputPath) | |
// .outputOptions('-c', 'copy') | |
.on('end', () => (console.log('mixing done'), res())) | |
.on('error', err => (console.error(err), rej())) | |
.run(); | |
}); | |
if (!keepTemps) { | |
console.log('--- clearing temporary files ---'); | |
await Promise.all([ | |
unlinkPromise(desksharePath), | |
unlinkPromise(webcamsPath), | |
unlinkPromise(audioPath), | |
]); | |
} | |
console.log('all done'); | |
} catch (err) { | |
console.error(err); | |
} | |
})(); | |
async function downloadVideo(url, filePath) { | |
try { | |
console.log(`... fetching ${url}`); | |
const response = await fetch(url); | |
console.log(`... buffering ${url}`); | |
const buffer = await response.buffer(); | |
console.log(`... writing ${url}`); | |
await writeFilePromise(filePath, buffer); | |
console.log(`finished downloading ${url}`); | |
} catch (err) { | |
console.error(err); | |
} | |
} |
This file contains 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
{ | |
"description": "A little script used to convert the lesson of HYF available on [openknowledge.be](https://meet.openknowledge.be/playback/presentation/2.0/playback.html?meetingId=48966e92bc14f80c53d450f9e59dc77e812b2f8b-1605437686426)", | |
"dependencies": { | |
"ffmpeg-extract-audio": "^1.0.2", | |
"fluent-ffmpeg": "^2.1.2", | |
"node-fetch": "^2.6.1" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment