diff --git a/README.md b/README.md index cfda426..ac7e657 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ A Python CLI app for downloading songs/podcasts/videos from Spotify. **Discord Server:** https://discord.gg/aBjMEZ9tnq ## Features -* Download songs and podcasts in Vorbis or AAC +* Download songs in AAC +* Download podcast in AAC or Vorbis * Download podcast videos * Download music videos with a premium account * Support for artist links to download all of their albums @@ -17,22 +18,23 @@ A Python CLI app for downloading songs/podcasts/videos from Spotify. * To export your cookies, use one of the following browser extensions while signed in to Spotify: * Firefox: https://addons.mozilla.org/addon/export-cookies-txt * Chromium based browsers: https://chrome.google.com/webstore/detail/gdocmgbfkjnnpapoeobnolbbkoibbcif +* FFmpeg + * Add FFmpeg to your system’s PATH or specify its path using command-line arguments or the config file. Old versions may not work. +* .wvd file + * A .wvd file contains the Widevine keys from a device and is required to decrypt music videos and songs in AAC. The easiest method of obtaining one is using KeyDive, which extracts it from an Android device. Detailed instructions can be found here: https://github.com/hyugogirubato/KeyDive. **.wvd files extracted from emulated devices may not work**. + +If you plan on only downloading podcasts in Vorbis quality, you can skip the .wvd file and FFmpeg requirements. ### Optional dependencies The following tools are optional but required for specific features. Add them to your system’s PATH or specify their paths using command-line arguments or the config file. -* [FFmpeg](https://ffmpeg.org/download.html) - * Used when setting `ffmpeg` as remux mode and when setting `mp4` or `webm` as video format. * [MP4Box](https://gpac.io/downloads/gpac-nightly-builds/) * Used when setting `mp4box` as remux mode. * [Shaka Packager](https://github.com/shaka-project/shaka-packager/releases/latest) - * Used when setting `webm` as video format and for downloading music videos. + * Used when setting `webm` as video format and when downloading music videos. * [mp4decrypt](https://www.bento4.com/downloads/) * Used when setting `mp4box` or `mp4decrypt` as remux mode. * [aria2c](https://github.com/aria2/aria2/releases) * Used when setting `aria2c` as download mode. -* .wvd file - * Used when setting `aac` as audio quality or when downloading music videos. - * A .wvd file contains the Widevine keys from a device and is required to decrypt music videos and songs in AAC. The easiest method of obtaining one is using KeyDive, which extracts it from an Android device. Detailed instructions can be found here: https://github.com/hyugogirubato/KeyDive. **Note:** .wvd files extracted from emulated devices may not work. ## Installation 1. Install the package `votify` using pip diff --git a/votify/cli.py b/votify/cli.py index 52656cd..ace9845 100644 --- a/votify/cli.py +++ b/votify/cli.py @@ -14,6 +14,7 @@ from .constants import ( AAC_AUDIO_QUALITIES, EXCLUDED_CONFIG_FILE_PARAMS, PREMIUM_AUDIO_QUALITIES, + VORBIS_AUDIO_QUALITIES, X_NOT_FOUND_STRING, ) from .downloader import Downloader @@ -394,9 +395,9 @@ def main( datefmt="%H:%M:%S", ) logger.setLevel(log_level) - logger.info("Starting Votify") if not cookies_path.exists(): logger.critical(X_NOT_FOUND_STRING.format("Cookies file", cookies_path)) + logger.info("Starting Votify") spotify_api = SpotifyApi.from_cookies_file(cookies_path) if spotify_api.config_info["isAnonymous"]: logger.critical( @@ -594,6 +595,11 @@ def main( playlist_track=index, ) elif media_type == "track": + if audio_quality in VORBIS_AUDIO_QUALITIES: + logger.warning( + "Vorbis audio quality is only supported for podcasts, " + "skipping" + ) downloader_song.download( track_id=media_id, track_metadata=media_metadata, diff --git a/votify/downloader.py b/votify/downloader.py index ec577e1..46135a5 100644 --- a/votify/downloader.py +++ b/votify/downloader.py @@ -10,14 +10,6 @@ import subprocess from io import BytesIO from pathlib import Path -try: - re_unpp_exc: ImportError | None = None - from re_unplayplay import decrypt_and_bind_key, get_token -except ImportError as err: - decrypt_and_bind_key = lambda a, b: None - get_token = lambda: None - re_unpp_exc = err - import requests from InquirerPy import inquirer from InquirerPy.base.control import Choice @@ -29,12 +21,6 @@ from pywidevine import PSSH, Cdm, Device from .constants import MEDIA_TYPE_MP4_MAPPING, MP4_TAGS_MAP, VORBIS_TAGS_MAPPING from .models import DownloadQueueItem, UrlInfo -from .playplay_pb2 import ( - AUDIO_TRACK, - Interactivity, - PlayPlayLicenseRequest, - PlayPlayLicenseResponse, -) from .spotify_api import SpotifyApi from .utils import check_response @@ -368,26 +354,7 @@ class Downloader: return self.spotify_api.get_gid_metadata(gid, media_type) def get_playplay_decryption_key(self, file_id: str) -> bytes: - # We want to throw re-unplayplay errors only when we actually need re-unplayplay - if re_unpp_exc is not None: - raise re_unpp_exc - - playplay_license_request = PlayPlayLicenseRequest( - version=2, - token=get_token(), - interactivity=Interactivity.INTERACTIVE, - content_type=AUDIO_TRACK, - ) - playplay_license_response_bytes = self.spotify_api.get_playplay_license( - file_id, - playplay_license_request.SerializeToString(), - ) - - playplay_license_response = PlayPlayLicenseResponse() - playplay_license_response.ParseFromString(playplay_license_response_bytes) - obfuscated_key = playplay_license_response.obfuscated_key - - return decrypt_and_bind_key(obfuscated_key, file_id) + raise NotImplementedError() def get_widevine_decryption_key( self,