1
0
Fork 0
mirror of https://github.com/nathom/streamrip.git synced 2025-05-30 15:05:19 -04:00

Misc fixes related to search

This commit is contained in:
Nathan Thomas 2023-12-22 23:25:43 -08:00
parent 8c9673a067
commit a515b9c1fd
6 changed files with 55 additions and 26 deletions

View file

@ -181,6 +181,7 @@ class DeezerClient(Client):
) )
dl_info["url"] = url dl_info["url"] = url
logger.debug("dz track info: %s", track_info)
return DeezerDownloadable(self.session, dl_info) return DeezerDownloadable(self.session, dl_info)
def _get_encrypted_file_url( def _get_encrypted_file_url(
@ -212,5 +213,6 @@ class DeezerClient(Client):
path = binascii.hexlify( path = binascii.hexlify(
AES.new(b"jo6aey6haid2Teih", AES.MODE_ECB).encrypt(info_bytes), AES.new(b"jo6aey6haid2Teih", AES.MODE_ECB).encrypt(info_bytes),
).decode("utf-8") ).decode("utf-8")
url = f"https://e-cdns-proxy-{track_hash[0]}.dzcdn.net/mobile/1/{path}"
return f"https://e-cdns-proxy-{track_hash[0]}.dzcdn.net/mobile/1/{path}" logger.debug("Encrypted file path %s", url)
return url

View file

@ -36,7 +36,7 @@ class SoundcloudClient(Client):
async def login(self): async def login(self):
self.session = await self.get_session() self.session = await self.get_session()
client_id, app_version = self.config.client_id, self.config.app_version client_id, app_version = self.config.client_id, self.config.app_version
if not client_id or not app_version or not (await self._announce()): if not client_id or not app_version or not (await self._announce_success()):
client_id, app_version = await self._refresh_tokens() client_id, app_version = await self._refresh_tokens()
# update file and session configs and save to disk # update file and session configs and save to disk
cf = self.global_config.file.soundcloud cf = self.global_config.file.soundcloud
@ -54,13 +54,14 @@ class SoundcloudClient(Client):
"""Fetch metadata for an item in Soundcloud API. """Fetch metadata for an item in Soundcloud API.
Args: Args:
----
item_id (str): Plain soundcloud item ID (e.g 1633786176) item_id (str): Plain soundcloud item ID (e.g 1633786176)
media_type (str): track or playlist media_type (str): track or playlist
Returns: Returns:
-------
API response. API response. The item IDs for the tracks in the playlist are modified to
include resolution status.
""" """
if media_type == "track": if media_type == "track":
# parse custom id that we injected # parse custom id that we injected
@ -227,12 +228,15 @@ class SoundcloudClient(Client):
async with self.session.get(url, params=_params, headers=headers) as resp: async with self.session.get(url, params=_params, headers=headers) as resp:
return await resp.content.read(), resp.status return await resp.content.read(), resp.status
async def _resolve_url(self, url: str) -> dict: async def resolve_url(self, url: str) -> dict:
resp, status = await self._api_request("resolve", params={"url": url}) resp, status = await self._api_request("resolve", params={"url": url})
assert status == 200 assert status == 200
if resp["kind"] == "track":
resp["id"] = self._get_custom_id(resp)
return resp return resp
async def _announce(self): async def _announce_success(self):
url = f"{BASE}/announcements" url = f"{BASE}/announcements"
_, status = await self._request_body(url) _, status = await self._request_body(url)
return status == 200 return status == 200

View file

@ -39,7 +39,7 @@ class ArtistSummary(Summary):
return "artist" return "artist"
def summarize(self) -> str: def summarize(self) -> str:
return self.name return clean(self.name)
def preview(self) -> str: def preview(self) -> str:
return f"{self.num_albums} Albums\n\nID: {self.id}" return f"{self.num_albums} Albums\n\nID: {self.id}"
@ -73,7 +73,8 @@ class TrackSummary(Summary):
return "track" return "track"
def summarize(self) -> str: def summarize(self) -> str:
return f"{self.name} by {self.artist}" # This char breaks the menu for some reason
return f"{clean(self.name)} by {clean(self.artist)}"
def preview(self) -> str: def preview(self) -> str:
return f"Released on:\n{self.date_released}\n\nID: {self.id}" return f"Released on:\n{self.date_released}\n\nID: {self.id}"
@ -119,7 +120,7 @@ class AlbumSummary(Summary):
return "album" return "album"
def summarize(self) -> str: def summarize(self) -> str:
return f"{self.name} by {self.artist}" return f"{clean(self.name)} by {clean(self.artist)}"
def preview(self) -> str: def preview(self) -> str:
return f"Date released:\n{self.date_released}\n\n{self.num_tracks} Tracks\n\nID: {self.id}" return f"Date released:\n{self.date_released}\n\n{self.num_tracks} Tracks\n\nID: {self.id}"
@ -188,11 +189,14 @@ class PlaylistSummary(Summary):
description: str description: str
def summarize(self) -> str: def summarize(self) -> str:
return f"{self.name} by {self.creator}" name = clean(self.name)
creator = clean(self.creator)
return f"{name} by {creator}"
def preview(self) -> str: def preview(self) -> str:
desc = clean(self.description, trunc=False)
wrapped = "\n".join( wrapped = "\n".join(
textwrap.wrap(self.description, os.get_terminal_size().columns - 4 or 70), textwrap.wrap(desc, os.get_terminal_size().columns - 4 or 70),
) )
return f"{self.num_tracks} tracks\n\nDescription:\n{wrapped}\n\nID: {self.id}" return f"{self.num_tracks} tracks\n\nDescription:\n{wrapped}\n\nID: {self.id}"
@ -214,6 +218,7 @@ class PlaylistSummary(Summary):
item.get("tracks_count") item.get("tracks_count")
or item.get("nb_tracks") or item.get("nb_tracks")
or item.get("numberOfTracks") or item.get("numberOfTracks")
or len(item.get("tracks", []))
or -1 or -1
) )
description = item.get("description") or "No description" description = item.get("description") or "No description"
@ -273,3 +278,11 @@ class SearchResults:
assert ind is not None assert ind is not None
i = int(ind.group(0)) i = int(ind.group(0))
return self.results[i - 1].preview() return self.results[i - 1].preview()
def clean(s: str, trunc=True) -> str:
s = s.replace("|", "").replace("\n", "")
if trunc:
max_chars = 50
return s[:max_chars]
return s

