Streaming YouTube music videos as MP3 with Python

I’ve been talking with my friend about the idea of standalone YouTube music player in Linux, since we are using YouTube to listen to music very often. There is great wealth of good quality tunes published there and easy access to them, just few clicks away makes YouTube a nice candidate for streaming music. My friend suggested that there should be a plugin for Amarok or any other player in Linux which allows you to enqueue some tracks from YouTube and let you play the music, without the burden of running browser.

I said that’s really not a problem since we are Python programmers and quickly thought I might try to quickly hack a proof of concept. After just under an hour, I’ve come up with a quick, dirty hack that allows you to stream music videos from YouTube as MP3.

So what I’ve done is write a really simple and basic script that would conform to the Unix philosophy – one program, one function – under Python of course. The script takes an argument with YouTube video ID and then streams it back to standard out (stdout). Here’s the script:

— cut —

# -*- coding: utf-8 -*-
import urllib
import sys

class AppURLopener(urllib.FancyURLopener):
  version = "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101230 Mandriva Linux/1.9.2.13-0.2mdv2010.2 (2010.2) Firefox/3.6.13"

if len(sys.argv) < 2:
  print 'Usage: {prog} URL'.format(prog = sys.argv[0])
  sys.exit(1)

video_id = sys.argv[1]

opener = AppURLopener()
fp = opener.open('http://www.youtube.com/get_video_info?video_id={vid}'.format(vid = video_id))
data = fp.read()
fp.close()

if data.startswith('status=fail'):
  print 'Error: Video not found!'
  sys.exit(2)

vid_list = []
tmp_list = urllib.unquote(urllib.unquote(data)).split('|')
for fmt_chk in tmp_list:
  if len(fmt_chk) == 0:
    continue
  if not fmt_chk.startswith('http://'):
    continue
  vid_list.append(fmt_chk)

# FIXME: Format choice
link = vid_list[0]

fp = opener.open(link)
data = fp.read(1024)
while data:
  sys.stdout.write(data)
  data = fp.read(1024)
fp.close()

— cut —

This script streams the raw video to stdout. It doesn’t have any error corrections and is really simple. If YouTube decides to change the description file it won’t work anymore. Nevertheless you can stream your video to stdout and under Linux you can take care of that stream with available tools.

So I have used FFMPEG transcoder that would rip out on the fly the audio stream, transcode it to MP3 that you can write or listen to if you want.

How can you do this? Pretty simple, given you’ve got necessary decoders installed (mainly FAAC, FAAD and others used in conjunction with libavcodec). If you want to listen to mp3 stream here’s how you do it:

python script.py YOUTUBE_VIDEO_ID | ffmpeg -i - -ab 192k -vn -acodec libmp3lame -f mp3 - | mpg123 -

Where YOUTUBE_VIDEO_ID is the identificator of YouTube video. What FFMPEG options mean:

  • “-i -” – that means the input is stdin (standard input)
  • “-ab 192k” – means you want to transcode to 192kbit/s mp3 stream
  • “-vn” – you don’t want to stream any video
  • “-acodec libmp3lame” – use LAME MP3 encoder for the trancoding process
  • “-f mp3” – output format should be MP3
  • “-” – at the end, means that output is to stdout (standard out)

Due to pipelining you use three programs which make all of this possible. First the streaming script I have written, then the transcoder (ffmpeg) to mp3 stream, then the player (in this case mpg123). If you want to download YouTube video music and save it as MP3 file you do:

python script.py YOUTUBE_VIDEO_ID > stream.flv; ffmpeg -i stream.flv -ab 192k -vn -acodec libmp3lame -f mp3 OUTFILE.MP3

Where you have to substitute YOUTUBE_VIDEO_ID with YouTube video identificator and OUTFILE.MP3 with name of your new MP3 file.

That’s all! Hope you find it useful.

About Wolverine

If you are looking for IT consultant, let me know! karol at karoltomala dot REMOVE com Just remove the REMOVE word from the e-mail above!
This entry was posted in Coding, Linux, Programming, Python and tagged , , , , . Bookmark the permalink.

