From b3f30f1575834c1ccb8ae979d78ac5a01b738ca7 Mon Sep 17 00:00:00 2001 From: Daniel Neto Date: Tue, 21 Feb 2023 12:21:14 -0300 Subject: [PATCH] Update --- .gitignore | 2 + Dockerfile | 110 +++++++++--------- deploy/apache/avideo.conf | 33 ++++-- deploy/apache/localhost.conf | 33 ++++++ deploy/crontab | 4 + deploy/docker-entrypoint | 91 ++++++++++++--- deploy/nginx/nginx.conf | 142 ++++++++++++++++++++++++ deploy/wait-for-db.php | 2 +- docker-compose.yml | 105 ++++++++++++------ env.example | 18 ++- install/cli.php | 28 ++++- nohup.out | 29 +++++ objects/playlist.php | 12 +- plugin/AVideoPlugin.php | 2 +- plugin/Gallery/view/mainAreaTags.php | 2 +- plugin/PlayLists/PlayLists.php | 2 - plugin/Scheduler/Scheduler.php | 9 +- plugin/Scheduler/install/install.sql | 1 + plugin/Scheduler/install/updateV4.2.sql | 2 + plugin/VideoTags/VideoTags.php | 21 +++- plugin/VideoTags/pluginMenu.html | 4 +- plugin/YPTSocket/server.php | 4 +- view/img/audio_wave_jpg.webp | Bin 0 -> 41284 bytes view/managerVideos_form.php | 1 + view/offline.php | 2 +- 25 files changed, 522 insertions(+), 137 deletions(-) create mode 100644 deploy/apache/localhost.conf create mode 100644 deploy/crontab create mode 100644 deploy/nginx/nginx.conf create mode 100644 nohup.out create mode 100644 plugin/Scheduler/install/updateV4.2.sql create mode 100644 view/img/audio_wave_jpg.webp diff --git a/.gitignore b/.gitignore index e91c62c4ac..fd99267b49 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,5 @@ view/videoComments_bkp.php /.compose/ test.php /plugin/JosephZ/ +/Encoder/ +/.env diff --git a/Dockerfile b/Dockerfile index 6a5edee48f..0fe3b6e8aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,94 +10,94 @@ LABEL maintainer="TRW " \ ARG DEBIAN_FRONTEND=noninteractive -ENV DB_MYSQL_HOST database -ENV DB_MYSQL_PORT 3306 -ENV DB_MYSQL_NAME avideo -ENV DB_MYSQL_USER avideo -ENV DB_MYSQL_PASSWORD avideo - -ENV SERVER_NAME localhost -ENV ENABLE_PHPMYADMIN yes -ENV CREATE_TLS_CERTIFICATE yes -ENV TLS_CERTIFICATE_FILE /etc/apache2/ssl/localhost.crt -ENV TLS_CERTIFICATE_KEY /etc/apache2/ssl/localhost.key -ENV CONTACT_EMAIL admin@localhost -ENV SYSTEM_ADMIN_PASSWORD password -ENV WEBSITE_TITLE AVideo -ENV MAIN_LANGUAGE en_US - -ENV HTTPS_PORT $HTTPS_PORT +ARG SOCKET_PORT +ARG HTTP_PORT +ARG HTTPS_PORT +ARG NGINX_RTMP_PORT +ARG NGINX_HTTP_PORT +ARG NGINX_HTTPS_PORT +ARG DB_MYSQL_HOST +ARG DB_MYSQL_PORT +ARG DB_MYSQL_NAME +ARG DB_MYSQL_USER +ARG DB_MYSQL_PASSWORD +ARG SERVER_NAME +ARG CREATE_TLS_CERTIFICATE +ARG TLS_CERTIFICATE_FILE +ARG TLS_CERTIFICATE_KEY +ARG CONTACT_EMAIL +ARG SYSTEM_ADMIN_PASSWORD +ARG WEBSITE_TITLE +ARG MAIN_LANGUAGE # Retrieve package list RUN apt update # Install dependencies RUN apt-get update -y && apt-get upgrade -y \ - && apt install -y --no-install-recommends ca-certificates apt-transport-https software-properties-common curl \ + && apt install -y --no-install-recommends dos2unix bash-completion lsof cron rsync ca-certificates apt-transport-https software-properties-common curl \ && curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp \ && chmod a+rx /usr/local/bin/yt-dlp \ - && apt install -y --no-install-recommends sshpass nano net-tools curl apache2 php8.1 libapache2-mod-php8.1 php8.1-mysql php8.1-curl php8.1-gd php8.1-intl \ - php-zip mysql-client ffmpeg git libimage-exiftool-perl libapache2-mod-xsendfile -y && a2enmod xsendfile && cd /var/www/html \ - && git clone https://github.com/WWBN/AVideo.git \ + && apt install -y --no-install-recommends sshpass nano net-tools curl apache2 php8.1 libapache2-mod-php8.1 php8.1-mysql php8.1-sqlite3 php8.1-curl php8.1-gd php8.1-intl \ + php-zip mysql-client ffmpeg git libimage-exiftool-perl libapache2-mod-xsendfile python3-certbot-apache -y && a2enmod xsendfile && cd /var/www/html \ && apt install -y --no-install-recommends && curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl \ && chmod a+rx /usr/local/bin/youtube-dl && apt install -y --no-install-recommends build-essential libpcre3 libpcre3-dev libssl-dev php8.1-xml -y \ - && a2enmod rewrite && chown www-data:www-data /var/www/html/AVideo/plugin && chmod 755 /var/www/html/AVideo/plugin \ + && a2enmod rewrite \ && apt install -y --no-install-recommends unzip -y && apt install -y --no-install-recommends htop python3-pip \ && pip3 install youtube-dl && pip3 install --upgrade youtube-dl && a2enmod expires \ && a2enmod headers COPY deploy/apache/avideo.conf /etc/apache2/sites-enabled/000-default.conf -COPY deploy/apache/phpmyadmin.conf /etc/apache2/conf-available/phpmyadmin.conf COPY deploy/docker-entrypoint /usr/local/bin/docker-entrypoint COPY deploy/wait-for-db.php /usr/local/bin/wait-for-db.php -COPY admin /var/www/html/AVideo/admin -COPY feed /var/www/html/AVideo/feed -COPY install /var/www/html/AVideo/install -COPY locale /var/www/html/AVideo/locale -COPY node_modules /var/www/html/AVideo/node_modules -COPY objects /var/www/html/AVideo/objects -COPY plugin /var/www/html/AVideo/plugin -COPY storage /var/www/html/AVideo/storage -COPY updatedb /var/www/html/AVideo/updatedb -COPY vendor /var/www/html/AVideo/vendor -COPY view /var/www/html/AVideo/view -COPY _config.yml /var/www/html/AVideo -COPY .htaccess /var/www/html/AVideo -COPY CNAME /var/www/html/AVideo -COPY LICENSE /var/www/html/AVideo -COPY README.md /var/www/html/AVideo -COPY web.config /var/www/html/AVideo -COPY index.php /var/www/html/AVideo -COPY git.json.php /var/www/html/AVideo -COPY sw.js /var/www/html/AVideo/ +# Install nginx +RUN apt-get install build-essential libssl-dev libpcre3 libpcre3-dev wget -y \ + && apt-get install --reinstall zlib1g zlib1g-dev -y \ + && mkdir /var/www/tmp && chmod -R 777 /var/www/tmp \ + && mkdir /HLS && mkdir /HLS/live && chmod -R 777 /HLS + +RUN mkdir ~/build \ + && cd ~/build \ + && git clone https://github.com/arut/nginx-rtmp-module.git \ + && git clone https://github.com/nginx/nginx.git \ + && cd nginx \ + && ./auto/configure --with-http_ssl_module --with-http_stub_status_module --add-module=../nginx-rtmp-module --with-cc-opt="-Wimplicit-fallthrough=0" \ + && make \ + && make install\ + && cd /usr/local/nginx/html && wget https://youphp.tube/docs/stat.xsl + +COPY deploy/nginx/nginx.conf /usr/local/nginx/conf/nginx.conf + +COPY deploy/crontab /etc/cron.d/crontab +RUN chmod 0644 /etc/cron.d/crontab # Configure AVideo -RUN chmod 755 /usr/local/bin/docker-entrypoint && \ +RUN dos2unix /usr/local/bin/docker-entrypoint && \ + chmod 755 /usr/local/bin/docker-entrypoint && \ + chmod +x /usr/local/bin/docker-entrypoint && \ pip3 install youtube-dl && \ - cd /var/www/html/AVideo && \ - git config --global advice.detachedHead false && \ - git clone https://github.com/WWBN/AVideo-Encoder.git Encoder && \ - chown -R www-data:www-data /var/www/html/AVideo && \ - cd /var/www/html/AVideo/plugin/User_Location/install && \ - unzip install.zip && \ sed -i 's/^post_max_size.*$/post_max_size = 10G/' /etc/php/8.1/apache2/php.ini && \ sed -i 's/^upload_max_filesize.*$/upload_max_filesize = 10G/' /etc/php/8.1/apache2/php.ini && \ + sed -i 's/^max_execution_time.*$/max_execution_time = 7200/' /etc/php/8.1/apache2/php.ini && \ + sed -i 's/^memory_limit.*$/memory_limit = 512M/' /etc/php/8.1/apache2/php.ini && \ a2enmod rewrite expires headers ssl xsendfile +# Add Apache configuration +RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf + VOLUME /var/www/tmp RUN mkdir -p /var/www/tmp && \ chown www-data:www-data /var/www/tmp && \ chmod 777 /var/www/tmp -VOLUME /var/www/html/AVideo/videos -RUN mkdir -p /var/www/html/AVideo/videos && \ - chown www-data:www-data /var/www/html/AVideo/videos && \ - chmod 777 /var/www/html/AVideo/videos - WORKDIR /var/www/html/AVideo/ +EXPOSE $SOCKET_PORT +EXPOSE $HTTP_PORT EXPOSE $HTTPS_PORT +EXPOSE $NGINX_RTMP_PORT +EXPOSE $NGINX_HTTPS_PORT ENTRYPOINT ["/usr/local/bin/docker-entrypoint"] CMD ["apache2-foreground"] diff --git a/deploy/apache/avideo.conf b/deploy/apache/avideo.conf index 097f159caa..567b254b53 100644 --- a/deploy/apache/avideo.conf +++ b/deploy/apache/avideo.conf @@ -1,29 +1,40 @@ - - ServerName SERVER_NAME + + ServerName localhost ServerAdmin CONTACT_EMAIL DocumentRoot "/var/www/html/AVideo" - ErrorLog /dev/stderr + CustomLog ${APACHE_LOG_DIR}/access.log combined TransferLog /dev/stdout - - RewriteEngine On - RewriteCond %{HTTPS} off - RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} - - - ServerName SERVER_NAME + + ServerName localhost ServerAdmin CONTACT_EMAIL DocumentRoot "/var/www/html/AVideo" ErrorLog /dev/stderr + CustomLog ${APACHE_LOG_DIR}/access.log combined TransferLog /dev/stdout SSLEngine on SSLCertificateFile TLS_CERTIFICATE_FILE SSLCertificateKeyFile TLS_CERTIFICATE_KEY - + + ServerName SERVER_NAME + ServerAdmin CONTACT_EMAIL + DocumentRoot "/var/www/html/AVideo" + ErrorLog /dev/stderr + CustomLog ${APACHE_LOG_DIR}/access.log combined + TransferLog /dev/stdout + + + ServerName SERVER_NAME + ServerAdmin CONTACT_EMAIL + DocumentRoot "/var/www/html/AVideo" + ErrorLog /dev/stderr + CustomLog ${APACHE_LOG_DIR}/access.log combined + TransferLog /dev/stdout + Options Indexes FollowSymLinks XSendFile on diff --git a/deploy/apache/localhost.conf b/deploy/apache/localhost.conf new file mode 100644 index 0000000000..d66c35ef74 --- /dev/null +++ b/deploy/apache/localhost.conf @@ -0,0 +1,33 @@ + + ServerName SERVER_NAME + ServerAlias localhost + ServerAdmin CONTACT_EMAIL + DocumentRoot "/var/www/html/AVideo" + + ErrorLog /dev/stderr + CustomLog ${APACHE_LOG_DIR}/access.log combined + TransferLog /dev/stdout + + + ServerName SERVER_NAME + ServerAlias localhost + ServerAdmin CONTACT_EMAIL + DocumentRoot "/var/www/html/AVideo" + + ErrorLog /dev/stderr + CustomLog ${APACHE_LOG_DIR}/access.log combined + TransferLog /dev/stdout + + SSLEngine on + SSLCertificateFile TLS_CERTIFICATE_FILE + SSLCertificateKeyFile TLS_CERTIFICATE_KEY + + + Options Indexes FollowSymLinks + XSendFile on + XSendFilePath /var/www/html/AVideo/ + AllowOverride All + Require all granted + Order Allow,Deny + Allow from All + diff --git a/deploy/crontab b/deploy/crontab new file mode 100644 index 0000000000..44d7e235e8 --- /dev/null +++ b/deploy/crontab @@ -0,0 +1,4 @@ +# m h dom mon dow command +1 1 * * * pip3 install --upgrade youtube-dl +* * * * * php /var/www/html/AVideo/plugin/Scheduler/run.php +#2 1 * * * php /var/www/html/AVideo/plugin/CDN/tools/moveMissingFiles.php \ No newline at end of file diff --git a/deploy/docker-entrypoint b/deploy/docker-entrypoint index 30de80e8fa..6e019debf8 100644 --- a/deploy/docker-entrypoint +++ b/deploy/docker-entrypoint @@ -2,6 +2,7 @@ echo "Starting AVideo Platform..." CONFIG_FILE=/etc/apache2/sites-enabled/000-default.conf +CONFIG_NGINX_FILE=/usr/local/nginx/conf/nginx.conf if [ "_${CREATE_TLS_CERTIFICATE}_" == "_yes_" ]; then echo "Generate Certificate..." @@ -42,18 +43,25 @@ if [ "_${CREATE_TLS_CERTIFICATE}_" == "_yes_" ]; then openssl x509 -in ${TLS_CERTIFICATE_FILE} -noout -text || true fi -echo "Configure Apache..." sed -i 's#SERVER_NAME#'${SERVER_NAME}'#' ${CONFIG_FILE} +sed -i 's#CONTACT_EMAIL#'${CONTACT_EMAIL}'#' ${CONFIG_FILE} +sed -i 's#server_name localhost _#server_name localhost _ '${SERVER_NAME}'#' ${CONFIG_NGINX_FILE} + +echo "Configure Apache..." sed -i 's#TLS_CERTIFICATE_FILE#'${TLS_CERTIFICATE_FILE}'#' ${CONFIG_FILE} sed -i 's#TLS_CERTIFICATE_KEY#'${TLS_CERTIFICATE_KEY}'#' ${CONFIG_FILE} -sed -i 's#CONTACT_EMAIL#'${CONTACT_EMAIL}'#' ${CONFIG_FILE} -if [ "_${ENABLE_PHPMYADMIN}_" = "_yes_" ]; then - echo "Enabling local PHPMyAdmin on https://${SERVER_NAME}/phpmyadmin" - cp /etc/apache2/conf-available/phpmyadmin.conf /etc/apache2/conf-enabled/phpmyadmin.conf - a2enmod proxy - a2enmod proxy_http -fi +echo "Configure Nginx..." +sed -i 's#ssl_certificate /etc/apache2/ssl/localhost.crt#ssl_certificate '${TLS_CERTIFICATE_FILE}'#' ${CONFIG_NGINX_FILE} +sed -i 's#ssl_certificate_key /etc/apache2/ssl/localhost.key#ssl_certificate_key '${TLS_CERTIFICATE_KEY}'#' ${CONFIG_NGINX_FILE} +sed -i 's#listen 1935#listen '${NGINX_RTMP_PORT}'#' ${CONFIG_NGINX_FILE} +sed -i 's#listen 8080#listen '${NGINX_HTTP_PORT}'#' ${CONFIG_NGINX_FILE} +sed -i 's#listen 8443#listen '${NGINX_HTTPS_PORT}'#' ${CONFIG_NGINX_FILE} + +mkdir -p /etc/letsencrypt/live/localhost/ + +cp ${TLS_CERTIFICATE_FILE} /etc/letsencrypt/live/localhost/fullchain.pem +cp ${TLS_CERTIFICATE_KEY} /etc/letsencrypt/live/localhost/privkey.pem echo "Waiting for database ${DB_MYSQL_HOST} to be up and running" php /usr/local/bin/wait-for-db.php @@ -62,14 +70,69 @@ if [ $? -ne 0 ]; then exit 1 fi -if [ -f /var/www/html/AVideo/videos/configuration.php ]; then - echo "Using existing configuration..." +if [ -f /var/www/html/AVideo/plugin/User_Location/install/install.sql ]; then + echo "Using existing location tables..." else - echo "Create new configuration..." - cd /var/www/html/AVideo/install/ - php ./cli.php - cd /var/www/html/AVideo/ + echo "Create new locations tables..." + cd /var/www/html/AVideo/plugin/User_Location/install && unzip install.zip + # Configure AVideo Encoder + cd /var/www/html/AVideo && git config --global advice.detachedHead false && git clone https://github.com/WWBN/AVideo-Encoder.git Encoder + # Configure AVideo permissions + chown -R www-data:www-data /var/www/html/AVideo fi +if [ -f /var/www/html/AVideo/Encoder/index.php ]; then + echo "Using existing Encoder configuration..." +else + echo "Create new Encoder configuration..." + # Configure AVideo Encoder + cd /var/www/html/AVideo && git config --global advice.detachedHead false && git clone https://github.com/WWBN/AVideo-Encoder.git Encoder +fi + +if [ -d /var/www/html/AVideo/Encoder/videos ]; then + echo "Using existing Encoder videos..." +else + echo "Create new Encoder videos..." + # Configure AVideo Encoder + mkdir -p /var/www/html/AVideo/Encoder/videos && chown www-data:www-data /var/www/html/AVideo/Encoder/videos && chmod 777 /var/www/html/AVideo/Encoder/videos +fi + +echo "Checking configuration..." +cd /var/www/html/AVideo/install/ +php ./cli.php +cd /var/www/html/AVideo/ + +echo "lets encrypt apache ${SERVER_NAME}" +certbot --apache --non-interactive --agree-tos --register-unsafely-without-email --redirect --keep-until-expiring -d ${SERVER_NAME} + +/etc/init.d/apache2 stop + +if [ -f /usr/local/nginx/sbin/nginx ]; then + /usr/local/nginx/sbin/nginx -s stop + echo "lets encrypt nginx ${SERVER_NAME}" + apt-get install -y --no-install-recommends python3-certbot-nginx + mv /usr/sbin/nginx /usr/sbin/nginx.old && cp /usr/local/nginx/sbin/nginx /usr/sbin/nginx + mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.old && cp /usr/local/nginx/conf/nginx.conf /etc/nginx/nginx.conf + certbot --nginx --non-interactive --agree-tos --nginx-server-root /usr/local/nginx/conf --no-redirect --register-unsafely-without-email --keep-until-expiring -d $SERVER_NAME + sed -i 's/listen 443 ssl/listen 8443 ssl/g' /usr/local/nginx/conf/nginx.conf + echo "nginx start" + /usr/local/nginx/sbin/nginx -s stop && sleep 3 && /usr/local/nginx/sbin/nginx +fi + +nohup php /var/www/html/AVideo/plugin/YPTSocket/server.php & +echo "socket done" + +echo "Reset log" +echo '' > /var/www/html/AVideo/videos/avideo.log +chmod 777 /var/www/html/AVideo/videos/avideo.log + +echo "deny access to .compose" +echo "Deny from all" > /var/www/html/AVideo/.compose/.htaccess + apache2-foreground +echo "apache2-foreground done" + +cd /var/www/html/AVideo/install && php updatedb.php && installPluginsTables.php 4 +cd /var/www/html/AVideo/Encoder/install && php reencodeAllVideos.php + #eof \ No newline at end of file diff --git a/deploy/nginx/nginx.conf b/deploy/nginx/nginx.conf new file mode 100644 index 0000000000..de39b50f52 --- /dev/null +++ b/deploy/nginx/nginx.conf @@ -0,0 +1,142 @@ +user www-data; +worker_processes 1; +error_log logs/error.log debug; +events { + worker_connections 1024; +} +rtmp { + server { + listen 1935; + buflen 1000ms; + chunk_size 4096; + ping_timeout 30s; + ### Use case option (max_streams; default is 32 ) + #max_streams 10; + allow play all; + #creates our "live" full-resolution HLS videostream from our incoming encoder stream and tells where to put the HLS video manifest and video fragments + application live { + allow play all; + live on; + hls on; + hls_nested on; + hls_path /HLS/live; + hls_playlist_length 60m; + hls_fragment 4s; + drop_idle_publisher 30s; + sync 500ms; + #Experimental. Force dropped stream, or ended stream from being watched. (idle_streams) + #idle_streams off; + on_publish http://127.0.0.1/plugin/Live/on_publish.php; + on_publish_done http://127.0.0.1/plugin/Live/on_publish_done.php; + on_play http://127.0.0.1/plugin/Live/on_play.php; + on_record_done http://127.0.0.1/plugin/Live/on_record_done.php; + + #exec ffmpeg -re -i rtmp://localhost/live/$name -c:v libx264 -preset veryfast -c:a copy -f hls -hls_time 5 -hls_list_size 0 -f flv rtmp://localhost/adaptive/$name_hi; + #exec ffmpeg -re -i rtmp://localhost/live/$name + # -c:v libx264 -vf scale=-2:240 -r 20 -g 40 -keyint_min 40 -sc_threshold 0 -bf 3 -b_strategy 2 -b:v 400k -maxrate 700k -bufsize 1400k -c:a aac -strict -2 -b:a 96k -f flv rtmp://localhost/adaptive/$name_low + # -c:v libx264 -vf scale=-2:480 -r 30 -g 60 -keyint_min 48 -sc_threshold 0 -bf 3 -b_strategy 2 -b:v 1200k -maxrate 2100k -bufsize 4200k -c:a aac -strict -2 -b:a 128k -f flv rtmp://localhost/adaptive/$name_mid + # -c:v libx264 -vf scale=-2:720 -r 30 -g 60 -keyint_min 48 -sc_threshold 0 -bf 3 -b_strategy 2 -b:v 2400k -maxrate 3000k -bufsize 6000k -c:a aac -strict -2 -b:a 128k -f flv rtmp://localhost/adaptive/$name_hi; + + recorder video { + record all manual; + record_path /var/www/tmp; + record_notify on; + record_max_size 2048M; + record_suffix -%d-%b-%y-%T.flv; + } + + ### Record Audio Separately ( For podcast ) + #recorder audio { + # record audio; + # record_path /var/www/tmp; + # record_max_size 1024M; + # record_suffix -%d-%b-%y-%T.mp3; + #} + } + + #application adaptive { + # live on; + # hls on; + + # hls_path /HLS/live; + # hls_nested on; + # hls_playlist_length 10m; + # allow play all; + # allow publish 127.0.0.1; + # deny publish all; + # hls_variant _hi BANDWIDTH=264000,RESOLUTION=1280x720; # this is for line 34 only, do not uncomment it + # hls_variant _low BANDWIDTH=900000; + # hls_variant _mid BANDWIDTH=2400000; + # hls_variant _hi BANDWIDTH=3500000; + #} + } +} +http { + include mime.types; + default_type application/octet-stream; + server { + listen 8080; + server_name localhost _; + #creates the http-location for our full-resolution (desktop) HLS stream - "http://my-ip/live/my-stream-key/index.m3u8" + location /live { + expires 60; + add_header 'Cache-Control' 'public'; + location ~ \.m3u8$ { + expires -1; + # Disable cache + add_header 'Cache-Control' 'no-cache'; + # CORS setup + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Expose-Headers' 'Content-Length'; + } + + # CORS setup + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Expose-Headers' 'Content-Length'; + + # allow CORS preflight requests + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + types { + application/vnd.apple.mpegurl m3u8; + } + alias /HLS/live; + } + #allows us to see how stats on viewers on our Nginx site using a URL like: "http://my-ip/stats" + #location /stats { + # stub_status; + #} + location /stat { + rtmp_stat all; + rtmp_stat_stylesheet stat.xsl; + } + location /stat.xsl { + root html; + } + location /control { + # replace this with the IP of your AVideo site + allow 127.0.0.1; + deny all; + rtmp_control all; + } + #allows us to host some webpages which can show our videos: "http://my-ip/my-page.html" + location / { + root html; + index index.html index.htm; + } + + #location ~ \.php$ { + # include /etc/nginx/snippets/fastcgi-php.conf; + # fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; + #} + listen 8443 ssl; + ssl_certificate /etc/apache2/ssl/localhost.crt; + ssl_certificate_key /etc/apache2/ssl/localhost.key; + + } +} diff --git a/deploy/wait-for-db.php b/deploy/wait-for-db.php index 97e2eb8f1d..508b6e5958 100644 --- a/deploy/wait-for-db.php +++ b/deploy/wait-for-db.php @@ -11,7 +11,7 @@ $db_user = getenv("DB_MYSQL_USER"); $db_pass = getenv("DB_MYSQL_PASSWORD"); while (!$connected) { - echo "Checking database connection...."; + echo "Checking database connection.... $db_host, $db_user"; $mysqli = @new mysqli($db_host, $db_user, $db_pass, $db_name, $db_port); if ($mysqli !== false) { echo "OK\n"; diff --git a/docker-compose.yml b/docker-compose.yml index 878fcdb82f..57a02fdb47 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,50 +1,67 @@ version: '3' services: - web: - build: . + avideo: + build: + context: . + args: + SOCKET_PORT: ${SOCKET_PORT} + HTTP_PORT: ${HTTP_PORT} + HTTPS_PORT: ${HTTPS_PORT} + NGINX_RTMP_PORT: ${NGINX_RTMP_PORT} + NGINX_HTTP_PORT: ${NGINX_HTTP_PORT} + NGINX_HTTPS_PORT: ${NGINX_HTTPS_PORT} + DB_MYSQL_HOST: ${DB_MYSQL_HOST} + DB_MYSQL_PORT: ${DB_MYSQL_PORT} + DB_MYSQL_NAME: ${DB_MYSQL_NAME} + DB_MYSQL_USER: ${DB_MYSQL_USER} + DB_MYSQL_PASSWORD: ${DB_MYSQL_PASSWORD} + SERVER_NAME: ${SERVER_NAME} + CREATE_TLS_CERTIFICATE: ${CREATE_TLS_CERTIFICATE} + TLS_CERTIFICATE_FILE: ${TLS_CERTIFICATE_FILE} + TLS_CERTIFICATE_KEY: ${TLS_CERTIFICATE_KEY} + CONTACT_EMAIL: ${CONTACT_EMAIL} + SYSTEM_ADMIN_PASSWORD: ${SYSTEM_ADMIN_PASSWORD} + WEBSITE_TITLE: ${WEBSITE_TITLE} + MAIN_LANGUAGE: ${MAIN_LANGUAGE} restart: "unless-stopped" environment: + SOCKET_PORT: ${SOCKET_PORT:-2053} HTTP_PORT: ${HTTP_PORT:-80} - HTTPS_PORT: ${HTTPS_PORT:-443} - DB_MYSQL_HOST: "database" + HTTPS_PORT: ${HTTP_PORT:-443} + NGINX_RTMP_PORT: ${NGINX_RTMP_PORT:-1935} + NGINX_HTTP_PORT: ${NGINX_HTTP_PORT:-8080} + NGINX_HTTPS_PORT: ${NGINX_HTTPS_PORT:-8443} + DB_MYSQL_HOST: "${DB_MYSQL_HOST:-database}" DB_MYSQL_PORT: ${DB_MYSQL_PORT:-3306} - DB_MYSQL_NAME: "avideo" - DB_MYSQL_USER: "avideo" - DB_MYSQL_PASSWORD: "avideo" - SERVER_NAME: "localhost" - ENABLE_PHPMYADMIN: "yes" - CREATE_TLS_CERTIFICATE: "yes" - TLS_CERTIFICATE_FILE: "/etc/apache2/ssl/localhost.crt" - TLS_CERTIFICATE_KEY: "/etc/apache2/ssl/localhost.key" - CONTACT_EMAIL: "admin@localhost" - SYSTEM_ADMIN_PASSWORD: "password" - WEBSITE_TITLE: "AVideo" - MAIN_LANGUAGE: "en_US" + DB_MYSQL_NAME: "${DB_MYSQL_NAME:-avideo}" + DB_MYSQL_USER: "${DB_MYSQL_USER:-avideo}" + DB_MYSQL_PASSWORD: "${DB_MYSQL_PASSWORD:-avideo}" + SERVER_NAME: "${SERVER_NAME:-localhost}" + CREATE_TLS_CERTIFICATE: "${CREATE_TLS_CERTIFICATE:-yes}" + TLS_CERTIFICATE_FILE: "${TLS_CERTIFICATE_FILE:-/etc/apache2/ssl/localhost.crt}" + TLS_CERTIFICATE_KEY: "${TLS_CERTIFICATE_KEY:-/etc/apache2/ssl/localhost.key}" + CONTACT_EMAIL: "${CONTACT_EMAIL:-admin@localhost}" + SYSTEM_ADMIN_PASSWORD: "${SYSTEM_ADMIN_PASSWORD:-password}" + WEBSITE_TITLE: "${WEBSITE_TITLE:-AVideo}" + MAIN_LANGUAGE: "${MAIN_LANGUAGE:-en_US}" + env_file: + - .env ports: - - '${HTTP_PORT}:80' - - '${HTTPS_PORT}:443' + - "${SOCKET_PORT:-2053}:2053" + - "${HTTP_PORT:-80}:80" + - "${HTTPS_PORT:-443}:443" + - "${NGINX_RTMP_PORT:-1935}:1935" + - "${NGINX_HTTP_PORT:-8080}:8080" + - "${NGINX_HTTPS_PORT:-8443}:8443" volumes: - - "./var/www/avideo:/var/www/avideo" - depends_on: - - database - - phpmyadmin - networks: - - app_net - - phpmyadmin: - image: "phpmyadmin/phpmyadmin" - restart: "unless-stopped" - environment: - PMA_ABSOLUTE_URI: "https://localhost/phpmyadmin" - PMA_HOST: "database" - PMA_PORT: 3306 - PMA_CONTROLUSER: "avideo" - PMA_CONTROLPASS: "avideo" - PMA_PMADB: "avideo" - HIDE_PHP_VERSION: "true" + - "./:/var/www/html/AVideo" + - "./.compose/videos:/var/www/html/AVideo/videos" + - "./.compose/encoder:/var/www/html/AVideo/Encoder" + - "./.compose/letsencrypt:/etc/letsencrypt/" depends_on: - database + - database_encoder networks: - app_net @@ -62,6 +79,20 @@ services: networks: - app_net + database_encoder: + image: "mariadb:latest" + restart: "unless-stopped" + environment: + MYSQL_RANDOM_ROOT_PASSWORD: "yes" + MYSQL_INITDB_SKIP_TZINFO: 1 + MYSQL_DATABASE: "avideo_encoder" + MYSQL_USER: "avideo" + MYSQL_PASSWORD: "avideo" + volumes: + - ./.compose/db_encoder:/var/lib/mysql + networks: + - app_net + networks: app_net: - driver: bridge + driver: bridge \ No newline at end of file diff --git a/env.example b/env.example index e8ca8518f9..33a14fc3cb 100644 --- a/env.example +++ b/env.example @@ -1,5 +1,13 @@ -webSiteRootURL={webSiteRootURL} -# Exposed HTTP port -HTTP_PORT={HTTP_PORT} -HTTPS_PORT={HTTPS_PORT} -Socket_PORT={Socket_PORT} \ No newline at end of file +SERVER_NAME=localhost + +SOCKET_PORT=2053 +HTTP_PORT=80 +HTTPS_PORT=443 +NGINX_RTMP_PORT=1935 +NGINX_HTTP_PORT=8080 +NGINX_HTTPS_PORT=8443 +DB_MYSQL_HOST=database +DB_MYSQL_PORT=3306 +DB_MYSQL_NAME=avideo +DB_MYSQL_USER=avideo +DB_MYSQL_PASSWORD=avideo \ No newline at end of file diff --git a/install/cli.php b/install/cli.php index bfe2938599..bb0854afdd 100644 --- a/install/cli.php +++ b/install/cli.php @@ -1,5 +1,7 @@ $value) { + if($value['videos_id']==$videos_id){ + return $key; + } + } + } + return 0; + } public static function getVideosIDFromPlaylistLight($playlists_id) { global $global, $getVideosIDFromPlaylistLight; diff --git a/plugin/AVideoPlugin.php b/plugin/AVideoPlugin.php index bf75cdb572..a530a27c51 100644 --- a/plugin/AVideoPlugin.php +++ b/plugin/AVideoPlugin.php @@ -300,7 +300,7 @@ class AVideoPlugin // need to add dechex because some times it return an negative value and make it fails on javascript playlists if (!isset($pluginIsLoaded[$name]) && empty($forceReload)) { $pluginIsLoaded[$name] = false; - if (file_exists($loadPluginFile)) { + if (file_exists($loadPluginFile) && !class_exists($name)) { require_once $loadPluginFile; if (class_exists($name)) { $code = "\$p = new {$name}();"; diff --git a/plugin/Gallery/view/mainAreaTags.php b/plugin/Gallery/view/mainAreaTags.php index 6784e4283c..a8ba7f5289 100644 --- a/plugin/Gallery/view/mainAreaTags.php +++ b/plugin/Gallery/view/mainAreaTags.php @@ -43,7 +43,7 @@ if ($totalPages < $page) {

