Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
WP integration with BBB to start/stop and download a video call recording
<?php
/*
Plugin Name: BBB Register
Plugin URI:
Description: Add support to start/stop and download videos from the BBB instance of Merge-IT.
Author: Daniele Mte90 Scasciafratte
Version: 1.0.0
Author URI:
*/
add_filter('manage_bbb-room_posts_columns', 'bbb_register_column');
function bbb_register_column($columns){
$columns[ 'record' ] = __( 'Stato Registrazione' );
return $columns;
}
add_action( 'manage_bbb-room_posts_custom_column' , 'bbb_register_record_column', 10, 2 );
function bbb_register_record_column( $column, $post_id ) {
if ( $column === 'record' ) {
if ( empty( get_option( 'bbb-recorder-status-' . get_the_ID() ) ) ) {
$nonce = wp_create_nonce( 'bbb_register_start' );
echo '<a href="edit.php?post_type=bbb-room&bbb-register=true&bbb-room=' . get_the_ID() . '&&bbb-nonce=' . esc_attr( $nonce ) . '">Avvia</a>';
} else {
$nonce = wp_create_nonce( 'bbb_register_stop' );
echo '<a href="edit.php?post_type=bbb-room&bbb-stop=true&bbb-room=' . get_the_ID() . '&&bbb-nonce=' . esc_attr( $nonce ) . '">Ferma</a>';
}
}
}
add_action( 'admin_init', 'bbb_register_endpoint' );
function bbb_register_endpoint() {
if ( !\current_user_can( 'edit_bbb_rooms' ) ) {
return;
}
if ( empty( $_GET[ 'bbb-register' ] ) ) {
return;
}
if ( !wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_GET[ 'bbb-nonce' ] ) ), 'bbb_register_start' ) ) {
return;
}
$bbbroom = sanitize_text_field( \wp_unslash( $_GET[ 'bbb-room' ] ) );
$bbbrom_slug = basename( get_permalink( $bbbroom ) );
$bbb_id = get_post_meta( $bbbroom, 'bbb-room-meeting-id', true );
update_option( 'bbb-recorder-status-' . $bbbroom, 'recording' );
$request = wp_remote_get( 'http://eventi.merge-it.net:8081/?bbb_meeting=' . $bbbrom_slug . '&bbb_id=' . $bbb_id . '&bbb_add=true&bbb_secret=' . get_option( 'bigbluebutton_salt' ) );
add_action( 'admin_notices', function() {
$class = 'notice notice-success';
printf( '<div class="%1$s"><p>La registrazione sarà avviata in un minuto!</p></div>', esc_attr( $class ) );
} );
}
add_action( 'admin_init', 'bbb_stop_endpoint' );
function bbb_stop_endpoint() {
if ( !\current_user_can( 'edit_bbb_rooms' ) ) {
return;
}
if ( empty( $_GET[ 'bbb-stop' ] ) ) {
return;
}
if ( !wp_verify_nonce( \sanitize_text_field( \wp_unslash( $_GET[ 'bbb-nonce' ] ) ), 'bbb_register_stop' ) ) {
return;
}
$bbbroom = sanitize_text_field( \wp_unslash( $_GET[ 'bbb-room' ] ) );
$bbbrom_slug = basename( get_permalink( $bbbroom ) );
$bbb_id = get_post_meta( $bbbroom, 'bbb-room-meeting-id', true );
$request = wp_remote_get( 'http://eventi.merge-it.net:8081/?bbb_meeting=' . $bbbrom_slug . '&bbb_id=' . $bbb_id . '&bbb_remove=true&bbb_secret=' . get_option( 'bigbluebutton_salt' ) );
delete_option( 'bbb-recorder-status-' . $bbbroom, '' );
add_action( 'admin_notices', function() {
$class = 'notice notice-success';
printf( '<div class="%1$s"><p>La registrazione è stata fermata!</p></div>', esc_attr( $class ) );
} );
}
add_filter( 'the_content', 'bbb_add_download' );
function bbb_add_download( $content ) {
if ( get_post_type() === 'bbb-room' ) {
$bbbrom_slug = basename( get_permalink( get_the_ID() ) );
$content .= '<br>Download Link: <a href="http://eventi.merge-it.net:8081/?bbb_meeting=' . $bbbrom_slug . '&bbb_recording=true">Click here</a>';
}
return $content;
}
version: '3.3'
services:
bbb-streamer:
image: netvandal/livestreamingld:1.94
container_name: your_meetingID
environment:
# BigBlueButton Server url:
- BBB_URL=URL
# BigBlueButton secret:
- BBB_SECRET=random
# BigBlueButton meetingID:
- BBB_MEETING_ID=your_meetingID
# start meeting (optional):
- BBB_START_MEETING=false
# meeting title (optional):
- BBB_MEETING_TITLE=liveStreaming
# download / save BigBlueButton meeting
- BBB_DOWNLOAD_MEETING=true
# play intro file (can be a local file in videodata folder e.g. /video/intro.mp4 or a url of a mediastream e.g. https://my.intro.stream)
- BBB_INTRO=false
# fake url to live without troubles
- BBB_STREAM_URL=rmtp://fakeurl.com/stream
# Timezone (default: Europe/Vienna):
- TZ=Europe/Rome
volumes:
- ./videodata:/video
#!/usr/bin/env python3
# pip3 install pyyaml
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import yaml
import urllib
import os
import shutil
import sys
class HTTPServer_RequestHandler(BaseHTTPRequestHandler):
def setup(self):
BaseHTTPRequestHandler.setup(self)
self.request.settimeout(180)
def do_GET(self):
parsed_path = urllib.parse.urlsplit(self.path)
query = urllib.parse.parse_qs(parsed_path.query)
self.send_response(200)
message = ''
bbb_meeting = ''
if 'bbb_meeting' not in query:
self.send_response(300)
message = {'status': 'error', 'text': 'you are looking to a private web server! Go away!'}
else:
bbb_meeting = ' '.join(map(str, query['bbb_meeting']))
dockercomposefolder = os.getcwd() + '/bbb-record-' + bbb_meeting
dockercompose = dockercomposefolder + '/docker-compose.yml'
video = dockercomposefolder + '/videodata/'
if 'bbb_secret' in query:
os.system('cd /root')
if not os.path.exists(os.getcwd() + '/docker-bbb-base.yml'):
print(os.getcwd() + '/docker-bbb-base.yml not exists!')
sys.exit()
with open(os.getcwd() + '/docker-bbb-base.yml') as f:
data = yaml.safe_load(f)
message = ''
for item in data['services']['bbb-streamer']['environment']:
if item == 'BBB_SECRET=' + ' '.join(map(str, query['bbb_secret'])):
message = {'status': 'success', 'text': 'secret found'}
break
if message == '':
self.send_response(300)
message = {'status': 'error', 'text': 'wrong secret'}
if 'bbb_meeting' in query:
bbb_id = ' '.join(map(str, query['bbb_id']))
if 'bbb_add' in query:
fin = open(os.getcwd() + '/docker-bbb-base.yml', "rt")
message = {'status': 'success', 'text': 'meeting configured'}
if os.path.exists(dockercomposefolder):
shutil.rmtree(dockercomposefolder, ignore_errors=True)
message = {'status': 'success', 'text': 'meeting already exist, removed and created again'}
os.mkdir(dockercomposefolder)
fout = open(dockercompose, "wt")
for line in fin:
fout.write(line.replace('your_meetingID', bbb_id).replace('containerID', bbb_meeting))
fin.close()
fout.close()
os.system('cd ' + dockercomposefolder + ' && docker-compose up --force-recreate --build -d &')
message = {'status': 'success', 'text': 'docker container started'}
elif 'bbb_remove' in query:
if os.path.exists(dockercomposefolder):
os.system('cd ' + dockercomposefolder + ' && docker-compose down &')
#os.remove(dockercompose)
message = {'status': 'success', 'text': 'meeting recording stopped'}
if 'bbb_recording' in query:
if os.path.isdir(video):
for i in os.listdir(video):
if os.path.isfile(os.path.join(video, i)) and 'record' in i:
self.send_header("Content-Type", 'application/octet-stream')
with open(os.path.join(video, i), 'rb') as f:
self.send_header("Content-Disposition", 'attachment; filename="{}"'.format(i))
fs = os.fstat(f.fileno())
self.send_header("Content-Length", str(fs.st_size))
self.end_headers()
try:
shutil.copyfileobj(f, self.wfile)
except:
print('Error on downloading ' + os.path.join(video, i))
else:
self.send_response(300)
message = {'status': 'error', 'text': 'recording not found'}
else:
message = {'status': 'success', 'text': 'recording not found, did you started it?'}
if message != '':
self.send_header('Content-type', 'text/json')
try:
self.end_headers()
except BrokenPipeError:
print('Browser blocked the download')
self.wfile.write(bytes(json.dumps(message), "utf8"))
return
#def log_message(self, format, *args):
#return
def run():
print('Server started...')
server_address = ('', 8081)
httpd = HTTPServer(server_address, HTTPServer_RequestHandler)
print('Server running...')
httpd.serve_forever()
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment