From a51eb37075cb80163e08c8d795fd2a5b1c343ca8 Mon Sep 17 00:00:00 2001 From: Jonathan Caicedo Date: Fri, 6 Mar 2026 08:10:30 -0500 Subject: [PATCH] fix: M3U EXTINF parsing for attributes ending in double equals fixes: #1055 --- apps/m3u/tasks.py | 6 ++-- apps/m3u/tests/test_extinf_parsing.py | 41 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 apps/m3u/tests/test_extinf_parsing.py diff --git a/apps/m3u/tasks.py b/apps/m3u/tasks.py index c0c90c57..162359e9 100644 --- a/apps/m3u/tasks.py +++ b/apps/m3u/tasks.py @@ -482,8 +482,10 @@ def parse_extinf_line(line: str) -> dict: attrs = {} last_attr_end = 0 - # Use a single regex that handles both quote types - for match in re.finditer(r'([^\s]+)=(["\'])([^\2]*?)\2', content): + # Use a single regex that handles both quote types. + # Keys must stop at '=' so values like base64-padded URLs ending with '==' + # don't get folded into the preceding attribute name. + for match in re.finditer(r'([^\s=]+)\s*=\s*(["\'])(.*?)\2', content): key = match.group(1) value = match.group(3) attrs[key] = value diff --git a/apps/m3u/tests/test_extinf_parsing.py b/apps/m3u/tests/test_extinf_parsing.py new file mode 100644 index 00000000..367f569b --- /dev/null +++ b/apps/m3u/tests/test_extinf_parsing.py @@ -0,0 +1,41 @@ +from django.test import SimpleTestCase + +from apps.m3u.tasks import parse_extinf_line + + +class ParseExtinfLineTests(SimpleTestCase): + def test_preserves_equals_padding_in_tvg_logo(self): + line = ( + '#EXTINF:-1 tvg-id="cp_891ee08a2cdfde210ec2c9137127103b" ' + 'tvg-chno="1001" ' + 'tvg-name="UK Sky Sports Premier League" ' + 'tvg-logo="https://e3.365dm.com/tvlogos/channels/1303-Logo.png?' + 'U2t5IFNwb3J0cyBQcmVtaWVyIExlYWd1ZQ==" ' + 'group-title="Team Games",UK Sky Sports Premier League' + ) + + parsed = parse_extinf_line(line) + + self.assertIsNotNone(parsed) + self.assertEqual( + parsed["attributes"]["tvg-logo"], + "https://e3.365dm.com/tvlogos/channels/1303-Logo.png?U2t5IFNwb3J0cyBQcmVtaWVyIExlYWd1ZQ==", + ) + self.assertEqual(parsed["attributes"]["group-title"], "Team Games") + self.assertEqual(parsed["name"], "UK Sky Sports Premier League") + + def test_supports_single_quoted_attributes(self): + line = ( + "#EXTINF:-1 tvg-name='Channel One' tvg-logo='https://example.com/logo==.png' " + "group-title='Sports',Channel One" + ) + + parsed = parse_extinf_line(line) + + self.assertIsNotNone(parsed) + self.assertEqual( + parsed["attributes"]["tvg-logo"], + "https://example.com/logo==.png", + ) + self.assertEqual(parsed["attributes"]["group-title"], "Sports") + self.assertEqual(parsed["display_name"], "Channel One")