#!/usr/bin/env bash # ============================================================================== # YetuStream — 1-Command Universal VPS Installer (Media Plane Engine) # Versão: 1.2.0 # Compatível com Ubuntu 20.04 / 22.04 / 24.04 LTS e Debian 11 / 12 # # Execução Universal: # curl -fsSL https://yetustream.bolayetu.com/install.sh | bash # Ou com argumentos: # bash install.sh --domain live.seudominio.com --email admin@seudominio.com --wp-auth https://seudominio.com/wp-json/yetustream/v1/auth # ============================================================================== set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # No Color INSTALL_DIR="/opt/yetustream-media" DOMAIN="" WP_AUTH_URL="" SSL_EMAIL="" # Parsing de argumentos de linha de comando while [[ $# -gt 0 ]]; do case "$1" in --domain) DOMAIN="$2" shift 2 ;; --wp-auth) WP_AUTH_URL="$2" shift 2 ;; --email) SSL_EMAIL="$2" shift 2 ;; --help|-h) echo "Uso: bash install.sh [--domain ] [--wp-auth ] [--email ]" exit 0 ;; *) shift ;; esac done clear || true echo -e "${CYAN}${BOLD}" echo "================================================================================" echo " 📡 YETUSTREAM — INSTALADOR AUTOMÁTICO DO SERVIDOR DE VÍDEO " echo " Motor Desacoplado de Baixa Latência (Media Plane) " echo "================================================================================" echo -e "${NC}" # Validação de Root if [ "$EUID" -ne 0 ]; then echo -e "${RED}[ERRO] Este instalador tem de ser executado como root ou via sudo.${NC}" echo "Execute: sudo bash $0" exit 1 fi # Solicitação interativa caso não tenha sido passado por flag if [ -z "$DOMAIN" ]; then echo -e "${YELLOW}Passo 1/3: Subdomínio de Streaming${NC}" echo "Insira o subdomínio apontado para o IP desta VPS (ex: live.bolayetu.com):" read -rp "👉 Subdomínio: " DOMAIN fi if [ -z "$WP_AUTH_URL" ]; then echo -e "\n${YELLOW}Passo 2/3: URL do Webhook do WordPress${NC}" echo "Insira a rota de autenticação de streams do YetuStream (ex: https://meusite.com/wp-json/yetustream/v1/auth):" read -rp "👉 Webhook URL: " WP_AUTH_URL fi if [ -z "$SSL_EMAIL" ]; then echo -e "\n${YELLOW}Passo 3/3: Email para Certificado SSL Let's Encrypt${NC}" echo "Insira o email para notificações de renovação de certificado SSL:" read -rp "👉 Email: " SSL_EMAIL fi DOMAIN=$(echo "$DOMAIN" | tr -d ' ' | sed 's|https://||g' | sed 's|http://||g' | tr -d '/') if [ -z "$DOMAIN" ] || [ -z "$WP_AUTH_URL" ] || [ -z "$SSL_EMAIL" ]; then echo -e "${RED}[ERRO] Todos os campos são obrigatórios para a emissão de SSL e configuração. Abortando.${NC}" exit 1 fi echo -e "\n${BLUE}==================================================================${NC}" echo -e "${BLUE}▶ A preparar diretório de instalação em ${INSTALL_DIR}...${NC}" echo -e "${BLUE}==================================================================${NC}" mkdir -p "$INSTALL_DIR" mkdir -p "$INSTALL_DIR/nginx" mkdir -p "$INSTALL_DIR/recordings" chmod 777 "$INSTALL_DIR/recordings" echo -e "\n${BLUE}[1/7] A atualizar repositórios e a instalar dependências do sistema...${NC}" export DEBIAN_FRONTEND=noninteractive apt-get update -y -qq apt-get install -y -qq curl wget git ufw certbot python3-certbot-nginx nginx ca-certificates gnupg lsb-release echo -e "\n${BLUE}[2/7] A verificar / instalar Docker e Docker Compose...${NC}" if ! command -v docker &> /dev/null; then echo "Instalando Docker oficial via script upstream..." curl -fsSL https://get.docker.com | sh systemctl enable --now docker else echo "Docker já se encontra instalado." fi # Instala docker compose plugin se necessário if ! docker compose version &> /dev/null; then echo "Instalando Docker Compose plugin..." apt-get install -y -qq docker-compose-plugin || true fi echo -e "\n${BLUE}[3/7] A configurar Firewall UFW...${NC}" ufw allow 22/tcp comment 'SSH' || true ufw allow 80/tcp comment 'HTTP Nginx / Certbot' || true ufw allow 443/tcp comment 'HTTPS HLS / WebRTC Streaming' || true ufw allow 1935/tcp comment 'RTMP Ingest OBS Studio' || true ufw allow 8189/udp comment 'WebRTC ICE Signaling' || true ufw --force enable || true echo -e "\n${BLUE}[4/7] A gerar Certificado SSL Let's Encrypt para ${CYAN}$DOMAIN${BLUE}...${NC}" systemctl stop nginx || true certbot certonly --standalone -d "$DOMAIN" --non-interactive --agree-tos -m "$SSL_EMAIL" --pre-hook "systemctl stop nginx || true" || { echo -e "${YELLOW}[AVISO] O Certbot standalone falhou. A tentar emissão via webroot...${NC}" systemctl start nginx || true certbot certonly --nginx -d "$DOMAIN" --non-interactive --agree-tos -m "$SSL_EMAIL" || { echo -e "${RED}[ERRO] Não foi possível emitir o certificado SSL para $DOMAIN.${NC}" echo -e "${YELLOW}Verifique se o domínio $DOMAIN aponta no DNS para o IP público desta VPS e se a Nuvem do Cloudflare está em CINZENTA (DNS Only).${NC}" exit 1 } } echo -e "\n${BLUE}[5/7] A configurar Nginx com Cache Otimizada para HLS e IP Real do Cloudflare...${NC}" # Configuração Central de IPs do Cloudflare mkdir -p /etc/nginx/conf.d cat << 'CFOEF' > /etc/nginx/conf.d/cloudflare.conf set_real_ip_from 173.245.48.0/20; set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; set_real_ip_from 141.101.64.0/18; set_real_ip_from 108.162.192.0/18; set_real_ip_from 190.93.240.0/20; set_real_ip_from 188.114.96.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.128.0/17; set_real_ip_from 162.158.0.0/15; set_real_ip_from 104.16.0.0/13; set_real_ip_from 104.24.0.0/14; set_real_ip_from 172.64.0.0/13; set_real_ip_from 131.0.72.0/22; set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2405:8100::/32; set_real_ip_from 2a06:98c0::/29; set_real_ip_from 2c0f:f248::/32; real_ip_header CF-Connecting-IP; CFOEF # Ficheiro de Configuração do VirtualHost Nginx cat << EOF > /etc/nginx/sites-available/yetustream.conf # Upstream persistente com keepalive para MediaMTX upstream mediamtx_hls_backend { server 127.0.0.1:8888; keepalive 64; } # Redirecionamento HTTP -> HTTPS server { listen 80; listen [::]:80; server_name $DOMAIN; location /.well-known/acme-challenge/ { root /var/www/html; } location / { return 301 https://\$host\$request_uri; } } # Servidor Seguro HTTPS (443) server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name $DOMAIN; # Certificados Let's Encrypt ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; client_max_body_size 50M; server_tokens off; # 1. Distribuição Low-Latency HLS (LL-HLS) location ~ ^/hls(/?)(.*)$ { proxy_pass http://mediamtx_hls_backend/\$2\$is_args\$args; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_set_header X-Forwarded-Host \$host; proxy_redirect / /hls/; # Streaming de Baixa Latência: Buffering desligado no proxy proxy_buffering off; proxy_request_buffering off; # Evita cabeçalhos CORS duplicados do MediaMTX proxy_hide_header Access-Control-Allow-Origin; proxy_hide_header Access-Control-Allow-Methods; proxy_hide_header Access-Control-Allow-Headers; proxy_hide_header Access-Control-Expose-Headers; add_header Access-Control-Allow-Origin * always; add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always; add_header Access-Control-Allow-Headers "*" always; add_header Access-Control-Expose-Headers "Content-Length, Content-Range" always; # Cache Inteligente: Fragmentos TS são estáticos e imutáveis; Playlist M3U8 nunca tem cache location ~* \.m3u8$ { proxy_pass http://mediamtx_hls_backend/\$2\$is_args\$args; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_buffering off; proxy_hide_header Access-Control-Allow-Origin; add_header Access-Control-Allow-Origin * always; add_header Cache-Control "no-cache, no-store, must-revalidate" always; } location ~* \.ts$ { proxy_pass http://mediamtx_hls_backend/\$2\$is_args\$args; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_buffering off; proxy_hide_header Access-Control-Allow-Origin; add_header Access-Control-Allow-Origin * always; add_header Cache-Control "public, max-age=604800, immutable" always; } if (\$request_method = 'OPTIONS') { return 204; } } # 2. WebRTC (WHEP / WHIP) location ~ ^/webrtc(/?)(.*)$ { proxy_pass http://127.0.0.1:8889/\$2\$is_args\$args; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_redirect / /webrtc/; proxy_read_timeout 86400s; proxy_send_timeout 86400s; proxy_hide_header Access-Control-Allow-Origin; add_header Access-Control-Allow-Origin * always; add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PATCH" always; add_header Access-Control-Allow-Headers "*" always; if (\$request_method = 'OPTIONS') { return 204; } } # 3. Fallback de Segurança para mídias solicitadas na raiz location ~* \.(m3u8|m4s|ts|mp4)$ { rewrite ^/(.*)$ /hls/\$1 permanent; } # 4. Boas-vindas / Health Check location = / { default_type text/html; return 200 'YetuStream Media Engine

