Search code examples
pythonurllibaiohttp

Different result between urllib and aiohttp


So basically i'm trying to get the currently playing track from online radio direct link (Example - http://air.radiorecord.ru:8101/rr_320).

Firstly i found something in the internet, written with urllib, my application is asynchronous so i needed to use aiohttp. With urllib it worked perfectly, while aiohttp sometimes just can't find anything. Pls help :(

before:

def get_now(self, session):
    request = urllib.Request(self.data.get('url'),headers={'Icy-MetaData': 1} ) # request metadata

    response = urllib.urlopen(request)
    metadata = response.headers

    metaint = int(response.headers['icy-metaint'])
    for _ in range(10):  # title may be empty initially, try several times
        response.read(metaint)  # skip to metadata
        metadata_length = struct.unpack('B', response.read(1))[0] * 16  # length byte
        metadata = response.read(metadata_length).rstrip(b'\0')

        # extract title from the metadata
        m = re.search(br"StreamTitle='([^']*)';", metadata)
        if m:
            title = m.group(1)
            if title:
                break
            else:
                return "No title found"
    return title.decode('utf8', errors='replace')  

except:
    return "No title found"

after:

async def get_now(self, session):
    
    async with session.get(self.stream_url, headers={'Icy-MetaData': "1"}) as resp:
        
        content = resp.content

        metadata = resp.headers
        metaint = int(metadata['icy-metaint'])

        for _ in range(30):
            await content.read(metaint)
            metadata_length = struct.unpack('B', await content.read(1))[0] * 16  # length byte
            metadata = (await content.read(metadata_length)).rstrip(b'\0')

            m = re.search(br"StreamTitle='([^']*)';", metadata)
            if m:
                title = m.group(1)
                if title:
                    return title.decode('utf8', errors='replace')
                else:
                    return "No title found"
            

        return "Nothing found"

Solution

  • The snippet below is always able to detect the current track (in around 400ms) but instead of processing only part of the chunk it checks the whole chunk as it's read:

    import aiohttp
    import asyncio
    import re
    
    
    async def get_now(stream_url, session):
        headers={"Icy-MetaData": "1"}
        async with session.get(stream_url, headers=headers) as resp:
            for _ in range(10):
                data = await resp.content.read(8192)
                m = re.search(br"StreamTitle='([^']*)';", data.rstrip(b"\0"))
                if m:
                    title = m.group(1)
                    if title:
                        return title.decode("utf8", errors="replace")
                    else:
                        return "No title found"
        return "Nothing found"
    
    
    async def get_track():
        session = aiohttp.ClientSession()
        stream_url = "http://air.radiorecord.ru:8101/rr_320"
        result = await get_now(stream_url, session)
        print(f"result: {result}")
        await session.close()
    
    
    asyncio.run(get_track())
    

    Result on my computer (CPU usage is very low on a quite old CPU: i7-3517U):

    [ionut@ionut-pc ~]$ time python test.py 
    result: Record Club - Nejtrino & Baur
    
    real    0m0.401s
    user    0m0.198s
    sys 0m0.031s