Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ tidalapi

Unofficial Python API for TIDAL music streaming service.

Requires Python 3.7 or higher.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might change very soon.. We should remember to update it to the latest supported version.


0.7.0 Rewrite
-------------
Expand Down Expand Up @@ -48,3 +49,14 @@ Documentation
-------------

Documentation is available at https://tidalapi.netlify.app/

Development
-----------

This project uses poetry for dependency management and packaging. To install dependencies and setup the project for development, run:

.. code-block:: bash

$ pip install pipx
$ pipx install poetry
$ poetry install --no-root
4 changes: 2 additions & 2 deletions tidalapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from .album import Album # noqa: F401
from .artist import Artist, Role # noqa: F401
from .genre import Genre # noqa: F401
from .media import Track, Video # noqa: F401
from .media import Quality, Track, Video, VideoQuality # noqa: F401
from .mix import Mix # noqa: F401
from .page import Page # noqa: F401
from .playlist import Playlist, UserPlaylist # noqa: F401
from .request import Requests # noqa: F401
from .session import Config, Quality, Session, VideoQuality # noqa: F401
from .session import Config, Session # noqa: F401
from .user import ( # noqa: F401
Favorites,
FetchedUser,
Expand Down
42 changes: 29 additions & 13 deletions tidalapi/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,35 @@
Classes: :class:`Media`, :class:`Track`, :class:`Video`
"""

from __future__ import annotations

import copy
from abc import abstractmethod
from datetime import datetime
from typing import List, Optional, Union, cast
from enum import Enum
from typing import TYPE_CHECKING, List, Optional, Union, cast

import dateutil.parser

import tidalapi
if TYPE_CHECKING:
import tidalapi

from tidalapi.types import JsonObj


class Quality(Enum):
lossless = "LOSSLESS"
high = "HIGH"
low = "LOW"
master = "HI_RES"


class VideoQuality(Enum):
high = "HIGH"
medium = "MEDIUM"
low = "LOW"


class Media:
"""
Base class for generic media, specifically :class:`Track` and :class:`Video`
Expand All @@ -52,16 +70,14 @@ class Media:
volume_num: int = 1
explicit: bool = False
popularity: int = -1
artist: Optional[tidalapi.artist.Artist] = None
artist: Optional[tidalapi.Artist] = None
#: For the artist credit page
artist_roles = None
artists: Optional[List[tidalapi.artist.Artist]] = None
artists: Optional[List[tidalapi.Artist]] = None
album: Optional[tidalapi.album.Album] = None
type: Optional[str] = None

def __init__(
self, session: tidalapi.session.Session, media_id: Optional[str] = None
):
def __init__(self, session: tidalapi.Session, media_id: Optional[str] = None):
self.session = session
self.requests = self.session.request
self.album = session.album()
Expand All @@ -70,7 +86,7 @@ def __init__(
self._get(self.id)

@abstractmethod
def _get(self, media_id: str) -> "Media":
def _get(self, media_id: str) -> Media:
raise NotImplementedError(
"You are not supposed to use the media class directly."
)
Expand Down Expand Up @@ -140,20 +156,20 @@ class Track(Media):
replay_gain = None
peak = None
isrc = None
audio_quality: Optional[tidalapi.session.Quality] = None
audio_quality: Optional[Quality] = None
version = None
full_name: Optional[str] = None
copyright = None

def parse_track(self, json_obj: JsonObj) -> "Track":
def parse_track(self, json_obj: JsonObj) -> Track:
Media.parse(self, json_obj)
self.replay_gain = json_obj["replayGain"]
# Tracks from the pages endpoints might not actually exist
if "peak" in json_obj and "isrc" in json_obj:
self.peak = json_obj["peak"]
self.isrc = json_obj["isrc"]
self.copyright = json_obj["copyright"]
self.audio_quality = tidalapi.session.Quality(json_obj["audioQuality"])
self.audio_quality = Quality(json_obj["audioQuality"])
self.version = json_obj["version"]

if self.version is not None:
Expand Down Expand Up @@ -284,7 +300,7 @@ class Video(Media):
video_quality: Optional[str] = None
cover: Optional[str] = None

def parse_video(self, json_obj: JsonObj) -> "Video":
def parse_video(self, json_obj: JsonObj) -> Video:
Media.parse(self, json_obj)
release_date = json_obj.get("releaseDate")
self.release_date = (
Expand All @@ -296,7 +312,7 @@ def parse_video(self, json_obj: JsonObj) -> "Video":

return copy.copy(self)

def _get(self, media_id: str) -> "Video":
def _get(self, media_id: str) -> Video:
"""Returns information about the video, and replaces the object used to call
this function.

Expand Down
3 changes: 3 additions & 0 deletions tidalapi/mix.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""A module containing functions relating to TIDAL mixes."""

from __future__ import annotations

import copy
from enum import Enum
from typing import TYPE_CHECKING, List, Optional, TypedDict, Union
Expand Down
9 changes: 7 additions & 2 deletions tidalapi/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""A module containing things related to TIDAL playlists."""

from __future__ import annotations

import copy
from typing import Optional
from typing import TYPE_CHECKING, List, Optional

if TYPE_CHECKING:
import tidalapi

import dateutil.parser

Expand Down Expand Up @@ -118,7 +123,7 @@ def parse_factory(self, json_obj):
self.parse(json_obj)
return copy.copy(self.factory())

def tracks(self, limit=None, offset=0):
def tracks(self, limit: Optional[int] = None, offset=0) -> List[tidalapi.Track]:
"""Gets the playlists̈́' tracks from TIDAL.

:param limit: The amount of items you want returned.
Expand Down
Loading