How to run an automated internet-radio station in a server environment?

68

Solution 1

The answer is: liquidsoap.

liquidsoap, a swiss-army knife for multimedia streaming, notably used for netradios and webtvs. It has tons of features, it's free and it's open-source!

It took me some days to understand how streams are generated. liquidsoap consists of little scripts which describe the stream parameters. a simple script which loads and plays a mp3-playlist, let's call it basic.liq, is show below:

#!/usr/bin/liquidsoap

# load a playlist file
stream = playlist("/path/to/playlist.pls")

# output the playlist to icecast
source = output.icecast(%mp3, host="localhost", port="8000",
         mount="listen.m3u", password="hackme")
source(stream)

test the script with:

liquidsoap --check ./basic.liq

and start the script with:

liquidsoap ./basic.liq &

but liquidsoap is much more powerful. for example, to play a jingle every full hour, simply add:

# add a jingle every full hour
jingle = single("/path/to/jingle.mp3")
add([stream,switch([({0m0s},jingle)])])

but a lot more features are described at the homepage:

  • static or dynamic playlists
  • time-based selection of the audio source
  • quota- or time-based insertions or mixes of jingles
  • live DJ interventions
  • interactive user requests
  • fully-customizable transitions, e.g. crossfading
  • speech-synthesis of track metadata
  • simple access to remote files
  • and much more

It's worth taking a look at the quick start guide, a more complex example and the documentation there.

Solution 2

AzuraCast: A Self-Hosted Web Radio Manager

Source: https://github.com/AzuraCast/AzuraCast

Azuracast is a self-hosted web radio management suite, including turnkey installer tools and an easy-to-use web app to manage your stations. http://azuracast.com/

Features

With AzuraCast, you can:

  • Manage your Media: Upload songs from the web, organize music into folders, and preview songs in your browser.
  • Create Playlists: Set up standard playlists that play all the time, scheduled playlists for time periods, or special playlists that play once per x songs, or once per x minutes.
  • Set Up Live DJs: Enable or disable live broadcasting from streamers/DJs, and create individual accounts for each streamer to use.
  • Take Listener Requests: Let your listeners request specific songs from your playlists, both via an API and a simple public-facing listener page.
  • Analytics and Reports: Keep track of every aspect of your station's listeners over time. View reports of each song's performance.
  • Station Autopilot: AzuraCast can automatically assign songs to a playlist based on the song's impact on listener numbers.
  • Delegate Management: Create and remove separate administrator accounts for each station manager.
  • ...and more.

Supported Web Radio Software

  • AzuraCast uses LiquidSoap as an "AutoDJ" to shuffle songs and playlists and provide an always-online radio stream. You can connect to LiquidSoap and broadcast your own live events as a DJ as well.

  • To broadcast your radio station to the public, AzuraCast supports both of the gold standards in web radio, IceCast (v2.4) and ShoutCast (v2). You can switch which of these your station uses anytime you want.

You can also use AzuraCast as a tool for relaying or collecting listener statistics and other data about stations that AzuraCast doesn't manage.

Supported Operating Systems

There are two ways to install AzuraCast:

  1. Using Docker and Docker Compose (Recommended): This method contains all of the AzuraCast dependencies in prebuilt container images. Updating and installing is faster, and there are far fewer issues with software compatibility. This method works on any computer that supports the latest version of the Docker Engine and Docker Compose; both can be installed as part of the AzuraCast installer script.

  2. Traditional Installation (Ubuntu 16.04 only): From a clean image of Ubuntu, you can install AzuraCast directly onto your server using the included installer scripts, which use Ansible to manage dependencies. Installation and updating are slower using this method, but you have more control over the software once installed. If you have other software installed on your server, it may conflict with AzuraCast, so always start from a clean installation using this method.

We are always looking to expand our compatibility with host operating systems, and we welcome any assistance in building new deployment scripts for other environments.

What's Included with AzuraCast

Whether you're using the traditional installer or Docker containers, AzuraCast depends on the same stack of software to operate:

  • NGINX for serving web pages and the radio proxy
  • MariaDB as the primary database
  • PHP 7.2 powering the web application
  • InfluxDB for time-series based statistics
  • Redis for sessions, database and general caching
  • LiquidSoap as the always-playing "AutoDJ"
  • IceCast 2 as a radio broadcasting frontend (Icecast-KH installed on supported platforms)
  • ShoutCast 2 DNAS as an alternative radio frontend (x86/x64 only) Installing AzuraCast

Installing AzuraCast

Docker Installation (Recommended)