📡 YetuStream Media Plane

Motor de streaming desportivo ativo e operacional.

HLS: /hls/<canal>/index.m3u8

WebRTC: /webrtc/<canal>/publish

'; } } EOF ln -sf /etc/nginx/sites-available/yetustream.conf /etc/nginx/sites-enabled/yetustream.conf rm -f /etc/nginx/sites-enabled/default || true nginx -t systemctl restart nginx systemctl enable nginx echo -e "\n${BLUE}[6/7] A gerar configurações do MediaMTX (Docker)...${NC}" # Gera o docker-compose.yml cat << 'EOF' > "$INSTALL_DIR/docker-compose.yml" services: mediamtx: image: bluenviron/mediamtx:latest container_name: yetustream-media-engine restart: always network_mode: host environment: - MTX_AUTHHTTPADDRESS=${WP_AUTH_URL} volumes: - ./mediamtx.yml:/mediamtx.yml:ro - ./recordings:/recordings logging: driver: "json-file" options: max-size: "20m" max-file: "5" EOF # Gera o .env cat << EOF > "$INSTALL_DIR/.env" WP_AUTH_URL=$WP_AUTH_URL DOMAIN=$DOMAIN EOF # Gera o mediamtx.yml com buffers perfeitos cat << EOF > "$INSTALL_DIR/mediamtx.yml" ############################################### # YetuStream — Configuração Otimizada MediaMTX ############################################### logLevel: info logDestinations: [stdout] # Autenticação Externa via Webhook WordPress authMethod: http authHTTPAddress: $WP_AUTH_URL authHTTPExclude: - action: playback path: "" # RTMP Ingest (Porta 1935) rtmp: yes rtmpAddress: :1935 # Low-Latency HLS (LL-HLS) hls: yes hlsAddress: :8888 hlsAlwaysRemux: yes hlsVariant: lowLatency hlsSegmentCount: 15 hlsSegmentDuration: 2s hlsPartDuration: 200ms hlsSegmentMaxSize: 50M hlsAllowOrigin: "*" hlsSegmentType: mpegts hlsTrustedProxies: [127.0.0.1, "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] # WebRTC (WHEP / WHIP) webrtc: yes webrtcAddress: :8889 webrtcAllowOrigin: "*" webrtcLocalUDPAddress: :8189 webrtcIPsFromInterfaces: yes # RTSP desativado por padrão rtsp: no # Caminhos de Gravação e Ingestão paths: all_others: record: no "~^jogo_.*$": record: yes recordPath: /recordings/%path_%Y-%m-%d_%H-%M-%S.mp4 recordFormat: fmp4 recordPartDuration: 2s recordSegmentDuration: 30m recordDeleteAfter: 7d EOF echo -e "\n${BLUE}[7/7] A inicializar contentores com Docker Compose...${NC}" cd "$INSTALL_DIR" docker compose down || true docker compose up -d echo -e "\n${GREEN}${BOLD}================================================================================" echo " 🎉 YETUSTREAM MEDIA PLANE INSTALADO E ATIVO COM SUCESSO! " echo "================================================================================${NC}" echo -e "Copie e cole estas credenciais no Assistente do WordPress (YetuStream Setup):" echo -e " 1. URL de Ingestão RTMP: ${YELLOW}rtmp://$DOMAIN:1935${NC}" echo -e " 2. URL de Saída HLS / LL-HLS: ${YELLOW}https://$DOMAIN/hls${NC}" echo -e " 3. URL WebRTC (WHEP / WHIP): ${YELLOW}https://$DOMAIN/webrtc${NC}\n" echo -e "🔍 ${CYAN}Para monitorizar o streaming em tempo real na VPS:${NC}" echo -e " cd $INSTALL_DIR && docker compose logs -f\n" echo -e "🧪 ${CYAN}Teste rápido de conectividade:${NC}" echo -e " curl -I https://$DOMAIN/hls\n" echo "================================================================================"