Last month I had a Halloween party. Many people don’t seem to like the music I like, and I generally don’t like their music either. While planning my party it occurred to me that the music selection should be a collaborative process that included the guests. I spent the next few hours making my “Partytube” script, a collaborative Youtube-video powered jukebox for the Raspberry Pi. It’s just a series of quick and dirty little scripts that glue existing projects together for my purpose.
A pygame based display shows the song queue momentarily before the next music video begins playing.
You can download the project from GitHub: https://github.com/ethertubes/partytube
Installation requires a few important third-party programs. First start with a copy of Raspbian, this will include most of the python/pygame stuff, the omxplayer and other things presumed to be in the working environment.
You’ll need the youtube-dl youtube ripping script available at https://github.com/rg3/youtube-dl, but it’s quicker just to download it from yt-dl.org (see below). It’s a fairly large and complex script, and not really knowing what is in it, I decided against running it as root. I made a dedicated youtube user on my Pi for the purpose of running the youtube-dl script.
root@raspberrypi:~# adduser youtube
Adding user `youtube' ...
Adding new group `youtube' (1004) ...
Adding new user `youtube' (1001) with group `youtube' ...
Creating home directory `/home/youtube' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: youtube
Retype new UNIX password: youtube
passwd: password updated successfully
Changing the user information for youtube
Enter the new value, or press ENTER for the default
Full Name : Youtube Video Fetcher
Room Number :
Work Phone :
Home Phone :
Is the information correct? [Y/n] y
root@raspberrypi:~# su - youtube
youtube@raspberrypi~$ curl https://yt-dl.org/latest/youtube-dl -o youtube-dl
youtube@raspberrypi~$ chmod a+x youtube-dl
Usage: youtube-dl [options] url [url...]
I also made some working directories, and downloaded and unpacked my scripts
youtube@raspberrypi~$ mkdir input
youtube@raspberrypi~$ mkdir output
youtube@raspberrypi~$ mkdir archive
youtube@raspberrypi~$ wget https://github.com/ethertubes/partytube/archive/master.zip
--2014-11-28 15:11:02-- https://github.com/ethertubes/partytube/archive/master.zip
Resolving github.com (github.com)... 18.104.22.168
Connecting to github.com (github.com)|22.214.171.124|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://codeload.github.com/ethertubes/partytube/zip/master [following]
--2014-11-28 15:11:08-- https://codeload.github.com/ethertubes/partytube/zip/master
Resolving codeload.github.com (codeload.github.com)... 126.96.36.199
Connecting to codeload.github.com (codeload.github.com)|188.8.131.52|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [application/zip]
Saving to: `master.zip'
[ <=> ] 247,104 923K/s in 0.3s
2014-11-28 15:11:14 (923 KB/s) - `master.zip' saved 
youtube@raspberrypi~$ unzip master.zip
youtube@raspberrypi~$ rm master.zip
youtube@raspberrypi~$ mv ./partytube-master/* .
youtube@raspberrypi~$ rmdir partytube-master/
Originally I planned on guests submitting their song selections over the web. I figured it would be nice to have a QR code to save them the effort of typing in a URL on their smartphone. I installed the qrencode package available in Raspbian.
root@raspberrypi:~# apt-get install libqrencode3 qrencode
This way I can do something like:
qrencode -o url.png -t PNG "http://www.example.org"
to create an image that I can print and display near the television. You’ll probably want to include the text form of the URL in your printed signage as well in case people don’t have a QR reader program. Obviously, replace example.org with whatever your URL is. You’ll want to use a web server capable of serving php content. Upload the content from the “php” directory from partytube to the place you selected on your web server. Make sure that the file playlist.txt is readable and writable by the effective user ID of the web server. I would recommend using public web space rather than a server hosted on the Pi itself, this prevents guests from needing to be part of your local network, but if your Pi is publicly facing I suppose this isn’t an issue. Please keep in mind that there is really no thought put into the security of this hastily created project, and anyone who knows the URL can clear the submissions waiting to be collected by the partytube scripts.
I showed a friend my script and he suggested it would be cool if I could add NFC support. Many new Android phones have NFC hardware that makes sharing website links as easy as bringing the phone in proximity to another phone or NFC capable device. I actually had a PN532 NFC/RFID controller breakout board from Adafruit.com that I purchased to play with, but never got around to doing anything with, so this sounded like a good opportunity to use it. The Adafruit PN532 breakout board has a 3.3v TTL UART interface that makes connecting it to the Raspberry Pi a straight forward process.
Solder some header pins on to the board in the area marked “FTDICABLE”
Then use some female to female jumper wires to connect the the NFC board to the Pi.
The RXD pin of the NFC board should go to the TXD pin of the Pi, and the TXD pin of the NFC board should go to the RXD pin on the Pi. You’ll also need to connect the ground pin of the NFC board to any of the ground pins available on the Pi, and connect the NFC board 5.0V pin to one of the 5v power pins on the Pi. Make sure to consult the latest documentation for your version of the Raspberry Pi in order to identify the correct pins as these could change with different versions of the Pi.
I also soldered the headers for the jumpers which let me select between operating modes (UART, SPI, or I2C), but this isn’t required since the default mode is UART.
I printed my QR code and some instructions for guests and adhered it over the antenna area on the NFC board.
So, of course we need a way to be able to get data from the NFC reader now. There is a python module that can interface to this reader available at https://launchpad.net/nfcpy with documentation at http://nfcpy.readthedocs.org/en/latest/. The sample beam.py script that comes with the nfcpy module source seems to do what I want, so I just used that.
root@raspberrypi:~# apt-get install bzr
root@raspberrypi:~# cd /usr/local/src
root@raspberrypi:/usr/local/src# bzr branch lp:nfcpy
I added the youtube user to the dialout group to give it access to the Pi’s TTL UART port on /dev/ttyAMA0.
root@raspberrypi:/usr/local/src# adduser youtube dialout
Adding user `youtube' to group `dialout' ...
Adding user youtube to group dialout
You’ll also want to disable the serial console setup on /dev/ttyAMA0 to make sure that doesn’t interfere with the communications to the NFC board. Make sure you have ssh access or some other way to access your Pi besides serial console, and then edit /etc/inittab and comment out the ttyAMA0 line:
#Spawn a getty on Raspberry Pi serial line
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
Edit /boot/cmdline.txt and remove the console=ttyAMA0,115200 portion of the line
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
At this point it’s probably a good idea to reboot the Pi. You should be able to test the NFC board by running the command:
root@raspberrypi:~# sudo -u youtube /usr/local/src/nfcpy/examples/beam.py --device tty:AMA0:pn53x recv print
Try beaming a URL from your phone over NFC, you should see the URL in the on-screen output.
All that should be left is to hook the Pi up to an HDMI display with audio support like a television set or multimedia receiver. Omxplayer can be also output through the analog sound port using the -o local command line argument, but you’ll have to edit the script to make use of that if you require it.
To start the whole thing in motion run the run.sh script as root
root@raspberrypi:# cd ~youtube
Songs are played in the order of their unix timestamp. I added a bunch of song selections before the party started because I didn’t want it to be boring and quiet until guests had a chance to figure out the partytube process. I didn’t want my playlist to preempt their choices though, so I modified the time/date of my selections to keep them at the bottom of the playlist queue (setting their dates to the future). Take a look at the future_date.sh script if you’d like to do something similar.
There are a hodgepodge of shell scripts at work here, so a brief description is probably in order.
This file should be hosted on a web server capable of running php and accessible to party guests. If collects a submitted youtube URL and writes it to the playlist.txt file within the same directory
This displays the contents of playlists.txt and then truncates the file. It should be hosted on a web server capable of running php alongside save.php and accessible to the Raspberry Pi.
Before running this script, the “URL=” variable should be modified to reflect the location where save.php and get.php are hosted on the web (for example http://example.org/party would be correct if http://example.org/party/save.php and http://example.org/party/get.php are hosted at those locations).
After starting the run.sh script, the URL specified in that script is turned into a QR code image which will be displayed on screen between songs. The NFC and web-based URL submission collection scripts are started in the background. The web-based collection script is passed (as a command line argument) the URL the user specified with “/get.php” appended. Next the show_playlist.py is ran. After show_playlist.py exists the omxplayer displays the next music video (as determined by timestamp) from the “./output” directory. Once the video is done playing it and it’s corresponding meta data are moved to the “./archive” directory, show_playlist.py is called again and the cycle repeats indefinitely until run.sh receives the SIGEXIT or SIGTERM signal, at which time it attempts clean up by killing all screen sessions owned by the youtube user and the omxplayer before exiting.
The show_playlist.py script is started which shows the aforementioned QR code as well as any songs currently in the queue. The “queue” consists of files in the “./output” directory. Songs are sorted according to their timestamp, from oldest to newest. All video files should have a corresponding (same base filename) text file containing the video title and a thumbnail image. Show_playlist.py displays the contents of these corresponding files and a list of the next few songs to be played. After a few seconds of displaying this information to the screen the script exits.
The get_songs.nfc.sh script runs the beam.py example from nfcpy and parses the output for URLs containing ‘youtube‘. The youtube video ID is parsed out and used as the filename for the video and corresponding files. The youtube URL is handed to youtube-dl which downloads the Youtube video to the “./input” directory. Once the download is complete the video ID is passed to the “youtube-title.sh” and “youtube-tumb.sh” scripts which save the video title and thumbnail image. The title, thumbnail and video file for all videos in “./input” are processed and moved to the “./output” directory. If a file in “./input” is not an mp4 file it is deleted. This process repeats until the script is killed.
The get_songs_http.sh script collects song submissions from a remote website. It takes one command line argument: the web address of a list of youtube URLs (one per line). This address is specified in the run.sh script. For each line in the downloaded list get_songs_http.sh passes the URL to youtube-dl to download the video. Youtube-dl saves the video to the “./input” directory. Once the download is complete the Youtube ID of the downloaded video is passed to the “youtube-title.sh” and “youtube-tumb.sh” scripts which save the video title and thumbnail image. The title, thumbnail and video file for all mp4 videos in the “./input” directory are processed and moved to the “./output” directory. If a file in “./input” is not an mp4 file it is deleted. The script waits 60 seconds before repeating this process. This continues until the script is killed.
Well, I think this covers everything, it’s a kludge that’s for sure, but it worked well for my party. Maybe I’ll make some updates for my next gathering.