We strongly recommend installing and using AzuraCast via Docker. All of the necessary software packages are built by our automated tools, so installation is as easy as just pulling down the pre-compiled images. There's no need to worry about compatibility with your host operating system, so any host (including Windows and MacOS) will work great out of the box.

  1. Install Docker and Docker Compose Your computer or server should be running the newest version of Docker and Docker Compose. You can use the easy scripts below to install both if you're starting from scratch:

    wget -qO- https://get.docker.com/ | sh
    COMPOSE_VERSION=`git ls-remote https://github.com/docker/compose | grep refs/tags | grep -oP "[0-9]+\.[0-9][0-9]+\.[0-9]+$" | tail -n 1`
    sudo sh -c "curl -L https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose"
    sudo chmod +x /usr/local/bin/docker-compose
    sudo sh -c "curl -L https://raw.githubusercontent.com/docker/compose/${COMPOSE_VERSION}/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose"
    

    If you're not installing as root, you may be given instructions to add your current user to the Docker group (i.e. usermod -aG docker $user). You should log out or reboot after doing this before continuing below.

  2. Pull the AzuraCast Docker Compose File

    Choose where on the host computer you would like AzuraCast's configuration file to exist on your server.

    Inside that directory, run this command to pull the Docker Compose configuration file.

    curl -L https://raw.githubusercontent.com/AzuraCast/AzuraCast/master/docker-compose.yml > docker-compose.yml
    
  3. Run the AzuraCast Docker Installer

    From the directory that contains your YML configuration file, run these commands:

    docker-compose pull  
    docker-compose run --rm cli azuracast_install
    docker-compose up -d
    

Setting up HTTPS with LetsEncrypt

AzuraCast now supports full encryption with LetsEncrypt. LetsEncrypt offers free SSL certificates with easy validation and renewal.

First, make sure your AzuraCast instance is set up and serving from the domain you want to use. Then, run the following command to generate a new LetsEncrypt certificate:

docker-compose run --rm letsencrypt certonly --webroot -w /var/www/letsencrypt  

You will be prompted to specify your e-mail address and domain name. Validation will happen automatically. Once complete, run this command to tell nginx to use your new LetsEncrypt certificate:

docker-compose run --rm nginx letsencrypt_connect YOURDOMAIN.example.com

Reload nginx using the command below:

docker-compose kill -s SIGHUP nginx

Your LetsEncrypt certificate is valid for 3 months. To renew the certificates, run this command:

docker-compose run --rm letsencrypt renew --webroot -w /var/www/letsencrypt  

Updating with Docker

From inside the base directory where AzuraCast is copied, run the following commands:

docker-compose down
docker-compose pull
docker-compose run --rm cli azuracast_update  
docker-compose up -d  

Docker Volume Backup and Restore

AzuraCast has utility scripts to allow for easy backup and restoration of Docker volumes.

You can use docker-backup.sh to back up existing volumes. You can specify a custom path as the script's argument. By default, the script will create a file, backup.tar.gz in the app root.

To restore the application's state from this compressed file use docker-restore.sh and provide it with the path of the existing backup file.

Note that the restoration process will replace any existing AzuraCast database or media that exists inside the Docker volumes.

Traditional Installation (Ubuntu 16.04 only)

Note: Some web hosts offer custom versions of Ubuntu that include different software repositories. These may cause compatibility issues with AzuraCast. Many VPS providers are known to work out of the box with AzuraCast (OVH, DigitalOcean, Vultr, etc), and are thus highly recommended if you plan to use the traditional installer.

AzuraCast is optimized for speed and performance, and can run on very inexpensive hardware, from the Raspberry Pi 3 to the lowest-level VPSes offered by most providers.

Since AzuraCast installs its own radio tools, databases and web servers, you should always install AzuraCast on a "clean" server instance with no other web or radio software installed previously.

Execute these commands as the root user to set up your AzuraCast server:

apt-get update
apt-get install -q -y git
mkdir -p /var/azuracast/www
cd /var/azuracast/www
git clone https://github.com/AzuraCast/AzuraCast.git .
chmod a+x install.sh
./install.sh

If you cannot directly log in as the root account on your server, try running sudo su before running the commands above.

The installation process will take between 5 and 15 minutes, depending on your internet connection.

Once the terminal-based installation is complete, you can visit your server's public IP address (http://ip.of.your.server/) to finish the web-based setup.

Updating

AzuraCast also includes a handy updater script that pulls down the latest copy of the codebase from Git, flushes the site caches and makes any necessary database updates. Run these commands as any user with sudo permissions:

