Fix deezer dynamic link parsing (#824)

* Fix links issues due tu recent PR

* Add tests for URL parsing

---------

Co-authored-by: Nathan Thomas <nathanthomas707@gmail.com>
This commit is contained in:
Louis2530 2025-03-12 22:55:37 +01:00 committed by GitHub
parent db52d493b8
commit 5bcadd3c6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 156 additions and 1 deletions

View file

@ -18,7 +18,7 @@ from ..media import (
logger = logging.getLogger("streamrip")
URL_REGEX = re.compile(
r"https?://(?:www|open|play|listen)?\.?(qobuz|tidal|deezer|dzr)\.?(com|page.link)(?:(?:/(album|artist|track|playlist|video|label))|(?:\/[-\w]+?))+\/([-\w]+)",
r"https?://(?:www|open|play|listen)?\.?(qobuz|tidal|deezer)\.com?(?:(?:/(album|artist|track|playlist|video|label))|(?:\/[-\w]+?))+\/([-\w]+)",
)
SOUNDCLOUD_URL_REGEX = re.compile(r"https://soundcloud.com/[-\w:/]+")
LASTFM_URL_REGEX = re.compile(r"https://www.last.fm/user/\w+/playlists/\w+")

155
tests/test_parse_url.py Normal file
View file

@ -0,0 +1,155 @@
import unittest
from unittest.mock import AsyncMock, patch
from streamrip.rip.parse_url import (
DeezerDynamicURL,
GenericURL,
SoundcloudURL,
parse_url,
)
class TestParseURL(unittest.TestCase):
def test_deezer_dynamic_url(self):
"""Test that Deezer dynamic URLs are matched correctly."""
url = "https://dzr.page.link/SnV6hCyHihkmCCwUA"
result = parse_url(url)
self.assertIsNotNone(result)
self.assertIsInstance(result, DeezerDynamicURL)
self.assertEqual(result.source, "deezer")
def test_qobuz_album_url(self):
"""Test that Qobuz album URLs are matched correctly."""
url = "https://www.qobuz.com/fr-fr/album/bizarre-ride-ii-the-pharcyde-the-pharcyde/0066991040005"
result = parse_url(url)
self.assertIsNotNone(result)
self.assertIsInstance(result, GenericURL)
self.assertEqual(result.source, "qobuz")
# Verify the regex match groups
groups = result.match.groups()
self.assertEqual(len(groups), 3)
self.assertEqual(groups[0], "qobuz") # source
self.assertEqual(groups[1], "album") # media_type
self.assertEqual(groups[2], "0066991040005") # item_id
def test_tidal_track_url(self):
"""Test that Tidal track URLs are matched correctly."""
url = "https://tidal.com/browse/track/3083287"
result = parse_url(url)
self.assertIsNotNone(result)
self.assertIsInstance(result, GenericURL)
self.assertEqual(result.source, "tidal")
# Verify the regex match groups
groups = result.match.groups()
self.assertEqual(len(groups), 3)
self.assertEqual(groups[0], "tidal") # source
self.assertEqual(groups[1], "track") # media_type
self.assertEqual(groups[2], "3083287") # item_id
def test_deezer_track_url(self):
"""Test that Deezer track URLs are matched correctly."""
url = "https://www.deezer.com/track/4195713"
result = parse_url(url)
self.assertIsNotNone(result)
self.assertIsInstance(result, GenericURL)
self.assertEqual(result.source, "deezer")
# Verify the regex match groups
groups = result.match.groups()
self.assertEqual(len(groups), 3)
self.assertEqual(groups[0], "deezer") # source
self.assertEqual(groups[1], "track") # media_type
self.assertEqual(groups[2], "4195713") # item_id
def test_invalid_url(self):
"""Test that invalid URLs return None."""
urls = [
"https://example.com",
"not a url",
"https://spotify.com/track/123456", # Unsupported source
"https://tidal.com/invalid/3083287", # Invalid media type
]
for url in urls:
result = parse_url(url)
self.assertIsNone(result, f"URL should not parse: {url}")
def test_alternate_url_formats(self):
"""Test various URL formats that should be valid."""
# Test with different domain prefixes
url1 = "https://open.tidal.com/track/3083287"
url2 = "https://play.qobuz.com/album/0066991040005"
url3 = "https://listen.tidal.com/track/3083287"
for url in [url1, url2, url3]:
result = parse_url(url)
self.assertIsNotNone(result, f"Should parse URL: {url}")
self.assertIsInstance(result, GenericURL)
def test_url_with_language_code(self):
"""Test URLs with different language codes."""
urls = [
"https://www.qobuz.com/us-en/album/name/id123456",
"https://www.qobuz.com/gb-en/album/name/id123456",
"https://www.deezer.com/en/track/4195713",
"https://www.deezer.com/fr/track/4195713",
]
for url in urls:
result = parse_url(url)
self.assertIsNotNone(result, f"Should parse URL: {url}")
self.assertIsInstance(result, GenericURL)
def test_soundcloud_url(self):
"""Test that Soundcloud URLs are matched correctly."""
urls = [
"https://soundcloud.com/artist-name/track-name",
"https://soundcloud.com/artist-name/sets/playlist-name",
]
for url in urls:
result = parse_url(url)
self.assertIsNotNone(result, f"Should parse URL: {url}")
self.assertIsInstance(result, SoundcloudURL)
self.assertEqual(result.source, "soundcloud")
class TestDeezerDynamicURL(unittest.TestCase):
@patch("streamrip.rip.parse_url.DeezerDynamicURL._extract_info_from_dynamic_link")
def test_into_pending_album(self, mock_extract):
"""Test conversion of Deezer dynamic URL to a PendingAlbum."""
import asyncio
async def run_test():
url = "https://dzr.page.link/SnV6hCyHihkmCCwUA"
result = parse_url(url)
# Mock the extract method to return album type and ID
mock_extract.return_value = ("album", "12345")
# Mock the client, config, db
mock_client = AsyncMock()
mock_client.source = "deezer"
mock_config = AsyncMock()
mock_db = AsyncMock()
# Call into_pending
pending = await result.into_pending(mock_client, mock_config, mock_db)
# Verify the correct pending type was created
self.assertEqual(pending.__class__.__name__, "PendingAlbum")
self.assertEqual(pending.id, "12345")
# Run the coroutine
asyncio.run(run_test())
if __name__ == "__main__":
unittest.main()