Playing youtube music from the command line

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}

18 thoughts on “Playing youtube music from the command line

  1. Pingback: Hackaday Links: Leap Day, 2012 - Hack a Day

  2. Pingback: Hackaday Links: Leap Day, 2012 « Hackaday « Cool Internet Projects

  3. Chris

    Heya not sure if im just being a dumb ass here or not but do u have a download link where we could download the compiled version of this or a zip file where we could download the source and the exe thanks.

    Reply
    1. Mike Post author

      I guess from your request for an exe that you’re running windows? If so I’m afraid you’re out of luck; this requires a linux machine with ruby (plus the hpricot gem), mpg321 and youtube-dl installed. My script just sits in between them and gives a nicer interface.

      It is a ruby script working together with a bash script neither of which need compilation; the top code block needs to be saved in a file called youtube_play.rb and the second in a file called random_music.sh which also needs to have the permissions changed to allow execution. Then to start the script navigate to the directory where it is and enter “ruby youtube_play.rb”; you will want to put it in its own directory unless you want all your filles mixed in with youtube music mp3s.

      Reply
  4. Jraz

    This is really cool and thanks for sharing. I found you off of Hackaday and could not resist reading. I love You Tube for the same reasons but also hate for the same reasons. I’ve got to share this with my readers.

    Reply
  5. 0ptimus

    Thanks for sharing this cool script, Mike :)! Not sure if it was just my setup, but to get it to work I needed to change line 43 from….

    if ! File.exist?(id+’.mp3′)

    to…
    if ! File.exist?(id+’.flv’)

    Reply
  6. Tomas

    Hey,

    Thank you for the script. It is indeed awesome :)

    It didn’t work straight away for me so I thought this might help some other people coming here to get started. I use Arch Linux so this might not be exactly the same on your linux dist.

    I installed the following with:

    sudo pacman -S ruby youtube-dl ffmpeg mpg123; gem install hpricot

    I then changed “mpg321 -q ${!random_num} ” to “mpg123 -q ${!random_num} “.

    Reply
    1. Mike Post author

      you are correct in installing ruby, youtube-dl, ffmpeg and hpicrot. As far as mpg321/mpg123 goes, I’m not really sure what the difference is but mpg123 sems to get the sample rate wrong occasionally and play things at double speed; mpg321 sems ok; you’ll need one or the other.

      Thanks,
      Mike

      Reply
        1. projectdelphai

          just in case you didn’t know. mpg321 is in the arch AUR repository so you’d have needed to compile it from there instead of straight from pacman.

          Reply
  7. Pingback: Hackaday Links: Leap Day, 2012 » Geko Geek

  8. LaurentGh

    Hi,

    Nice trick !
    Ok, so I’m on a Mac, I’ve installed ruby, hpicrot and mpg321 via Brew. But the script fails with a :
    *.mp3: No such file or directory
    Any idea ? Please ?

    Thanks !
    Laurent

    Reply
    1. Mike Post author

      That error is coming from the random_music.sh script; do you have bash installed? You either need bash, or you can simply disable the shuffle feature by replacing the line ” system(‘./random_music.sh’) ” with “sleep 1″.

      Reply
  9. Inhalebot

    Why do I get this?:

    youtube_play.rb:1:in `require’: no such file to load — hpricot (LoadError)
    from youtube_play.rb:1
    I have everything required installed

    Reply
    1. Inhalebot

      I fixed it by just downloading it again. Now I get:
      What would you like to listen to?
      Traceback (most recent call last):
      File “/usr/bin/youtube-dl”, line 15, in
      import gzip
      File “/usr/local/lib/python2.7/gzip.py”, line 9, in
      import zlib
      ImportError: No module named zlib
      America fuck yeah-team america added to playlist

      And nothing plays

      Reply
      1. Mike Post author

        it looks like youtube-dl isn’t working properly; what happens if you type “youtube-dl oHg5SJYRHA0″?

        Reply
  10. Pingback: Updating Youtube Music Player | Products of Mike's Mind

Leave a Reply