Misc enhancements

This commit is contained in:
nathom 2021-04-16 11:38:22 -07:00
parent 02eb2cdc9a
commit 1253d20173
4 changed files with 42 additions and 11 deletions

View file

@ -241,6 +241,9 @@ class QobuzClient(Client):
epoint = f"{media_type}/get" epoint = f"{media_type}/get"
response, status_code = self._api_request(epoint, params) response, status_code = self._api_request(epoint, params)
if status_code != 200:
raise Exception(f'Error fetching metadata. "{response["message"]}"')
return response return response
def _api_search(self, query, media_type, limit=500) -> Generator: def _api_search(self, query, media_type, limit=500) -> Generator:
@ -410,6 +413,8 @@ class TidalClient(Client):
source = "tidal" source = "tidal"
max_quality = 3 max_quality = 3
# ----------- Public Methods --------------
def __init__(self): def __init__(self):
self.logged_in = False self.logged_in = False
@ -461,7 +466,10 @@ class TidalClient(Client):
} }
return self._api_request(f"search/{media_type}s", params=params) return self._api_request(f"search/{media_type}s", params=params)
def get_file_url(self, track_id, quality: int = 3): def get_file_url(self, track_id, quality: int = 3, video=False):
if video:
return self._get_video_stream_url(track_id)
params = { params = {
"audioquality": get_quality(min(quality, TIDAL_MAX_Q), self.source), "audioquality": get_quality(min(quality, TIDAL_MAX_Q), self.source),
"playbackmode": "STREAM", "playbackmode": "STREAM",
@ -492,6 +500,8 @@ class TidalClient(Client):
) )
} }
# ------------ Utilities to login -------------
def _login_new_user(self, launch=True): def _login_new_user(self, launch=True):
login_link = f"https://{self._get_device_code()}" login_link = f"https://{self._get_device_code()}"
@ -613,6 +623,15 @@ class TidalClient(Client):
self.access_token = token self.access_token = token
self._update_authorization() self._update_authorization()
def _update_authorization(self):
self.session.headers.update(self.authorization)
@property
def authorization(self):
return {"authorization": f"Bearer {self.access_token}"}
# ------------- Fetch data ------------------
def _api_get(self, item_id: str, media_type: str) -> dict: def _api_get(self, item_id: str, media_type: str) -> dict:
url = f"{media_type}s/{item_id}" url = f"{media_type}s/{item_id}"
item = self._api_request(url) item = self._api_request(url)
@ -644,13 +663,22 @@ class TidalClient(Client):
r = self.session.get(f"{TIDAL_BASE}/{path}", params=params).json() r = self.session.get(f"{TIDAL_BASE}/{path}", params=params).json()
return r return r
def _get_video_stream_url(self, video_id) -> str:
params = {
"videoquality": "HIGH",
"playbackmode": "STREAM",
"assetpresentation": "FULL",
}
resp = self._api_request(
f"videos/{video_id}/playbackinfopostpaywall", params=params
)
manifest = json.loads(base64.b64decode(resp['manifest']).decode("utf-8"))
return manifest['urls'][0]
def _api_post(self, url, data, auth=None): def _api_post(self, url, data, auth=None):
r = self.session.post(url, data=data, auth=auth, verify=False).json() r = self.session.post(url, data=data, auth=auth, verify=False).json()
return r return r
def _update_authorization(self):
self.session.headers.update({"authorization": f"Bearer {self.access_token}"})
class SoundCloudClient(Client): class SoundCloudClient(Client):
source = "soundcloud" source = "soundcloud"

View file

@ -158,7 +158,8 @@ TIDAL_Q_MAP = {
DEEZER_MAX_Q = 6 DEEZER_MAX_Q = 6
AVAILABLE_QUALITY_IDS = (0, 1, 2, 3, 4) AVAILABLE_QUALITY_IDS = (0, 1, 2, 3, 4)
MEDIA_TYPES = ("track", "album", "artist", "label", "playlist") # video only for tidal
MEDIA_TYPES = {"track", "album", "artist", "label", "playlist", "video"}
# used to homogenize cover size keys # used to homogenize cover size keys
COVER_SIZES = ("thumbnail", "small", "large", "original") COVER_SIZES = ("thumbnail", "small", "large", "original")

View file

@ -10,6 +10,7 @@ import re
import shutil import shutil
import subprocess import subprocess
from tempfile import gettempdir from tempfile import gettempdir
from dataclasses import dataclass
from typing import Any, Generator, Iterable, Union from typing import Any, Generator, Iterable, Union
import click import click
@ -812,7 +813,7 @@ class Album(Tracklist):
return Playlist.from_api(resp, client) return Playlist.from_api(resp, client)
info = cls._parse_get_resp(resp, client) info = cls._parse_get_resp(resp, client)
return cls(client, **info) return cls(client, **info.asdict())
def _prepare_download(self, **kwargs): def _prepare_download(self, **kwargs):
self.folder_format = kwargs.get("folder_format", FOLDER_FORMAT) self.folder_format = kwargs.get("folder_format", FOLDER_FORMAT)

View file

@ -3,6 +3,7 @@
import logging import logging
import re import re
from typing import Generator, Hashable, Optional, Tuple, Union from typing import Generator, Hashable, Optional, Tuple, Union
from collections import OrderedDict
from .constants import ( from .constants import (
COPYRIGHT, COPYRIGHT,
@ -136,7 +137,7 @@ class TrackMetadata:
# Non-embedded information # Non-embedded information
self.version = resp.get("version") self.version = resp.get("version")
self.cover_urls = resp.get("image") self.cover_urls = OrderedDict(resp.get("image"))
self.cover_urls["original"] = self.cover_urls["large"].replace("600", "org") self.cover_urls["original"] = self.cover_urls["large"].replace("600", "org")
self.streamable = resp.get("streamable", False) self.streamable = resp.get("streamable", False)
self.bit_depth = resp.get("maximum_bit_depth") self.bit_depth = resp.get("maximum_bit_depth")
@ -162,10 +163,10 @@ class TrackMetadata:
self.explicit = resp.get("explicit", False) self.explicit = resp.get("explicit", False)
# 80, 160, 320, 640, 1280 # 80, 160, 320, 640, 1280
uuid = resp.get("cover") uuid = resp.get("cover")
self.cover_urls = { self.cover_urls = OrderedDict({
sk: tidal_cover_url(uuid, size) sk: tidal_cover_url(uuid, size)
for sk, size in zip(COVER_SIZES, (160, 320, 640, 1280)) for sk, size in zip(COVER_SIZES, (160, 320, 640, 1280))
} })
self.streamable = resp.get("allowStreaming", False) self.streamable = resp.get("allowStreaming", False)
self.quality = TIDAL_Q_MAP[resp["audioQuality"]] self.quality = TIDAL_Q_MAP[resp["audioQuality"]]
@ -185,13 +186,13 @@ class TrackMetadata:
self.explicit = bool(resp.get("parental_warning")) self.explicit = bool(resp.get("parental_warning"))
self.quality = 2 self.quality = 2
self.bit_depth = 16 self.bit_depth = 16
self.cover_urls = { self.cover_urls = OrderedDict({
sk: resp.get(rk) # size key, resp key sk: resp.get(rk) # size key, resp key
for sk, rk in zip( for sk, rk in zip(
COVER_SIZES, COVER_SIZES,
("cover", "cover_medium", "cover_large", "cover_xl"), ("cover", "cover_medium", "cover_large", "cover_xl"),
) )
} })
self.sampling_rate = 44100 self.sampling_rate = 44100
self.streamable = True self.streamable = True