diff --git a/plugin/PlayLists/PlayLists.php b/plugin/PlayLists/PlayLists.php index 9697cd02ed..5dffbe9968 100644 --- a/plugin/PlayLists/PlayLists.php +++ b/plugin/PlayLists/PlayLists.php @@ -406,8 +406,6 @@ class PlayLists extends PluginAbstract { foreach ($fullData as $row) { $rows[] = $row; } - } else { - die($sql . '\nError : (' . $global['mysqli']->errno . ') ' . $global['mysqli']->error); } return $rows; } diff --git a/plugin/Scheduler/Scheduler.php b/plugin/Scheduler/Scheduler.php index aec81f9595..83be1b155d 100644 --- a/plugin/Scheduler/Scheduler.php +++ b/plugin/Scheduler/Scheduler.php @@ -30,7 +30,7 @@ class Scheduler extends PluginAbstract { } public function getPluginVersion() { - return "4.1"; + return "4.2"; } public function updateScript() { @@ -56,6 +56,13 @@ class Scheduler extends PluginAbstract { sqlDal::writeSql(trim($value)); } } + if (AVideoPlugin::compareVersion($this->getName(), "4.2") < 0) { + $sqls = file_get_contents($global['systemRootPath'] . 'plugin/Scheduler/install/updateV4.2.sql'); + $sqlParts = explode(";", $sqls); + foreach ($sqlParts as $value) { + sqlDal::writeSql(trim($value)); + } + } return true; } diff --git a/plugin/Scheduler/install/install.sql b/plugin/Scheduler/install/install.sql index a8f93a2762..a757b7189a 100644 --- a/plugin/Scheduler/install/install.sql +++ b/plugin/Scheduler/install/install.sql @@ -13,6 +13,7 @@ CREATE TABLE IF NOT EXISTS `scheduler_commands` ( `repeat_hour` INT NULL, `repeat_day_of_month` INT NULL, `repeat_month` INT NULL, + `repeat_day_of_week` INT NULL, `day_of_week` INT NULL, `videos_id` INT(11) NULL, PRIMARY KEY (`id`), diff --git a/plugin/Scheduler/install/updateV4.2.sql b/plugin/Scheduler/install/updateV4.2.sql new file mode 100644 index 0000000000..c8a0cca3b2 --- /dev/null +++ b/plugin/Scheduler/install/updateV4.2.sql @@ -0,0 +1,2 @@ +ALTER TABLE `scheduler_commands` +ADD COLUMN `repeat_day_of_week` INT(11) NULL DEFAULT NULL; \ No newline at end of file diff --git a/plugin/VideoTags/VideoTags.php b/plugin/VideoTags/VideoTags.php index 96a6484417..c75e642bb4 100644 --- a/plugin/VideoTags/VideoTags.php +++ b/plugin/VideoTags/VideoTags.php @@ -116,6 +116,18 @@ class VideoTags extends PluginAbstract { static function getAllVideosIdFromTagsId($tags_id) { return TagsHasVideos::getAllVideosIdFromTagsId($tags_id); } + + static function getVideoIndexFromTagsId($tags_id, $videos_id) { + if(!empty($videos_id)){ + $pl = self::getAllVideosFromTagsId($tags_id); + foreach ($pl as $key => $value) { + if($value['videos_id']==$videos_id){ + return $key; + } + } + } + return 0; + } static function getAllVideosFromTagsId($tags_id) { return TagsHasVideos::getAllVideosFromTagsId($tags_id); @@ -216,7 +228,7 @@ $(\'#inputTags' . $tagTypesId . '\').tagsinput({ return $_isUserSubscribedTags[$users_id][$tags_id]; } - public static function getButton($tags_id, $btnClass = 'btn-xs', $btnClassPrimary = 'btn-primary', $btnClassSuccess = 'btn-success', $btnClassDefault = 'btn-default'){ + public static function getButton($tags_id, $videos_id = 0, $btnClass = 'btn-xs', $btnClassPrimary = 'btn-primary', $btnClassSuccess = 'btn-success', $btnClassDefault = 'btn-default'){ if(empty($tags_id)){ return ''; } @@ -235,7 +247,9 @@ $(\'#inputTags' . $tagTypesId . '\').tagsinput({ $playAllLink = '#'; $playAllClass = 'hidden'; if(AVideoPlugin::isEnabledByName('PlayLists')){ - $playAllLink = PlayLists::getTagLink($tags_id, $embed = false, $playlist_index = null); + $playlist_index = self::getVideoIndexFromTagsId($tags_id, $videos_id); + //var_dump($videos_id,getVideos_id(), $playlist_index);exit; + $playAllLink = PlayLists::getTagLink($tags_id, false, $playlist_index); $playAllClass = ''; } @@ -248,7 +262,6 @@ $(\'#inputTags' . $tagTypesId . '\').tagsinput({ $btnFile = $global['systemRootPath'] . 'plugin/VideoTags/subscribeBtn.html'; $email = User::getMail(); $subs = self::isUserSubscribed($users_id, $tags_id); - if (!empty($subs)) { if (!empty($subs['notify'])) { $notify = 'notify'; @@ -378,7 +391,7 @@ $(\'#inputTags' . $tagTypesId . '\').tagsinput({ if($obj->disableTagsSubscriptions){ $strT .= self::getTagHTMLLink($value['tags_id'], $value['total']); }else{ - $strT .= self::getButton($value['tags_id']); + $strT .= self::getButton($value['tags_id'], $videos_id); } } if (!empty($strT)) { diff --git a/plugin/VideoTags/pluginMenu.html b/plugin/VideoTags/pluginMenu.html index f612e06272..7ee7f456ca 100644 --- a/plugin/VideoTags/pluginMenu.html +++ b/plugin/VideoTags/pluginMenu.html @@ -1,2 +1,4 @@ - \ No newline at end of file + diff --git a/plugin/YPTSocket/server.php b/plugin/YPTSocket/server.php index 5a79b396ac..e7d594b4e9 100644 --- a/plugin/YPTSocket/server.php +++ b/plugin/YPTSocket/server.php @@ -56,7 +56,9 @@ _error_log("Starting Socket server at port {$SocketDataObj->port}"); $scheme = parse_url($global['webSiteRootURL'], PHP_URL_SCHEME); echo "Starting AVideo Socket server version {$SocketDataObj->serverVersion} on port {$SocketDataObj->port}" . PHP_EOL; -if (strtolower($scheme) !== 'https' || !empty($SocketDataObj->forceNonSecure)) { +$sslFound = file_exists($SocketDataObj->server_crt_file) && is_readable($SocketDataObj->server_crt_file) && file_exists($SocketDataObj->server_key_file) && is_readable($SocketDataObj->server_key_file); + +if ((strtolower($scheme) !== 'https' || !empty($SocketDataObj->forceNonSecure)) && !$sslFound) { echo "Your socket server does NOT use a secure connection" . PHP_EOL; $server = IoServer::factory( new HttpServer( diff --git a/view/img/audio_wave_jpg.webp b/view/img/audio_wave_jpg.webp new file mode 100644 index 0000000000000000000000000000000000000000..a198ae0eb5bce4953c80d0b387d6c1408d869374 GIT binary patch literal 41284 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