diff --git a/README.md b/README.md index 2b87376..5b12cd0 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ npm install --ignore-scripts ### Recording export ```sh -node export.js "https://BBB_HOST/playback/presentation/2.0/playback.html?meetingId=MEETING_ID" meeting.webm 10 true +node export.js "https://BBB_HOST/playback/presentation/2.0/playback.html?meetingId=MEETING_ID" meeting.webm 10 true s3 ``` **Options** @@ -54,7 +54,7 @@ You can pass 4 args 2) (Optional) Export file name (should be `.webm` at end). You can use "MEETING_ID" (without `.webm`) to set the meeting ID as export name. Default: MEETING_ID 3) (Optional) Duration of recording (in seconds). You can set it to 0 use the real duration of recording. Default: real duration of recording 4) (Optional) Convert to mp4 or not (true for convert to mp4). Default: false - +5) (Optional) Possible Values: local - to store in local file system; s3 - To store in S3 complient service like AWS-S3/Digital Ocean Space. To upload in s3 config.json needs to updated with aws config details. Default: local ### Live recording diff --git a/config.json b/config.json index b70473e..4694354 100644 --- a/config.json +++ b/config.json @@ -4,5 +4,12 @@ "ffmpegServerPort": 4000, "auth": "mZFZN4yc", "copyToPath": "/var/www/bigbluebutton-default/record", - "playbackFile": "playback\\.html" + "playbackFile": "playback\\.html", + "s3Config":{ + "endpoint":"", + "accessKeyId":"", + "secretAccessKey": "", + "bucketName":"", + "ACL":"private|public-read" + } } diff --git a/export.js b/export.js index 42ad7d7..f1ba997 100644 --- a/export.js +++ b/export.js @@ -6,6 +6,7 @@ const homedir = os.homedir(); const platform = os.platform(); const config = JSON.parse(fs.readFileSync("config.json", 'utf8')); const spawn = require('child_process').spawn; +const AWS = require('aws-sdk') var xvfb = new Xvfb({ silent: true, @@ -77,7 +78,18 @@ async function main() { }else if(convert !== "true" && convert !== "false"){ console.warn("Invalid convert value!"); process.exit(1); + }else{ + convert = eval(convert); + } + + var storage_type = process.argv[6] + if(!storage_type){ + storage_type = 'local' + }else if(storage_type !== "local" && storage_type !== "s3"){ + console.warn("Invalid storage type only local/s3"); + process.exit(1); } + const browser = await puppeteer.launch(options) const pages = await browser.pages() @@ -143,11 +155,26 @@ async function main() { xvfb.stopSync() } - if(convert){ + if(storage_type=='local'){ + if(convert){ convertAndCopy(exportname) - }else{ + }else{ copyOnly(exportname) + } + }else if(storage_type=='s3'){ + if(convert){ + convertAndUploadToS3(exportname) + }else{ + uploadToS3(exportname) + } } + + + // if(upload_to_s3){ + // uploadToS3(exportname,convert) + // }else{ + // console.log("Upload is false", upload_to_s3) + // } }catch(err) { console.log(err) @@ -157,7 +184,6 @@ async function main() { main() function convertAndCopy(filename){ - var copyFromPath = homedir + "/Downloads"; var copyToPath = config.copyToPath; var onlyfileName = filename.split(".webm") @@ -169,8 +195,8 @@ function convertAndCopy(filename){ fs.mkdirSync(copyToPath); } - console.log(copyTo); - console.log(copyFrom); + console.log("Copy to:......",copyTo); + console.log("Copy FROM:.......",copyFrom); const ls = spawn('ffmpeg', [ '-y', @@ -211,7 +237,9 @@ function convertAndCopy(filename){ } + function copyOnly(filename){ + console.log("Calling copyOnly", ); var copyFrom = homedir + "/Downloads/" + filename; var copyToPath = config.copyToPath; @@ -232,3 +260,111 @@ function copyOnly(filename){ console.log(err) } } +function convertAndUploadToS3(filename){ + var copyFromPath = homedir + "/Downloads"; + var copyToPath = '/tmp'; + var onlyfileName = filename.split(".webm") + var mp4File = onlyfileName[0] + ".mp4" + var copyFrom = copyFromPath + "/" + filename + "" + var copyTo = "/tmp/" + mp4File; + + if(!fs.existsSync(copyToPath)){ + fs.mkdirSync(copyToPath); + } + + console.log("Copy to:......",copyTo); + console.log("Copy FROM:.......",copyFrom); + + const ls = spawn('ffmpeg', + [ '-y', + '-i "' + copyFrom + '"', + '-c:v libx264', + '-preset veryfast', + '-movflags faststart', + '-profile:v high', + '-level 4.2', + '-max_muxing_queue_size 9999', + '-vf mpdecimate', + '-vsync vfr "' + copyTo + '"' + ], + { + shell: true + } + + ); + + ls.stdout.on('data', (data) => { + console.log(`stdout: ${data}`); + }); + + ls.stderr.on('data', (data) => { + console.error(`stderr: ${data}`); + }); + + ls.on('close', (code) => { + console.log(`child process exited with code ${code}`); + if(code == 0) + { + const s3 = getS3Client(); + const fileContent = fs.readFileSync(copyTo); + var params = { + Bucket: "amazon-school500117", + Key: mp4File, + Body: fileContent, + // Metadata:{ + // "Content-Type": 'video/mpeg' + // } + }; + s3.upload(params, function(err, data) { + if (err) { + console.log(err, err.stack) + }else{ + console.log(`File uploaded successfully. ${JSON.stringify(data)}`); + fs.unlinkSync(copyFrom); + fs.unlinkSync(copyTo); + console.log('successfully deleted ' + copyFrom); + } + }); + + } + + }); + +} + +function uploadToS3(filename){ + + const s3 = getS3Client(); + + var copyFrom = homedir + "/Downloads/" + filename; + const fileContent = fs.readFileSync(copyFrom); + var params = { + // Bucket: "amazon-school500117", + Bucket: config.s3Config.bucketName, + Key: filename, + Body: fileContent, + // Metadata:{ + // "Content-Type": 'video/webem' + // } + }; + s3.upload(params, function(err, data) { + if (err) { + console.log(err, err.stack) + }else{ + fs.unlinkSync(copyFrom); + console.log(`File uploaded successfully. ${JSON.stringify(data)}`); + } + }); + +} + +function getS3Client(){ + const spacesEndpoint = new AWS.Endpoint(config.s3Config.endpoint); + const s3 = new AWS.S3({ + endpoint: spacesEndpoint, + accessKeyId: config.s3Config.accessKeyId, + secretAccessKey: config.s3Config.secretAccessKey, + ACL: config.s3Config.ACL + }); + return s3; +} diff --git a/package.json b/package.json index dd66260..ae2e259 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,10 @@ "screencast" ], "dependencies": { + "aws-sdk": "^2.720.0", + "express": "^4.16.4", "puppeteer": "^2.1.1", - "xvfb": "^0.3.0", - "ws": "^7.2.3" + "ws": "^7.2.3", + "xvfb": "^0.3.0" } } diff --git a/scout_recording.js b/scout_recording.js new file mode 100644 index 0000000..c5fdbc9 --- /dev/null +++ b/scout_recording.js @@ -0,0 +1,39 @@ +const exec = require("child_process").exec; +recordings = ['9cdfe0c61d329282ea038bb3be48d3f46271baa0-1598332811537', '3b57c208d66f57573d34fe194274c30254c5a765-1602570375739', + '666af640b314368e84bd65e6ff679c97ecae03e7-1609914304959', '7b6822f672b79408a668de092ef41324972272bc-1610432884722', + 'e7da8a28cef7126dbf697e7edee114ee0878ab92-1611043044711', 'eb1093d7a470d540776bd961b5c88fb6e48f8a4e-1611037505150', + 'd6faa1af56510c73f6186bee1f59648b9471243d-1612247130053', '605b06c7c74349616a795db2f4cc585ee0966581-1614147904618', + '88d6a5f73092c70866a7c32be761ad915d0e79e4-1618970379653', '191212e459fb7b8080279bf6c4611ba410f24156-1620784504521', + 'e124fdcc120670ec32d726cbacf435bed220ddc5-1620714304300', 'b62503dd4788a8cdab6d28f37dcd6bfefe8060cd-1621907704517', + 'a698fda1fcf0a271c89f080c965389acf5e79f74-1621319158930', '14a39d7b7ea31406532cc88f4f5a76c1548dee57-1621923914872', + '151f834378a4b6e2bf39c633d47aa0f9ce4aa98d-1623219961887', 'fd8daf3b9279eabb632c2b225009944f52aa0950-1624256706332', + '4d46f47a2a2c04e36775b33dd37e69fe5e9fbc95-1624429518860', 'df85123a98784ef8b5b1136d97b536490dc7766d-1626762305562', + 'a85c9529b257a83aae161573b43f9b3c6b502533-1629354305853', 'd4d763385d478586d49ced453d93faa93b19a41d-1631687174572', + '7c8532534d11239ea1dcf28176be7a10e2693293-1631672725399', '7c8532534d11239ea1dcf28176be7a10e2693293-1631672725399', + '4d46f47a2a2c04e36775b33dd37e69fe5e9fbc95-1624429518860', 'df85123a98784ef8b5b1136d97b536490dc7766d-1626762305562', + 'a85c9529b257a83aae161573b43f9b3c6b502533-1629354305853', 'd4d763385d478586d49ced453d93faa93b19a41d-1631687174572', + '7c8532534d11239ea1dcf28176be7a10e2693293-1631672725399', '74542fb40640660760803a41e32774e3352d2966-1634106311717', + '1081c1273a40b00aed84ef935170490b683aad9c-1635227723654', 'bcc631509d644a252e9174b18d7a4c9ae0e8ac19-1635315912112', + '35dfb19ea3bd65535f4191128a583ad7597059a9-1636437332274', '7567aafa4631f93d35724df7180da949eb17c927-1637646904541', + '7e4974983353ae962b4450f26becc5bfca0d44cb-1634709304962', 'd403df08755dc84763f54e07d6e63699b8a79bc9-1637735118387', + '13fea9bfcdda2afbeceba9dff29bbfb2a7faae8c-1638944707552', 'ca4b87dcffe4ae82169ac50bc095f85e2077298f-1642574022494', + 'a77e957324c59fc564b6a1d1625b2541fd9b6390-1641363946338', 'aaffb84909575167a8b3bbb446f130c0004100f0-1647496639376', + 'a10bb620478136e3f0479a82560b93ac57828444-1647502327620', '' + ] +recordings.forEach(function(record_id){ + console.log( + `node export.js "https://meet5.scoutlive.in/playback/presentation/2.0/playback.html?meetingId=${record_id}" ${record_id}.webm 10 true s3` + ); + const myShellScript = exec( + `node export.js "https://meet5.scoutlive.in/playback/presentation/2.0/playback.html?meetingId=${record_id}" ${record_id}.webm 10 true s3` + ); + + myShellScript.stdout.on("data", (data) => { + if (data.search("File uploaded successfully") > -1) { + getRecordMeta(eventObj); + } + }); + myShellScript.stderr.on("data", (data) => { + console.error(data); + }); +});