mirror of
https://github.com/nathom/streamrip.git
synced 2025-05-19 01:35:24 -04:00
Misc enhancements
This commit is contained in:
parent
02eb2cdc9a
commit
1253d20173
4 changed files with 42 additions and 11 deletions
|
@ -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"
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue