4

I'm writing a simple frontend in Python to play and record internet radio channels (e.g. from shoutcast) using mplayer (in a subprocess). When a user clicks a station the following code is run:


url = http://77.111.88.131:8010 # only an example
cmd = "mplayer %s" % url
p = subprocess.Popen(cmd.split(), shell=False)
wait = os.waitpid(p.pid, 1)
return int(p.pid)

This works perfectly, the stream starts playing as it should. Although I would like to somehow parse the title of the stream. It seems that I need to fetch the title from the mplayer output. This is the output when I play the stream in a terminal:

$ mplayer http://77.111.88.131:8010
MPlayer 1.0rc4-4.4.5 (C) 2000-2010 MPlayer Team
mplayer: could not connect to socket
mplayer: No such file or directory
Failed to open LIRC support. You will not be able to use your remote control.

Playing http://77.111.88.131:8010.
Resolving 77.111.88.131 for AF_INET6...
Couldn't resolve name for AF_INET6: 77.111.88.131
Connecting to server 77.111.88.131[77.111.88.131]: 8010...
Name   : Justmusic.Fm
Genre  : House
Website: http://www.justmusic.fm
Public : yes
Bitrate: 192kbit/s
Cache size set to 320 KBytes
Cache fill:  0.00% (0 bytes)   
ICY Info: StreamTitle='(JustMusic.FM) Basement - Zajac, Migren live at Justmusic 2010-10-09';StreamUrl='http://www.justmusic.fm';
Cache fill: 17.50% (57344 bytes)   
Audio only file format detected.

It then runs until it's stopped. So the question is, how can I retrieve "(JustMusic.FM) Basement - Zajac, Migren live at Justmusic 2010-10-09" and still let the process run? I don't think subprocess() actually stores the output, but I might be mistaken. Any help is deeply appreciated :)

BЈовић
  • 62,405
  • 41
  • 173
  • 273
Fredrik
  • 1,741
  • 4
  • 24
  • 40

2 Answers2

5

Set the stdout argument to PIPE and you'll be able to listen to the output of the command:

p= subprocess.Popen(['mplayer', url], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout:
    if line.startswith('ICY Info:'):
        info = line.split(':', 1)[1].strip()
        attrs = dict(re.findall("(\w+)='([^']*)'", info))
        print 'Stream title: '+attrs.get('StreamTitle', '(none)')
Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
bobince
  • 528,062
  • 107
  • 651
  • 834
  • Thanks a lot! Although stdout=PIPE,stderr=STDOUT totally deadlocks the TK GUI. If I "break" the loop then mplayer stops after about 30 seconds. Any way around this? Maybe escape the loop without affecting the process? – Fredrik Oct 14 '10 at 07:44
  • Try it with `p.communicate()` first. It sounds like a buffer is getting filled up and not read in time for some reason, cauing the child process to block. You may not need `stdout` *and* `stderr`; presumably the message you want only comes out on one of those, I just don't know which! Maybe stderr, in which case try just `stderr=PIPE`? – bobince Oct 14 '10 at 11:51
1
import re
import shlex
from subprocess import PIPE, Popen

URL = 'http://relay2.slayradio.org:8000/'

def get_exitcode_stdout_stderr(cmd):
    """
    Execute the external command and get its exitcode, stdout and stderr.
    """
    args = shlex.split(cmd)

    proc = Popen(args, stdout=PIPE, stderr=PIPE)
    out, err = proc.communicate()
    exitcode = proc.returncode
    #
    return exitcode, out, err

def get_title():
    cmd = "mplayer -endpos 1 -ao null {url}".format(url=URL)
    out = get_exitcode_stdout_stderr(cmd)[1]

    for line in out.split("\n"):
#        print(line)
        if line.startswith('ICY Info:'):
            match = re.search(r"StreamTitle='(.*)';StreamUrl=", line)
            title = match.group(1)
            return title

def main():
    print(get_title())

Edit: I had a different (simpler) solution here that stopped working so I updated my solution. The idea: mplayer stops after 1 sec. (-endpos 1).

Jabba
  • 19,598
  • 6
  • 52
  • 45