From 9457306a881cfe476ee0abbdba4f6f1eaa80db9d Mon Sep 17 00:00:00 2001 From: heqnx Date: Sat, 5 Jul 2025 12:21:29 +0300 Subject: initial commit on a working icecast2 setup --- templates/hugo.toml.j2 | 13 ++++ templates/icecast2/icecast.xml.j2 | 65 ++++++++++++++++++ templates/icecast2/ices-playlist.xml.j2 | 44 ++++++++++++ templates/icecast2/mp3-to-ogg.sh.j2 | 32 +++++++++ templates/nginx/nginx.conf.j2 | 118 ++++++++++++++++++++++++++++++++ templates/systemd/system/ices2.service | 17 +++++ 6 files changed, 289 insertions(+) create mode 100644 templates/hugo.toml.j2 create mode 100644 templates/icecast2/icecast.xml.j2 create mode 100644 templates/icecast2/ices-playlist.xml.j2 create mode 100644 templates/icecast2/mp3-to-ogg.sh.j2 create mode 100644 templates/nginx/nginx.conf.j2 create mode 100644 templates/systemd/system/ices2.service (limited to 'templates') diff --git a/templates/hugo.toml.j2 b/templates/hugo.toml.j2 new file mode 100644 index 0000000..85c0e3a --- /dev/null +++ b/templates/hugo.toml.j2 @@ -0,0 +1,13 @@ +baseURL = "https://{{ domain }}" +languageCode = "en-us" +title = "{{ username }}" +theme = "dos-theme" +publishDir = "/var/www/html/docs" +disableKinds = ["taxonomy", "RSS", "sitemap"] +disablePathToLower = true +disableHugoGeneratorInject = true + +[params] + contacts = [ + "{{ radio_email }}", + ] diff --git a/templates/icecast2/icecast.xml.j2 b/templates/icecast2/icecast.xml.j2 new file mode 100644 index 0000000..4c665b7 --- /dev/null +++ b/templates/icecast2/icecast.xml.j2 @@ -0,0 +1,65 @@ + + Earth + {{ email }} + + + 1000 + 2 + 524288 + 30 + 15 + 10 + 1 + 65535 + + + + + disabled + admin + password + + + localhost + + + 8000 + 127.0.0.1 + + + +
+ + + + /stream + 1000 + 1 + 1 + + + 1 + + + /usr/share/icecast2 + /var/log/icecast2 + /usr/share/icecast2/web + /usr/share/icecast2/admin + + + + + access.log + error.log + 2 + 10000 + + + + 1 + + icecast2 + icecast + + + diff --git a/templates/icecast2/ices-playlist.xml.j2 b/templates/icecast2/ices-playlist.xml.j2 new file mode 100644 index 0000000..ae9a6be --- /dev/null +++ b/templates/icecast2/ices-playlist.xml.j2 @@ -0,0 +1,44 @@ + + + 1 + /var/log/ices + ices.log + 4 + 1 + + + + + + Example stream name + Example genre + A short description of your stream + + + + playlist + basic + playlist.txt + 1 + 0 + 0 + + + + localhost + 8000 + password + /stream + 0 + 60 + 10 + 80 + + + + + diff --git a/templates/icecast2/mp3-to-ogg.sh.j2 b/templates/icecast2/mp3-to-ogg.sh.j2 new file mode 100644 index 0000000..b37f5af --- /dev/null +++ b/templates/icecast2/mp3-to-ogg.sh.j2 @@ -0,0 +1,32 @@ +#!/bin/bash + +if ! command -v ffmpeg &>/dev/null; then + printf "%s\n" "[err] ffmpeg not found" + exit 1 +fi + +DIR="{{ radio_music_dir }}" + +shopt -s nullglob +for mp3file in "${DIR}"/*.mp3; do + oggfile="${mp3file%.mp3}.ogg" + + printf "%s\n" "[inf] converting ${mp3file} to ${oggfile}" + + if ffmpeg -loglevel error -y -i "${mp3file}" -acodec libvorbis -q:a 5 "${oggfile}"; then + printf "%s\n" "[inf] conversion successful, removing ${mp3file}" + rm -f "${mp3file}" + else + printf "%s\n" "[err] conversion failed for ${mp3file}" + fi +done + +ls "${DIR}"/*.ogg > "${DIR}/playlist.txt" + +if id -u icecast2 >/dev/null 2>&1 && getent group icecast >/dev/null 2>&1; then + chown -R icecast2:icecast "$DIR" + printf "%s\n" "[inf] chowned ${DIR} with icecast2:icecast" +else + printf "%s\n" "[err] user or group icecast2:icecast does not exist, skipping chown" +fi + diff --git a/templates/nginx/nginx.conf.j2 b/templates/nginx/nginx.conf.j2 new file mode 100644 index 0000000..7f1ac2b --- /dev/null +++ b/templates/nginx/nginx.conf.j2 @@ -0,0 +1,118 @@ +user www-data; +worker_processes auto; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; + +events { + worker_connections 1024; + multi_accept on; +} + +http { + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log warn; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_min_length 256; + gzip_types + text/plain + text/css + application/json + application/javascript + text/xml + application/xml + application/xml+rss + text/javascript + image/svg+xml; + + server { + listen 80; + server_name {{ domain }}; + return 301 https://{{ domain }}$request_uri; + } + + server { + listen 443 ssl http2; + server_name {{ domain }}; + + ssl_certificate /etc/letsencrypt/live/{{ domain }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem; + #ssl_trusted_certificate /etc/letsencrypt/live/{{ domain }}/chain.pem; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options "DENY" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Content-Security-Policy "default-src 'self'; connect-src *; media-src * blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline';" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + + root /var/www/html; + index index.html index.htm; + + location / { + try_files $uri $uri/ =404; + } + + location /stream { + proxy_pass http://localhost:8000/stream; + 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_http_version 1.1; + proxy_buffering off; + proxy_request_buffering off; + + proxy_read_timeout 3600s; + chunked_transfer_encoding on; + + add_header Access-Control-Allow-Origin "*" always; + add_header Access-Control-Allow-Methods "GET, OPTIONS" always; + add_header Access-Control-Allow-Headers "Range" always; + add_header Access-Control-Expose-Headers "Content-Length,Content-Range" always; + } + + location /admin { + deny all; + return 403; + } + + location /admin/ { + deny all; + return 403; + } + + location ~ ^/(status|statistics|server|webadmin) { + deny all; + return 403; + } + + access_log /var/log/nginx/icecast-access.log; + error_log /var/log/nginx/icecast-error.log warn; + } +} + diff --git a/templates/systemd/system/ices2.service b/templates/systemd/system/ices2.service new file mode 100644 index 0000000..d3a4c2f --- /dev/null +++ b/templates/systemd/system/ices2.service @@ -0,0 +1,17 @@ +[Unit] +Description=ICES2 Icecast source client +After=network.target icecast2.service +Requires=icecast2.service + +[Service] +Type=simple +User=icecast2 +Group=icecast +WorkingDirectory={{ radio_music_dir }} +ExecStart=/usr/bin/ices2 /etc/icecast2/ices-playlist.xml +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target + -- cgit v1.2.3