diff --git a/CHANGELOG.md b/CHANGELOG.md index 43f32b4..fdcc35a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# STILL IN DEVELOPMENT, SOME CHANGES AREN'T IMPLEMENTED AND SOME AREN'T FINAL! +# STILL IN DEVELOPMENT, EVERYTHING HERE IS SUBJECT TO CHANGE! ## v1.0.0 An unexpected reboot @@ -41,6 +41,7 @@ An unexpected reboot - `{album_artists}` - !!`{duration}` - In milliseconds - `{explicit}` + - `{explicit_symbol}` - For output format, will be \[E] if track is explicit. - `{isrc}` - `{licensor}` - !!`{popularity}` diff --git a/setup.cfg b/setup.cfg index 85f442c..dc16e10 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = zotify -version = 0.9.0 +version = 0.9.1 author = Zotify Contributors description = A highly customizable music and podcast downloader long_description = file: README.md diff --git a/zotify/__main__.py b/zotify/__main__.py index 0fce19d..ed2d449 100644 --- a/zotify/__main__.py +++ b/zotify/__main__.py @@ -7,7 +7,7 @@ from zotify.app import client from zotify.config import CONFIG_PATHS, CONFIG_VALUES from zotify.utils import OptionalOrFalse -VERSION = "0.9.0" +VERSION = "0.9.1" def main(): diff --git a/zotify/app.py b/zotify/app.py index b017c1a..14f56e8 100644 --- a/zotify/app.py +++ b/zotify/app.py @@ -170,7 +170,7 @@ class App: self.__playable_list.append( PlayableData( PlayableType.TRACK, - bytes_to_hex(track.gid), + TrackId.from_hex(bytes_to_hex(track.gid)), self.__config.music_library, self.__config.output_album, ) @@ -187,7 +187,7 @@ class App: self.__playable_list.append( PlayableData( PlayableType.TRACK, - bytes_to_hex(track.gid), + TrackId.from_hex(bytes_to_hex(track.gid)), self.__config.music_library, self.__config.output_album, ) @@ -215,7 +215,7 @@ class App: self.__playable_list.append( PlayableData( PlayableType.EPISODE, - bytes_to_hex(episode.gid), + EpisodeId.from_hex(bytes_to_hex(episode.gid)), self.__config.podcast_library, self.__config.output_podcast, ) diff --git a/zotify/config.py b/zotify/config.py index 295aa2c..087185a 100644 --- a/zotify/config.py +++ b/zotify/config.py @@ -138,7 +138,7 @@ CONFIG_VALUES = { AUDIO_FORMAT: { "default": "vorbis", "type": AudioFormat, - "choices": [n.value for n in AudioFormat], + "choices": [n.value.name for n in AudioFormat], "arg": "--audio-format", "help": "Audio format of final track output", }, @@ -335,7 +335,7 @@ class Config: elif config_type == Path: return Path(value).expanduser() elif config_type == AudioFormat: - return AudioFormat(value) + return AudioFormat[value.upper()] elif config_type == ImageSize.from_string: return ImageSize.from_string(value) elif config_type == Quality.from_string: diff --git a/zotify/file.py b/zotify/file.py index 0cf7885..c50acd6 100644 --- a/zotify/file.py +++ b/zotify/file.py @@ -6,7 +6,7 @@ from typing import Any from music_tag import load_file from mutagen.oggvorbis import OggVorbisHeaderError -from zotify.utils import AudioFormat, ExtMap +from zotify.utils import AudioFormat # fmt: off @@ -18,18 +18,16 @@ class FFmpegExecutionError(OSError, TranscodingError): ... class LocalFile: - audio_format: AudioFormat - def __init__( self, path: Path, audio_format: AudioFormat | None = None, bitrate: int | None = None, ): - self.path = path - self.bitrate = bitrate + self.__path = path + self.__bitrate = bitrate if audio_format: - self.audio_format = audio_format + self.__audio_format = audio_format def transcode( self, @@ -48,10 +46,10 @@ class LocalFile: ffmpeg: Location of FFmpeg binary opt_args: Additional arguments to pass to ffmpeg """ - if audio_format: - new_ext = ExtMap[audio_format.value] + if audio_format is not None: + new_ext = audio_format.value.ext else: - new_ext = ExtMap[self.audio_format.value] + new_ext = self.__audio_format.value.ext cmd = [ ffmpeg, "-y", @@ -59,18 +57,18 @@ class LocalFile: "-loglevel", "error", "-i", - str(self.path), + str(self.__path), ] - newpath = self.path.parent.joinpath( - self.path.name.rsplit(".", 1)[0] + new_ext.value + newpath = self.__path.parent.joinpath( + self.__path.name.rsplit(".", 1)[0] + new_ext ) - if self.path == newpath: + if self.__path == newpath: raise TargetExistsError( - f"Transcoding Error: Cannot overwrite source, target file is already a {self.audio_format} file." + f"Transcoding Error: Cannot overwrite source, target file is already a {self.__audio_format} file." ) cmd.extend(["-b:a", str(bitrate) + "k"]) if bitrate else None - cmd.extend(["-c:a", audio_format.value]) if audio_format else None + cmd.extend(["-c:a", audio_format.value.name]) if audio_format else None cmd.extend(opt_args) cmd.append(str(newpath)) @@ -88,11 +86,11 @@ class LocalFile: ) if replace: - Path(self.path).unlink() - self.path = newpath - self.bitrate = bitrate + self.__path.unlink() + self.__path = newpath + self.__bitrate = bitrate if audio_format: - self.audio_format = audio_format + self.__audio_format = audio_format def write_metadata(self, metadata: dict[str, Any]) -> None: """ @@ -100,7 +98,7 @@ class LocalFile: Args: metadata: key-value metadata dictionary """ - f = load_file(self.path) + f = load_file(self.__path) f.save() for k, v in metadata.items(): try: @@ -118,7 +116,7 @@ class LocalFile: Args: image: raw image data """ - f = load_file(self.path) + f = load_file(self.__path) f["artwork"] = image try: f.save() diff --git a/zotify/playable.py b/zotify/playable.py index 8513a33..7653ecb 100644 --- a/zotify/playable.py +++ b/zotify/playable.py @@ -3,7 +3,6 @@ from pathlib import Path from typing import Any from librespot.core import PlayableContentFeeder -from librespot.metadata import AlbumId from librespot.util import bytes_to_hex from librespot.structure import GeneralAudioStream from requests import get @@ -132,12 +131,6 @@ class Track(PlayableContentFeeder.LoadedStream, Playable): track.metrics, ) self.__api = api - try: - isinstance(self.track.album.genre, str) - except AttributeError: - self.album = self.__api.get_metadata_4_album( - AlbumId.from_hex(bytes_to_hex(self.track.album.gid)) - ) self.cover_images = self.album.cover_group.image self.metadata = self.__default_metadata() @@ -155,22 +148,19 @@ class Track(PlayableContentFeeder.LoadedStream, Playable): "artist": self.artist[0].name, "artists": "\0".join([a.name for a in self.artist]), "date": f"{date.year}-{date.month}-{date.day}", - "release_date": f"{date.year}-{date.month}-{date.day}", "disc_number": self.disc_number, "duration": self.duration, "explicit": self.explicit, - "genre": self.album.genre, + "explicit_symbol": "[E]" if self.explicit else "", "isrc": self.external_id[0].id, - "licensor": self.licensor, - "popularity": self.popularity, - "track_number": self.number, + "popularity": (self.popularity * 255) / 100, + "track_number": str(self.number).zfill(2), + # "year": self.album.date.year, + "title": self.name, "replaygain_track_gain": self.normalization_data.track_gain_db, "replaygain_track_peak": self.normalization_data.track_peak, "replaygain_album_gain": self.normalization_data.album_gain_db, - "replaygain_album_prak": self.normalization_data.album_peak, - "title": self.name, - "track_title": self.name, - # "year": self.album.date.year, + "replaygain_album_peak": self.normalization_data.album_peak, } def get_lyrics(self) -> Lyrics: diff --git a/zotify/utils.py b/zotify/utils.py index bcb5456..ead1cee 100644 --- a/zotify/utils.py +++ b/zotify/utils.py @@ -2,6 +2,7 @@ from argparse import Action, ArgumentError from enum import Enum, IntEnum from re import IGNORECASE, sub from sys import platform as PLATFORM +from typing import NamedTuple from librespot.audio.decoders import AudioQuality from librespot.util import Base62, bytes_to_hex @@ -13,26 +14,20 @@ LYRICS_URL = "https://sp" + "client.wg.sp" + "otify.com/color-lyrics/v2/track/" BASE62 = Base62.create_instance_with_inverted_character_set() +class AudioCodec(NamedTuple): + ext: str + name: str + + class AudioFormat(Enum): - AAC = "aac" - FDK_AAC = "fdk_aac" - FLAC = "flac" - MP3 = "mp3" - OPUS = "opus" - VORBIS = "vorbis" - WAV = "wav" - WV = "wavpack" - - -class ExtMap(Enum): - AAC = "m4a" - FDK_AAC = "m4a" - FLAC = "flac" - MP3 = "mp3" - OPUS = "ogg" - VORBIS = "ogg" - WAV = "wav" - WAVPACK = "wv" + AAC = AudioCodec("aac", "m4a") + FDK_AAC = AudioCodec("fdk_aac", "m4a") + FLAC = AudioCodec("flac", "flac") + MP3 = AudioCodec("mp3", "mp3") + OPUS = AudioCodec("opus", "ogg") + VORBIS = AudioCodec("vorbis", "ogg") + WAV = AudioCodec("wav", "wav") + WV = AudioCodec("wavpack", "wv") class Quality(Enum):