diff --git a/homeassistant/components/cast/helpers.py b/homeassistant/components/cast/helpers.py index dd98a2bc051..dfeb9fce25b 100644 --- a/homeassistant/components/cast/helpers.py +++ b/homeassistant/components/cast/helpers.py @@ -237,12 +237,14 @@ def _is_url(url): return all([result.scheme, result.netloc]) -async def _fetch_playlist(hass, url): +async def _fetch_playlist(hass, url, supported_content_types): """Fetch a playlist from the given url.""" try: session = aiohttp_client.async_get_clientsession(hass, verify_ssl=False) async with session.get(url, timeout=5) as resp: charset = resp.charset or "utf-8" + if resp.content_type in supported_content_types: + raise PlaylistSupported try: playlist_data = (await resp.content.read(64 * 1024)).decode(charset) except ValueError as err: @@ -260,7 +262,16 @@ async def parse_m3u(hass, url): Based on https://github.com/dvndrsn/M3uParser/blob/master/m3uparser.py """ - m3u_data = await _fetch_playlist(hass, url) + # From Mozilla gecko source: https://github.com/mozilla/gecko-dev/blob/c4c1adbae87bf2d128c39832d72498550ee1b4b8/dom/media/DecoderTraits.cpp#L47-L52 + hls_content_types = ( + # https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-10 + "application/vnd.apple.mpegurl", + # Some sites serve these as the informal HLS m3u type. + "application/x-mpegurl", + "audio/mpegurl", + "audio/x-mpegurl", + ) + m3u_data = await _fetch_playlist(hass, url, hls_content_types) m3u_lines = m3u_data.splitlines() playlist = [] @@ -301,7 +312,7 @@ async def parse_pls(hass, url): Based on https://github.com/mariob/plsparser/blob/master/src/plsparser.py """ - pls_data = await _fetch_playlist(hass, url) + pls_data = await _fetch_playlist(hass, url, ()) pls_parser = configparser.ConfigParser() try: diff --git a/tests/components/cast/test_helpers.py b/tests/components/cast/test_helpers.py index 0d7a3b1ff14..d729d36a225 100644 --- a/tests/components/cast/test_helpers.py +++ b/tests/components/cast/test_helpers.py @@ -14,10 +14,25 @@ from homeassistant.components.cast.helpers import ( from tests.common import load_fixture -async def test_hls_playlist_supported(hass, aioclient_mock): +@pytest.mark.parametrize( + "url,fixture,content_type", + ( + ( + "http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_low/ak/bbc_radio_fourfm.m3u8", + "bbc_radio_fourfm.m3u8", + None, + ), + ( + "https://rthkaudio2-lh.akamaihd.net/i/radio2_1@355865/master.m3u8", + "rthkaudio2.m3u8", + "application/vnd.apple.mpegurl", + ), + ), +) +async def test_hls_playlist_supported(hass, aioclient_mock, url, fixture, content_type): """Test playlist parsing of HLS playlist.""" - url = "http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_low/ak/bbc_radio_fourfm.m3u8" - aioclient_mock.get(url, text=load_fixture("bbc_radio_fourfm.m3u8", "cast")) + headers = {"content-type": content_type} + aioclient_mock.get(url, text=load_fixture(fixture, "cast"), headers=headers) with pytest.raises(PlaylistSupported): await parse_playlist(hass, url)