cd /var/azuracast/www
sudo chmod a+x update.sh
sudo ./update.sh
Share:
68
supersize
Author by

supersize

Updated on September 18, 2022

Comments

  • supersize
    supersize over 1 year

    I have the following markup:

    <div id="div1" class="divs">some content</div>
    <div id="div2" class="divs">some content</div>
    <div id="div3" class="divs">some content</div>
    <div id="div4" class="divs">some content</div>
    <div id="div5" class="divs">some content</div>
    <div id="div6" class="divs">some content</div>
    

    and they all get a simple .click() function:

    $divs = $('.divs');
    $divs.click(function() {
      // some actions
    }
    

    what I would like to do is pass a parameter in the .click() function which gives me the current number of each id e.g. integer 1 for id="div1. Something like:

     $divs.click(function(e) {
      // e would be the number of the id inside the function
    }
    

    How could I do this?

    Thanks in advance!

    • Admin
      Admin over 11 years
      dear users, thanks for adding this question as a favourite, this shows me that's an interesting topic and people are interested in learning more. so ... why not upvoting it?
  • user2167382
    user2167382 about 10 years
    and parameter 'e' can be omitted
  • supersize
    supersize about 10 years
    okay that works, but could you please explain how this works and what exactly happens there?
  • Milind Anantwar
    Milind Anantwar about 10 years
    @supersize: looking at the DOM you share, elements have id getting incremented in by 1. .index() returns the current index(0 based) of element. So adding one to fetched index solves the problem.
  • supersize
    supersize about 10 years
    does this affect any other HTML, because my markup isn't just the .divs, furthermore I have #div_1_1, #div_1_2 and so on.
  • Milind Anantwar
    Milind Anantwar about 10 years
    @supersize: what do you mean by that. can you share the. as long as you are only targetting above DOM, it should work.
  • cookie monster
    cookie monster about 10 years
    @supersize: This isn't how you'd accomplish this task. If you want to get the numbers from the ID, just extract them from the .id string.
  • cookie monster
    cookie monster about 10 years
    +1, but .prop() is unnecessary. this.id requires much less overhead. Also remember that .match returns an Array.
  • Milind Anantwar
    Milind Anantwar about 10 years
    @cookiemonster: ohh really...tell me a smarter way to do this.
  • supersize
    supersize about 10 years
    but then I have to split the number from the string, does seem to complicate!
  • cookie monster
    cookie monster about 10 years
    Yes really. Your solution relies on them being siblings with no previous elements. It's hardly what anyone would do in a real world situation. Also it requires much more overhead. A much smarter way is to extract the information needed from this.id. If he wants 1_1, then there are many ways to do it, like this.id.slice(this.id.indexOf("_") + 1), with a conditional constraint if OP is unsure if the .id will have the desired data.
  • Milind Anantwar
    Milind Anantwar about 10 years
    @cookiemonster: that is the reason i followed this approach.
  • cookie monster
    cookie monster about 10 years
    That makes no sense. It's slow and fragile.
  • cookie monster
    cookie monster about 10 years
    I'm not sure what you mean about IDs being unique. I'm just saying that id is going to hold an Array that holds the number at index 0 instead of the number itself.
  • Bazinga
    Bazinga about 10 years
    I have updated my answer. I missed the part about extracting the number from the string... still on the first cup of coffee.
  • supersize
    supersize about 10 years
    @cookiemonster there might be better solutions, but I gave this post my accept since I have average knowledge regarding some JS things. And regex is one of them, I simply understand what happens on Milinds answer, and that is what matters for me! When I don't understand it, it doesn't help me!
  • Felix
    Felix about 10 years
    @supersize That's the reason why you need to ask, when you don't ask, people assume that you've already understand what happen, just like how you study in school?
  • cookie monster
    cookie monster about 10 years
    @supersize: And that's why I downvoted it. People rely on getting good information. This doesn't qualify.
  • Milind Anantwar
    Milind Anantwar about 10 years
    @cookiemonster: huhh. you have different defination of qualify then....you can see that by looking at upvotes.
  • supersize
    supersize about 10 years
    @Felix sure, I will have an eye on it next time to make it clear what I don't get, I simply didn't want to be cheeky that you explain me regular expressions!
  • cookie monster
    cookie monster about 10 years
    @MilindAnantwar: Upvotes on this site are meaningless. No knowledgeable developer would do this. You seem to post a lot of bad answers that get upvotes. I wouldn't be surprised if voter fraud was happening here.