5 Responses to Streaming YouTube music videos as MP3 with Python

  1. Giuseppe says:

    Hi, thank you for your script, I was looking for something similar for months.
    Unfortunately I received this error:
    “Traceback (most recent call last):
    File “main.py”, line 33, in
    link = vid_list[0]
    IndexError: list index out of range”

    with this cmd:
    “python main.py UW1nWBquc7M | ffmpeg -i – -ab 192k -vn -acodec libmp3lame -f mp3 – | mpg123 -”

    Probably there is something wrong in the get_video_info parsing. Could you give me an example of the correct input necessary to fffmpeg?

    Thank you again
    Giuseppe

  2. Wolverine says:

    Hi. It was busy month for me recently, so I apologise for the delay. This script was written a long time ago, so it’s probably outdated. I am not aware how youtube changed their XML info about the video. I’ve done a quick check and this get_video_info result are GET parameters that are probably feed to javascript by means of AJAX.

    By a quick glance there is a parameter url_encoded_fmt_stream_map and that’s where you should start looking. All that python script does is check get_video_info and then download Flash Video (FLV) stream for output on stdout. This stdout is then piped to ffmpeg’s stdin.

    This is all I can do to help you since I don’t have time for researching this topic further, but you should be able to change the script according to your needs and make it work again.

  3. Luke says:

    Hi; first of thanks for the script even though the technique used to parse the information
    no longer works you solved the hard part. Secondly I fixed the bug with parsing the
    information and would like to post it here (hope that’s alright) so it could be of some help
    for others wanting to use this script.

    # -*- coding: utf-8 -*-
    import urllib
    import sys

    class AppURLopener(urllib.FancyURLopener):
    version = “Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101230 Mandriva Linux/1.9.2.13-0.2mdv2010.2 (2010.2) Firefox/3.6.13”

    if len(sys.argv) < 2:
    print 'Usage: {prog} URL'.format(prog = sys.argv[0])
    sys.exit(1)

    video_id = sys.argv[1]

    opener = AppURLopener()
    fp = opener.open('http://www.youtube.com/get_video_info?video_id={vid}'.format(vid = video_id))
    data = urllib.unquote(urllib.unquote(fp.read()))
    fp.close()

    if data.startswith('status=fail'):
    print 'Error: Video not found!'
    sys.exit(2)

    link = data[data.find('url=http://')+4:]
    link = link[:link.find('"')]

    fp = opener.open(link)
    data = fp.read(1024)
    while data:
    sys.stdout.write(data)
    data = fp.read(1024)
    fp.close()

  4. Pingback: Using Python to make a terminal-based YouTube audio streamer - BlogoSfera

  5. SLDB says:

    Hey! thanks for the script 😀
    I get this error ..any help plz 🙂
    Happy new year btw !!

    sldb@sldb-Alternative:~/Documents$ python youtube.py zmyVvrdZN0c | ffmpeg -i – -ab 192k -vn -acodec libmp3lame -f mp3 – | mpg123 –
    High Performance MPEG 1.0/2.0/2.5 Audio Player for Layer 1, 2, and 3.
    Version 0.3.2-1 (2012/03/25). Written and copyrights by Joe Drew,
    now maintained by Nanakos Chrysostomos and others.
    Uses code from various people. See ‘README’ for more!
    THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! USE AT YOUR OWN RISK!
    ffmpeg version N-77455-g4707497 Copyright (c) 2000-2015 the FFmpeg developers
    built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04)
    configuration: –extra-libs=-ldl –prefix=/opt/ffmpeg –mandir=/usr/share/man –enable-avresample –disable-debug –enable-nonfree –enable-gpl –enable-version3 –enable-libopencore-amrnb –enable-libopencore-amrwb –disable-decoder=amrnb –disable-decoder=amrwb –enable-libpulse –enable-libdcadec –enable-libfreetype –enable-libx264 –enable-libx265 –enable-libfdk-aac –enable-libvorbis –enable-libmp3lame –enable-libopus –enable-libvpx –enable-libspeex –enable-libass –enable-avisynth –enable-libsoxr –enable-libxvid –enable-libvo-aacenc –enable-libvidstab
    libavutil 55. 11.100 / 55. 11.100
    libavcodec 57. 20.100 / 57. 20.100
    libavformat 57. 20.100 / 57. 20.100
    libavdevice 57. 0.100 / 57. 0.100
    libavfilter 6. 21.101 / 6. 21.101
    libavresample 3. 0. 0 / 3. 0. 0
    libswscale 4. 0.100 / 4. 0.100
    libswresample 2. 0.101 / 2. 0.101
    libpostproc 54. 0.100 / 54. 0.100
    –: No such file or directory
    tcgetattr(): Inappropriate ioctl for device

    Playing MPEG stream from – …

    [0:00] Decoding of – finished.

Leave a Reply

Your email address will not be published. Required fields are marked *