valentin vannay

valentincognito

Raspberry PI online Jukebox

2016-02-07

Ever wanted to turn your RPI into an online jukebox ? In this tutorial I am going to show you how to build your own RPI jukebox using Youtube-dl, Omxplayer and a bit of PHP. I will only explain in short what I did and present the tools I used. And if you want my code I'll be glad to share it with you if you send my an e-mail. Maybe later I will put it on Github if I have the courage to clean my code a little ^^   Here you will learn how to:
  • Download videos / music from youtube
  • Store the songs on your RPI
  • Create a queue system
  • Play the songs
I got this idea because in my company we have music playing all day in the open space. And I wanted to give the opportunity to everyone to play songs they like !

1. The webserver

The first thing you need to do is to setup a web server on your RPI, to do so I invite you to follow my previous tutorial available at this link.

2. Youtube-dl

There is a real sweet tools that allows you to download youtube (and other videos websites) videos in command line. It's called youtube-dl and it's available on our best friend Github. To download it simply
sudo curl https://yt-dl.org/downloads/2016.02.01/youtube-dl -o /usr/local/bin/youtube-dl

sudo chmod a+rx /usr/local/bin/youtube-dl
And then don't forget to update it
youtube-dl -U
Now you can download any videos by simply entering
youtube-dl www.youtube-video-url.com
But in this project I needed more customisation. I wanted to tell youtube-dl where to store the file and which format to use and also I wanted to do all this in quiet mode to prevent the command to be blocking. For the format I first chose to save all my songs in webm format because it the lightest format but I quickly realised that a few videos were returning an error with this format so I instead chose to save them in m4a. To specify a format you need to add the -f option plus the code corresponding to the wanted format. In my case the m4a format is 140. So:
youtube-dl -f 140 www.youtube-video-url.com
If you want to see all the available format for a video:
youtube-dl --list-formats www.youtube-video-url.com
Next I want to specify where to store the song. To do that:
youtube-dl -f 140 -o "/var/www/your_app/thePublicFolderOfYourProject/%(title)s.m4a" --restrict-filenames -q www.youtube-video-url.com
(The --restrict-filename option avoid "&" and spaces in filename. And -q is the quiet mode. Ah and one more thing: make sure the folder where you store the songs is writable !)

3. OMXPlayer

To play music on your RPI you can use the native player called OMXplayer. To play a song:
omxplayer /path/to/your/song
If you want to kill the process:
pkill omxplayer
If you have the error concerning vchiq you have to change some permissions:
sudo chmod a+rw /dev/vchiq
Note that when you reboot your RPI those permissions are reset ....  

4. Running those commands with PHP

What we want to do now is off course uses those command in our web app. I chose to do it in PHP and I used the shell_exec or exec command. But I suppose there are plenty other ways to do it ! One of the difficulty with PHP was to run the commands silently. We don't want to block the page until the command is over. And also I had a few problems with permissions. I also didn't know we cannot use commands with sudo with shell_exec. Executing command silently with shell_exec was a little more tricky than I thought but thanks to a certain Peec on php.net that wrote us a very nice class to help us with this.
/* An easy way to keep in track of external processes.
* Ever wanted to execute a process in php, but you still wanted to have somewhat controll of the process ? Well.. This is a way of doing it.
* @compability: Linux only. (Windows does not work).
* @author: Peec
*/

class Process{
    private $pid;
    private $command;

    public function __construct($cl=false){
        if ($cl != false){
            $this->command = $cl;
            $this->runCom();
        }
    }
    private function runCom(){
        $command = 'nohup '.$this->command.' > /dev/null 2>&1 & echo $!';
        exec($command ,$op);
        $this->pid = (int)$op[0];
    }

    public function setPid($pid){
        $this->pid = $pid;
    }

    public function getPid(){
        return $this->pid;
    }

    public function status(){
        $command = 'ps -p '.$this->pid;
        exec($command,$op);
        if (!isset($op[1]))return false;
        else return true;
    }

    public function start(){
        if ($this->command != '')$this->runCom();
        else return true;
    }

    public function stop(){
        $command = 'kill '.$this->pid;
        exec($command);
        if ($this->status() == false)return true;
        else return false;
    }
}
With this class you can now simply launch command like this:
$process = new Process('omxplayer /path/to/your/song');

5. Create a queue system

All right so now we are getting into serious business. How the heck can we create a queue system ? I thought about many different possibilities. Including CRON scheduling / at linux scheduler etc... But all of them were pretty hard (impossible?) to put in place. So I decided to use something not very flexible but it's working and I am happy with that. ^^ I actually queue the songs in a DB and I run the commands with a wait time. To wait until all the previous songs are played. So I created a 'queue' table with the following fields:
  • my song path
  • duration of the song (in ms)
  • start time (epoch time)
  • stop time (epoch time)
So let's say it's now 4pm and I start a song that is 2 min long. The first record in my DB will be as follow:
  • myJustinBieberSongPath
  • 120000
  • 4:00pm (in epoch time, should be something like: 1454829056)
  • 4:02pm
So now we have to figure out how long my next song has to wait to be played. Well it's just simple math. The next record will be something like this:
  • myNextSongPath
  • 180000
  • previous stop time - current time
  • current time + (previous stop time - current time)
Etc, etc... Here is an example of how it looks in PHP:
$sleep_time = $last_song->stop - time();

$queue = new Queue;
$queue->title     = $title;
$queue->path      = $path;
$queue->duration  = $duration;
$queue->start     = time() + $sleep_time;
$queue->stop      = time() + $sleep_time + ($duration / 1000);
Again, I will not go too much into details, if you want my code, please request it by email.

6. MediaInfo

One last thing that I haven't talk about but that I found useful during the development of my jukebox was the media info tool to get information about the songs. Like their title, duration etc... You can download it with:
sudo apt-get install mediainfo
Then you can get info like so:
// Get everything
mediainfo "/path/of/your/song"
// Get the format
mediainfo --Inform="General;%Format%" "/path/of/your/song"
// Get the duration
mediainfo --Inform="Audio;%Duration%" "/path/of/your/song"
  That's it for today ! Please do not hesitate to comment and give your insights about this project and don't hesitate to request the code !