Record collections certainly used to be nice, but sometimes it’s much nicer to have every song you can possibly think of at your fingertips. Youtube is a good source, but has several things holding it back; most significantly ads, playlists that don’t function as on-the-fly play queues and it cannot easily be operated remotely over ssh. Remote ssh control is useful because I’m often sat in a room with both my netbook and another computer with much better sounding speakers. I’ve overcome these problems by writing a ruby script.

It is admittedly quite crude, doesn’t validate input properly and can be a little cryptic when output messages start flowing out; it does however work. The interface is even non-geek friendly, I passed it around friends, with the instruction “search for what you want, then pick the number”; it wasn’t long before the speakers sprang into life. It works best when run from within its own directory, as it downloads all the tracks to the current working directory. It keeps them for future use; if the same track is requested again, the cached copy is played and if the playlist becomes empty cached tracks will play on shuffle while waiting for more requests.

It scrapes the youtube search results page using Hpricot, then downloads and extracts the audio to an mp3 file using the excellent youtube-dl package in combination with ffmpeg. Tracks are downloaded in parallell and added to the playlist once they have been downloaded and converted to mp3, this means that tracks are not always played in the order they were selected.

Update: youtube search results have slightly changed, a newer version of this script is here.

youtube_play.rb:

require 'hpricot' require 'open-uri' playlist=Array.new #This thread actually plays the tracks in the playlist then removes them. player=Thread.new { while true #If the playlist is empty, play random mp3s until someone adds something if playlist.length==0 then system('./random_music.sh') else system('mpg321 -q '+playlist.shift) end end } #While the main thread handles the user interface: while true puts "What would you like to listen to?" search_string= gets.chomp puts"Searching for #{search_string}..." #Use Hpricot to load the youtube search page and scrape the titles and ids of the top 5 results doc = Hpricot(open("http://www.youtube.com/results?search_type=search_videos&search_query="+search_string.gsub(' ','+'))) result_divs=doc.search("div[@id=search-results]/div") results=Array.new #Ask the user which one they want for i in 0..4 do #keep the a object as it has both bits of data results[i]=result_divs[i].search("div.result-item-main-content/h3/a") puts (i+1).to_s + ') ' + results[i].inner_html end choice=gets.chomp.to_i if (1..5).include?(choice) then choice-=1 youtube_id = results[choice].attr('href').split('&')[0].split('=')[1] #Download/extract the audio in parallel so that more tracks can be added in the meantime Thread.new(youtube_id,results[choice].inner_html) {|id,title| if ! File.exist?(id+'.mp3') puts 'Downloading ' + title system('youtube-dl --extract-audio -w -q -f 5 --no-part '+id) end playlist<<id+'.mp3' puts title+' added to playlist' } end end

random_music.sh:

#! /bin/bash set -- *.mp3 length=$# random_num=$(( $RANDOM % ($length + 1) )) mpg321 -q ${!random_num}