diff --git a/roles/jitsi/templates/prosody.cfg.lua.j2 b/roles/jitsi/templates/prosody.cfg.lua.j2 index 250aeb3..7477416 100644 --- a/roles/jitsi/templates/prosody.cfg.lua.j2 +++ b/roles/jitsi/templates/prosody.cfg.lua.j2 @@ -54,17 +54,25 @@ VirtualHost "{{ jitsi_domain }}" "turncredentials"; "speakerstats"; "conference_duration"; + "muc_lobby_rooms"; + "participant_metadata"; } c2s_require_encryption = false allow_unencrypted_plain_auth = true speakerstats_component = "speakerstats.{{ jitsi_domain }}" conference_duration_component = "conferenceduration.{{ jitsi_domain }}" + lobby_muc = "lobby.{{ jitsi_domain }}" + main_muc = "conference.{{ jitsi_domain }}" + muc_lobby_whitelist = { "recorder.{{ jitsi_domain }}" } {% if jitsi_auth == 'ldap' %} -- Guest virtual domain VirtualHost "guest.{{ jitsi_domain }}" authentication = "anonymous" c2s_require_encryption = false + modules_enabled = { + "participant_metadata"; + } {% endif %} VirtualHost "{{ jitsi_auth_domain }}" @@ -100,3 +108,8 @@ Component "speakerstats.{{ jitsi_domain }}" "speakerstats_component" Component "conferenceduration.{{ jitsi_domain }}" "conference_duration_component" muc_component = "conference.{{ jitsi_domain }}" +Component "lobby.{{ jitsi_domain }}" "muc" + storage = "memory" + restrict_room_creation = true + muc_room_locking = false + muc_room_default_public_jids = true diff --git a/roles/jitsi_jibri/defaults/main.yml b/roles/jitsi_jibri/defaults/main.yml index 182f62e..b58bd08 100644 --- a/roles/jitsi_jibri/defaults/main.yml +++ b/roles/jitsi_jibri/defaults/main.yml @@ -21,3 +21,8 @@ jitsi_jibri_xmpp_domain: recorder.{{ jitsi_jibri_domain }} jitsi_jibri_recorder_xmpp_user: recorder # jitsi_jibri_recorder_xmpp_pass: p@ssw0rd +# List of email address which will be notified when a recording is finished +jitsi_jibri_recording_notify: [] +# Base URL where you can reach your Jitsi host. +# Might need to be adjusted if running behing a reverse proxy +jitsi_jibri_recordings_base_url: https://{{ inventory_hostname }}/recordings diff --git a/roles/jitsi_jibri/tasks/conf.yml b/roles/jitsi_jibri/tasks/conf.yml index cebbd78..6bd105a 100644 --- a/roles/jitsi_jibri/tasks/conf.yml +++ b/roles/jitsi_jibri/tasks/conf.yml @@ -1,10 +1,12 @@ --- - name: Deploy configuration - template: src={{ item }}.j2 dest={{ jitsi_root_dir }}/etc/jibri/{{ item }} + template: src={{ item.src }} dest={{ item.dest }} loop: - - xorg-video-dummy.conf - - jibri.conf + - src: xorg-video-dummy.conf.j2 + dest: /etc/X11/xorg.conf.d/jibri.conf + - src: jibri.conf.j2 + dest: "{{ jitsi_root_dir }}/etc/jibri/jibri.conf" notify: restart jitsi-jibri tags: jitsi @@ -52,3 +54,7 @@ template: src=nginx.conf.j2 dest=/etc/nginx/ansible_location.d/10-jitsi-jibri.conf notify: reload nginx tags: jitsi + +- name: Configure the finalize script + template: src=finalize.yml.j2 dest={{ jitsi_root_dir }}/etc/jibri/finalize.yml + tags: jitsi diff --git a/roles/jitsi_jibri/tasks/install.yml b/roles/jitsi_jibri/tasks/install.yml index 7c11d14..db76772 100644 --- a/roles/jitsi_jibri/tasks/install.yml +++ b/roles/jitsi_jibri/tasks/install.yml @@ -12,6 +12,9 @@ - xdotool #- xorg-x11-drv-void - xorg-x11-drv-dummy + - perl-YAML-Tiny + - perl-Email-MIME + - perl-Email-Sender tags: jitsi # If the repo changed since the last run, we rebuild and restart the bridge @@ -66,10 +69,12 @@ tags: jitsi - name: Deploy systemd units - template: src=jitsi-{{ item }}.service.j2 dest=/etc/systemd/system/jitsi-{{ item }}.service + template: src=jitsi-{{ item }}.j2 dest=/etc/systemd/system/jitsi-{{ item }} loop: - - jibri - - jibri-xorg + - jibri.service + - jibri-xorg.service + - jibri-cleaner.service + - jibri-cleaner.timer register: jitsi_jibri_unit notify: restart jitsi-jibri tags: jitsi @@ -78,3 +83,11 @@ systemd: daemon_reload=True when: jitsi_jibri_unit.results | selectattr('changed','equalto',True) | list | length > 0 tags: jitsi + +- name: Install finalize script + template: src=finalize.pl.j2 dest={{ jitsi_root_dir }}/jibri/finalize.pl mode=755 + tags: jitsi + +- name: Install record cleaning script + template: src=clean_records.sh.j2 dest={{ jitsi_root_dir }}/jibri/clean_records.sh mode=755 + tags: jitsi diff --git a/roles/jitsi_jibri/tasks/services.yml b/roles/jitsi_jibri/tasks/services.yml index 88af930..fe8dd5f 100644 --- a/roles/jitsi_jibri/tasks/services.yml +++ b/roles/jitsi_jibri/tasks/services.yml @@ -3,3 +3,7 @@ - name: Start and enable services service: name=jitsi-jibri state=started enabled=True tags: jitsi + +- name: Start cleaer timer + systemd: name=jitsi-jibri-cleaner.timer state=started enabled=True + tags: jitsi diff --git a/roles/jitsi_jibri/templates/clean_records.sh.j2 b/roles/jitsi_jibri/templates/clean_records.sh.j2 new file mode 100644 index 0000000..65637ec --- /dev/null +++ b/roles/jitsi_jibri/templates/clean_records.sh.j2 @@ -0,0 +1,3 @@ +#!/bin/bash -e + +find {{ jitsi_root_dir }}/data/recordings -maxdepth 1 -type d -mtime +30 -exec rm -rf "{}" \; diff --git a/roles/jitsi_jibri/templates/finalize.pl.j2 b/roles/jitsi_jibri/templates/finalize.pl.j2 new file mode 100644 index 0000000..70f0483 --- /dev/null +++ b/roles/jitsi_jibri/templates/finalize.pl.j2 @@ -0,0 +1,83 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use JSON; +use YAML::Tiny; +use Getopt::Long; +use Email::MIME; +use Email::Sender::Simple qw(sendmail); +use File::Basename; +use utf8; + +my $conf = {}; +my $opt = { + config => '{{ jitsi_root_dir }}/etc/jibri/finilize.yml' +}; + +GetOptions( + "config=s" => \$opt->{config} +); + +sub send_mail { + my ($subject, $dest, $body) = @_; + my $mail = Email::MIME->create( + header_str => [ + From => $conf->{from}, + To => $dest, + Subject => $subject + ], + attributes => { + charset => 'utf-8', + encoding => 'base64' + }, + body_str => $body + ); + sendmail($mail); +} + +# Read config file +if (-e $opt->{config}){ + my $yaml = YAML::Tiny->read( $opt->{config} ) + or die "Config file " . $opt->{config} . " is invalid\n"; + if ( not $yaml->[0] ) { + die "Config file " . $opt->{config} . " is invalid\n"; + } + $conf = $yaml->[0]; +} + +$conf->{notify} || []; +my $recording = $ARGV[0]; + +my $metadata = {}; +if (-e "$recording/metadata.json"){ + open(my $jst, "$recording/metadata.json") or die "$!"; + local $/; + $metadata = from_json(<$jst>); +} + +my $video = undef; +opendir ( DIR, $recording ) || die "Error in opening dir $recording\n"; +while( (my $filename = readdir(DIR))) { + if ($filename =~ m/\.mp4$/){ + $video = basename($filename); + last; + } +} +closedir(DIR); + +foreach my $participant (@{$metadata->{participants}}){ + push @{$conf->{notify}}, $participant->{user}->{id}; +} + +if (defined $video){ + foreach my $dest (@{$conf->{notify}}){ + send_mail( + "Enregistrement Jitsi Meet", + $dest, + "L'enregistrement vidéo est disponible à l'adresse $conf->{base_url}/" . + basename($recording) . "/$video\n" + ); + } +} diff --git a/roles/jitsi_jibri/templates/finalize.yml.j2 b/roles/jitsi_jibri/templates/finalize.yml.j2 new file mode 100644 index 0000000..8b63c2c --- /dev/null +++ b/roles/jitsi_jibri/templates/finalize.yml.j2 @@ -0,0 +1,10 @@ +--- + +from: jitsi-noreply@{{ ansible_domain }} +{% if jitsi_jibri_recording_notify | length > 0 %} +notify: +{% for dest in jitsi_jibri_recording_notify %} + - {{ dest }} +{% endfor %} +{% endif %} +base_url: {{ jitsi_jibri_recordings_base_url }} diff --git a/roles/jitsi_jibri/templates/jibri.conf.j2 b/roles/jitsi_jibri/templates/jibri.conf.j2 index 2bf5949..0ae90bb 100644 --- a/roles/jitsi_jibri/templates/jibri.conf.j2 +++ b/roles/jitsi_jibri/templates/jibri.conf.j2 @@ -40,7 +40,7 @@ jibri { } recording { recordings-directory = "{{ jitsi_root_dir }}/data/recordings" - //finalize-script = "/path/to/finalize_recording.sh" + finalize-script = "{{ jitsi_root_dir }}/jibri/finalize.pl" } streaming { rtmp-allow-list = [ @@ -48,7 +48,7 @@ jibri { ] } ffmpeg { - resolution = "1024x768" + //resolution = "1024x768" // The audio source that will be used to capture audio on Linux audio-source = "alsa" @@ -59,7 +59,8 @@ jibri { // The flags which will be passed to chromium when launching flags = [ "--use-fake-ui-for-media-stream", - "--start-maximized", + //"--start-maximized", + "--window-size=1920,1080", "--kiosk", "--enabled", "--disable-infobars", @@ -69,27 +70,6 @@ jibri { stats { enable-stats-d = true } - webhook { - // A list of subscribers interested in receiving webhook events - subscribers = [] - } - jwt-info { - // The path to a .pem file which will be used to sign JWT tokens used in webhook - // requests. If not set, no JWT will be added to webhook requests. - # signing-key-path = "/path/to/key.pem" - - // The kid to use as part of the JWT - # kid = "key-id" - - // The issuer of the JWT - # issuer = "issuer" - - // The audience of the JWT - # audience = "audience" - - // The TTL of each generated JWT. Can't be less than 10 minutes. - # ttl = 1 hour - } call-status-checks { // If all clients have their audio and video muted and if Jibri does not // detect any data stream (audio or video) comming in, it will stop diff --git a/roles/jitsi_jibri/templates/jitsi-jibri-cleaner.service.j2 b/roles/jitsi_jibri/templates/jitsi-jibri-cleaner.service.j2 new file mode 100644 index 0000000..3c74e23 --- /dev/null +++ b/roles/jitsi_jibri/templates/jitsi-jibri-cleaner.service.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Jibri Recordings Cleaner + +[Service] +Type=oneshot +PrivateTmp=yes +User={{ jitsi_jibri_user }} +Group={{ jitsi_jibri_user }} +ExecStart={{ jitsi_root_dir }}/jibri/clean_records.sh + diff --git a/roles/jitsi_jibri/templates/jitsi-jibri-cleaner.timer.j2 b/roles/jitsi_jibri/templates/jitsi-jibri-cleaner.timer.j2 new file mode 100644 index 0000000..8293f61 --- /dev/null +++ b/roles/jitsi_jibri/templates/jitsi-jibri-cleaner.timer.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Jibri Recordings Cleaner + +[Timer] +OnCalendar=daily +Persistent=true + +[Install] +WantedBy=timers.target + diff --git a/roles/jitsi_jibri/templates/jitsi-jibri-xorg.service.j2 b/roles/jitsi_jibri/templates/jitsi-jibri-xorg.service.j2 index e39718f..bd90c20 100644 --- a/roles/jitsi_jibri/templates/jitsi-jibri-xorg.service.j2 +++ b/roles/jitsi_jibri/templates/jitsi-jibri-xorg.service.j2 @@ -8,7 +8,7 @@ User={{ jitsi_jibri_user }} Group={{ jitsi_jibri_user }} Environment=DISPLAY=:0 WorkingDirectory={{ jitsi_root_dir }}/etc/jibri/ -ExecStart=/usr/bin/Xorg -nocursor -noreset +extension RANDR +extension RENDER -config xorg-video-dummy.conf :0 +ExecStart=/usr/bin/Xorg -nocursor -noreset +extension RANDR +extension RENDER -config jibri.conf :0 ExecReload=/bin/kill -HUP $MAINPID KillMode=process Restart=on-failure @@ -16,4 +16,4 @@ RestartPreventExitStatus=255 SyslogIdentifier=jibri-xorg [Install] -WantedBy=jitsi-jibri.service jitsi-jibri-icewm.service +WantedBy=jitsi-jibri.service diff --git a/roles/jitsi_jibri/templates/nginx.conf.j2 b/roles/jitsi_jibri/templates/nginx.conf.j2 index f83db22..11c620b 100644 --- a/roles/jitsi_jibri/templates/nginx.conf.j2 +++ b/roles/jitsi_jibri/templates/nginx.conf.j2 @@ -1,5 +1,5 @@ # Serve recordings through nginx -location ~* /recordings/.+\.mp4 { +location /recordings { alias /opt/jitsi/data/recordings; add_header Content-disposition "attachment"; } diff --git a/roles/jitsi_jibri/templates/xorg-video-dummy.conf.j2 b/roles/jitsi_jibri/templates/xorg-video-dummy.conf.j2 index 508b2e7..1751de5 100644 --- a/roles/jitsi_jibri/templates/xorg-video-dummy.conf.j2 +++ b/roles/jitsi_jibri/templates/xorg-video-dummy.conf.j2 @@ -1,135 +1,26 @@ -# This xorg configuration file is meant to be used by xpra -# to start a dummy X11 server. -# For details, please see: -# https://xpra.org/Xdummy.html - -Section "ServerFlags" - Option "DontVTSwitch" "true" - Option "AllowMouseOpenFail" "true" - Option "PciForceNone" "true" - Option "AutoEnableDevices" "false" - Option "AutoAddDevices" "false" -EndSection - -Section "InputDevice" - Identifier "dummy_mouse" - Option "CorePointer" "true" - Driver "void" -EndSection - -Section "InputDevice" - Identifier "dummy_keyboard" - Option "CoreKeyboard" "true" - Driver "void" -EndSection - Section "Device" - Identifier "dummy_videocard" - Driver "dummy" - Option "ConstantDPI" "true" - #VideoRam 4096000 - #VideoRam 256000 - VideoRam 192000 + Identifier "Configured Video Device" + Driver "dummy" + VideoRam 256000 EndSection + Section "Monitor" - Identifier "dummy_monitor" - HorizSync 5.0 - 1000.0 + Identifier "Configured Monitor" + HorizSync 5.0 - 1000.0 VertRefresh 5.0 - 200.0 - #This can be used to get a specific DPI, but only for the default resolution: - #DisplaySize 508 317 - #NOTE: the highest modes will not work without increasing the VideoRam - # for the dummy video card. - Modeline "32768x32768" 15226.50 32768 35800 39488 46208 32768 32771 32781 32953 - Modeline "32768x16384" 7516.25 32768 35544 39192 45616 16384 16387 16397 16478 - Modeline "16384x8192" 2101.93 16384 16416 24400 24432 8192 8390 8403 8602 - Modeline "8192x4096" 424.46 8192 8224 9832 9864 4096 4195 4202 4301 - Modeline "5496x1200" 199.13 5496 5528 6280 6312 1200 1228 1233 1261 - Modeline "5280x1080" 169.96 5280 5312 5952 5984 1080 1105 1110 1135 - Modeline "5280x1200" 191.40 5280 5312 6032 6064 1200 1228 1233 1261 - Modeline "5120x3200" 199.75 5120 5152 5904 5936 3200 3277 3283 3361 - Modeline "4800x1200" 64.42 4800 4832 5072 5104 1200 1229 1231 1261 - Modeline "3840x2880" 133.43 3840 3872 4376 4408 2880 2950 2955 3025 - Modeline "3840x2560" 116.93 3840 3872 4312 4344 2560 2622 2627 2689 - Modeline "3840x2048" 91.45 3840 3872 4216 4248 2048 2097 2101 2151 - Modeline "3840x1080" 100.38 3840 3848 4216 4592 1080 1081 1084 1093 - Modeline "3600x1200" 106.06 3600 3632 3984 4368 1200 1201 1204 1214 - Modeline "3288x1080" 39.76 3288 3320 3464 3496 1080 1106 1108 1135 - Modeline "2048x2048" 49.47 2048 2080 2264 2296 2048 2097 2101 2151 - Modeline "2048x1536" 80.06 2048 2104 2312 2576 1536 1537 1540 1554 - Modeline "2560x1600" 47.12 2560 2592 2768 2800 1600 1639 1642 1681 - Modeline "2560x1440" 42.12 2560 2592 2752 2784 1440 1475 1478 1513 - Modeline "1920x1440" 69.47 1920 1960 2152 2384 1440 1441 1444 1457 - Modeline "1920x1200" 26.28 1920 1952 2048 2080 1200 1229 1231 1261 - Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135 - Modeline "1680x1050" 20.08 1680 1712 1784 1816 1050 1075 1077 1103 - Modeline "1600x1200" 22.04 1600 1632 1712 1744 1200 1229 1231 1261 - Modeline "1600x900" 33.92 1600 1632 1760 1792 900 921 924 946 - Modeline "1440x900" 30.66 1440 1472 1584 1616 900 921 924 946 - ModeLine "1366x768" 72.00 1366 1414 1446 1494 768 771 777 803 - Modeline "1280x1024" 31.50 1280 1312 1424 1456 1024 1048 1052 1076 - Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841 - Modeline "1280x768" 23.11 1280 1312 1392 1424 768 786 789 807 - Modeline "1360x768" 24.49 1360 1392 1480 1512 768 786 789 807 - Modeline "1024x768" 18.71 1024 1056 1120 1152 768 786 789 807 - Modeline "768x1024" 19.50 768 800 872 904 1024 1048 1052 1076 - - - #common resolutions for android devices (both orientations): - Modeline "800x1280" 25.89 800 832 928 960 1280 1310 1315 1345 - Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841 - Modeline "720x1280" 30.22 720 752 864 896 1280 1309 1315 1345 - Modeline "1280x720" 27.41 1280 1312 1416 1448 720 737 740 757 - Modeline "768x1024" 24.93 768 800 888 920 1024 1047 1052 1076 - Modeline "1024x768" 23.77 1024 1056 1144 1176 768 785 789 807 - Modeline "600x1024" 19.90 600 632 704 736 1024 1047 1052 1076 - Modeline "1024x600" 18.26 1024 1056 1120 1152 600 614 617 631 - Modeline "536x960" 16.74 536 568 624 656 960 982 986 1009 - Modeline "960x536" 15.23 960 992 1048 1080 536 548 551 563 - Modeline "600x800" 15.17 600 632 688 720 800 818 822 841 - Modeline "800x600" 14.50 800 832 880 912 600 614 617 631 - Modeline "480x854" 13.34 480 512 560 592 854 873 877 897 - Modeline "848x480" 12.09 848 880 920 952 480 491 493 505 - Modeline "480x800" 12.43 480 512 552 584 800 818 822 841 - Modeline "800x480" 11.46 800 832 872 904 480 491 493 505 - #resolutions for android devices (both orientations) - #minus the status bar - #38px status bar (and width rounded up) - Modeline "800x1242" 25.03 800 832 920 952 1242 1271 1275 1305 - Modeline "1280x762" 22.93 1280 1312 1392 1424 762 780 783 801 - Modeline "720x1242" 29.20 720 752 856 888 1242 1271 1276 1305 - Modeline "1280x682" 25.85 1280 1312 1408 1440 682 698 701 717 - Modeline "768x986" 23.90 768 800 888 920 986 1009 1013 1036 - Modeline "1024x730" 22.50 1024 1056 1136 1168 730 747 750 767 - Modeline "600x986" 19.07 600 632 704 736 986 1009 1013 1036 - Modeline "1024x562" 17.03 1024 1056 1120 1152 562 575 578 591 - Modeline "536x922" 16.01 536 568 624 656 922 943 947 969 - Modeline "960x498" 14.09 960 992 1040 1072 498 509 511 523 - Modeline "600x762" 14.39 600 632 680 712 762 779 783 801 - Modeline "800x562" 13.52 800 832 880 912 562 575 578 591 - Modeline "480x810" 12.59 480 512 552 584 810 828 832 851 - Modeline "848x442" 11.09 848 880 920 952 442 452 454 465 - Modeline "480x762" 11.79 480 512 552 584 762 779 783 801 + ModeLine "1920x1080" 148.50 1920 2448 2492 2640 1080 1084 1089 1125 +Hsync +Vsync EndSection + Section "Screen" - Identifier "dummy_screen" - Device "dummy_videocard" - Monitor "dummy_monitor" + Identifier "Default Screen" + Monitor "Configured Monitor" + Device "Configured Video Device" DefaultDepth 24 SubSection "Display" - Viewport 0 0 - Depth 24 - #Modes "32768x32768" "32768x16384" "16384x8192" "8192x4096" "5120x3200" "3840x2880" "3840x2560" "3840x2048" "2048x2048" "2560x1600" "1920x1440" "1920x1200" "1920x1080" "1600x1200" "1680x1050" "1600x900" "1400x1050" "1440x900" "1280x1024" "1366x768" "1280x800" "1024x768" "1024x600" "800x600" "320x200" - Modes "5120x3200" "3840x2880" "3840x2560" "3840x2048" "2048x2048" "2560x1600" "1920x1440" "1920x1200" "1920x1080" "1600x1200" "1680x1050" "1600x900" "1400x1050" "1440x900" "1280x1024" "1366x768" "1280x800" "1024x768" "1024x600" "800x600" "320x200" - #Virtual 1280 720 - Virtual 1920 1080 + Depth 24 + Modes "1920x1080" EndSubSection EndSection -Section "ServerLayout" - Identifier "dummy_layout" - Screen "dummy_screen" - InputDevice "dummy_mouse" - InputDevice "dummy_keyboard" -EndSection diff --git a/roles/prosody/defaults/main.yml b/roles/prosody/defaults/main.yml index 88c406a..6dd6af8 100644 --- a/roles/prosody/defaults/main.yml +++ b/roles/prosody/defaults/main.yml @@ -35,6 +35,8 @@ prosody_base_modules: url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_client_proxy.lua - name: mod_roster_command url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_roster_command.lua + - name: mod_muc_lobby_rooms + url: https://raw.githubusercontent.com/jitsi/jitsi-meet/master/resources/prosody-plugins/mod_muc_lobby_rooms.lua prosody_extra_modules: [] prosody_modules: "{{ (prosody_base_modules + prosody_extra_modules) | unique }}" diff --git a/roles/prosody/tasks/install.yml b/roles/prosody/tasks/install.yml index 4d57027..c1002d6 100644 --- a/roles/prosody/tasks/install.yml +++ b/roles/prosody/tasks/install.yml @@ -20,6 +20,13 @@ notify: restart prosody tags: prosody +- name: Install Participan Metadata module + copy: + src: mod_participant_metadata.lua + dest: /opt/prosody/modules/ + notify: restart prosody + tags: prosody + - name: Remove useless unit override file: path=/etc/systemd/system/prosody.service.d/99-ansible.conf state=absent register: prosody_unit