diff --git a/streamrip/client/downloadable.py b/streamrip/client/downloadable.py index 4a4e2c0..a4a8f69 100644 --- a/streamrip/client/downloadable.py +++ b/streamrip/client/downloadable.py @@ -133,29 +133,23 @@ class DeezerDownloadable(Downloadable): blowfish_key, ) - assert self.chunk_size == 2048 * 3 + buf = bytearray() + async for data, _ in resp.content.iter_chunks(): + buf += data + callback(len(data)) - # Write data from server to tempfile because there's no - # efficient way to guarantee a fixed chunk size for all iterations - # in async - async with aiofiles.tempfile.TemporaryFile("wb+") as tmp: - async for chunk in resp.content.iter_chunks(): - data, _ = chunk - await tmp.write(data) - callback(len(data)) - - await tmp.seek(0) - async with aiofiles.open(path, "wb") as audio: - while chunk := await tmp.read(self.chunk_size): - if len(chunk) >= 2048: - decrypted_chunk = ( - self._decrypt_chunk(blowfish_key, chunk[:2048]) - + chunk[2048:] - ) - else: - decrypted_chunk = chunk - - await audio.write(decrypted_chunk) + async with aiofiles.open(path, "wb") as audio: + buflen = len(buf) + for i in range(0, buflen, self.chunk_size): + data = buf[i : min(i + self.chunk_size, buflen - 1)] + if len(data) >= 2048: + decrypted_chunk = ( + self._decrypt_chunk(blowfish_key, data[:2048]) + + data[2048:] + ) + else: + decrypted_chunk = data + await audio.write(decrypted_chunk) @staticmethod def _decrypt_chunk(key, data): diff --git a/streamrip/config.toml b/streamrip/config.toml index cfbf72e..b420d0d 100644 --- a/streamrip/config.toml +++ b/streamrip/config.toml @@ -161,7 +161,7 @@ add_singles_to_folder = false folder_format = "{albumartist} - {title} ({year}) [{container}] [{bit_depth}B-{sampling_rate}kHz]" # Available keys: "tracknumber", "artist", "albumartist", "composer", "title", # and "albumcomposer", "explicit" -track_format = "{tracknumber}. {artist} - {title}{explicit}" +track_format = "{tracknumber:02}. {artist} - {title}{explicit}" # Only allow printable ASCII characters in filenames. restrict_characters = false # Truncate the filename if it is greater than this number of characters diff --git a/streamrip/media/album.py b/streamrip/media/album.py index 446097c..7d97346 100644 --- a/streamrip/media/album.py +++ b/streamrip/media/album.py @@ -7,6 +7,7 @@ from .. import progress from ..client import Client from ..config import Config from ..db import Database +from ..exceptions import NonStreamableError from ..filepath_utils import clean_filepath from ..metadata import AlbumMetadata from ..metadata.util import get_album_track_ids @@ -50,7 +51,14 @@ class PendingAlbum(Pending): db: Database async def resolve(self) -> Album | None: - resp = await self.client.get_metadata(self.id, "album") + try: + resp = await self.client.get_metadata(self.id, "album") + except NonStreamableError as e: + logger.error( + f"Album {self.id} not available to stream on {self.client.source} ({e})", + ) + return None + meta = AlbumMetadata.from_album_resp(resp, self.client.source) if meta is None: logger.error( diff --git a/streamrip/media/artist.py b/streamrip/media/artist.py index 319bcb4..0093e06 100644 --- a/streamrip/media/artist.py +++ b/streamrip/media/artist.py @@ -7,6 +7,7 @@ from ..client import Client from ..config import Config, QobuzDiscographyFilterConfig from ..console import console from ..db import Database +from ..exceptions import NonStreamableError from ..metadata import ArtistMetadata from .album import Album, PendingAlbum from .media import Media, Pending @@ -180,8 +181,15 @@ class PendingArtist(Pending): config: Config db: Database - async def resolve(self) -> Artist: - resp = await self.client.get_metadata(self.id, "artist") + async def resolve(self) -> Artist | None: + try: + resp = await self.client.get_metadata(self.id, "artist") + except NonStreamableError as e: + logger.error( + f"Artist {self.id} not available to stream on {self.client.source} ({e})", + ) + return None + meta = ArtistMetadata.from_resp(resp, self.client.source) albums = [ PendingAlbum(album_id, self.client, self.config, self.db) diff --git a/streamrip/media/playlist.py b/streamrip/media/playlist.py index 8227899..2e84a85 100644 --- a/streamrip/media/playlist.py +++ b/streamrip/media/playlist.py @@ -147,7 +147,14 @@ class PendingPlaylist(Pending): db: Database async def resolve(self) -> Playlist | None: - resp = await self.client.get_metadata(self.id, "playlist") + try: + resp = await self.client.get_metadata(self.id, "playlist") + except NonStreamableError as e: + logger.error( + f"Playlist {self.id} not available to stream on {self.client.source} ({e})", + ) + return None + meta = PlaylistMetadata.from_resp(resp, self.client.source) name = meta.name parent = self.config.session.downloads.folder @@ -261,6 +268,7 @@ class PendingLastfmPlaylist(Pending): if that fails. Args: + ---- query (str): Query to search s (Status): callback: function to call after each query completes