From cb7d57be3bed57a72e04582dbf311a691efcff1f Mon Sep 17 00:00:00 2001 From: oskvr37 Date: Tue, 21 Jan 2025 17:57:29 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20update=20TidalResource=20with=20pyd?= =?UTF-8?q?antic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_utils.py | 10 ++++----- tiddl/cli/download/__init__.py | 10 ++++----- tiddl/cli/download/file.py | 2 +- tiddl/cli/download/url.py | 2 +- tiddl/utils.py | 41 +++++++++++++++++----------------- 5 files changed, 31 insertions(+), 34 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 81250d3..34d8d2b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,12 +1,10 @@ import unittest -import json from tiddl.models import Track from tiddl.utils import TidalResource, formatTrack class TestTidalResource(unittest.TestCase): - def test_resource_parsing(self): positive_cases = [ ("https://tidal.com/browse/track/12345678", "track", "12345678"), @@ -21,9 +19,9 @@ class TestTidalResource(unittest.TestCase): for resource, expected_type, expected_id in positive_cases: with self.subTest(resource=resource): - tidal_url = TidalResource(resource) - self.assertEqual(tidal_url.resource_type, expected_type) - self.assertEqual(tidal_url.resource_id, expected_id) + tidal_resource = TidalResource.fromString(resource) + self.assertEqual(tidal_resource.type, expected_type) + self.assertEqual(tidal_resource.id, expected_id) def test_failing_cases(self): failing_cases = [ @@ -41,7 +39,7 @@ class TestTidalResource(unittest.TestCase): for resource in failing_cases: with self.subTest(resource=resource): with self.assertRaises(ValueError): - TidalResource(resource) + TidalResource.fromString(resource) class TestFormatTrack(unittest.TestCase): diff --git a/tiddl/cli/download/__init__.py b/tiddl/cli/download/__init__.py index 787a6aa..8ee08a9 100644 --- a/tiddl/cli/download/__init__.py +++ b/tiddl/cli/download/__init__.py @@ -22,21 +22,21 @@ class TrackCollector: def addResource(self, resource: TidalResource): try: - match resource.resource_type: + match resource.type: case "track": - track = self.api.getTrack(resource.resource_id) + track = self.api.getTrack(resource.id) self._addTrack(track) case "album": - album_tracks = self.api.getAlbumItems(resource.resource_id) + album_tracks = self.api.getAlbumItems(resource.id) self._addItems(album_tracks.items) case "playlist": - playlist_tracks = self.api.getPlaylistItems(resource.resource_id) + playlist_tracks = self.api.getPlaylistItems(resource.id) self._addItems(playlist_tracks.items) case "artist": - artist_albums = self.api.getArtistAlbums(resource.resource_id) + artist_albums = self.api.getArtistAlbums(resource.id) for artist_album in artist_albums.items: album_tracks = self.api.getAlbumItems(artist_album.id) self._addItems(album_tracks.items) diff --git a/tiddl/cli/download/file.py b/tiddl/cli/download/file.py index 0245aac..9204101 100644 --- a/tiddl/cli/download/file.py +++ b/tiddl/cli/download/file.py @@ -33,7 +33,7 @@ def FileGroup(ctx: Context, filename: TextIOWrapper): for string in resource_strings: try: - ctx.obj.resources.append(TidalResource(string)) + ctx.obj.resources.append(TidalResource.fromString(string)) except ValueError as e: click.echo(click.style(e, "red")) diff --git a/tiddl/cli/download/url.py b/tiddl/cli/download/url.py index 7552f57..265987d 100644 --- a/tiddl/cli/download/url.py +++ b/tiddl/cli/download/url.py @@ -8,7 +8,7 @@ from tiddl.utils import TidalResource class TidalURL(click.ParamType): def convert(self, value: str, param, ctx) -> TidalResource: try: - return TidalResource(value) + return TidalResource.fromString(value) except ValueError as e: self.fail(message=str(e), param=param, ctx=ctx) diff --git a/tiddl/utils.py b/tiddl/utils.py index 53797de..b6ba52e 100644 --- a/tiddl/utils.py +++ b/tiddl/utils.py @@ -1,4 +1,6 @@ import re + +from pydantic import BaseModel from urllib.parse import urlparse from typing import Literal, get_args @@ -7,40 +9,37 @@ from tiddl.models import Track, QUALITY_TO_ARG ResourceTypeLiteral = Literal["track", "album", "playlist", "artist"] -class TidalResource: - """ - A parser for Tidal resource URLs or strings. +class TidalResource(BaseModel): + type: ResourceTypeLiteral + id: str - Extracts the resource type (e.g., "track", "album") and resource ID - from a given input string. The input string can either be a full URL or a - shorthand string in the format "resource_type/resource_id" (e.g., "track/12345678"). - """ + @property + def url(self) -> str: + return f"https://listen.tidal.com/{self.type}/{self.id}" - resource: str - resource_type: ResourceTypeLiteral - resource_id: str - url: str + @classmethod + def fromString(cls, string: str): + """ + Extracts the resource type (e.g., "track", "album") + and resource ID from a given input string. - def __init__(self, resource: str) -> None: - self.resource = resource + The input string can either be a full URL or a shorthand string + in the format `resource_type/resource_id` (e.g., `track/12345678`). + """ - path = urlparse(self.resource).path + path = urlparse(string).path resource_type, resource_id = path.split("/")[-2:] if resource_type not in get_args(ResourceTypeLiteral): raise ValueError(f"Invalid resource type: {resource_type}") - self.resource_type = resource_type # type: ignore - - if not resource_id.isdigit() and self.resource_type != "playlist": + if not resource_id.isdigit() and resource_type != "playlist": raise ValueError(f"Invalid resource id: {resource_id}") - self.resource_id = resource_id - - self.url = f"https://listen.tidal.com/{self.resource_type}/{self.resource_id}" + return cls(type=resource_type, id=resource_id) # type: ignore def __str__(self) -> str: - return f"{self.resource_type}/{self.resource_id}" + return f"{self.type}/{self.id}" def sanitizeString(string: str) -> str: