使用Python读取SHOUTcast/Icecast电台流的元数据

7 投票
4 回答
6728 浏览
提问于 2025-04-16 21:02

有没有人成功从远程广播流中读取SHOUTcast/Icecast的元数据?

有一些可以读取本地MP3文件的元数据,但似乎没有哪个是专门为广播流设计的(广播流本质上就是一个在远程服务器上不断播放的MP3文件)。

其他一些建议是从MP3流的开头下载有限数量的数据,但这通常会导致一堆十六进制的输出,里面没有任何看起来像文本元数据的东西。

有没有人知道更成功的解决方案?谢谢。

4 个回答

0

如果你在这里找到了这个内容,十年后,这里有@dbogdan的代码的python3版本。需要注意的是,content[metaint:].split("'")[1]这个方法非常不可靠。而且,一旦你遇到像“Queensrÿche”这样的非英语标题,标题中就会充满特殊字符的字节。你不能直接decode整个标签,所以需要一些“曲折”的方法来把标签简化成只有标题。我没有从响应头中获取sample ratebitrate,因为它们总是错误的。最好从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个流媒体。这个库运行得很好,稳定性也不错,还支持一些元数据,比如歌曲标题、艺术家名字、比特率和内容类型。

你可以在这里找到它: https://github.com/Dirble/streamscrobbler-python

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'

想了解更多细节,可以查看这个链接

撰写回答