forked from switnet/quick-jibri-installer
6.1.0
Fixed, - JWT authentication New, - Start Over script, to purge, remove and start over a jitsi installation - Jibri resolution enhancer A script to allegedly increase the quality resolution of the recordings, results may vary under hardware and connection quality, so no warranties at all.
This commit is contained in:
commit
7a75f3fdff
|
@ -1,5 +1,5 @@
|
|||
# Quick Jibri Installer
|
||||
Bash installer for Jibri on supported **Ubuntu LTS** based systems using **nginx** as default webserver.
|
||||
Bash installer for Jitsi Meet standalone along with Jibri on supported **Ubuntu LTS** based systems using **nginx** as default webserver.
|
||||
|
||||
## Usage
|
||||
As for our current latest release, as we have integrated more and more features, we highly recommend to use a purpose specific-newly spawn server to host the jitsi-meet framework, making sure you stick to the requirements and recommendations as much as possible, in order to avoid issues.
|
||||
|
@ -13,6 +13,9 @@ git clone https://github.com/switnet-ltd/quick-jibri-installer
|
|||
cd quick-jibri-installer
|
||||
sudo bash quick_jibri_installer.sh
|
||||
```
|
||||
If your server meet the necessary resources, then at the end on the installer you should have a working Jitsi Meet Server along with a Jibri server ready to record.
|
||||
|
||||
Additional jibris need to be set on separate servers, only necesary on simultaneous recordings for that please use add-jibri-node.sh.
|
||||
|
||||
### Add Jibri node
|
||||
|
||||
|
@ -24,6 +27,8 @@ Copy the modified `add-jibri-node.sh` file from your early cloned installation d
|
|||
bash add-jibri-node.sh
|
||||
```
|
||||
|
||||
Please remember that on newer versions, jibri will record on FHD (1920x1080) so please make sure your server have enough CPU power in orther to handle the encoding load.
|
||||
|
||||
### Add JVB2 node
|
||||
|
||||
Copy the modified `add-jvb2-node.sh` file from your early cloned installation directory once the installation is completed, to the new server meant to be a jibri node using your preferred method, then run it
|
||||
|
@ -40,7 +45,7 @@ Check more details on our wiki.
|
|||
* Clean VM/VPS/Server using a supported Ubuntu LTS
|
||||
* Valid domain with DNS record, **mandatory** for SSL certs via Let's Encrypt.
|
||||
* Ports open for ACME (SSL) interaction & validation.
|
||||
* Highly recommended: 8 GB RAM / 4 Cores.
|
||||
* Highly recommended: Above 8 GB RAM / 4 Cores.
|
||||
* Webcam
|
||||
|
||||
### Jigasi Transcript
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Jibri Node Aggregator
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
|
||||
### 0_LAST EDITION TIME STAMP ###
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# JVB2 Node Aggregator
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
|
||||
### 0_LAST EDITION TIME STAMP ###
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Etherpad Installer for Jitsi Meet
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
#
|
||||
# GPLv3 or later.
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
# - https://grafana.com/grafana/dashboards/11969
|
||||
# by "mephisto"
|
||||
#
|
||||
# Igor Kerstges © - 2020
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# Igor Kerstges © - 2021
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
#
|
||||
# GPLv3 or later.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
# Jitsi Meet recurring upgrader and customization keeper
|
||||
# for Debian/*buntu binaries.
|
||||
# 2020 - SwITNet Ltd
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GNU GPLv3 or later.
|
||||
|
||||
Blue='\e[0;34m'
|
||||
|
|
2
jm-bm.sh
2
jm-bm.sh
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
# Jitsi Meet brandless mode
|
||||
# for Debian/*buntu binaries.
|
||||
# 2020 - SwITNet Ltd
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GNU GPLv3 or later.
|
||||
|
||||
DOMAIN="$(ls /etc/prosody/conf.d/ | awk -F'.cfg' '!/localhost/{print $1}' | awk '!NF || !seen[$0]++')"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# JRA (Jibri Recordings Access) via Nextcloud
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
while getopts m: option
|
||||
do
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Custom High Performance Jitsi conf
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
|
||||
#Check if user is root
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
# Custom Selenium Grid-Node fro Jitsi Meet
|
||||
# Pandian © - https://community.jitsi.org/u/Pandian
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
|
||||
#Check if user is root
|
||||
|
|
21
mode/jwt.sh
21
mode/jwt.sh
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# JWT Mode Setup
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
DOMAIN=$(ls /etc/prosody/conf.d/ | grep -v localhost | awk -F'.cfg' '{print $1}' | awk '!NF || !seen[$0]++')
|
||||
MEET_CONF="/etc/jitsi/meet/$DOMAIN-config.js"
|
||||
|
@ -42,9 +42,14 @@ sed -i "s|c2s_require_encryption = true|c2s_require_encryption = false|" $PROSOD
|
|||
sed -i "$SRP_STR,$SRP_END{s|authentication = \"anonymous\"|authentication = \"token\"|}" $PROSODY_FILE
|
||||
sed -i "s|--app_id=\"example_app_id\"|app_id=\"$APP_ID\"|" $PROSODY_FILE
|
||||
sed -i "s|--app_secret=\"example_app_secret\"|app_secret=\"$SECRET_APP\"|" $PROSODY_FILE
|
||||
sed -i "/app_secret/a \ \ \ \ \ \ \ \ asap_accepted_issuers = { \"$APP_ID\" }" $PROSODY_FILE
|
||||
sed -i "/app_secret/a \ \ \ \ \ \ \ \ asap_accepted_audiences = { \"$APP_ID\", \"RocketChat\" }" $PROSODY_FILE
|
||||
#allow_empty_token = false
|
||||
sed -i "/app_secret/a \\\\" $PROSODY_FILE
|
||||
sed -i "/app_secret/a \ \ \ \ allow_empty_token = false" $PROSODY_FILE
|
||||
sed -i "/app_secret/a \\\\" $PROSODY_FILE
|
||||
sed -i "/app_secret/a \ \ \ \ asap_accepted_issuers = { \"$APP_ID\" }" $PROSODY_FILE
|
||||
sed -i "/app_secret/a \ \ \ \ asap_accepted_audiences = { \"$APP_ID\", \"RocketChat\" }" $PROSODY_FILE
|
||||
sed -i "/app_secret/a \\\\" $PROSODY_FILE
|
||||
sed -i "s|--allow_empty_token =.*|allow_empty_token = false|" $PROSODY_FILE
|
||||
sed -i 's|--"token_verification"|"token_verification"|' $PROSODY_FILE
|
||||
|
||||
#Request auth
|
||||
sed -i "s|#org.jitsi.jicofo.auth.URL=EXT_JWT:|org.jitsi.jicofo.auth.URL=EXT_JWT:|" $JICOFO_SIP
|
||||
|
@ -57,7 +62,7 @@ VirtualHost "recorder.$DOMAIN"
|
|||
modules_enabled = {
|
||||
"ping";
|
||||
}
|
||||
authentication = "internal_plain"
|
||||
authentication = "internal_hashed"
|
||||
|
||||
REC-JIBRI
|
||||
|
||||
|
@ -68,9 +73,7 @@ VirtualHost "guest.$DOMAIN"
|
|||
authentication = "token"
|
||||
allow_empty_token = true
|
||||
c2s_require_encryption = false
|
||||
-- muc_lobby_whitelist = { "recorder.$DOMAIN", "auth.$DOMAIN" }
|
||||
speakerstats_component = "speakerstats.$DOMAIN"
|
||||
-- conference_duration_component = "conferenceduration.$DOMAIN"
|
||||
app_id="$APP_ID";
|
||||
app_secret="$SECRET_APP";
|
||||
|
||||
|
@ -81,8 +84,8 @@ VirtualHost "guest.$DOMAIN"
|
|||
P_SR
|
||||
|
||||
echo -e "\nUse the following for your App (e.g. Rocket.Chat):\n"
|
||||
echo -e "\n$APP_ID" && \
|
||||
echo -e "$SECRET_APP\n"
|
||||
echo -e "\nAPP_ID: $APP_ID" && \
|
||||
echo -e "SECRET_APP: $SECRET_APP\n"
|
||||
|
||||
echo -e "You can test JWT authentication with the following token:\n"
|
||||
pyjwt3 --key="$SECRET_APP" \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Quick Jibri Installer - *buntu (LTS) based systems.
|
||||
# SwITNet Ltd © - 2020, https://switnet.net/
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
{
|
||||
echo "Started at $(date +'%Y-%m-%d %H:%M:%S')" >> qj-installer.log
|
||||
|
@ -42,12 +42,16 @@ fi
|
|||
}
|
||||
exit_ifinstalled jitsi-meet
|
||||
|
||||
if [ $DIST = flidas ]; then
|
||||
DIST="xenial"
|
||||
fi
|
||||
if [ $DIST = etiona ]; then
|
||||
DIST="bionic"
|
||||
rename_distro() {
|
||||
if [ "$DIST" = "$1" ]; then
|
||||
DIST="$2"
|
||||
fi
|
||||
}
|
||||
#Trisquel distro renaming
|
||||
rename_distro flidas xenial
|
||||
rename_distro etiona bionic
|
||||
rename_distro nabia focal
|
||||
|
||||
install_ifnot() {
|
||||
if [ "$(dpkg-query -W -f='${Status}' $1 2>/dev/null | grep -c "ok installed")" == "1" ]; then
|
||||
echo " $1 is installed, skipping..."
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Simple Fail2ban configuration
|
||||
# 2020 - SwITNet Ltd
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GNU GPLv3 or later.
|
||||
|
||||
while getopts m: option
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Simple Jibri conf updater
|
||||
# 2020 - SwITNet Ltd
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GNU GPLv3 or later.
|
||||
|
||||
while getopts m: option
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
#!/bin/bash
|
||||
# Simple Jibri resolution enhancer
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GNU GPLv3 or later.
|
||||
|
||||
while getopts m: option
|
||||
do
|
||||
case "${option}"
|
||||
in
|
||||
m) MODE=${OPTARG};;
|
||||
\?) echo "Usage: sudo ./jibri-resolution-enhancer.sh [-m debug]" && exit;;
|
||||
esac
|
||||
done
|
||||
|
||||
#DEBUG
|
||||
if [ "$MODE" = "debug" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
#Check if user is root
|
||||
if ! [ $(id -u) = 0 ]; then
|
||||
echo "You need to be root or have sudo privileges!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Make sure jibri is installed
|
||||
if [ "$(dpkg-query -W -f='${Status}' jibri 2>/dev/null | grep -c "ok installed")" == "1" ]; then
|
||||
echo "Good Jibri is installed on this server"
|
||||
else
|
||||
echo "Jibri is not on this system, it is a requirement.
|
||||
Exiting..."
|
||||
exit
|
||||
fi
|
||||
|
||||
apt-get -y install apt-show-versions
|
||||
|
||||
JIBRI_OPT="/opt/jitsi/jibri"
|
||||
JIBRI_ENH_PATH="/opt/jibri-res-enhancer"
|
||||
INSTALLED_JIBRI_VERSION="$(apt-show-versions jibri|awk '{print$2}')"
|
||||
|
||||
#Check if already run
|
||||
if [ -f "$JIBRI_OPT/jibri-res_enh.jar" ] && \
|
||||
[ -d "$JIBRI_ENH_PATH" ]; then
|
||||
echo "Seems this tools have been run before..."
|
||||
exit
|
||||
fi
|
||||
|
||||
mkdir /tmp/jibri
|
||||
cd /tmp/jibri
|
||||
|
||||
#Get md5sum for current jibri installed.
|
||||
apt-get download jibri=$INSTALLED_JIBRI_VERSION
|
||||
ar x jibri_*.deb
|
||||
tar xvf data.tar.xz
|
||||
UPSTREAM_DEB_JAR_SUM="$(md5sum 2>/dev/null /tmp/jibri/opt/jitsi/jibri/jibri.jar |awk '{print$1}')"
|
||||
|
||||
if [ -z $UPSTREAM_DEB_JAR_SUM ]; then
|
||||
echo "Not possible to continue, exiting..."
|
||||
exit
|
||||
fi
|
||||
|
||||
#Compile requisites
|
||||
apt-get -y install devscripts \
|
||||
git \
|
||||
maven \
|
||||
openjdk-8-jdk
|
||||
|
||||
#Build repository
|
||||
git clone https://github.com/jitsi/jibri $JIBRI_ENH_PATH
|
||||
cd $JIBRI_ENH_PATH
|
||||
|
||||
# Default values
|
||||
## videoEncodePreset - "veryfast" || h264ConstantRateFactor - 25
|
||||
# Recomemended values based on: https://trac.ffmpeg.org/wiki/Encode/H.264#crf
|
||||
## videoEncodePreset - "medium" || h264ConstantRateFactor - 15
|
||||
sed -i "/videoEncodePreset/s|String =.*|String = \"medium\",|" src/main/kotlin/org/jitsi/jibri/capture/ffmpeg/FfmpegCapturer.kt
|
||||
sed -i "/h264ConstantRateFactor/s|Int =.*|Int = 15,|" src/main/kotlin/org/jitsi/jibri/capture/ffmpeg/FfmpegCapturer.kt
|
||||
mvn package
|
||||
|
||||
JIBRI_JAR="$(ls -Sh $JIBRI_ENH_PATH/target|awk '/dependencies/&&/.jar/{print}'|awk 'NR==1{print}')"
|
||||
cp $JIBRI_ENH_PATH/target/$JIBRI_JAR $JIBRI_ENH_PATH/target/jibri.jar
|
||||
|
||||
# Backing up default binaries
|
||||
if [ "$UPSTREAM_DEB_JAR_SUM" = "$(md5sum 2>/dev/null $JIBRI_OPT/jibri.jar|awk '{print$1}')" ]; then
|
||||
cp $JIBRI_OPT/jibri.jar $JIBRI_OPT/jibri-dpkg-package.jar
|
||||
fi
|
||||
|
||||
# Migrate original to enhanced jibri
|
||||
cp $JIBRI_ENH_PATH/target/jibri.jar $JIBRI_OPT/jibri-res_enh.jar
|
||||
if [ -f $JIBRI_OPT/jibri-dpkg-package.jar ];then
|
||||
cp $JIBRI_OPT/jibri-res_enh.jar $JIBRI_OPT/jibri.jar
|
||||
fi
|
||||
|
||||
JIBRI_RES_ENH_HASH="$(md5sum 2>/dev/null $JIBRI_OPT/jibri-res_enh.jar|awk '{print$1}')"
|
||||
USED_JIBRI_HASH="$(md5sum 2>/dev/null $JIBRI_OPT/jibri.jar|awk '{print$1}')"
|
||||
|
||||
if [ "$JIBRI_RES_ENH_HASH" = "$USED_JIBRI_HASH" ]; then
|
||||
echo "Everything seems to have gone well."
|
||||
else
|
||||
echo "Something went wrong, restoring default package..."
|
||||
if [ "$(md5sum 2>/dev/null $JIBRI_OPT/jibri-dpkg-package.jar|awk '{print$1}')" = "$UPSTREAM_DEB_JAR_SUM" ]; then
|
||||
cp $JIBRI_OPT/jibri-dpkg-package.jar $JIBRI_OPT/jibri.jar
|
||||
CLEAN="true"
|
||||
else
|
||||
if [ -f /tmp/jibri/opt/jitsi/jibri/jibri.jar ]; then
|
||||
echo "Restoring from upstream package..."
|
||||
cp /tmp/jibri/opt/jitsi/jibri/jibri.jar $JIBRI_OPT/jibri.jar
|
||||
CLEAN="true"
|
||||
else
|
||||
echo "Wow, someone took the time to avoid restauration, please manually review your changes."
|
||||
echo "Exiting..."
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ "$CLEAN" = "true" ]; then
|
||||
rm -r /tmp/jibri
|
||||
rm -r $JIBRI_ENH_PATH
|
||||
rm /opt/jitsi/jibri/jibri-res_enh.jar
|
||||
fi
|
||||
|
||||
systemctl restart jibri
|
||||
echo "This will be a good time to test the enhanced resolution."
|
|
@ -0,0 +1,128 @@
|
|||
#!/bin/bash
|
||||
#Start over
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GPLv3 or later.
|
||||
|
||||
while getopts m: option
|
||||
do
|
||||
case "${option}"
|
||||
in
|
||||
m) MODE=${OPTARG};;
|
||||
\?) echo "Usage: sudo ./start-over.sh [-m debug]" && exit;;
|
||||
esac
|
||||
done
|
||||
|
||||
#DEBUG
|
||||
if [ "$MODE" = "debug" ]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
#Check if user is root
|
||||
if ! [ $(id -u) = 0 ]; then
|
||||
echo "You need to be root or have sudo privileges!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
wait_seconds() {
|
||||
secs=$(($1))
|
||||
while [ $secs -gt 0 ]; do
|
||||
echo -ne "$secs\033[0K\r"
|
||||
sleep 1
|
||||
: $((secs--))
|
||||
done
|
||||
}
|
||||
remove_residuals() {
|
||||
if [ -d $1 ]; then
|
||||
rm -r $1
|
||||
fi
|
||||
}
|
||||
purge_debconf() {
|
||||
echo PURGE | debconf-communicate $1
|
||||
}
|
||||
remove_services() {
|
||||
systemctl disable $1
|
||||
systemctl stop $1
|
||||
}
|
||||
echo -e '
|
||||
########################################################################
|
||||
Welcome to the Start Over cleaner script
|
||||
########################################################################
|
||||
by Software, IT & Networks Ltd
|
||||
\n'
|
||||
|
||||
SYNC_USER="$(ls /home|awk '/jbsync/{print}')"
|
||||
|
||||
echo "We are about to remove and clean all the jitsi-meet plaform bits and pieces...
|
||||
Please make sure you have backed up anything you don't want to loose."
|
||||
|
||||
echo "
|
||||
# WARGNING #: This is only recommended if you want to start over a failed installation,
|
||||
or plain and simple remove jitsi from your system."
|
||||
|
||||
while [[ "$CONTINUE_PURGE1" != "yes" && "$CONTINUE_PURGE1" != "no" ]]
|
||||
do
|
||||
read -p "> Do you want to continue?: (yes or no)"$'\n' -r CONTINUE_PURGE1
|
||||
if [ "$CONTINUE_PURGE1" = "no" ]; then
|
||||
echo " Good, see you next time..."
|
||||
exit
|
||||
elif [ "$CONTINUE_PURGE1" = "yes" ]; then
|
||||
echo ""
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Let me ask just one more time..."
|
||||
while [[ "$CONTINUE_PURGE2" != "yes" && "$CONTINUE_PURGE2" != "no" ]]
|
||||
do
|
||||
read -p "> Do you want to continue?: (yes or no)"$'\n' -r CONTINUE_PURGE2
|
||||
if [ "$CONTINUE_PURGE2" = "no" ]; then
|
||||
echo " Good, see you next time..."
|
||||
exit
|
||||
elif [ "$CONTINUE_PURGE2" = "yes" ]; then
|
||||
echo "No going back, lets start..."
|
||||
wait_seconds 5
|
||||
fi
|
||||
done
|
||||
|
||||
#Purging all jitsi meet packages
|
||||
apt-get -y purge jibri \
|
||||
jicofo \
|
||||
jigasi \
|
||||
jitsi-meet \
|
||||
jitsi-meet-web \
|
||||
jitsi-meet-web-config \
|
||||
jitsi-meet-prosody \
|
||||
jitsi-meet-turnserver \
|
||||
jitsi-videobridge2 \
|
||||
prosody
|
||||
|
||||
#Services stop
|
||||
remove_services jibri*
|
||||
|
||||
#Cleaning packages
|
||||
apt-get -y autoremove
|
||||
apt-get clean
|
||||
|
||||
#Removing residual files
|
||||
remove_residuals /etc/jitsi
|
||||
remove_residuals /opt/jitsi
|
||||
remove_residuals /usr/share/jicofo
|
||||
remove_residuals /usr/share/jitsi-*
|
||||
|
||||
#Purging debconf db
|
||||
purge_debconf jicofo
|
||||
purge_debconf jigasi
|
||||
purge_debconf jitsi-meet
|
||||
purge_debconf jitsi-meet-prosody
|
||||
purge_debconf jitsi-meet-turnserver
|
||||
purge_debconf jitsi-meet-web-config
|
||||
purge_debconf jitsi-videobridge2
|
||||
|
||||
#Remove unused users
|
||||
if [ ! -z $SYNC_USER ]; then
|
||||
deluser --remove-home $SYNC_USER
|
||||
fi
|
||||
if [ -d /home/jibri ]; then
|
||||
deluser --remove-home jibri
|
||||
fi
|
||||
|
||||
echo "We are done..."
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Simple Jibri Env tester
|
||||
# 2020 - SwITNet Ltd
|
||||
# SwITNet Ltd © - 2021, https://switnet.net/
|
||||
# GNU GPLv3 or later.
|
||||
|
||||
while getopts m: option
|
||||
|
|
Loading…
Reference in New Issue