使用Python读取SHOUTcast/Icecast电台流的元数据
有没有人成功从远程广播流中读取SHOUTcast/Icecast的元数据?
有一些库可以读取本地MP3文件的元数据,但似乎没有哪个是专门为广播流设计的(广播流本质上就是一个在远程服务器上不断播放的MP3文件)。
其他一些建议是从MP3流的开头下载有限数量的数据,但这通常会导致一堆十六进制的输出,里面没有任何看起来像文本元数据的东西。
有没有人知道更成功的解决方案?谢谢。
4 个回答
0
如果你在这里找到了这个内容,十年后,这里有@dbogdan的代码的python3版本。需要注意的是,content[metaint:].split("'")[1]
这个方法非常不可靠。而且,一旦你遇到像“Queensrÿche”这样的非英语标题,标题中就会充满特殊字符的字节。你不能直接decode
整个标签,所以需要一些“曲折”的方法来把标签简化成只有标题。我没有从响应头中获取sample rate
和bitrate
,因为它们总是错误的。最好从MP3头部获取这些数据。公平地说,Shoutcast/Icecast提供的标签在各个方面都可能是错误的!只要遇到一次像“C C Revival - I hearded through the grapevine”这样的情况(这真的发生过),你就会意识到这些标签没有任何官方或可靠的依据。如果你把Shout/Icecast的标签当作MusicBrainz搜索的参数,这可能会造成很大的问题。
from urllib import request as urequest
import re
SRCHTITLE = re.compile(br'StreamTitle=\\*(?P<title>[^;]*);').search
def get_stream_title(tag:bytes) -> str:
title = ''
if m := SRCHTITLE(tag):
#decode, strip, unescape and remove surrounding quotes (may not even be the same type of quote)
title = m.group('title').decode('utf-8').strip().replace('\\', '')[1:-1]
return title
def id3(url:str) -> dict:
request = urequest.Request(url, headers={'Icy-MetaData': 1})
with urequest.urlopen(request) as resp:
metaint = int(resp.headers.get('icy-metaint', '-1'))
if metaint<0: return False
resp.read(metaint) #this isn't seekable so, arbitrarily read to the point we want
tagdata = dict(
site_url = resp.headers.get('icy-url' ) ,
name = resp.headers.get('icy-name' ).title(),
genre = resp.headers.get('icy-genre').title(),
title = get_stream_title(resp.read(255)) )
return tagdata
0
我用了@dbogdan的一些代码,创建了一个库,每天用来处理超过4000个流媒体。这个库运行得很好,稳定性也不错,还支持一些元数据,比如歌曲标题、艺术家名字、比特率和内容类型。
6
#!/usr/bin/env python
import urllib2
stream_url = 'http://pub1.di.fm/di_classictrance'
request = urllib2.Request(stream_url)
try:
request.add_header('Icy-MetaData', 1)
response = urllib2.urlopen(request)
icy_metaint_header = response.headers.get('icy-metaint')
if icy_metaint_header is not None:
metaint = int(icy_metaint_header)
read_buffer = metaint+255
content = response.read(read_buffer)
title = content[metaint:].split("'")[1]
print title
except:
print 'Error'
想了解更多细节,可以查看这个链接