问题
I would like to continually read the stream and update the title information. (Something like this is posted here: Receive ice cast meta data with python, however it makes multiple requests, and I was looking to just make one stream request. The protocol is described here: http://www.smackfu.com/stuff/programming/shoutcast.html. It seems to only work once then stop working. Here is what I have so far:
import urllib2
import struct
import re
request = urllib2.Request('http://icy1.abacast.com:80/wbeb-wbebhd2aac-64')
request.add_header('Icy-MetaData','1')
opener = urllib2.build_opener()
data=opener.open(request)
while True:
audio = data.read(2048) # 2048 is the metadata interval for this stream
metadata_size = struct.unpack('B', data.read(1))[0]*16
metadata = data.read(metadata_size).rstrip(b'\0')
m = re.search(br"StreamTitle='([^']*)';", metadata)
title = m.group(1).decode('latin1', errors='replace')
print title
I believe the reason why that one doesn't work is that the url is not opened as a stream, is that possible to do with urllib2?
I also have one that doesnt work made with requests.get:
def monitor():
url = 'http://icy1.abacast.com:80/wbeb-wbebhd2aac-64'
r = requests.get(url, headers={'Icy-MetaData': 1}, stream=True)
metadata = StringIO.StringIO()
byte_counter = 0
meta_counter = 0
metadata_interval = r.headers['icy-metaint']
metadata_size = 0
length = None
data_bool = True
for data in r.iter_content(1):
byte_counter+=1
print "byte %s" % byte_counter
if (byte_counter <= 2048):
pass # audio data
if (byte_counter > 2048):
if (meta_counter == 0):
metadata_size = struct.unpack('B', data)[0]*16
print "METADATA SIZE: %s" % metadata_size
meta_counter+=1
elif (meta_counter <= int(metadata_size+1)):
metadata.write(data)
meta_counter+=1
else: data_bool = False
if (data_bool is False):
byte_counter = 0
meta_counter = 0
meta_interval = 0
metadata_size = 0
meta = metadata.read().rstrip(b'\0')
m = re.search(br"StreamTitle='([^']*)';", meta)
if m is not None:
title = m.group(1).decode('latin1', errors='replace')
print "Title is: %s" % title
metadata = StringIO.StringIO()
data_bool = True
回答1:
Not sure if you did already find a way in the meantime. But as I came across the same question - here is my (working but barely tested) version.
Widely based on your suggestion, with some adaptions and python3 support:
from __future__ import unicode_literals
import re
import requests
import sys
try:
from StringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
def icy_monitor(stream_url, callback=None):
r = requests.get(stream_url, headers={'Icy-MetaData': '1'}, stream=True)
if r.encoding is None:
r.encoding = 'utf-8'
byte_counter = 0
meta_counter = 0
metadata_buffer = BytesIO()
metadata_size = int(r.headers['icy-metaint']) + 255
data_is_meta = False
for byte in r.iter_content(1):
byte_counter += 1
if (byte_counter <= 2048):
pass
if (byte_counter > 2048):
if (meta_counter == 0):
meta_counter += 1
elif (meta_counter <= int(metadata_size + 1)):
metadata_buffer.write(byte)
meta_counter += 1
else:
data_is_meta = True
if (byte_counter > 2048 + metadata_size):
byte_counter = 0
if data_is_meta:
metadata_buffer.seek(0)
meta = metadata_buffer.read().rstrip(b'\0')
m = re.search(br"StreamTitle='([^']*)';", bytes(meta))
if m:
title = m.group(1).decode(r.encoding, errors='replace')
print('New title: {}'.format(title))
if callback:
callback(title)
byte_counter = 0
meta_counter = 0
metadata_buffer = BytesIO()
data_is_meta = False
def print_title(title):
print('Title: {}'.format(title))
if __name__ == '__main__':
stream_url = sys.argv[1]
icy_monitor(stream_url, callback=print_title)
来源:https://stackoverflow.com/questions/41022893/monitoring-icy-stream-metadata-title-python