HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Get metadata from an Icecast radio stream

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
streamradioicecastgetfrommetadata

Problem

I am new to Python and not very familiar with advanced Python data structures.

I have written a function to receive data from a socket in Python and perform string manipulations on it. The basic purpose of the code is to get the metadata from an Icecast radio stream based on suggestions I found elsewhere.

The function seems to work but it would be great it someone can help me optimize the string operation.

```
def radioPoller():
msg1="GET / HTTP/1.1"+'\r'+'\n'+"Host: sc.buddharadio.com"+'\r'+'\n'+"User-Agent: VLC/2.0.5 LibVLC/2.0.5"+'\r'+'\n'+"Range: bytes=0-"+'\r'+'\n'+"Connection: close"+'\r'+'\n'+"Icy-MetaData: 1"+'\r'+'\n'+'\r'+'\n'

if os.path.exists("/media/hdd1/data.txt"):
os.unlink("/media/hdd1/data.txt")

HOST = 'sc.buddharadio.com' # The remote host
PORT = 80 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

f=open("/media/hdd1/data.txt","w")

s.send(msg1) #sending http request
data=""
while len(data)<1024:
data=s.recv(1024) #recieving a few characters

t=data.find("metaint") #finding the metaint response header which contains interval after which metadata will be visible in data stream eg:-icy-metaint:32768
data=data[t+8:] #jumping 8 characters to the end of the string

splitter="\r\n" # to split the contents and find when header ends and
data1=data.split(splitter) #data1 stores the splitted data. First list has length and last has data

metaInt=int(data1[0]) #find the metadata interval byte length
#print metaInt

string12=data1[len(data1)-1] #contains only the data part

lengthTotal= len(string12) #length of data we have

print

Solution

def radioPoller():


Python convention says that function names should be lowercase_with_undescores. Also, they should be verb, so: poll_radio would be better

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))


I recommend not using s as its not obvious what that means.

finalData=[]


Python convention, local names should be lowercase_with_underscores.

f=open("/media/hdd1/data.txt","a")


Again, don't use single letter variable names.

s.send(msg1)                                 #sending http request


Instead of doing HTTP requests yourself, it'd make more sense to use urllib2 to do it for you. That way it'd take care of parsing etc.
Where is msg1 coming from? Also, you don't know that this sent all the data. To be sure all data was sent, use s.sendall

data=""
    while len(data)<1024:
        data=s.recv(1024)               #recieving a few characters


Shouldn't you be using +=?

t=data.find("metaint")                       #finding the metaint response header
    data=data[t+8:]                      #jumping 8 characters to the end of the string

    splitter="\r\n"                         # to split the contents and find when header ends and                              
    data1=data.split(splitter)                      #data1 stores the splitted data. First list has length and last has data


Why assign splitter to a local variable? just pass the constant.

metaInt=int(data1[0])                      #find the metadata interval byte length
    #print metaInt


Rather then mucking with this use, use python's tool to parse the headers for you.

finalData.append(data1[len(data1)-1])           #contains only the data part


You can use data1[-1] for the same result.

lengthTotal= len(finalData[0])                  #length of data we have

    #print "total data is", lengthTotal,"\n"

    #print "TOtal bytes we should get is", (metaInt+4080),"\n"


Don't leave dead code in comments

while lengthTotal<(metaInt+4080):
        data = s.recv(8192)

        lengthTotal=lengthTotal+len(data)
        #print "Total length now is", lengthTotal 
        finalData.append(data)


In this case I suggest using StringIO rather than a list.

print finalData
    string12=''.join(finalData)


Don't use meaningless names

metaLen=ord(string12[metaInt])
    #print "This is multiplier", metaLen, "No of characters to read ", (metaLen*16)

    metaString=string12[metaInt+1:metaInt+1+(metaLen*16)]
    metaString=metaString+"\r\n"

    #print "Extracted String is \n", metaString

    f.write(str(metaString))


It's already a string, don't pass it to str

s.close
    f.close


These last two don't do anything.

Here's my reworking of your code:

def parse_headers(response):
    headers = {}
    while True:
        line = response.readline()
        if line == '\r\n':
            break # end of headers
        if ':' in line:
            key, value = line.split(':', 1)
            headers[key] = value
    return headers

def poll_radio():
    request = urllib2.Request("http://sc.buddharadio.com:80/", headers = {
        'User-Agent' : 'User-Agent: VLC/2.0.5 LibVLC/2.0.5',
        'Icy-MetaData' : '1',
        'Range' : 'bytes=0-',
    })
    # the connection will be close on exit from with block
    with contextlib.closing(urllib2.urlopen(request)) as response:

        headers = parse_headers(response)

        meta_interval = int(headers['icy-metaint'])
        response.read(meta_interval) # throw away the data until the meta interval

        length = ord(response.read(1)) * 16 # length is encoded in the stream
        metadata = response.read(length)
        print metadata

Code Snippets

def radioPoller():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
finalData=[]
f=open("/media/hdd1/data.txt","a")
s.send(msg1)                                 #sending http request

Context

StackExchange Code Review Q#23364, answer score: 5

Revisions (0)

No revisions yet.