In this tutorial we're going to setup a web radio for lainons on a debian server VM, figuring out how barrucadu installed lainon.life by looking at his github repository.
First let's clone the repository into /srv
root@lain:/srv# apt update -y ; apt upgrade -y ; apt install git net-utils vim -y
root@lain:/srv# cd /srv
root@lain:/srv# git clone https://github.com/barrucadu/lainonlife
Once that's done we start to install the required dependencies:
root@lain:/srv# apt install curl mpd mpc alsa-utils nginx -y
We're going to start with the nginx configuration:
root@lain:/srv# ls
lainonlife
root@lain:/srv# cd lainonlife/examples/
root@lain:/srv/lainonlife/examples# ls -l
total 12
-rw-r--r-- 1 root root 1944 Jul 11 22:11 icecast.xml
-rw-r--r-- 1 root root 1136 Jul 11 22:11 mpd-cyberia.conf
-rw-r--r-- 1 root root 1161 Jul 11 22:11 nginx.conf
root@lain:/srv/lainonlife/examples# cp nginx.conf /etc/nginx/sites-available/lainon.conf
root@lain:/srv/lainonlife/examples# cd /etc/nginx/sites-available/
root@lain:/etc/nginx/sites-available# rm default
root@lain:/etc/nginx/sites-available# rm ../sites-enabled/default
root@lain:/etc/nginx/sites-available# vim lain.conf
root@lain:/etc/nginx/sites-available# cat lain.conf
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name lain.void.yt;
root /srv/http;
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_set_header X-Forwarded-Server $host;
proxy_set_header Accept-Encoding "";
# this is needed to let people ajax stuff from other sites.
add_header 'Access-Control-Allow-Origin' '*';
# this is very important! if you don't set it, nginx will buffer
# the proxied audio stream to disk and eat all your space!
proxy_max_temp_file_size 0;
location / {
try_files $uri $uri/ @script;
}
# icecast
location /radio/ {
proxy_pass http://localhost:8000/;
}
# grafana
#location /graphs/ {
# proxy_pass http://localhost:8001/;
#}
# backend
location @script {
proxy_pass http://localhost:8002;
}
}
root@lain:/etc/nginx/sites-available# ln -s /etc/nginx/sites-available/lain.conf /etc/nginx/sites-enabled/
root@lain:/etc/nginx/sites-available# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
So right here in the nginx config i changed the server_name to match the DNS i will give it, and i commented out the grafana block because i won't use it. Although we do need the @script backend and icecast. So now from here let's move on to icecast, we're going to clone it in /srv too:
root@lain:/etc/nginx/sites-available# cd /srv
root@lain:/srv/Icecast-Server# apt install vorbis-tools libvorbis-dev libxml2 libxml2-dev libxslt1.1 libxslt1-dev curl pkg-config dh-autoreconf -y
root@lain:/srv/Icecast-Server# source ~/.bashrc
root@lain:/srv# git clone https://github.com/xiph/Icecast-Server --recursive
root@lain:/srv# cd Icecast-Server/
root@lain:/srv/Icecast-Server# ./autogen.sh
root@lain:/srv/Icecast-Server# chmod +x configure
root@lain:/srv/Icecast-Server# ./configure
[...]
config.status: executing libtool commands
Icecast configuration
---------------------
Version : 2.4.99.2
cURL : no
TLS (openSSL) : no
Format/Codec support:
Ogg : yes
Theora : no
Speex : no
Features:
YP support : no
Client tests : no
Development logging: no
root@lain:/srv/Icecast-Server# make
root@lain:/srv/Icecast-Server# make install
Once it's done we should be able to get the icecast binary added to our PATH, and a sample config file in /usr/local/etc/:
root@lain:/srv/Icecast-Server# which icecast
/usr/local/bin/icecast
root@lain:/srv/Icecast-Server# ls /usr/local/etc
icecast.xml
Although barrucadu also left a sample config for us to use, so let's test it after we modify it to match our Icecast install's directory paths for /var/log/icecast, /srv/IcecastServer/admin/, and /srv/Icecast-Server/web/:
root@lain:/srv/Icecast-Server# mkdir /var/log/icecast
root@lain:/srv/Icecast-Server# ls -l /var/log/icecast /srv/Icecast-Server/admin/ /srv/Icecast-Server/web/
/srv/Icecast-Server/admin/:
total 88
-rw-r--r-- 1 root root 834 Jul 11 22:36 error-html.xsl
-rw-r--r-- 1 root root 650 Jul 11 22:36 error-plaintext.xsl
drwxr-xr-x 2 root root 4096 Jul 11 22:36 includes
-rw-r--r-- 1 root root 1735 Jul 11 22:36 listclients.xsl
-rw-r--r-- 1 root root 2431 Jul 11 22:36 listmounts.xsl
-rw-r--r-- 1 root root 16245 Jul 11 22:55 Makefile
-rw-r--r-- 1 root root 495 Jul 11 22:36 Makefile.am
-rw-r--r-- 1 root root 16338 Jul 11 22:48 Makefile.in
-rw-r--r-- 1 root root 2556 Jul 11 22:36 manageauth.xsl
-rw-r--r-- 1 root root 1686 Jul 11 22:36 moveclients.xsl
-rw-r--r-- 1 root root 714 Jul 11 22:36 response.xsl
-rw-r--r-- 1 root root 4541 Jul 11 22:36 stats.xsl
-rw-r--r-- 1 root root 1227 Jul 11 22:36 updatemetadata.xsl
-rw-r--r-- 1 root root 2322 Jul 11 22:36 vclt.xsl
-rw-r--r-- 1 root root 2981 Jul 11 22:36 xspf.xsl
/srv/Icecast-Server/web/:
total 108
drwxr-xr-x 4 root root 4096 Jul 11 22:36 assets
-rw-r--r-- 1 root root 1432 Jul 11 22:36 auth.xsl
-rw-r--r-- 1 root root 1406 Jul 11 22:36 favicon.ico
-rw-r--r-- 1 root root 9124 Jul 11 22:36 icecast.png
-rw-r--r-- 1 root root 2228 Jul 11 22:36 key.png
-rw-r--r-- 1 root root 16426 Jul 11 22:55 Makefile
-rw-r--r-- 1 root root 811 Jul 11 22:36 Makefile.am
-rw-r--r-- 1 root root 16525 Jul 11 22:48 Makefile.in
-rw-r--r-- 1 root root 1632 Jul 11 22:36 server_version.xsl
-rw-r--r-- 1 root root 2009 Jul 11 22:36 status-json.xsl
-rw-r--r-- 1 root root 5387 Jul 11 22:36 status.xsl
-rw-r--r-- 1 root root 3879 Jul 11 22:36 style.css
-rw-r--r-- 1 root root 2042 Jul 11 22:36 tunein.png
-rw-r--r-- 1 root root 9450 Jul 11 22:36 xml2json.xslt
/var/log/icecast:
total 0
root@lain:/srv/Icecast-Server# cd conf/
root@lain:/srv/Icecast-Server/conf# cp /srv/lainonlife/examples/icecast.xml .
root@lain:/srv/Icecast-Server/conf# vim icecast.xml
Here i basically changed the hostname, the admin password and the icecast directory paths we mentionned earlier:
Below those you can also see that we will need to setup the mpd mountpoints later on. We now change the ownership of /var/log/icecast to nobody:nogroup
root@lain:/srv/Icecast-Server/conf# ls -lash icecast.xml
4.0K -rw-r--r-- 1 root root 1.9K Jul 11 23:20 icecast.xml
root@lain:/srv/Icecast-Server/conf# chown -R nobody:nogroup /var/log/icecast/
root@lain:/srv/Icecast-Server/conf# ls -lash /var/log
total 760K
4.0K drwxr-xr-x 10 root root 4.0K Jul 11 23:04 .
4.0K drwxr-xr-x 12 root root 4.0K Jul 11 22:13 ..
28K -rw-r--r-- 1 root root 24K Jul 11 22:45 alternatives.log
4.0K drwxr-xr-x 2 root root 4.0K Jul 11 23:22 apt
4.0K -rw-r----- 1 root adm 3.8K Jul 11 23:17 auth.log
0 -rw-rw---- 1 root utmp 0 Jul 11 21:41 btmp
4.0K drwxr-xr-x 2 root root 4.0K Jul 11 21:52 cups
36K -rw-r----- 1 root adm 30K Jul 11 23:24 daemon.log
12K -rw-r----- 1 root adm 9.3K Jul 11 21:52 debug
408K -rw-r--r-- 1 root root 403K Jul 11 23:22 dpkg.log
8.0K -rw-r--r-- 1 root root 32K Jul 11 22:13 faillog
4.0K -rw-r--r-- 1 root root 2.1K Jul 11 22:13 fontconfig.log
4.0K drwxr-xr-x 3 root root 4.0K Jul 11 21:48 hp
4.0K drwxr-xr-x 2 nobody nogroup 4.0K Jul 11 23:04 icecast
4.0K drwxr-xr-x 3 root root 4.0K Jul 11 21:52 installer
60K -rw-r----- 1 root adm 60K Jul 11 21:59 kern.log
12K -rw-rw-r-- 1 root utmp 286K Jul 11 22:13 lastlog
52K -rw-r----- 1 root adm 51K Jul 11 21:59 messages
4.0K drwxr-xr-x 2 mpd audio 4.0K Jul 11 22:13 mpd
4.0K drwxr-xr-x 2 root adm 4.0K Jul 11 22:28 nginx
4.0K drwx------ 2 root root 4.0K Jul 11 21:52 private
92K -rw-r----- 1 root adm 90K Jul 11 23:24 syslog
4.0K -rw-rw-r-- 1 root utmp 3.0K Jul 11 21:59 wtmp
root@lain:/srv/Icecast-Server/conf# icecast -c /srv/Icecast-Server/conf/icecast.xml
[2021-07-11 23:24:33] WARN CONFIG/_parse_root Warning, <location> not configured, using default value "Earth".
[2021-07-11 23:24:33] WARN CONFIG/_parse_root Warning, <admin> contact not configured, using default value "icemaster@localhost". This breaks YP directory listings. YP directory support will be disabled.
Changed groupid to 65534.
Changed supplementary groups based on user: nobody.
Changed userid to 65534.
We can check that our icecast server is running on port 8000:
Now that's done, exit out of icecast with CTRL+C and let's configure mpd using barrucadu's sample config file:
root@lain:/srv/Icecast-Server/conf# cd ../..
root@lain:/srv# cd lainonlife/
root@lain:/srv/lainonlife# cd examples/
root@lain:/srv/lainonlife/examples# vim mpd-cyberia.conf
First we setup the root directory for our music files in /srv/radio/music/cyberia/ and the /srv/radio/data/cyberia/playlists directory:
root@lain:/srv# mkdir -p /srv/radio/data/cyberia/playlists
root@lain:/srv# mkdir -p /srv/radio/music/cyberia/
root@lain:/srv/radio# tree .
.
├── data
│ └── cyberia
│ └── playlists
└── music
└── cyberia
root@lain:/srv# mkdir -p ~/.config/mpd/
root@lain:/srv# kill $(pidof mpd)
root@lain:/srv# cp /srv/lainonlife/examples/mpd-cyberia.conf ~/.config/mpd/mpd.conf
root@lain:/srv# mpd
Now basically we will put our music files into /srv/radio/cyberia/, and we can make playlists of it using ncmpcpp i will do it from my local machine since my debian VM is in the same local network as i am :
[ 10.10.14.8/23 ] [ /dev/pts/30 ] [~]
→ vim ~/.config/ncmpcpp/config
[ 10.10.14.8/23 ] [ /dev/pts/30 ] [~]
→ cat ~/.config/ncmpcpp/config
##
# Files
# mpd_music_dir = "~/Music"
lyrics_directory = ~/.ncmpcpp/lyrics
ncmpcpp_directory = ~/.ncmpcpp
# mpd_host = "localhost"
mpd_host = "10.0.0.201"
mpd_port = "6601"
mpd_connection_timeout = "5"
mpd_crossfade_time = "5"
# Playlist
playlist_disable_highlight_delay = "0"
playlist_display_mode = "columns"
playlist_show_remaining_time = "yes"
browser_display_mode = "columns"
autocenter_mode = "yes"
fancy_scrolling = "yes"
follow_now_playing_lyrics = "yes"
display_screens_numbers_on_start = "yes"
ignore_leading_the = "yes"
lyrics_database = "1"
song_columns_list_format = "(10)[blue]{l} (30)[green]{a} (30)[magenta]{b} (50)[yellow]{t}"
colors_enabled = "yes"
main_window_color = "white"
main_window_highlight_color = "blue"
header_window_color = "cyan"
volume_color = "red"
progressbar_color = "cyan"
statusbar_color = "white"
active_column_color = "cyan"
active_window_border = "blue"
alternative_header_first_line_format = "$0$aqqu$/a {$7%a - $9}{$5%t$9}|{$8%f$9} $0$atqq$/a$9"
alternative_header_second_line_format = "{{$6%b$9}{ [$6%y$9]}}|{%D}"
song_list_format = "{$3%n │ $9}{$7%a - $9}{$5%t$9}|{$8%f$9}$R{$6 │ %b$9}{$3 │ %l$9}"
user_interface = "alternative"
#user_interface = "classic"
default_place_to_search_in = "database"
# visualizer
#visualizer_fifo_path = "/tmp/mpd.fifo"
#visualizer_output_name = "my_fifo"
##visualizer_sync_interval = "12"
##visualizer_type = "wave" (spectrum/wave)
#visualizer_type = "spectrum" (spectrum/wave)
#visualizer_in_stereo = "yes"
#visualizer_look = "+|"
## Navigation ##
cyclic_scrolling = "yes"
header_text_scrolling = "yes"
jump_to_now_playing_song_at_start = "yes"
lines_scrolled = "2"
## Other ##
system_encoding = "utf-8"
regular_expressions = "extended"
## Selected tracks ##
selected_item_prefix = "* "
discard_colors_if_item_is_selected = "no"
## Seeking ##
incremental_seeking = "yes"
seek_time = "1"
## Visivility ##
header_visibility = "yes"
statusbar_visibility = "yes"
titles_visibility = "yes"
progressbar_look = "=>-"
#progressbar_boldness = "yes"
progressbar_elapsed_color = "white"
now_playing_prefix = "> "
song_status_format = " $2%a $4⟫$3⟫ $8%t $4⟫$3⟫ $5%b "
autocenter_mode = "yes"
centered_cursor = "yes"
# Misc
display_bitrate = "yes"
# enable_window_title = "no"
follow_now_playing_lyrics = "yes"
ignore_leading_the = "yes"
empty_tag_marker = ""
Now if we want to connec to the mpd server with ncmpcpp we need the mpd server to be bound to it's local ip address instead of it's localhost address, so let's change it and restart it:
root@lain:/srv/radio# vim ~/.config/mpd/mpd.conf
root@lain:/srv/radio# cat ~/.config/mpd/mpd.conf
music_directory "/srv/radio/music/cyberia"
playlist_directory "/srv/radio/data/cyberia/playlists"
db_file "/srv/radio/data/cyberia/db"
state_file "/srv/radio/data/cyberia/state"
sticker_file "/srv/radio/data/cyberia/sticker.sql"
log_file "/srv/radio/data/syslog"
#bind_to_address "127.0.0.1"
bind_to_address "10.0.0.201"
port "6601"
[...]
root@lain:/srv/radio# mpd
Jul 12 00:13 : hybrid_dsd: The Hybrid DSD decoder is disabled because it was not explicitly enabled
Jul 12 00:13 : exception: Input plugin 'tidal' is unavailable: No Tidal application token configured
Jul 12 00:13 : exception: Input plugin 'qobuz' is unavailable: No Qobuz app_id configured
root@lain:/srv/radio# pidof mpd
23898
You can ignore the the stderr from mpd, mpd is anyway launched as you can see from the pidof command. Now that's done we can connect to it with ncmpcpp:
[ 10.10.14.8/23 ] [ /dev/pts/30 ] [~]
→ nmap -sCV -p 6601 10.0.0.201
Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-12 00:19 CEST
Nmap scan report for 10.0.0.201
Host is up (0.0047s latency).
PORT STATE SERVICE VERSION
6601/tcp open mpd Music Player Daemon 0.21.4
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.71 seconds
[ 10.10.14.8/23 ] [ /dev/pts/30 ] [~]
→ ncmpcpp -h 10.0.0.201 -p 6601
Reading configuration from /home/nothing/.config/ncmpcpp/config...
Now obviously there are no music files to play yet, so we need to add them to the server's /srv/radio/music/cyberia/ directory:
root@lain:/srv/radio/music/cyberia# apt purge youtube-dl -y #youtube-dl is severly outdated on debian's repositories
root@lain:/srv/radio/music/cyberia# apt install python3-pip -y
root@lain:/srv/radio/music/cyberia# pip3 install youtube-dl
root@lain:/srv/radio/music/cyberia# bash
root@lain:/srv/radio/music/cyberia# youtube-dl --version
2021.06.06
root@lain:/srv/radio/music/cyberia# youtube-dl -x https://www.youtube.com/playlist?list=PLMR9WY6VV-0c0bv7MsYjudCxx0X1cXWBQ --audio-format mp3
[youtube:tab] PLMR9WY6VV-0c0bv7MsYjudCxx0X1cXWBQ: Downloading webpage
[download] Downloading playlist: Serial Experiments Lain OST
[youtube:tab] playlist Serial Experiments Lain OST: Downloading 14 videos
[download] Downloading video 1 of 14
[youtube] AsPLBQfZQ04: Downloading webpage
[download] Destination: Serial Experiment Lain - Infanity world-AsPLBQfZQ04.webm
[download] 5.3% of 4.98MiB at 54.61KiB/s ETA 01:28
[...]
[download] Downloading video 14 of 14
[youtube] 5dbi4N6NGn4: Downloading webpage
[download] Destination: Serial Experiments Lain - Cyberia Theme-5dbi4N6NGn4.webm
[download] 100% of 2.57MiB in 00:37
[ffmpeg] Destination: Serial Experiments Lain - Cyberia Theme-5dbi4N6NGn4.mp3
Deleting original file Serial Experiments Lain - Cyberia Theme-5dbi4N6NGn4.webm (pass -k to keep)
[download] Finished downloading playlist: Serial Experiments Lain OST
root@lain:/srv/radio/music/cyberia# ls -lash
total 62M
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 00:55 .
4.0K drwxr-xr-x 3 root root 4.0K Jul 12 00:04 ..
2.7M -rw-r--r-- 1 root root 2.7M Jun 9 2017 "Lain's Theme - Lain-o-cwuTmqz8c.mp3"
5.7M -rw-r--r-- 1 root root 5.7M Dec 15 2019 'Serial Experiment Lain - Antidepressant 044-KaOsmUDMdaE.mp3'
4.7M -rw-r--r-- 1 root root 4.7M Dec 24 2019 'Serial Experiment Lain - Cloudy with occasional rain-1TunrW7dRr0.mp3'
4.8M -rw-r--r-- 1 root root 4.8M Dec 9 2019 'Serial Experiment Lain - Duvet (cyberia remix)-juDqDMlTfUg.mp3'
1.5M -rw-r--r-- 1 root root 1.5M Mar 18 2015 'Serial Experiment Lain - Duvet (tv version)-EnEaNaqGMqU.mp3'
5.0M -rw-r--r-- 1 root root 5.0M Oct 15 2017 'Serial Experiment Lain - Infanity world-AsPLBQfZQ04.mp3'
4.6M -rw-r--r-- 1 root root 4.6M Jan 23 2020 'Serial Experiment Lain - Invisible file-32IlMQ8Bs6w.mp3'
4.7M -rw-r--r-- 1 root root 4.7M Dec 9 2019 'Serial Experiment Lain - Island in video cassette-CCdyzUPrLpM.mp3'
7.1M -rw-r--r-- 1 root root 7.1M Jul 9 2014 'Serial Experiment Lain - k.i.d.s-TvCJnW46ISo.mp3'
6.1M -rw-r--r-- 1 root root 6.1M Dec 16 2019 'Serial Experiment Lain - Prayer-yyPMWWjzMG8.mp3'
4.0M -rw-r--r-- 1 root root 4.0M Dec 25 2019 'Serial Experiment Lain - Professed intention and real-D4G97Xsc8PA.mp3'
3.8M -rw-r--r-- 1 root root 3.8M Jan 23 2020 'Serial Experiment Lain - Psychedelic farm-XEGz6CJnY04.mp3'
4.7M -rw-r--r-- 1 root root 4.7M Dec 17 2019 'Serial Experiment Lain - Speed-zFwQFdAsGGA.mp3'
2.7M -rw-r--r-- 1 root root 2.7M Nov 7 2018 'Serial Experiments Lain - Cyberia Theme-5dbi4N6NGn4.mp3'
Now that's done, let's go back into ncmpcpp to make our playlist:
[ 10.10.14.8/23 ] [ /dev/pts/30 ] [~]
→ ncmpcpp -h 10.0.0.201 -p 6601
#once in ncmpcpp
#press 1 to get to the playlist tab
#press a to add
#if you can't connect to it, then use ncmpcpp on the remote host:
root@lain:~# mkdir ~/.config/ncmpcpp
root@lain:~# which ncmpcpp
/usr/bin/ncmpcpp
root@lain:~# vim ~/.config/ncmpcpp/config
#make sure that you have changed the 10.0.0.201 part to 127.0.0.1
#same goes for mpd:
#also make mpd use alsa for now:
root@lain:~# cat ~/.config/mpd/mpd.conf
music_directory "/srv/radio/music/cyberia"
playlist_directory "/srv/radio/data/cyberia/playlists"
db_file "/srv/radio/data/cyberia/db"
state_file "/srv/radio/data/cyberia/state"
sticker_file "/srv/radio/data/cyberia/sticker.sql"
log_file "/srv/radio/data/syslog"
bind_to_address "127.0.0.1"
#bind_to_address "10.0.0.201"
port "6601"
audio_output {
name "[mpd] cyberia (ogg)"
description "classic lainchan radio: electronic, chiptune, weeb"
type "shout"
encoder "vorbis"
host "localhost"
port "8000"
mount "/mpd-cyberia.ogg"
user "source"
password "password for icecast"
quality "3"
format "44100:16:2"
always_on "yes"
}
audio_output {
name "[mpd] cyberia (mp3)"
description "classic lainchan radio: electronic, chiptune, weeb"
type "shout"
encoder "lame"
host "localhost"
port "8000"
mount "/mpd-cyberia.mp3"
user "source"
password "password for icecast"
quality "3"
format "44100:16:2"
always_on "yes"
}
audio_output {
type "null"
name "null"
}
audio_output {
type "alsa"
name "alsa audio"
mixer_type "software"
}
audio_output {
type "fifo"
name "my_fifo"
path "/tmp/mpd.fifo"
format "44100:16:2"
}
root@lain:~# mpd
Jul 12 08:29 : hybrid_dsd: The Hybrid DSD decoder is disabled because it was not explicitly enabled
Jul 12 08:29 : exception: Input plugin 'tidal' is unavailable: No Tidal application token configured
Jul 12 08:29 : exception: Input plugin 'qobuz' is unavailable: No Qobuz app_id configured
root@lain:~# pidof mpd
29091
root@lain:~# netstat -alntup | grep 6601
tcp 0 0 127.0.0.1:6601 0.0.0.0:* LISTEN 29091/mpd
root@lain:~# ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
#press 2 to go to the browsing tab and
#press u to update the mpd database
#select the songs you want to add to the playlist with INSERT
#then once you selected them press a to add to a playlist
#hit 'New playlist', choose the name you want for it:
#press 5 to go to the playlist editor tab to check the result:
#now here you see we have a small cyberia playlist.
#press 8 to go to the visualizer to verify that it's working properly:
#now press 7 to go to the Outputs tab to enable the ogg and mp3 outputs:
#We're going to need those for icecast. Now that's done we can exit out of ncmpcpp and mpd:
#mpd is going to run in the background, so we can always return to it with # ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
#press q to exit ncmpcpp
We can verify our cyberia ogg output exists on port 8000 by launching icecast and using it to verify it:
root@lain:~# icecast -c /srv/Icecast-Server/conf/icecast.xml
[2021-07-12 09:25:43] WARN CONFIG/_parse_root Warning, not configured, using default value "Earth".
[2021-07-12 09:25:43] WARN CONFIG/_parse_root Warning, contact not configured, using default value "icemaster@localhost". This breaks YP directory listings. YP directory support will be disabled.
Changed groupid to 65534.
Changed supplementary groups based on user: nobody.
Changed userid to 65534.
We can log into icecast using the admin:P@SSW0RD_FOR_ADM1N credentials we used earlier in the config:
Now we need to fix the fact that icecast doesn't detect our mountpoints:
First we fix the passwords in the mpd and icecast config files:
root@lain:~# vim ~/.config/mpd/mpd.conf
root@lain:~# cat ~/.config/mpd/mpd.conf
music_directory "/srv/radio/music/cyberia"
playlist_directory "/srv/radio/data/cyberia/playlists"
db_file "/srv/radio/data/cyberia/db"
state_file "/srv/radio/data/cyberia/state"
sticker_file "/srv/radio/data/cyberia/sticker.sql"
log_file "/srv/radio/data/syslog"
bind_to_address "127.0.0.1"
#bind_to_address "10.0.0.201"
port "6601"
audio_output {
name "[mpd] cyberia (ogg)"
description "classic lainchan radio: electronic, chiptune, weeb"
type "shout"
encoder "vorbis"
host "localhost"
port "8000"
mount "/mpd-cyberia.ogg"
user "source"
password "P@SSW0RD"
quality "3"
format "44100:16:2"
always_on "yes"
}
audio_output {
name "[mpd] cyberia (mp3)"
description "classic lainchan radio: electronic, chiptune, weeb"
type "shout"
encoder "lame"
host "localhost"
port "8000"
mount "/mpd-cyberia.mp3"
user "source"
password "P@SSW0RD"
quality "3"
format "44100:16:2"
always_on "yes"
}
[...]
root@lain:~# vim /srv/Icecast-Server/conf/icecast.xml
Next we restart mpd and icecast:
root@lain:~# systemctl restart mpd
root@lain:~# icecast -c /srv/Icecast-Server/conf/icecast.xml
Now icecast doesn't pick up our ogg stream, so let's try out the mp3 one:
root@lain:~# ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
And as you can see on icecast's admin page we see that the mp3 output does get picked up:
You can also see it appear on icecast's public /status.xsl page:
And we can play the direct .m3u link to our VLC player:
And it works! now let's make sure that mpd is running on loop while shuffling the songs, from ncmpcpp you can press r for repeat mode, and z for random mode. After that's working, we need to setup the website itself.
root@lain:/srv/lainonlife# mkdir /srv/http/
root@lain:/srv/lainonlife# cp config.json.example config.json
root@lain:/srv/lainonlife# vim config.json
root@lain:/srv/lainonlife# cat config.json
{ "channels":
{ "cyberia": { "mpd_host": "localhost", "mpd_port": 6601, "description": "classic lainchan radio: electronic, chiptune, weeb" }
}
, "template":
{ "default_channel": "cyberia"
, "icecast_status_url": "/radio/status-json.xsl"
, "icecast_stream_url_base": "https://lain.void.yt/"
, "server_cost": 10.00
, "currency_symbol": "€"
}
}
For now we only have one channel, but later on we can always add more. Now let's edit build.sh to use our new config json file:
root@lain:/srv/lainonlife#cd frontend
root@lain:/srv/lainonlife/frontend# vim build.sh
root@lain:/srv/lainonlife/frontend# cat build.sh
#!/usr/bin/env bash
if [ -z "$1" ]; then
#./build.py ../config.json.example
./build.py ../config.json.example
else
./build.py "$1"
fi
Now that's done we can build the frontend assets:
apt install python3-pip python-pip virtualenv -y
root@lain:/srv/lainonlife/frontend# pip install jinja2
root@lain:/srv/lainonlife/frontend# pip3 install jinja2
root@lain:/srv/lainonlife/frontend# which virtualenv
/usr/bin/virtualenv
root@lain:/srv/lainonlife/frontend# ./run.sh ../config.json
root@lain:/srv/lainonlife/frontend# ./run.sh ../config.json
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Requirement already satisfied: jinja2 in ./__venv__/lib/python2.7/site-packages (from -r requirements.txt (line 1)) (2.11.3)
Requirement already satisfied: MarkupSafe>=0.23 in ./__venv__/lib/python2.7/site-packages (from jinja2->-r requirements.txt (line 1)) (1.1.1)
Now that's done, we can see our frontend assets in the _site directory:
root@lain:/srv/lainonlife/frontend# cd _site/
root@lain:/srv/lainonlife/frontend/_site# ls -lash
total 300K
4.0K drwxr-xr-x 9 root root 4.0K Jul 12 10:16 .
4.0K drwxr-xr-x 10 root root 4.0K Jul 12 10:16 ..
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 10:16 404
4.0K -rw-r--r-- 1 root root 1.2K Jul 12 10:16 404.html
52K -rw-r--r-- 1 root root 52K Jul 12 10:16 android-chrome-192x192.png
88K -rw-r--r-- 1 root root 88K Jul 12 10:16 android-chrome-256x256.png
44K -rw-r--r-- 1 root root 41K Jul 12 10:16 apple-touch-icon.png
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 10:16 backgrounds
4.0K -rw-r--r-- 1 root root 246 Jul 12 10:16 browserconfig.xml
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 10:16 bumps
4.0K drwxr-xr-x 3 root root 4.0K Jul 12 10:16 css
4.0K -rw-r--r-- 1 root root 1.2K Jul 12 10:16 donate.html
4.0K -rw-r--r-- 1 root root 1.4K Jul 12 10:16 favicon-16x16.png
4.0K -rw-r--r-- 1 root root 2.9K Jul 12 10:16 favicon-32x32.png
16K -rw-r--r-- 1 root root 15K Jul 12 10:16 favicon.ico
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 10:16 file-list
8.0K -rw-r--r-- 1 root root 4.3K Jul 12 10:16 index.html
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 10:16 js
4.0K -rw-r--r-- 1 root root 414 Jul 12 10:16 manifest.json
28K -rw-r--r-- 1 root root 26K Jul 12 10:16 mstile-150x150.png
4.0K -rw-r--r-- 1 root root 1.8K Jul 12 10:16 safari-pinned-tab.svg
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 10:16 upload
And it's all here! so let's copy it to the root of our webserver at /srv/http/
root@lain:/srv/lainonlife/frontend/_site# cd ..
root@lain:/srv/lainonlife/frontend# cp -r _site/* /srv/http/
now that's done, we need to run the backend bashscript called run.sh and we need to pass it a few environment variables as we run it:
root@lain:/srv/lainonlife/frontend# cd ..
root@lain:/srv/lainonlife# cd backend/
root@lain:/srv/lainonlife/backend# ls -lash
total 36K
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 10:00 .
4.0K drwxr-xr-x 9 root root 4.0K Jul 12 10:05 ..
4.0K -rwxr-xr-x 1 root root 1.5K Jul 11 22:11 backend.py
4.0K -rw-r--r-- 1 root root 21 Jul 11 22:11 .gitignore
4.0K -rw-r--r-- 1 root root 58 Jul 11 22:11 requirements.txt
4.0K -rwxr-xr-x 1 root root 205 Jul 11 22:11 run.sh
8.0K -rw-r--r-- 1 root root 4.1K Jul 11 22:11 stream.py
4.0K -rw-r--r-- 1 root root 3.2K Jul 11 22:11 web.py
root@lain:/srv/lainonlife/backend# CONFIG=../config.json HTTP_DIR=/srv/http PORT=5000 ./run.sh
Basically here it's going to (try to) install the required python dependencies, if it fails like for me you can install the missing dependencies:
[...]
Collecting MarkupSafe>=0.23
Using cached MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl (24 kB)
Installing collected packages: itsdangerous, click, MarkupSafe, Jinja2, Werkzeug, Flask, python-mpd2, pytz, tzlocal, futures, funcsigs, six, apscheduler, chardet, certifi, urllib3, idna, requests
Successfully installed Flask-1.1.4 Jinja2-2.11.3 MarkupSafe-1.1.1 Werkzeug-1.0.1 apscheduler-3.7.0 certifi-2021.5.30 chardet-4.0.0 click-7.1.2 funcsigs-1.0.2 futures-3.3.0 idna-2.10 itsdangerous-1.1.0 python-mpd2-1.1.0 pytz-2021.1 requests-2.25.1 six-1.16.0 tzlocal-2.1 urllib3-1.26.6
Traceback (most recent call last):
File "backend.py", line 7, in module>
import stream as stream
File "/srv/lainonlife/backend/stream.py", line 1, in module>
from apscheduler.schedulers.background import BackgroundScheduler
ModuleNotFoundError: No module named 'apscheduler'
root@lain:/srv/lainonlife/backend# pip3 install apscheduler ; pip install apscheduler
root@lain:/srv/lainonlife/backend# pip install --upgrade pip setuptools
root@lain:/srv/lainonlife/backend# pip3 install --upgrade pip setuptools
root@lain:/srv/lainonlife/backend# pip install --upgrade virtualenv
root@lain:/srv/lainonlife/backend# apt install python-mpd python3-mpd
root@lain:/srv/lainonlife/backend# pip install flask ; pip3 install flask
root@lain:/srv/lainonlife/backend# CONFIG=../config.json HTTP_DIR=/srv/http PORT=5000 ./run.sh
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Requirement already satisfied: Flask>=0.12.0 in ./__venv__/lib/python2.7/site-packages (from -r requirements.txt (line 1)) (1.1.4)
[...]
Requirement already satisfied: MarkupSafe>=0.23 in ./__venv__/lib/python2.7/site-packages (from Jinja2 3.0,>=2.10.1->Flask>=0.12.0->-r requirements.txt (line 1)) (1.1.1)
* Serving Flask app 'web' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
error talking to prometheus: HTTPConnectionPool(host='localhost', port=9090): Max retries exceeded with url: /api/v1/query?query=sum%28listeners%29+by+%28channel%29 (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f82323eabe0>: Failed to establish a new connection: [Errno 111] Connection refused'))
Now it seems we forgot to install prometheus on port 9090. So let's install it following a tutorial i already made here, obviously use an up to date release version of prometheus from their release pages here to this date, the latest version is 2-28.1:
root@lain:/tmp# mkdir /srv/prometheus
root@lain:/tmp# cd /srv/prometheus
root@lain:/srv/prometheus# apt update -y ; apt upgrade -y
root@lain:/srv/prometheus# wget https://github.com/prometheus/prometheus/releases/download/v2.28.1/prometheus-2.28.1.linux-amd64.tar.gz
root@lain:/srv/prometheus# tar -xvzf prometheus-2.28.1.linux-amd64.tar.gz
root@lain:/srv/prometheus# cd prometheus-2.28.1.linux-amd64
root@lain:/srv/prometheus/prometheus-2.28.1.linux-amd64# useradd -rs /bin/false prometheus
root@lain:/srv/prometheus/prometheus-2.28.1.linux-amd64# cp prometheus promtool /usr/local/bin/
root@lain:/srv/prometheus/prometheus-2.28.1.linux-amd64# chown prometheus:prometheus /usr/local/bin/prometheus
root@lain:/srv/prometheus/prometheus-2.28.1.linux-amd64# mkdir /etc/prometheus
root@lain:/srv/prometheus/prometheus-2.28.1.linux-amd64# cp -R consoles/ console_libraries/ prometheus.yml /etc/prometheus/
root@lain:/srv/prometheus/prometheus-2.28.1.linux-amd64# cd /lib/systemd/system
root@lain:/lib/systemd/system# wget https://ech1.github.io/blog/servers/progra/p.service -O /lib/systemd/system/prometheus.service
root@lain:/etc/prometheus# mkdir /data/prometheus -p
root@lain:/etc/prometheus# chown -R prometheus:prometheus /data/prometheus /etc/prometheus/*
root@lain:/lib/systemd/system# systemctl enable --now prometheus
root@lain:/lib/systemd/system# systemctl enable --now prometheus
root@lain:/etc/prometheus# systemctl status prometheus
● prometheus.service - Prometheus
Loaded: loaded (/lib/systemd/system/prometheus.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-07-12 11:01:40 CEST; 1min 9s ago
Main PID: 545 (prometheus)
Tasks: 9 (limit: 4700)
Memory: 29.8M
CGroup: /system.slice/prometheus.service
└─545 /usr/local/bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/data/prometheus --web.console.templates=/etc/prometheus/consoles --web.console.libraries=/etc/prometheus/
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.623Z caller=head.go:780 component=tsdb msg="Replaying on-disk memory mappable chunks if any"
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.624Z caller=head.go:794 component=tsdb msg="On-disk memory mappable chunks replay completed" duration=26.9µs
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.624Z caller=head.go:800 component=tsdb msg="Replaying WAL, this may take a while"
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.627Z caller=head.go:854 component=tsdb msg="WAL segment loaded" segment=0 maxSegment=0
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.629Z caller=head.go:860 component=tsdb msg="WAL replay completed" checkpoint_replay_duration=143.441µs wal_replay_duration=3.876793ms total_re
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.632Z caller=main.go:851 fs_type=EXT4_SUPER_MAGIC
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.633Z caller=main.go:854 msg="TSDB started"
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.633Z caller=main.go:981 msg="Loading configuration file" filename=/etc/prometheus/prometheus.yml
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.636Z caller=main.go:1012 msg="Completed loading of configuration file" filename=/etc/prometheus/prometheus.yml totalDuration=2.953804ms remote
Jul 12 11:01:40 lain prometheus[545]: level=info ts=2021-07-12T09:01:40.636Z caller=main.go:796 msg="Server is ready to receive web requests."
And from here we can check if prometheus is running on the correct port:
Now that prometheus is running on port 9090 we can also setup grafana but we commented it out earlier. Let's run run.sh to see if it works this time:
root@lain:/etc/prometheus# cd /srv/lainonlife/backend/
root@lain:/srv/lainonlife/backend# CONFIG=../config.json HTTP_DIR=/srv/http PORT=5000 ./run.sh
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Requirement already satisfied: Flask>=0.12.0 in ./__venv__/lib/python2.7/site-packages (from -r requirements.txt (line 1)) (1.1.4)
[...]
Requirement already satisfied: MarkupSafe>=0.23 in ./__venv__/lib/python2.7/site-packages (from Jinja2<3.0,>=2.10.1->Flask>=0.12.0->-r requirements.txt (line 1)) (1.1.1)
* Serving Flask app 'web' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
This time around we don't have the missing prometheus errors, so let's check if the server is running properly, and let's restart it to run on port 8002 since we told nginx we would use it for the @script block:
root@lain:/srv/lainonlife/backend# CONFIG=../config.json HTTP_DIR=/srv/http PORT=8002 ./run.sh
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Requirement already satisfied: MarkupSafe>=0.23 in ./__venv__/lib/python2.7/site-packages (from Jinja2<3.0,>=2.10.1->Flask>=0.12.0->-r requirements.txt (line 1)) (1.1.1)
* Serving Flask app 'web' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:8002/ (Press CTRL+C to quit)
Now let's check if it works properly in the browser on port 80:
We got some progress! However we need to correct a few things, first the background:
* Serving Flask app 'web' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:8002/ (Press CTRL+C to quit)
127.0.0.1 - - [12/Jul/2021 11:12:55] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:13:10] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:13:25] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:13:40] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:13:55] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:14:09] "GET /css/font-awesome.min.css HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:14:09] "GET /background HTTP/1.0" 404 -
127.0.0.1 - - [12/Jul/2021 11:14:09] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:14:24] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:14:39] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:14:53] "GET /css/font-awesome.min.css HTTP/1.0" 304 -
127.0.0.1 - - [12/Jul/2021 11:14:53] "GET /background HTTP/1.0" 404 -
127.0.0.1 - - [12/Jul/2021 11:14:53] "GET /playlist/cyberia.json HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 11:15:08] "GET /playlist/cyberia.json HTTP/1.0" 200 -
Let's upload a few backgrounds into /srv/http/backgrounds/:
root@lain:/srv/http# cd backgrounds/
root@lain:/srv/http/backgrounds# ls -l
total 4
-rw-r--r-- 1 root root 68 Jul 12 10:18 index.html
root@lain:/srv/http/backgrounds# wget https://wallpapercave.com/wp/wp6600388.jpg
root@lain:/srv/http/backgrounds# wget https://blog.void.yt/wallpapers/joi1.png
root@lain:/srv/http/backgrounds# wget https://blog.void.yt/wallpapers/wallpaper_blue_original.png
Now that we got some backgrounds we also need the missing /css/font-awesome.min.css in the root of the webdirectory:
root@lain:/srv/lainonlife/# cd /srv/http/css/
root@lain:/srv/http/css# wget https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css
Now we get some missing icons, however this is probably related to the fact that we're not accessing our lainradio server from the correct domain name lain.void.yt:
Now i intend to run this lain radio behind a reverse nginx proxy, so i'll set it up from main debian node:
root@home:~# curl ifconfig.me ; echo; ping lain.void.yt
86.243.158.34
PING void.yt (86.243.158.34) 56(84) bytes of data.
First we get the DNS to point to the right IP, i used a CNAME to point at my root domain's public IP, but you can also use an A record to specify which IP the subdomain / domain name should point to. Now on my main debian node i use acme.sh to get letsencrypt TLS 1.3 certificates, so let's get them after i make the reverse proxy nginx config:
root@home:/var/www/void.yt/config# cd /etc/nginx/sites-available/
root@home:/etc/nginx/sites-available# vim lain.void.yt.conf
root@home:/etc/nginx/sites-available# cat lain.void.yt.conf
upstream lainbackend {
server 10.0.0.201:80;
}
server {
listen 80;
listen [::]:80;
server_name lain.void.yt;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name lain.void.yt;
ssl_certificate /root/.acme.sh/lain.void.yt/fullchain.cer;
ssl_trusted_certificate /root/.acme.sh/lain.void.yt/lain.void.yt.cer;
ssl_certificate_key /root/.acme.sh/lain.void.yt/lain.void.yt.key;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers 'TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_ecdh_curve auto;
ssl_stapling on;
ssl_stapling_verify on;
resolver 80.67.188.188 80.67.169.40 valid=300s;
resolver_timeout 10s;
add_header X-XSS-Protection "1; mode=block"; #Cross-site scripting
add_header X-Frame-Options "SAMEORIGIN" always; #clickjacking
add_header X-Content-Type-Options nosniff; #MIME-type sniffing
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
location / {
proxy_pass http://lainbackend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
You can tweak it however you like it, then we enable the site and get the letsencrypt certificates:
root@home:/etc/nginx/sites-available# ln -s /etc/nginx/sites-available/lain.void.yt.conf /etc/nginx/sites-enabled/
root@home:/etc/nginx/sites-available# nginx -t
nginx: [emerg] BIO_new_file("/root/.acme.sh/lain.void.yt/fullchain.cer") failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/root/.acme.sh/lain.void.yt/fullchain.cer','r') error:2006D080:BIO routines:BIO_new_file:no such file)
nginx: configuration file /etc/nginx/nginx.conf test failed
root@home:/etc/nginx/sites-available# systemctl stop nginx
root@home:/etc/nginx/sites-available# acme.sh --issue --standalone -d lain.void.yt -k 4096
[...]
[Mon 12 Jul 2021 12:08:38 PM CEST] Your cert is in /root/.acme.sh/lain.void.yt/lain.void.yt.cer
[Mon 12 Jul 2021 12:08:38 PM CEST] Your cert key is in /root/.acme.sh/lain.void.yt/lain.void.yt.key
[Mon 12 Jul 2021 12:08:38 PM CEST] The intermediate CA cert is in /root/.acme.sh/lain.void.yt/ca.cer
[Mon 12 Jul 2021 12:08:38 PM CEST] And the full chain certs is there: /root/.acme.sh/lain.void.yt/fullchain.cer
Once that's done we can reactivate nginx and see the result:
root@home:/etc/nginx/sites-available# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
root@home:/etc/nginx/sites-available# systemctl start nginx
Now we access our webservice and we see that the domain has been verified by letsencrypt, but we're still missing the song names: so let's take a look into the /srv/lainonlife/scripts/ directory:
root@lain:/srv/lainonlife/scripts# ls -lash
total 24K
4.0K drwxr-xr-x 2 root root 4.0K Jul 12 12:23 .
4.0K drwxr-xr-x 9 root root 4.0K Jul 12 10:05 ..
4.0K -rwxr-xr-x 1 root root 2.3K Jul 11 22:11 album_times.py
4.0K -rwxr-xr-x 1 root root 943 Jul 11 22:11 file-list.sh
8.0K -rwxr-xr-x 1 root root 4.5K Jul 11 22:11 schedule.py
Here we have some scripts we should run. An easy one is file-list.sh:
root@lain:/srv/lainonlife/scripts# ./file-list.sh
That script is there so we can get a list of files to use:
Next we need to figure out schedule.py:
root@lain:/srv/lainonlife/scripts# pip3 install docopt
root@lain:/srv/lainonlife/scripts# python3 schedule.py --host=127.0.0.1 6601
Now if this schedule.py script errors out, that's because it's looking for the Lainchan Radio Transitions album, so to make this script work, we need the aforementionned album containing some songs, so let's create it with tracks we put into /srv/radio/music/cyberia/transitions/:
root@lain:/srv/radio/music/cyberia# mkdir transitions
root@lain:/srv/radio/music/cyberia/transitions# apt install ffmpeg
root@lain:/srv/radio/music/cyberia/transitions# youtube-dl -x https://www.youtube.com/playlist?list=OLAK5uy_kBzNHyOb4LcpwQI-uca1LApynboNkEvAo --audio-format mp3
[youtube:tab] OLAK5uy_kBzNHyOb4LcpwQI-uca1LApynboNkEvAo: Downloading webpage
[download] Downloading playlist: Coloris
[youtube:tab] playlist Coloris: Downloading 15 videos
[download] Downloading video 1 of 15
[youtube] YTY7UV6GpkM: Downloading webpage
[download] Coloris-YTY7UV6GpkM.webm has already been downloaded
[download] 100% of 2.57MiB
[ffmpeg] Destination: Coloris-YTY7UV6GpkM.mp3
[...]
[youtube] 75yOApYoc6Y: Downloading webpage
[download] Destination: Traveling By Night-75yOApYoc6Y.webm
[download] 100% of 2.59MiB in 00:00
[ffmpeg] Destination: Traveling By Night-75yOApYoc6Y.mp3
Deleting original file Traveling By Night-75yOApYoc6Y.webm (pass -k to keep)
[download] Finished downloading playlist: Coloris
Now that we have our transition tracks let's add them to the correct album via ncmpcpp:
root@lain:/srv/radio/music/cyberia/transitions# cd ..
root@lain:/srv/radio/music/cyberia# tree .
.
├── Lain's Theme - Lain-o-cwuTmqz8c.mp3
├── Serial Experiment Lain - Antidepressant 044-KaOsmUDMdaE.mp3
├── Serial Experiment Lain - Cloudy with occasional rain-1TunrW7dRr0.mp3
├── Serial Experiment Lain - Duvet (cyberia remix)-juDqDMlTfUg.mp3
├── Serial Experiment Lain - Duvet (tv version)-EnEaNaqGMqU.mp3
├── Serial Experiment Lain - Infanity world-AsPLBQfZQ04.mp3
├── Serial Experiment Lain - Invisible file-32IlMQ8Bs6w.mp3
├── Serial Experiment Lain - Island in video cassette-CCdyzUPrLpM.mp3
├── Serial Experiment Lain - k.i.d.s-TvCJnW46ISo.mp3
├── Serial Experiment Lain - Prayer-yyPMWWjzMG8.mp3
├── Serial Experiment Lain - Professed intention and real-D4G97Xsc8PA.mp3
├── Serial Experiment Lain - Psychedelic farm-XEGz6CJnY04.mp3
├── Serial Experiment Lain - Speed-zFwQFdAsGGA.mp3
├── Serial Experiments Lain - Cyberia Theme-5dbi4N6NGn4.mp3
└── transitions
├── Autumn In Space-nWqcLWxZOH0.mp3
├── Circuit Lover-zduKG8YUKxM.mp3
├── Coloris-YTY7UV6GpkM.mp3
├── Destination Luna4-VGaQwDylJPM.mp3
├── Distort Into Me-_zLINqJYH0U.mp3
├── Fuse-MhHshcp5tQs.mp3
├── Gum-tTGBo0Fmf4A.mp3
├── In Time-8PmEsQY5rq4.mp3
├── Monochrome-uFNppANT-Dk.mp3
├── Orbit-SHFmUt1Jit4.mp3
├── Reality-C0ybGDNZos8.mp3
├── Together-ohViqbZZnpE.mp3
├── Tokyo Nights-5fXc8aoF2eU.mp3
├── Touch and Go-gxfyax1IPdk.mp3
└── Traveling By Night-75yOApYoc6Y.mp3
root@lain:/srv/radio/music/cyberia# ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
#press 2 and u to update the database, and you will see the transitions directory appear:
#and in it we have our transition audio tracks:
#so let's add them to the correct playlist album:
#select the tracks with INSERT
#then press a to add them to the Lainchan Radio Transitions playlist:
#press 5 to go to the playlists and you should see that result:
#now add a few more albums just like we did previously
#so that schedule.py has a few albums to cycle through and to put transitions in between:
root@lain:/srv/radio/music/cyberia# youtube-dl -x --audio-format mp3 https://www.youtube.com/playlist?list=PL9AnroPjNnk0ukW3RvRQlbxA6nsNjG_9k
root@lain:/srv/radio/music/cyberia# youtube-dl -x --audio-format mp3 https://www.youtube.com/playlist?list=PLEBA2CD188C3A8278
root@lain:/srv/radio/music/cyberia# youtube-dl -x --audio-format mp3 https://www.youtube.com/playlist?list=PL4D3810FF245A180B
#Each time make sure you add them to separate albums with ncmpcpp just like we previously did to keep things clean.
So right now i have 3 albums (playlists) and a transition playlist, now let's use schedule.py:
root@lain:/srv/lainonlife/scripts# ls -l
total 16
-rwxr-xr-x 1 root root 2334 Jul 11 22:11 album_times.py
-rwxr-xr-x 1 root root 943 Jul 11 22:11 file-list.sh
-rwxr-xr-x 1 root root 4631 Jul 12 12:48 schedule.py
root@lain:/srv/lainonlife/scripts# python3 schedule.py --host=127.0.0.1 6601
Traceback (most recent call last):
File "schedule.py", line 168, in <module>
schedule_radio(client)
File "schedule.py", line 114, in schedule_radio
album, album_dur = pick_album(client, target_dur)
File "schedule.py", line 68, in pick_album
album = all_albums[0]
IndexError: list index out of range
Apologies on my part, for MPD playlists are NOT albums, so we need to create albums for each artist:
To do that we can add id3 tags:
root@lain:/srv/lainonlife/scripts# apt install libid3-tools -y
root@lain:/srv/lainonlife/scripts# which id3tag
/usr/bin/id3tag
So with id3tag we're going to add the album tags to our mp3 files:
root@lain:/srv/radio/music# vim tagger.sh
Now before running it we're going to add a tag to one track and see if it works as intended:
cd cyberia/transitions/
root@lain:/srv/radio/music/cyberia/transitions# ls -lash Coloris-YTY7UV6GpkM.mp3
2.5M -rw-r--r-- 1 root root 2.5M Sep 15 2019 Coloris-YTY7UV6GpkM.mp3
Now since this is a Transition track let's add the Lainchan Radio Transitions album tag to it:
root@lain:/srv/radio/music/cyberia/transitions# id3tag --album "Lainchan Radio Transitions" Coloris-YTY7UV6GpkM.mp3
+++ Album = Lainchan Radio Transitions
Tagging Coloris-YTY7UV6GpkM.mp3: attempting v1 and v2, tagged v1 and v2
And when we check it inside of ncmpcpp we see the following after updating the database with u:
root@lain:/srv/radio/music/cyberia/transitions# ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
#press u to update the database
#press 4 to goto the Media Library
So right here we have been able to create an album thanks to the id3tag tool, now if you're a masochist you can go ahead and do that manually for every song, if you're like me and want to make things simpler, we can use a script. i place it into /srv/radio/music/ to tag the transition tracks:
root@lain:/srv/radio/music# vim tagger_transitions.sh
root@lain:/srv/radio/music# cat tagger_transitions.sh
#! /bin/bash
GREEN="\033[0;32m"
ORANGE="\033[0;33m"
RED="\033[0;31m"
NC="\033[0m"
echo -en "${GREEN}[+]${NC} Type the name of the channel directory (ex: cyberia): "
read channel
echo -en "${GREEN}[+]${NC} default album name: ${GREEN} Lainchan Radio Transitions"
album="Lainchan Radio Transitions"
#### First list the songs and ask if the user wants to add the album tag to them:
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
cd $channel/transitions
SONGS=*
for SONG in $SONGS ;
do
echo -en "\n ${ORANGE}[+] $SONG${NC}"
done
cd ../..
#restore IFS
IFS=$SAVEIFS
echo -en "\n\n${GREEN}[+]${NC} add the ${RED}$album${NC} ${ORANGE}album${NC} tag to these songs ? (y/n)"
read choice
####### If user says 'y' then add the album tag to the selected songs:
if [ "$choice" == "y" ];
then
echo 'yes'
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
cd $channel/transitions
SONGS=*
for SONG in $SONGS ;
do
echo -en "\n ${GREEN}[+] $SONG${RED} ADDING TAG: ${ORANGE}$album${NC}"
id3tag --album="\"$album\"" $SONG
done
cd ../..
#restore IFS
IFS=$SAVEIFS
else
echo 'cancelling...'
fi
echo
exit
So for me this script works, make sure you place it in /srv/radio/music/ otherwise it will not work as intended.
root@lain:/srv/radio/music# ./tagger_transitions.sh
[+] Type the name of the channel directory (ex: cyberia): cyberia
[+] default album name: Lainchan Radio Transitions
[+] Autumn In Space-nWqcLWxZOH0.mp3
[+] Circuit Lover-zduKG8YUKxM.mp3
[+] Coloris-YTY7UV6GpkM.mp3
[+] Destination Luna4-VGaQwDylJPM.mp3
[+] Distort Into Me-_zLINqJYH0U.mp3
[+] Fuse-MhHshcp5tQs.mp3
[+] Gum-tTGBo0Fmf4A.mp3
[+] In Time-8PmEsQY5rq4.mp3
[+] Monochrome-uFNppANT-Dk.mp3
[+] Orbit-SHFmUt1Jit4.mp3
[+] Reality-C0ybGDNZos8.mp3
[+] Together-ohViqbZZnpE.mp3
[+] Tokyo Nights-5fXc8aoF2eU.mp3
[+] Touch and Go-gxfyax1IPdk.mp3
[+] Traveling By Night-75yOApYoc6Y.mp3
[+] add the Lainchan Radio Transitions album tag to these songs ? (y/n)y
yes
[+] Autumn In Space-nWqcLWxZOH0.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Autumn In Space-nWqcLWxZOH0.mp3: attempting v1 and v2, tagged v1 and v2
[+] Circuit Lover-zduKG8YUKxM.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Circuit Lover-zduKG8YUKxM.mp3: attempting v1 and v2, tagged v1 and v2
[+] Coloris-YTY7UV6GpkM.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Coloris-YTY7UV6GpkM.mp3: attempting v1 and v2, tagged v1 and v2
[+] Destination Luna4-VGaQwDylJPM.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Destination Luna4-VGaQwDylJPM.mp3: attempting v1 and v2, tagged v1 and v2
[+] Distort Into Me-_zLINqJYH0U.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Distort Into Me-_zLINqJYH0U.mp3: attempting v1 and v2, tagged v1 and v2
[+] Fuse-MhHshcp5tQs.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Fuse-MhHshcp5tQs.mp3: attempting v1 and v2, tagged v1 and v2
[+] Gum-tTGBo0Fmf4A.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Gum-tTGBo0Fmf4A.mp3: attempting v1 and v2, tagged v1 and v2
[+] In Time-8PmEsQY5rq4.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging In Time-8PmEsQY5rq4.mp3: attempting v1 and v2, tagged v1 and v2
[+] Monochrome-uFNppANT-Dk.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Monochrome-uFNppANT-Dk.mp3: attempting v1 and v2, tagged v1 and v2
[+] Orbit-SHFmUt1Jit4.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Orbit-SHFmUt1Jit4.mp3: attempting v1 and v2, tagged v1 and v2
[+] Reality-C0ybGDNZos8.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Reality-C0ybGDNZos8.mp3: attempting v1 and v2, tagged v1 and v2
[+] Together-ohViqbZZnpE.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Together-ohViqbZZnpE.mp3: attempting v1 and v2, tagged v1 and v2
[+] Tokyo Nights-5fXc8aoF2eU.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Tokyo Nights-5fXc8aoF2eU.mp3: attempting v1 and v2, tagged v1 and v2
[+] Touch and Go-gxfyax1IPdk.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Touch and Go-gxfyax1IPdk.mp3: attempting v1 and v2, tagged v1 and v2
[+] Traveling By Night-75yOApYoc6Y.mp3 ADDING TAG: Lainchan Radio Transitions+++ Album = "Lainchan Radio Transitions"
Tagging Traveling By Night-75yOApYoc6Y.mp3: attempting v1 and v2, tagged v1 and v2
Now to tag the mp3 files in /channel/songname.mp3 with their album names i made a different script in /srv/radio/music/tagger.sh, again make sure the location is correct otherwise it won't work either :
root@lain:/srv/radio/music# cat tagger.sh
#! /bin/bash
GREEN="\033[0;32m"
ORANGE="\033[0;33m"
RED="\033[0;31m"
NC="\033[0m"
echo -en "${GREEN}[+]${NC} Type the name of the channel directory (ex: cyberia): "
read channel
echo -en "${GREEN}[+]${NC} Type the name of the album: "
read album
echo -en "${GREEN}[+]${NC} Type the name of the regex to match the album: "
read regex
#### First list the songs and ask if the user wants to add the album tag to them:
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
cd $channel
SONGS=*$regex*
for SONG in $SONGS ;
do
echo -en "\n ${ORANGE}[+] $SONG${NC}"
done
cd ..
#restore IFS
IFS=$SAVEIFS
echo -en "\n\n${GREEN}[+]${NC} add the ${RED}$album${NC} ${ORANGE}album${NC} tag to these songs ? (y/n)"
read choice
####### If user says 'y' then add the album tag to the selected songs:
if [ "$choice" == "y" ];
then
echo 'yes'
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
cd $channel
SONGS=*$regex*
for SONG in $SONGS ;
do
echo -en "\n ${GREEN}[+] $SONG${RED} ADDING TAG: ${ORANGE}$album${NC}"
id3tag --album="\"$album\"" $SONG
done
cd ..
#restore IFS
IFS=$SAVEIFS
else
echo 'cancelling....'
fi
echo
exit
This time we specify the channel, the album, and the regex that we will use to match the files to add the tag to:
root@lain:/srv/radio/music# ./tagger.sh
[+] Type the name of the channel directory (ex: cyberia): cyberia
[+] Type the name of the album: Pendulum - Hold your Color^C
root@lain:/srv/radio/music# ./tagger.sh
[+] Type the name of the channel directory (ex: cyberia): cyberia
[+] Type the name of the album: Infected Mushroom - Army Of Mushrooms
[+] Type the name of the regex to match the album: Infected
[+] Infected Mushroom - (01) Nevermind [HQ] 2012-Fzj7BZeqVzk.mp3
[+] Infected Mushroom - (02) Nothing to Say [HQ] 2012-fQcB8yo46eI.mp3
[+] Infected Mushroom - (03) Send Me an Angel [HQ] 2012-W2Ks5AkCHow.mp3
[+] Infected Mushroom - (04) U R So Fucked [HQ] 2012-62gm17b2bMI.mp3
[+] Infected Mushroom - (05) The Rat [HQ] 2012-nHIWgbBDx0o.mp3
[+] Infected Mushroom - (06) Nation of Wusses [HQ] 2012-L25QxK0Csg0.mp3
[+] Infected Mushroom - (07) Wanted To [HQ] 2012-2PaSS6JFvXc.mp3
[+] Infected Mushroom - (08) Serve My Thirst [HQ] 2012-_J6feIrbcEo.mp3
[+] Infected Mushroom (09) - I Shine [HQ] 2012-OtbFUedy6Xs.mp3
[+] Infected Mushroom - (10) Drum n Baasa [HQ] 2012-4IhPuiuYx30.mp3
[+] Infected Mushroom - (11) The Pretender (Foo Fighters Cover) [HQ] 2012-boKAyiI-8WQ.mp3
[+] Infected Mushroom - (12) The Messenger 2012 [HQ] 2012-x0lRqrID3i4.mp3
[+] Infected Mushroom - (13) Swingish (Bonus Track) [HQ] 2012-7KwBE6XJbj4.mp3
[+] add the Infected Mushroom - Army Of Mushrooms album tag to these songs ? (y/n)y
yes
[+] Infected Mushroom - (01) Nevermind [HQ] 2012-Fzj7BZeqVzk.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (01) Nevermind [HQ] 2012-Fzj7BZeqVzk.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (02) Nothing to Say [HQ] 2012-fQcB8yo46eI.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (02) Nothing to Say [HQ] 2012-fQcB8yo46eI.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (03) Send Me an Angel [HQ] 2012-W2Ks5AkCHow.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (03) Send Me an Angel [HQ] 2012-W2Ks5AkCHow.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (04) U R So Fucked [HQ] 2012-62gm17b2bMI.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (04) U R So Fucked [HQ] 2012-62gm17b2bMI.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (05) The Rat [HQ] 2012-nHIWgbBDx0o.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (05) The Rat [HQ] 2012-nHIWgbBDx0o.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (06) Nation of Wusses [HQ] 2012-L25QxK0Csg0.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (06) Nation of Wusses [HQ] 2012-L25QxK0Csg0.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (07) Wanted To [HQ] 2012-2PaSS6JFvXc.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (07) Wanted To [HQ] 2012-2PaSS6JFvXc.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (08) Serve My Thirst [HQ] 2012-_J6feIrbcEo.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (08) Serve My Thirst [HQ] 2012-_J6feIrbcEo.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom (09) - I Shine [HQ] 2012-OtbFUedy6Xs.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom (09) - I Shine [HQ] 2012-OtbFUedy6Xs.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (10) Drum n Baasa [HQ] 2012-4IhPuiuYx30.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (10) Drum n Baasa [HQ] 2012-4IhPuiuYx30.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (11) The Pretender (Foo Fighters Cover) [HQ] 2012-boKAyiI-8WQ.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (11) The Pretender (Foo Fighters Cover) [HQ] 2012-boKAyiI-8WQ.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (12) The Messenger 2012 [HQ] 2012-x0lRqrID3i4.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (12) The Messenger 2012 [HQ] 2012-x0lRqrID3i4.mp3: attempting v1 and v2, tagged v1 and v2
[+] Infected Mushroom - (13) Swingish (Bonus Track) [HQ] 2012-7KwBE6XJbj4.mp3 ADDING TAG: Infected Mushroom - Army Of Mushrooms+++ Album = "Infected Mushroom - Army Of Mushrooms"
Tagging Infected Mushroom - (13) Swingish (Bonus Track) [HQ] 2012-7KwBE6XJbj4.mp3: attempting v1 and v2, tagged v1 and v2
After adding the other albums we can check the result in ncmpcpp:
root@lain:/srv/radio/music# ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
#press 2 to get to the browsing tab
#press u to update the database
#see the album tags appear:
#press 4 to get to the media library:
We also fix the missing fonts in /srv/http/fonts/
127.0.0.1 - - [12/Jul/2021 14:55:14] "GET /fonts/fontawesome-webfont.woff2?v=4.7.0 HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 14:55:16] "GET /fonts/fontawesome-webfont.woff?v=4.7.0 HTTP/1.0" 200 -
127.0.0.1 - - [12/Jul/2021 14:55:17] "GET /fonts/fontawesome-webfont.ttf?v=4.7.0 HTTP/1.0" 304 -
root@lain:/srv/http# mkdir fonts
root@lain:/srv/http# cd fonts
root@lain:/srv/http/fonts# wget https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.woff2
root@lain:/srv/http/fonts# wget https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/fonts/fontawesome-webfont.ttf
Now the fonts are fixed once we check them in the browser:
first edit : index.html had broken links to the radio:
root@lain:/srv/http# cat index.html | grep void
lain.void.yt
const ICECAST_STREAM_URL_BASE = "https://lain.void.yt/radio/";
<source src="https://lain.void.yt//cyberia.ogg" type="audio/ogg"/>
<source src="https://lain.void.yt//cyberia.mp3" type="audio/mpeg"/>
<a id="ogglink" href="https://lain.void.yt//cyberia.ogg.m3u">ogg</a> /
<a id="mp3link" href="https://lain.void.yt//cyberia.mp3.m3u">mp3</a>
root@lain:/srv/http# vim index.html
root@lain:/srv/http# cat index.html | grep void
lain.void.yt
const ICECAST_STREAM_URL_BASE = "https://lain.void.yt/radio/";
<source src="https://lain.void.yt/radio/cyberia.ogg" type="audio/ogg"/>
<source src="https://lain.void.yt/radio/cyberia.mp3" type="audio/mpeg"/>
<a id="ogglink" href="https://lain.void.yt/radio/cyberia.ogg.m3u">ogg</a> /
<a id="mp3link" href="https://lain.void.yt/radio/cyberia.mp3.m3u">mp3</a>
Now the radio actually plays from the browser :
However the title of each track are still missing.
EDIT 2: figured out how to go around the schedule.py errors
root@lain:/srv/lainonlife/scripts# cat schedule.py
#!/usr/bin/env python3
"""Radio scheduling program.
Usage:
schedule.py [--host=HOST] PORT
Options:
--host=HOST Hostname of MPD [default: localhost]
-h --help Show this text
Takes a port number, and does the following:
1. Sets the play order to normal, looping.
2. Clears all music before the cursor position in the playlist.
3. Appends a roughly three-hour block of music to the end of the playlist in this format:
a. A transition track
b. A full album
c. Enough random tracks to make up the difference.
The new segment may be a little shorter if an exact fit is not possible, but in practice it will
be close.
Why do this? Listening to entire albums in order is nice, as tracks in an album often build off
each other. On the other hand, variety is also nice!
"""
from docopt import docopt
from mpd import MPDClient
from random import shuffle
def duration_of(filterty, filterval):
"""Get the combined duration of all tracks matching a filter."""
return int(client.count(filterty, filterval).get("playtime", "0"))
def pick_transition(client):
"""Picks a transition track."""
all_transitions = list(
filter(lambda t: "directory" not in t, client.listall("transitions"))
)
shuffle(all_transitions)
transition = all_transitions[0]["file"]
transition_dur = duration_of("file", transition)
return transition, transition_dur
def pick_album(client, dur):
"""Picks a random album which fits in the duration."""
# Get all albums
all_albums = [
a["album"]
for a in client.list("album")
if "album" in a and a["album"] not in ["", "Lainchan Radio Transitions"]
]
for a in client.list("album"):
print(a)
all_albums.append(a)
print(all_albums)
shuffle(all_albums)
album = all_albums[0]
album_dur = duration_of("album", album)
return album, album_dur
def pick_tracks(client, chosen_album, dur):
"""Attempts to pick a list of tracks to fill the given time.
Radio transitions and the chosen album are excluded from the list.
Because retrieving and operating over the list of all tracks is expensive, this does not try
more than once. It uses the simple greedy algorithm, and so may exceed the limit.
"""
all_tracks = []
for t in client.list("file"):
#print(t)
all_tracks.append(t)
#print(all_tracks)
#all_tracks = [
# t["file"]
# for t in client.list("file")
# if "file" in t
#]
shuffle(all_tracks)
chosen = []
remaining = dur
for t in all_tracks:
album = client.list("album", "file", t)[0]
duration = duration_of("file", t)
if album in [chosen_album, "Lainchan Radio Transitions"]:
continue
if duration > remaining:
continue
chosen.append(t)
remaining = remaining - duration
return chosen, dur - remaining
def schedule_radio(client, target_dur= 3 * 60 * 60):
"""Schedule music.
Keyword arguments:
target_dur -- the target duration to fill.
"""
# Pick a transition and two albums
transition, transition_dur = pick_transition(client)
album, album_dur = pick_album(client, target_dur)
# Determine how much time is remaining in the two-hour slot
time_remaining = target_dur - transition_dur - album_dur
# Pick a list of tracks to fill the gap
tracks, tracks_dur = pick_tracks(client, album, time_remaining)
# Some stats first
print("Transition: {} ({}s)".format(transition, transition_dur))
print("Album: {} ({}s)".format(album, album_dur))
print("Tracks: #{} ({}s)".format(len(tracks), tracks_dur))
print()
print("Target duration: {}s".format(target_dur))
print("Actual duration: {}s".format(transition_dur + album_dur + tracks_dur))
print("Difference: {}s".format(time_remaining - tracks_dur))
# Set playback to in-order repeat
client.random(0)
client.repeat(1)
# Make sure it's playing
client.play()
# Add tracks
client.add(transition)
client.findadd("album", album)
for t in tracks:
client.add(t)
# Delete up to the current track, minus 10 tracks (for the web
# playlist)
status = client.status()
if "song" in status:
client.delete((0, max(0, int(status["song"]) - 10)))
if __name__ == "__main__":
args = docopt(__doc__)
try:
args["PORT"] = int(args["PORT"])
except ValueError:
print("PORT must be an integer")
exit(1)
try:
client = MPDClient()
client.connect(args["--host"], args["PORT"])
except Exception as e:
print(f"could not connect to MPD: {e.args[0]}")
exit(2)
client.update()
schedule_radio(client)
Editing schedule.py to fix the python errors now it can be used:
root@lain:/srv/lainonlife/scripts# python3 schedule.py --host=127.0.0.1 6601
"Cyberia"
"Infected Mushroom - Army Of Mushrooms"
"Lainchan Radio Transitions"
"Pendulum"
['"Cyberia"', '"Infected Mushroom - Army Of Mushrooms"', '"Lainchan Radio Transitions"', '"Pendulum"']
Transition: transitions/Destination Luna4-VGaQwDylJPM.mp3 (142s)
Album: "Pendulum" (8631s)
Tracks: #0 (0s)
Target duration: 1800s
Actual duration: 8773s
Difference: -6973s
Now another guess for the 'undefined' song names problem, i assume that it's the missing tag titles, so let's see if that's the case:
root@lain:/srv/radio/music/cyberia# id3tag --song "Pendulum - The Tempest" The\ Tempest\ -\ Pendulum\ \[HQ\]-uy-3tWaKyg8.mp3
+++ Song = Pendulum - The Tempest
Tagging The Tempest - Pendulum [HQ]-uy-3tWaKyg8.mp3: attempting v1 and v2, tagged v1 and v2
root@lain:/srv/radio/music/cyberia# id3tag --song "Pendulum - The Other Side" Pendulum\ \- The\ Other\ Side\ -\ Pendulum\ \[HQ\]-z0hwFJ1rbbU.mp3
+++ Song = Pendulum - The Other Side
root@lain:/srv/radio/music/cyberia# ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
#press u to update database
Looks like we were right! What we were missing here was the song/title tags for each mp3 file. so let's make a script to automatically add all of them:
root@lain:/srv/radio/music# vim tagger_titles.sh
root@lain:/srv/radio/music# cat tagger_titles.sh
#! /bin/bash
GREEN="\033[0;32m"
ORANGE="\033[0;33m"
RED="\033[0;31m"
NC="\033[0m"
echo -en "${GREEN}[+]${NC} Type the name of the channel directory (ex: cyberia): "
read channel
#### First list the songs and ask if the user wants to add the album tag to them:
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
cd $channel
SONGS=*
for SONG in $SONGS ;
do
echo -en "\n ${ORANGE}[+] $SONG${NC}"
done
cd ..
#restore IFS
IFS=$SAVEIFS
echo -en "\n\n${GREEN}[+]${NC} add the filename as ${ORANGE}title${NC} tag to these songs ? (y/n)"
read choice
####### If user says 'y' then add the album tag to the selected songs:
if [ "$choice" == "y" ];
then
echo 'yes'
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
cd $channel
SONGS=*
for SONG in $SONGS ;
do
echo -en "\n ${GREEN}[+] $SONG${RED} ADDING TITLE TAG: ${ORANGE}$SONG${NC}"
id3tag --song="\"$SONG\"" $SONG
done
cd ..
#restore IFS
IFS=$SAVEIFS
else
echo 'cancelling....'
fi
echo
exit
Now that's done we update the database with ncmpcpp as usual:
root@lain:/srv/radio/music# ncmpcpp -h 127.0.0.1 -p 6601 -c ~/.config/ncmpcpp/config
#press u to update the database
And there you go! We managed to make each file's name display.
So now that we know our webserver is working as intended, let's write systemd services to handle automatically mpd, icecast and the run.sh bashscript:
root@lain:~# icecast -c /srv/Icecast-Server/conf/icecast.xml
root@lain:~# CONFIG=../config.json HTTP_DIR=/srv/http PORT=8002 ./run.sh
root@lain:~# mpd ~/.config/mpd.conf
In essence we need a systemd service to make the root user run these 3 commands so let's make them one by one. First the icecast systemd service:
root@lain:/srv/radio/music# cd /etc/systemd/system/
root@lain:/etc/systemd/system# vim icecast.service
root@lain:/etc/systemd/system# cat icecast.service
[Unit]
Description=Icecast systemd service with config in /srv/Icecast-Server/conf/icecast.xml
After=network.target
After=systemd-user-sessions.service
After=network-online.target
[Service]
Type=simple
ExecStart=icecast -c /srv/Icecast-Server/conf/icecast.xml
Restart=always
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
root@lain:/etc/systemd/system# systemctl daemon-reload
root@lain:/etc/systemd/system# systemctl enable --now icecast
Created symlink /etc/systemd/system/multi-user.target.wants/icecast.service → /etc/systemd/system/icecast.service.
root@lain:/etc/systemd/system# systemctl status icecast
● icecast.service - Icecast systemd service with config in /srv/Icecast-Server/conf/icecast.xml
Loaded: loaded (/etc/systemd/system/icecast.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-07-12 22:52:27 CEST; 5s ago
Main PID: 10092 (icecast)
Tasks: 4 (limit: 4700)
Memory: 1.0M
CGroup: /system.slice/icecast.service
└─10092 /usr/local/bin/icecast -c /srv/Icecast-Server/conf/icecast.xml
Jul 12 22:52:27 lain systemd[1]: Started Icecast systemd service with config in /srv/Icecast-Server/conf/icecast.xml.
Jul 12 22:52:27 lain icecast[10092]: [2021-07-12 22:52:27] WARN CONFIG/_parse_root Warning, <location> not configured, using default value "Earth".
Jul 12 22:52:27 lain icecast[10092]: [2021-07-12 22:52:27] WARN CONFIG/_parse_root Warning, <admin> contact not configured, using default value "icemaster@localhost".
We do the same for the backend script run.sh used to run the radio itself:
root@lain:/etc/systemd/system# vim lainradio.service
root@lain:/etc/systemd/system# cat lainradio.service
[Unit]
Description=running the run.sh script with the correct arguements
After=network.target
After=systemd-user-sessions.service
After=network-online.target
[Service]
Type=simple
WorkingDirectory=/srv/lainonlife/backend/
Environment="CONFIG=../config.json"
Environment="HTTP_DIR=/srv/http"
Environment="PORT=8002"
ExecStart=/srv/lainonlife/backend/run.sh
Restart=always
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
root@lain:/etc/systemd/system# systemctl daemon-reload
root@lain:/etc/systemd/system# systemctl enable --now lainradio
root@lain:/etc/systemd/system# systemctl status lainradio
● lainradio.service - running the run.sh script with the correct arguements
Loaded: loaded (/etc/systemd/system/lainradio.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-07-12 23:03:15 CEST; 2min 47s ago
Main PID: 10345 (bash)
Tasks: 13 (limit: 4700)
Memory: 30.8M
CGroup: /system.slice/lainradio.service
├─10345 bash /srv/lainonlife/backend/run.sh
└─10354 python3 backend.py
Jul 12 23:04:58 lain run.sh[10345]: 127.0.0.1 - - [12/Jul/2021 23:04:58] "GET /playlist/cyberia.json HTTP/1.0" 200 -
Jul 12 23:04:58 lain run.sh[10345]: 127.0.0.1 - - [12/Jul/2021 23:04:58] "GET /playlist/cyberia.json HTTP/1.0" 200 -
Jul 12 23:05:13 lain run.sh[10345]: 127.0.0.1 - - [12/Jul/2021 23:05:13] "GET /playlist/cyberia.json HTTP/1.0" 200 -
Jul 12 23:05:13 lain run.sh[10345]: 127.0.0.1 - - [12/Jul/2021 23:05:13] "GET /playlist/cyberia.json HTTP/1.0" 200 -
Jul 12 23:05:28 lain run.sh[10345]: 127.0.0.1 - - [12/Jul/2021 23:05:28] "GET /playlist/cyberia.json HTTP/1.0" 200 -
Now that's done we're going to setup the systemd service for mpd, now i intend to make a multiple-channel setup so i will make mpd use the config file:
root@lain:/etc/systemd/system# vim mpd-cyberia.service
root@lain:/etc/systemd/system# cat mpd-cyberia.service
[Unit]
Description=mpd systemd service with config in /root/.config/mpd/cyberia.conf
After=network.target
After=systemd-user-sessions.service
After=network-online.target
[Service]
Type=simple
ExecStart=mpd --no-daemon /root/.config/mpd/cyberia.conf
Restart=always
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
root@lain:/etc/systemd/system# systemctl daemon-reload
root@lain:/etc/systemd/system#
root@lain:/etc/systemd/system# cd ~/.config/mpd/
root@lain:~/.config/mpd# ls -l
total 4
-rw-r--r-- 1 root root 1773 Jul 12 09:32 mpd.conf
root@lain:~/.config/mpd# cp mpd.conf cyberia.conf
root@lain:~/.config/mpd# cd -
/etc/systemd/system
root@lain:/etc/systemd/system# systemctl disable mpd
root@lain:/etc/systemd/system# kill $(pidof mpd)
root@lain:/etc/systemd/system# kill $(pidof mpd)
root@lain:/etc/systemd/system# kill $(pidof mpd)
root@lain:/etc/systemd/system# systemctl enable --now mpd-cyberia
root@lain:/etc/systemd/system# systemctl status mpd-cyberia.service
● mpd-cyberia.service - mpd systemd service with config in /root/.config/mpd/cyberia.conf
Loaded: loaded (/etc/systemd/system/mpd-cyberia.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-07-12 23:20:47 CEST; 3min 13s ago
Main PID: 10921 (mpd)
Tasks: 10 (limit: 4700)
Memory: 15.7M
CGroup: /system.slice/mpd-cyberia.service
└─10921 /usr/bin/mpd --no-daemon /root/.config/mpd/cyberia.conf
Jul 12 23:20:47 lain systemd[1]: Started mpd systemd service with config in /root/.config/mpd/cyberia.conf.
Jul 12 23:20:48 lain mpd[10921]: Jul 12 23:20 : hybrid_dsd: The Hybrid DSD decoder is disabled because it was not explicitly enabled
Jul 12 23:20:48 lain mpd[10921]: Jul 12 23:20 : exception: Input plugin 'tidal' is unavailable: No Tidal application token configured
Jul 12 23:20:48 lain mpd[10921]: Jul 12 23:20 : exception: Input plugin 'qobuz' is unavailable: No Qobuz app_id configured
And that's it! We managed to create 3 independent systemd services that will run everytime the host boots up, ensuring that the radio stays up and running from the start automatically. Now that should already be done but make sure that nginx and prometheus' systemd services are enabled too:
root@lain:/etc/systemd/system# systemctl enable --now nginx
root@lain:/etc/systemd/system# systemctl enable --now prometheus
Before adding channels let's get a more organized music folder architecture:
Basically, for each channel, we're going to have a folder for each album, and inside of each album folder we're going to have our song files. And also let's not forget that we have our transition files in the transitions folder, we want to tag them correctly with Lainchan Radio Transitions special album name. So from here we proceed:
First i move all of my songs into folders:
mkdir "Serial Experiments Lain OST"
mkdir "Infected Mushroom - Army of Mushrooms"
mkdir "Pendulum - In Silico"
mv Infec* Infected\ Mushroom\ -\ Army\ of\ Mushrooms/
mv Serial* Serial\ Experiments\ Lain\ OST/
mv *Pendulum* Pendulum\ -\ In\ Silico/
mv *pendulum* Pendulum\ -\ In\ Silico/
mv Lain\'s\ Theme\ -\ Lain-o-cwuTmqz8c.mp3 Serial\ Experiments\ Lain\ OST/
root@lain:/srv/radio/music/cyberia# ls -lash
total 24K
4.0K drwxr-xr-x 6 root root 4.0K Jul 13 09:48 .
4.0K drwxr-xr-x 3 root root 4.0K Jul 13 09:44 ..
4.0K drwxr-xr-x 2 root root 4.0K Jul 13 09:47 'Infected Mushroom - Army of Mushrooms'
4.0K drwxr-xr-x 2 root root 4.0K Jul 13 09:48 'Pendulum - In Silico'
4.0K drwxr-xr-x 2 root root 4.0K Jul 13 09:48 'Serial Experiments Lain OST'
4.0K drwxr-xr-x 2 root root 4.0K Jul 13 09:11 transitions
root@lain:/srv/radio/music/cyberia# tree .
.
├── Infected Mushroom - Army of Mushrooms
│ ├── Infected Mushroom - (01) Nevermind [HQ] 2012-Fzj7BZeqVzk.mp3
│ ├── Infected Mushroom - (02) Nothing to Say [HQ] 2012-fQcB8yo46eI.mp3
│ ├── Infected Mushroom - (03) Send Me an Angel [HQ] 2012-W2Ks5AkCHow.mp3
│ ├── Infected Mushroom - (04) U R So Fucked [HQ] 2012-62gm17b2bMI.mp3
│ ├── Infected Mushroom - (05) The Rat [HQ] 2012-nHIWgbBDx0o.mp3
│ ├── Infected Mushroom - (06) Nation of Wusses [HQ] 2012-L25QxK0Csg0.mp3
│ ├── Infected Mushroom - (07) Wanted To [HQ] 2012-2PaSS6JFvXc.mp3
│ ├── Infected Mushroom - (08) Serve My Thirst [HQ] 2012-_J6feIrbcEo.mp3
│ ├── Infected Mushroom (09) - I Shine [HQ] 2012-OtbFUedy6Xs.mp3
│ ├── Infected Mushroom - (10) Drum n Baasa [HQ] 2012-4IhPuiuYx30.mp3
│ ├── Infected Mushroom - (11) The Pretender (Foo Fighters Cover) [HQ] 2012-boKAyiI-8WQ.mp3
│ ├── Infected Mushroom - (12) The Messenger 2012 [HQ] 2012-x0lRqrID3i4.mp3
│ └── Infected Mushroom - (13) Swingish (Bonus Track) [HQ] 2012-7KwBE6XJbj4.mp3
├── Pendulum - In Silico
│ ├── 9,000 Miles - Pendulum [HQ]-9AudFoAMTik.mp3
│ ├── Different - Pendulum [HQ]-3JUwQglYFig.mp3
│ ├── Granite - Pendulum [HQ]-h17WaF6alVY.mp3
│ ├── Midnight Runner - Pendulum [HQ]-oSbop6jmBS4.mp3
│ ├── Mutiny - Pendulum [HQ]-ucchDCKCTTs.mp3
│ ├── pendulum axle grinder-UiGJbBlsrqE.mp3
│ ├── pendulum out here-SM3fWfBgyJk.mp3
│ ├── pendulum-prelude-PYUHFPqI3Rw.mp3
│ ├── Propane Nightmares - Pendulum [HQ]-SBohx23x0jw.mp3
│ ├── Showdown - Pendulum [HQ]-mszCJSB02_8.mp3
│ ├── The Other Side - Pendulum [HQ]-z0hwFJ1rbbU.mp3
│ ├── The Tempest - Pendulum [HQ]-uy-3tWaKyg8.mp3
│ └── Visions - Pendulum [HQ]-HXSfB-zQUAg.mp3
├── Serial Experiments Lain OST
│ ├── Lain's Theme - Lain-o-cwuTmqz8c.mp3
│ ├── Serial Experiment Lain - Antidepressant 044-KaOsmUDMdaE.mp3
│ ├── Serial Experiment Lain - Cloudy with occasional rain-1TunrW7dRr0.mp3
│ ├── Serial Experiment Lain - Duvet (cyberia remix)-juDqDMlTfUg.mp3
│ ├── Serial Experiment Lain - Duvet (tv version)-EnEaNaqGMqU.mp3
│ ├── Serial Experiment Lain - Infanity world-AsPLBQfZQ04.mp3
│ ├── Serial Experiment Lain - Invisible file-32IlMQ8Bs6w.mp3
│ ├── Serial Experiment Lain - Island in video cassette-CCdyzUPrLpM.mp3
│ ├── Serial Experiment Lain - k.i.d.s-TvCJnW46ISo.mp3
│ ├── Serial Experiment Lain - Prayer-yyPMWWjzMG8.mp3
│ ├── Serial Experiment Lain - Professed intention and real-D4G97Xsc8PA.mp3
│ ├── Serial Experiment Lain - Psychedelic farm-XEGz6CJnY04.mp3
│ ├── Serial Experiment Lain - Speed-zFwQFdAsGGA.mp3
│ └── Serial Experiments Lain - Cyberia Theme-5dbi4N6NGn4.mp3
└── transitions
├── Autumn
├── Autumn In Space-nWqcLWxZOH0.mp3
├── Circuit
├── Circuit Lover-zduKG8YUKxM.mp3
├── Coloris-YTY7UV6GpkM.mp3
├── Destination
├── Destination Luna4-VGaQwDylJPM.mp3
├── Distort
├── Distort Into Me-_zLINqJYH0U.mp3
├── Fuse-MhHshcp5tQs.mp3
├── Gum-tTGBo0Fmf4A.mp3
├── In
├── In Time-8PmEsQY5rq4.mp3
├── Monochrome-uFNppANT-Dk.mp3
├── Orbit-SHFmUt1Jit4.mp3
├── Reality-C0ybGDNZos8.mp3
├── Together-ohViqbZZnpE.mp3
├── Tokyo
├── Tokyo Nights-5fXc8aoF2eU.mp3
├── Touch
├── Touch and Go-gxfyax1IPdk.mp3
├── Traveling
└── Traveling By Night-75yOApYoc6Y.mp3
And now that's done we're going to change our tagger script as follows:
root@lain:/srv/radio/music# vim tagger.sh
root@lain:/srv/radio/music# cat tagger.sh
#! /bin/bash
GREEN="\033[0;32m"
ORANGE="\033[0;33m"
RED="\033[0;31m"
NC="\033[0m"
echo -en "${GREEN}[+]${NC} Type the name of the channel directory (ex: cyberia): "
read channel
#### First list the songs and ask if the user wants to add the album tag to them:
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
ALBUMS="*"
cd $channel
for ALBUM in $ALBUMS ;
do
echo -en "\n ${ORANGE}[+] $ALBUM${NC}"
cd $ALBUM
SONGS="*"
for SONG in $SONGS;
do
echo -en "\n ${GREEN} [+] $SONG${NC}"
done
cd ..
done
cd ..
#restore IFS
IFS=$SAVEIFS
echo -en "\n\n${GREEN}[+]${NC} add the ${RED}$album${NC} ${ORANGE}album${NC} tag to these ${GREEN}songs ${NC}? (y/n)"
read choice
####### If user says 'y' then add the album tag to the selected songs:
if [ "$choice" == "y" ];
then
echo 'yes'
#change IFS
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
ALBUMS="*"
cd $channel
for ALBUM in $ALBUMS ;
do
echo -en "\n ${ORANGE}[+] $ALBUM${NC}"
cd $ALBUM
SONGS="*"
for SONG in $SONGS;
do
echo -en "\n ${GREEN} [+] $SONG${NC}"
if [ $ALBUM == "transitions" ];
then
id3tag --album="Lainchan Radio Transitions" $SONG
id3tag --song="\"$SONG\"" $SONG
else
id3tag --album="\"$ALBUM\"" $SONG
id3tag --song="\"$SONG\"" $SONG
fi
done
cd ..
done
cd ..
#restore IFS
IFS=$SAVEIFS
else
echo 'cancelling....'
fi
echo
exit
Basically, this script goes into the channel/ directory, then goes into each album/ directory, and then will add the tags as follows: the filename.mp3 becomes the song's id3tag, and the song's album/ directory becomes the song's album id3tag. Which fits how we want our service to function. Once that's done we simply run the schedule.py script to schedule our songs once again after updating the database:
root@lain:/srv/radio/music# ./tagger.sh
root@lain:/srv/radio/music# ncmpcpp -h 127.0.0.1 -p 6601
#press u to update database
#press q to quit
root@lain:/srv/radio/music# python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6601
"Infected Mushroom - Army of Mushrooms"
"Pendulum - In Silico"
"Serial Experiments Lain OST"
Lainchan Radio Transitions
['"Infected Mushroom - Army of Mushrooms"', '"Pendulum - In Silico"', '"Serial Experiments Lain OST"', 'Lainchan Radio Transitions']
Transition: transitions/In Time-8PmEsQY5rq4.mp3 (129s)
Album: "Serial Experiments Lain OST" (4057s)
Tracks: #18 (6581s)
Target duration: 10800s
Actual duration: 10767s
Difference: 33s
Now another thing we can do is run the schedule.py script automatically every 3 hours so that our songs are continuously being updated, we can do that with a cronjob:
root@lain:/srv/radio/music# crontab -e
no crontab for root - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano ---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
Choose 1-3 [1]: 2
# m h dom mon dow command
0 */3 * * * python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6601
:wq
Now in order to test our cronjob we can use cronitor:
root@lain:/srv/radio/music# curl -sOL https://cronitor.io/dl/cronitor-stable-linux-amd64.tgz
root@lain:/srv/radio/music# tar xvf cronitor-stable-linux-amd64.tgz -C /usr/bin/
cronitor
root@lain:/srv/radio/music# cronitor configure --api-key 1234567890
Configuration File:
/etc/cronitor/cronitor.json
Version:
27.0
API Key:
1234567890
Ping API Key:
Not Set
Environment:
Not Set
Hostname:
lain
Timezone Location:
{Europe/Paris}
Debug Log:
Off
root@lain:/srv/radio/music# cronitor list
----► Checking user "root" crontab
+-------------------+------------------------------------------------------------------------------------------------------+
| SCHEDULE | COMMAND |
+-------------------+------------------------------------------------------------------------------------------------------+
| 0 */3 * * * | python3 |
| | /srv/lainonlife/scripts/schedule.py |
| | --host=127.0.0.1 6601 |
+-------------------+------------------------------------------------------------------------------------------------------+
----► Checking /etc/crontab
+-------------------+------------------------------------------------------------------------------------------------------+
| SCHEDULE | COMMAND |
+-------------------+------------------------------------------------------------------------------------------------------+
| 17 * * * * | cd / && run-parts --report |
| | /etc/cron.hourly |
| 25 6 * * * | test -x /usr/sbin/anacron || |
| | ( cd / && run-parts --report |
| | /etc/cron.daily ) |
| 47 6 * * 7 | test -x /usr/sbin/anacron || |
| | ( cd / && run-parts --report |
| | /etc/cron.weekly ) |
| 52 6 1 * * | test -x /usr/sbin/anacron || |
| | ( cd / && run-parts --report |
| | /etc/cron.monthly ) |
+-------------------+------------------------------------------------------------------------------------------------------+
Now to test the cronjob we run cronitor select
root@lain:/srv/radio/music# cronitor select
✔ python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6601
----► Running command: python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6601
"Infected Mushroom - Army of Mushrooms"
"Pendulum - In Silico"
"Serial Experiments Lain OST"
Lainchan Radio Transitions
['"Infected Mushroom - Army of Mushrooms"', '"Pendulum - In Silico"', '"Serial Experiments Lain OST"', 'Lainchan Radio Transitions']
Transition: transitions/Together-ohViqbZZnpE.mp3 (144s)
Album: "Serial Experiments Lain OST" (4057s)
Tracks: #19 (6506s)
Target duration: 10800s
Actual duration: 10707s
Difference: 93s
----► ✔ Command successful Elapsed time 0.223s
root@lain:/srv/radio/music#
And here we see that we have been able to successfully run the script through our cronjob, and the way we set it up makes it so schedule.py is being ran every 3 hours, that way it matches the 3 hours of audio it tries to queue. Now let's add another channel. To do that we need 3 things, a channel directory in /srv/radio/music/, another mpd service running on another port (i'll do ports 6601, 6602, 6603, 6604, etc) and to edit the website configuration config.json to let the clients choose which channel they want to listen to:
root@lain:/srv/radio/music# mkdir metal
root@lain:/srv/radio/music# cd metal
root@lain:/srv/radio/music/metal# mkdir "Rivers of Nihil - Where Owls Know My Name"
root@lain:/srv/radio/music/metal# mkdir "Behemoth - The Satanist"
root@lain:/srv/radio/music/metal# mkdir "Carnifex - Slow Death"
root@lain:/srv/radio/music/metal# mkdir "Rivers of Nihil - Monarchy"
root@lain:/srv/radio/music/metal# cd Rivers\ of\ Nihil\ -\ Where\ Owls\ Know\ My\ Name/
root@lain:/srv/radio/music/metal/Rivers of Nihil - Where Owls Know My Name#
root@lain:/srv/radio/music/metal/Rivers of Nihil - Where Owls Know My Name# youtube-dl https://www.youtube.com/playlist?list=PLEouLkiLHdSDIqsVoq7a9bJDifla6XNq9 -x --audio-format mp3
[youtube:tab] PLEouLkiLHdSDIqsVoq7a9bJDifla6XNq9: Downloading webpage
[download] Downloading playlist: Rivers Of Nihil - Where Owls Know My Name
[youtube:tab] playlist Rivers Of Nihil - Where Owls Know My Name: Downloading 10 videos
Now like this you can fill in each of your album folders just like we did for the cyberia channel, don't forget the transitions either:
root@lain:/srv/radio/music/metal# mkdir transitions
root@lain:/srv/radio/music/metal# youtube-dl LINK -x --audio-format mp3
once you filled up the channel directory with album directories, run the tagger:
root@lain:/srv/radio/music# ls -l metal/
total 40
drwxr-xr-x 2 root root 4096 Jul 13 11:34 'Behemoth - I Loved You At Your Darkest'
drwxr-xr-x 2 root root 4096 Jul 13 11:11 'Behemoth - The Satanist'
drwxr-xr-x 2 root root 4096 Jul 13 11:09 'Carnifex - Slow Death'
drwxr-xr-x 2 root root 4096 Jul 13 11:22 'Meshuggah - Obzen'
drwxr-xr-x 2 root root 4096 Jul 13 11:24 'Meshuggah - The Violent Sleep of Reason'
drwxr-xr-x 2 root root 4096 Jul 13 11:07 'Rivers of Nihil - Monarchy'
drwxr-xr-x 2 root root 4096 Jul 13 11:06 'Rivers of Nihil - Where Owls Know My Name'
drwxr-xr-x 2 root root 4096 Jul 13 11:27 'The Black Dahlia Murder - Abysmal'
drwxr-xr-x 2 root root 4096 Jul 13 11:26 'The Black Dahlia Murder - Deflorate'
drwxr-xr-x 2 root root 4096 Jul 13 11:20 transitions
root@lain:/srv/radio/music# ./tagger.sh
Once that's done we're going to make another mpd config for our second channel, my second channel's mpd config will be in /root/.config/mpd/metal.conf:
root@lain:~/.config/mpd# cat metal.conf
music_directory "/srv/radio/music/metal"
playlist_directory "/srv/radio/data/metal/playlists"
db_file "/srv/radio/data/metal/db"
state_file "/srv/radio/data/metal/state"
sticker_file "/srv/radio/data/metal/sticker.sql"
log_file "/srv/radio/data/syslog.metal"
bind_to_address "127.0.0.1"
#bind_to_address "10.0.0.201"
port "6602"
audio_output {
name "[mpd] metal (ogg)"
description "classic lainchan radio: electronic, chiptune, weeb"
type "shout"
encoder "vorbis"
host "localhost"
port "8000"
mount "/mpd-metal.ogg"
user "source"
password "P@SSWORD"
quality "3"
format "44100:16:2"
always_on "yes"
}
audio_output {
name "[mpd] metal (mp3)"
description "classic lainchan radio: electronic, chiptune, weeb"
type "shout"
encoder "lame"
host "localhost"
port "8000"
mount "/mpd-metal.mp3"
user "source"
password "P@SSW0RD"
quality "3"
format "44100:16:2"
always_on "yes"
}
audio_output {
type "null"
name "null"
}
audio_output {
type "alsa"
name "alsa audio"
mixer_type "software"
}
audio_output {
type "fifo"
name "my_fifo2"
path "/tmp/mpd.fifo2"
format "44100:16:2"
}
I also create the directories it requires for the playlists and database:
root@lain:~/.config/mpd# mkdir -p /srv/radio/data/metal/playlists/
root@lain:~/.config/mpd# cd /srv/radio/data/
root@lain:/srv/radio/data# tree .
.
├── cyberia
│ ├── db
│ ├── playlists
│ │ ├── cyberia.m3u
│ │ ├── Infected Mushroom - Army of Mushrooms.m3u
│ │ ├── Lainchan Radio Transitions.m3u
│ │ └── pendulum.m3u
│ ├── state
│ └── sticker.sql
├── metal
│ └── playlists
└── syslog
4 directories, 8 files
Now since we already made a systemd service to run the cyberia mpd process, let's make another one for the second channel:
root@lain:/srv/radio/data# cd /etc/systemd/system/
root@lain:/etc/systemd/system# ls -l | grep mpd
-rw-r--r-- 1 root root 334 Jul 12 23:19 mpd-cyberia.service
root@lain:/etc/systemd/system# cp mpd-cyberia.service mpd-metal.service
root@lain:/etc/systemd/system# vim mpd-metal.service
root@lain:/etc/systemd/system# cat mpd-metal.service
[Unit]
Description=mpd systemd service with config in /root/.config/mpd/metal.conf
After=network.target
After=systemd-user-sessions.service
After=network-online.target
[Service]
Type=simple
ExecStart=mpd --no-daemon /root/.config/mpd/metal.conf
Restart=always
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
root@lain:/etc/systemd/system# systemctl daemon-reload
root@lain:/etc/systemd/system# systemctl enable --now mpd-metal
Created symlink /etc/systemd/system/multi-user.target.wants/mpd-metal.service → /etc/systemd/system/mpd-metal.service.
Now once that's done we can connect to it on port 6602:
root@lain:/srv/radio/data# ncmpcpp -h 127.0.0.1 -p 6602
#press u to update database
#press 7 to go to the outputs
#press enter to activate the metal ogg and mp3 outputs
#press 4 to check the album tags
#press q to exit
root@lain:/srv/radio/data# python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6602
"Behemoth - I Loved You At Your Darkest"
"Behemoth - The Satanist"
"Carnifex - Slow Death"
"Meshuggah - Obzen"
"Meshuggah - The Violent Sleep of Reason"
"Rivers of Nihil - Monarchy"
"Rivers of Nihil - Where Owls Know My Name"
"The Black Dahlia Murder - Abysmal"
"The Black Dahlia Murder - Deflorate"
Lainchan Radio Transitions
['"Behemoth - I Loved You At Your Darkest"', '"Behemoth - The Satanist"', '"Carnifex - Slow Death"', '"Meshuggah - Obzen"', '"Meshuggah - The Violent Sleep of Reason"', '"Rivers of Nihil - Monarchy"', '"Rivers of Nihil - Where Owls Know My Name"', '"The Black Dahlia Murder - Abysmal"', '"The Black Dahlia Murder - Deflorate"', 'Lainchan Radio Transitions']
Transition: transitions/Buckethead - Asylum Of Glass (Song Only)-AgzzsbPj_1w.mp3 (278s)
Album: "Behemoth - The Satanist" (2671s)
Tracks: #29 (7762s)
Target duration: 10800s
Actual duration: 10711s
Difference: 89s
root@lain:/srv/radio/data# ncmpcpp -h 127.0.0.1 -p 6602
#press enter to start playing the tracks
#press q to exit
Now that's done let's add another cronjob to make sure the tracks are constantly added every 3 hours for our second channel:
root@lain:/srv/radio/data# crontab -e
# m h dom mon dow command
0 */3 * * * python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6601
0 */3 * * * python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6602
:wq
root@lain:/srv/radio/data# cronitor select
✔ python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6602
----► Running command: python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6602
"Behemoth - I Loved You At Your Darkest"
"Behemoth - The Satanist"
"Carnifex - Slow Death"
"Meshuggah - Obzen"
"Meshuggah - The Violent Sleep of Reason"
"Rivers of Nihil - Monarchy"
"Rivers of Nihil - Where Owls Know My Name"
"The Black Dahlia Murder - Abysmal"
"The Black Dahlia Murder - Deflorate"
Lainchan Radio Transitions
['"Behemoth - I Loved You At Your Darkest"', '"Behemoth - The Satanist"', '"Carnifex - Slow Death"', '"Meshuggah - Obzen"', '"Meshuggah - The Violent Sleep of Reason"', '"Rivers of Nihil - Monarchy"', '"Rivers of Nihil - Where Owls Know My Name"', '"The Black Dahlia Murder - Abysmal"', '"The Black Dahlia Murder - Deflorate"', 'Lainchan Radio Transitions']
Transition: transitions/Buckethead - I love my parents-BwXlzy9k7jI.mp3 (253s)
Album: "The Black Dahlia Murder - Deflorate" (2041s)
Tracks: #27 (8418s)
Target duration: 10800s
Actual duration: 10712s
Difference: 88s
----► ✔ Command successful Elapsed time 0.291s
Now that we finished our cronitor job for our second channel, we need to edit icecast.xml:
root@lain:/srv/radio/data# vim /srv/Icecast-Server/conf/icecast.xml
Once we add the following block we restart our icecast service:
root@lain:/srv/radio/data# systemctl restart icecast
root@lain:/srv/radio/data# systemctl status icecast
● icecast.service - Icecast systemd service with config in /srv/Icecast-Server/conf/icecast.xml
Loaded: loaded (/etc/systemd/system/icecast.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2021-07-13 12:06:07 CEST; 1s ago
Main PID: 5283 (icecast)
Tasks: 4 (limit: 4915)
Memory: 1.2M
CGroup: /system.slice/icecast.service
└─5283 /usr/local/bin/icecast -c /srv/Icecast-Server/conf/icecast.xml
Jul 13 12:06:07 lain systemd[1]: Started Icecast systemd service with config in /srv/Icecast-Server/conf/icecast.xml.
Jul 13 12:06:07 lain icecast[5283]: [2021-07-13 12:06:07] WARN CONFIG/_parse_root Warning, location> not configured, using default value "Earth".
Jul 13 12:06:07 lain icecast[5283]: [2021-07-13 12:06:07] WARN CONFIG/_parse_root Warning, admin> contact not configured, using default value "icemaster@localhost". This breaks YP
root@lain:/srv/radio/data#
Now if you do this you will probably see that icecast doesnt detect the mountpoints anymore, to fix that restart the mpd channels:
root@lain:/srv/radio/data# systemctl restart mpd-cyberia
root@lain:/srv/radio/data# systemctl restart mpd-metal
You can verify if it's working at localip:8000/admin/listmounts.xsl
Now we need to change config.json to include our second channel:
root@lain:/srv/lainonlife# ls -l
total 36
drwxr-xr-x 4 root root 4096 Jul 12 18:49 backend
drwxr-xr-x 2 root root 4096 Jul 12 16:34 concourse
-rw-r--r-- 1 root root 366 Jul 12 10:05 config.json
-rw-r--r-- 1 root root 695 Jul 11 22:11 config.json.example
drwxr-xr-x 2 root root 4096 Jul 11 23:32 examples
drwxr-xr-x 10 root root 4096 Jul 12 10:16 frontend
-rw-r--r-- 1 root root 1082 Jul 11 22:11 LICENSE
-rw-r--r-- 1 root root 2344 Jul 11 22:11 README.md
drwxr-xr-x 2 root root 4096 Jul 13 08:48 scripts
root@lain:/srv/lainonlife# vim config.json
root@lain:/srv/lainonlife# cat config.json
{ "channels":
{ "cyberia": { "mpd_host": "localhost", "mpd_port": 6601, "description": "classic lainchan radio: electronic, chiptune, weeb" }
, "metal": { "mpd_host": "localhost", "mpd_port": 6602, "description": "Death Metal, Deathcore, Black Metal" }}
, "template":
{ "default_channel": "cyberia"
, "icecast_status_url": "/radio/status-json.xsl"
, "icecast_stream_url_base": "https://lain.void.yt/radio/"
, "server_cost": 10.00
, "currency_symbol": "€"
}
}
Now since we modified the config file let's restart the lainradio service:
root@lain:/srv/lainonlife# systemctl restart lainradio.service
Now by default the radio.js file looks for the channel names by their ogg extention, so i modified it to look for mp3 instead, and we finally get the channels to be displayed:
root@lain:/srv/http/js# cat radio.js | grep mp3
if(sname !== undefined && sname.startsWith("[mpd] ") && sname.endsWith(" (mp3)")) {
I changed it from ogg to mp3 because for some reason i can't get my .ogg output streams to work, but the mp3 ones work fine so we're using them instead:
And when we change channels we see that everything updates as intended:
So with the same process we can add other channels, each of them have a systemd mpd-channel.service file to launch a mpd process on a specific port (ex: 6603 with a mpd config at /root/.config/mpd/channel.conf, with a directory at /srv/radio/music/channel/of their own. Of course you need to edit /srv/lainonlife/config.json and icecast.xml with your changes everytime. and make sure that you have a cronjob running python3 /srv/lainonlife/scripts/schedule.py --host=127.0.0.1 6603 every 3 hours after you added the transition tracks just like for our previous 2 channels in order to keep the radios constantly updated with new albums / songs to play for the listeners.
here's the forked repository with all important files i edited here:
-icecast.service
-mpd-cyberia.service
-lainradio.service
-schedule.py
-tagger.sh
-index.html
-ncmpcpp.conf
-mpd-cyberia.conf
-js/player.js
Some Address 67120,
Duttlenheim, France.
This cute theme was created to showcase your work in a simple way. Use it wisely.