View file

@ -25,8 +25,6 @@ class ProgressManager:
BarColumn(bar_width=None), BarColumn(bar_width=None),
"[progress.percentage]{task.percentage:>3.1f}%", "[progress.percentage]{task.percentage:>3.1f}%",
"", "",
# DownloadColumn(),
# "•",
TransferSpeedColumn(), TransferSpeedColumn(),
"", "",
TimeRemainingColumn(), TimeRemainingColumn(),

View file

@ -36,8 +36,14 @@ def coro(f):
"--config-path", "--config-path",
default=DEFAULT_CONFIG_PATH, default=DEFAULT_CONFIG_PATH,
help="Path to the configuration file", help="Path to the configuration file",
type=click.Path(readable=True, writable=True),
)
@click.option(
"-f",
"--folder",
help="The folder to download items into.",
type=click.Path(file_okay=False, dir_okay=True),
) )
@click.option("-f", "--folder", help="The folder to download items into.")
@click.option( @click.option(
"-ndb", "-ndb",
"--no-db", "--no-db",
@ -46,11 +52,14 @@ def coro(f):
is_flag=True, is_flag=True,
) )
@click.option( @click.option(
"-q", "--quality", help="The maximum quality allowed to download", type=int "-q",
"--quality",
help="The maximum quality allowed to download",
type=click.IntRange(min=0, max=4),
) )
@click.option( @click.option(
"-c", "-c",
"--convert", "--codec",
help="Convert the downloaded files to an audio codec (ALAC, FLAC, MP3, AAC, or OGG)", help="Convert the downloaded files to an audio codec (ALAC, FLAC, MP3, AAC, or OGG)",
) )
@click.option( @click.option(
@ -66,7 +75,7 @@ def coro(f):
is_flag=True, is_flag=True,
) )
@click.pass_context @click.pass_context
def rip(ctx, config_path, folder, no_db, quality, convert, no_progress, verbose): def rip(ctx, config_path, folder, no_db, quality, codec, no_progress, verbose):
"""Streamrip: the all in one music downloader.""" """Streamrip: the all in one music downloader."""
global logger global logger
logging.basicConfig( logging.basicConfig(
@ -112,7 +121,8 @@ def rip(ctx, config_path, folder, no_db, quality, convert, no_progress, verbose)
return return
# set session config values to command line args # set session config values to command line args
c.session.database.downloads_enabled = not no_db if no_db:
c.session.database.downloads_enabled = False
if folder is not None: if folder is not None:
c.session.downloads.folder = folder c.session.downloads.folder = folder
@ -122,10 +132,10 @@ def rip(ctx, config_path, folder, no_db, quality, convert, no_progress, verbose)
c.session.deezer.quality = quality c.session.deezer.quality = quality
c.session.soundcloud.quality = quality c.session.soundcloud.quality = quality
if convert is not None: if codec is not None:
c.session.conversion.enabled = True c.session.conversion.enabled = True
assert convert.upper() in ("ALAC", "FLAC", "OGG", "MP3", "AAC") assert codec.upper() in ("ALAC", "FLAC", "OGG", "MP3", "AAC")
c.session.conversion.codec = convert.upper() c.session.conversion.codec = codec.upper()
if no_progress: if no_progress:
c.session.cli.progress_bars = False c.session.cli.progress_bars = False
@ -147,7 +157,9 @@ async def url(ctx, urls):
@rip.command() @rip.command()
@click.argument("path", required=True) @click.argument(
"path", required=True, type=click.Path(file_okay=True, dir_okay=False, exists=True)
)
@click.pass_context @click.pass_context
@coro @coro
async def file(ctx, path): async def file(ctx, path):
@ -275,7 +287,7 @@ async def search(ctx, first, source, media_type, query):
"""Search for content using a specific source. """Search for content using a specific source.
Example: Example:
-------
rip search qobuz album 'rumours' rip search qobuz album 'rumours'
""" """
with ctx.obj["config"] as cfg: with ctx.obj["config"] as cfg:

View file

@ -140,7 +140,7 @@ class SoundcloudURL(URL):
config: Config, config: Config,
db: Database, db: Database,
) -> Pending: ) -> Pending:
resolved = await client._resolve_url(self.url) resolved = await client.resolve_url(self.url)
media_type = resolved["kind"] media_type = resolved["kind"]
item_id = str(resolved["id"]) item_id = str(resolved["id"])
if media_type == "track": if media_type == "track":