Records the source stream and makes it available for playback and download, split into predefined time-slots (weekly broadcast schedule) and individual tracks (based on stream metadata).
The project was created in 2012 in order to keep up with the beautiful music aired by our beloved non-commercial FM radio station Kanal 103. It was designed to be cheap to run, relying on minimal support infrastructure (it should work on almost any device with Linux and static web server).
Debian 10/11 assumed
# audio/metadata tools
sudo apt install -y streamripper sox libsox-fmt-all mp3val id3v2 ffmpeg lame vorbis-tools
# other script dependencies
sudo apt install -y cron bc tree rename moreutils
# download
git clone https://github.com/skopjehacklab/radio.spodeli.org.git && cd radio.spodeli.org
# copy core files to permanent destination
sudo rsync -av scripts static /opt/spodeli_radio/
sudo cp systemd/[email protected] /etc/systemd/system/
core files could be put anywhere, as long as
RADIO_APP
variable is set to thescripts
dir path and the root of web-server points to path ofstatic
The app uses a streamripper fork with added option for metadata export, expected to be found at /usr/local/bin/streamripper_e
the name is modified so it can be used easily alongside unpached or different version of
streamripper
quick way to do this in debian:
cd src && apt-get source streamripper
patch -p0 < streamripper_export_metadata.deb.patch
sudo apt-get -y build-dep streamripper
cd streamripper-1.64.6 && dpkg-buildpackage -us -uc -b
sudo cp "$(pwd)/debian/streamripper/usr/bin/streamripper" /usr/local/bin/streamripper_e
it's assumed that relevant core files were copied to
/opt/spodeli_radio
and current working directory is where repository was cloned into
export STREAM_URL RELAY_PORT WEB_BASE RADIO_APP STATION_ID STATION_NAME STATION_URL APP_ROOT WEB_ROOT DELAY OGG_BITRATE OVERLAP XS TRACK_NAME MP3_MIN_LEN MP3_HIST
core settings
# path to radio.spodeli.org core scripts
RADIO_APP=/opt/spodeli_radio/scripts
# web-server domain URL (used for creating absolute web-links to recordings and playlists)
WEB_BASE=//radio.spodeli.org
station settings
# channel handle (BASE_URL will be `${WEB_BASE}/${STATION_ID}`)
STATION_ID=kanal103
STATION_NAME='Канал 103'
STATION_URL=http://kanal103.com.mk
# source URL of the stream to record
STREAM_URL=http://radiostream.neotel.mk:8000/kanal103
channel settings
# local streamripper relay will be on `localhost:${RELAY_PORT}`
RELAY_PORT=56724
# channel runtime and configuration files path
APP_ROOT=/home/kanal103
# channel web files path (BASE_URL on web-server should point here)
WEB_ROOT=/home/kanal103/public_html
# override default user locale for displaying dates
LC_TIME=mk_MK.UTF-8
#LANG=mk_MK.UTF-8
recording settings
# seconds to include from next programme in schedule
OVERLAP=900
# buffer delay in seconds (default 0, max 59) - use to sync playlist times with broadcast times
DELAY=0
# bitrate of created ogg media files, in Kbps (default 192)
OGG_BITRATE=192
# minimum length of saved mp3 files, in miliseconds (default 1000)
MP3_MIN_LEN=10000
# time to keep saved mp3 files, in hours (default 24)
MP3_HIST=24
streamripper settings
# split-point configuration (read man pages for details)
XS='--xs_search_window=1500:500 --xs_offset=-1250'
# filename format for tracks split by metadata (check -D option)
TRACK_NAME='%A - %T'
Assumed environment file location is
/home/${RUN_USER}/etc/radio.env
. Update systemd unit and crontab files accordingly if you want to change it.
When recording multiple channels, it's better to create separate user for each, because
streamripper
reads its default configuration from~/.config/streamripper/streamripper.ini
# specify the runtime user
RUN_USER=kanal103
For quick setup of 'kanal103' channel from the repo, run
sudo useradd --create-home --shell /usr/sbin/nologin --skel channels/kanal103 ${RUN_USER}
Alternatively, copy the contents of channels/kanal103
somewhere and set APP_ROOT
and WEB_ROOT
variables accordingly.
# load environment vars
. /home/${RUN_USER}/etc/radio.env
# make sure required APP_ROOT structure exists
sudo mkdir -p "$APP_ROOT"/{etc,var,log,run,templates}
sudo touch "$APP_ROOT"/etc/parse_rules.txt
# make sure required WEB_ROOT structure exists
# (there should be sufficient space available for the recordings)
sudo mkdir -p "$WEB_ROOT"/{{0..6},mp3,playlists}
# update ownership of writeable paths
sudo chown -R ${RUN_USER} "${APP_ROOT}"/{var,run,log} "${WEB_ROOT}"
# start and enable the service
sudo systemctl start streamripper@${RUN_USER}
sudo systemctl enable streamripper@${RUN_USER}
Important: the schedule must be "full" (24/7) for everything to work properly
# location of crontab file containing the recording schedule
CRON_FILE=/home/${RUN_USER}/etc/crontab
# add it to RUN_USER crontab
sudo crontab -u ${RUN_USER} -l | cat - "${CRON_FILE}" | sudo crontab -u ${RUN_USER} -
channel template files:
${APP_ROOT}/templates
channel html web-root:${WEB_ROOT}
base path: {{ day-of-week }}/{{ timeslot }}.*
%w/%H%M-%H%M.{html,txt,tag,parts?}
stream related files: [0XXX.
]{mp3,ogg,cue,index}
when the stream is interrupted, additional files are created with extension prefixed by part number (4 digit, zero padded)
web page is created by joining .header, generated recording playlists (wrapped in <pre class='playlist'>
) and .footer
-
recording.html.header
#getbasepath
g
#gettitle
#gettimeslot
#getdate
-
recording.html.footer
#getbasepath
g
#getdlname
g
#getnextbasepath
g
#getnextdate
#getnexttitle
client js application path:
${WEB_BASE}/js/trackplayer.js
The client app interacts with the following page DOM elements:
location.hash
document.title
audio
#playtime
#trackname
#timeleft
pre.playlist
(auto-added)a#playnext
#mp3
#ogg
web path: mp3/index.html
, mp3/playlist.{m3u,xspf}
- mp3.xspf.header
#getxmldate
- mp3.{html,m3u,xspf}.footer
web path: playlists/{{ date }}.{{ format }}
playlists/%Y_%m_%d.{txt,html}
- playlist.html.header
#getdate
- playlist.html.footer
#getfile
- playlist.txt.separator
#gettitle
#gettimeslot
web path: playlists/index.html
- pl_index.html.footer