Getting Started¶
Installation¶
Minim is a Python package and can be installed from source using pip, the package installer for Python.
Note
Minim will be coming to PyPI and conda-forge once the PEP 541 request is resolved!
Grab a copy of the Minim repository:
git clone https://github.com/bbye98/minim.git
Enter the repository directory:
cd minim
Optional: Create a virtual environment to prevent dependency conflicts.
Conda
Create an environment named
minimand install the required dependencies using one of the following commands:conda create -n minim --file requirements_minimal.txt # required dependencies only conda env create -f environment.yml # all dependencies
Activate the environment:
conda activate minim
venv
Create an environment named
minim:python -m venv minim
Activate the environment using one of the following commands:
source minim/bin/activate # POSIX: bash or zsh minim\Scripts\activate.bat # Windows: cmd.exe minim\Scripts\Activate.ps1 # Windows: PowerShell
The required dependencies will be installed automatically alongside Minim in the next step. To install all dependencies instead:
python -m pip install -r requirements.txt
virtualenv
Create an environment named
minim:virtualenv minim
Activate the environment using one of the following commands:
source minim/bin/activate # Linux or macOS .\minim\Scripts\activate # Windows
The required dependencies will be installed automatically alongside Minim in the next step. To install all dependencies instead:
python -m pip install -r requirements.txt
Install Minim (and required dependencies, if you have not already done so) using pip:
python -m pip install -e .
Try importing Minim in Python:
python -c "import minim"
If no errors like
ModuleNotFoundError: No module named 'minim'are raised, you have successfully installed Minim!
Usage¶
Music service APIs¶
from minim import itunes, qobuz, spotify, tidal
Currently, clients for iTunes Search API, Qobuz API, Spotify Web API, and TIDAL APIs have been implemented. Other than the iTunes Search API, which does not require client credentials or support user authentication and can be used out of the box, the other APIs have a few additional prerequisite steps before they can be used. If you authenticate via Minim, the tokens and their related information will be cached and updated automatically as they expire and are refreshed.
iTunes Search API (minim.itunes.SearchAPI)¶
To use the iTunes Search API, simply create a client by instantiating a minim.itunes.SearchAPI object with no arguments:
client_itunes = itunes.SearchAPI()
Private Qobuz API (minim.qobuz.PrivateAPI)¶
If you already have a user authentication token, you can provide it and its accompanying app credentials to the client as keyword arguments auth_token, app_id, and app_secret, respectively, and skip this section.
To use the Qobuz API without user authentication, simply create a client by instantiating a minim.qobuz.PrivateAPI object with no arguments:
client_qobuz = qobuz.PrivateAPI()
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
Cell In[3], line 1
----> 1 client_qobuz = qobuz.PrivateAPI()
File ~/checkouts/readthedocs.org/user_builds/minim/conda/stable/lib/python3.14/site-packages/minim/qobuz.py:236, in PrivateAPI.__init__(self, app_id, app_secret, flow, browser, user_agent, email, password, auth_token, overwrite, save)
233 app_id = _config.get(self._NAME, "app_id")
234 app_secret = _config.get(self._NAME, "app_secret")
--> 236 self.set_flow(
237 flow,
238 app_id=app_id,
239 app_secret=app_secret,
240 auth_token=auth_token,
241 browser=browser,
242 save=save,
243 )
244 self.set_auth_token(auth_token, email=email, password=password)
File ~/checkouts/readthedocs.org/user_builds/minim/conda/stable/lib/python3.14/site-packages/minim/qobuz.py:552, in PrivateAPI.set_flow(self, flow, app_id, app_secret, auth_token, browser, save)
546 if (app_id is None or app_secret is None) and auth_token is not None:
547 emsg = (
548 "App credentials are required when an user "
549 "authentication token is provided."
550 )
--> 552 self._set_app_credentials(app_id, app_secret)
File ~/checkouts/readthedocs.org/user_builds/minim/conda/stable/lib/python3.14/site-packages/minim/qobuz.py:379, in PrivateAPI._set_app_credentials(self, app_id, app_secret)
377 continue
378 else:
--> 379 raise RuntimeError("No valid app secret could be found.")
380 logger.setLevel(logging_level)
381 else:
RuntimeError: No valid app secret could be found.
To use the Qobuz API with user authentication and get access to all public and protected endpoints, you can pass flow="password" and provide your Qobuz email and password as keyword arguments email and password to the constructor:
client_qobuz = qobuz.PrivateAPI(flow="password", email=<QOBUZ_EMAIL>, password=<QOBUZ_PASSWORD>)
which will authenticate you via a POST request to and retrieve the user authentication token from the Qobuz Web Player, or specify browser=True to have Minim spawn a web browser with the Qobuz Web Player login page:
client_qobuz = qobuz.PrivateAPI(flow="password", browser=True)
which you can use to log in normally.
Private Spotify Lyrics Service (minim.spotify.PrivateLyricsService)¶
If you already have a user access token, you can provide it and optionally its accompanying expiry time and sp_dc cookie to the client as keyword arguments access_token, expiry, and sp_dc, respectively, and skip this section.
To use the Spotify Lyrics service,
Launch a web browser and log into the Spotify Web Player.
Find the
sp_dccookie in your web browser’s storage.For Chromium-based browsers, press
F12to open DevTools and navigate toApplication > Storage > Cookies > https://open.spotify.com.For Firefox, press
Shift+F9to open Storage Inspector and nagivate toStorage > Cookies > https://open.spotify.com.
Create a client by instantiating a
minim.spotify.PrivateLyricsServiceobject with thesp_dccookie as a keyword argument:client_spotify_lyrics = spotify.PrivateLyricsService(sp_dc=<SPOTIFY_SP_DC>)
or store the
sp_dccookie as an environment variableSPOTIFY_SP_DCand call the constructor with no arguments:
client_spotify_lyrics = spotify.PrivateLyricsService()
Spotify Web API (minim.spotify.WebAPI)¶
If you already have an access token, you can provide it and optionally its accompanying refresh token, expiry time, and client credentials to the client as keyword arguments access_token, refresh_token, expiry, client_id, and client_secret, respectively, and skip this section.
First, register a Spotify application here and grab its client credentials. For the redirect URI, use http://localhost:8888/callback. You can replace 8888 with an open port of your choice, but you will need to pass port=<SPOTIFY_PORT> when you create a client.
To use the Spotify Web API without user authentication, you can provide the client credentials as keyword arguments client_id and client_secret to the constructor:
client_spotify = spotify.WebAPI(client_id=<SPOTIFY_CLIENT_ID>,
client_secret=<SPOTIFY_CLIENT_SECRET>)
or store the client credentials as environment variables SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET and call the constructor with no arguments:
client_spotify = spotify.WebAPI()
To use the Spotify Web API with user authentication,
Get the necessary authorization scopes using
spotify.WebAPI.get_scopes():
scopes = spotify.WebAPI.get_scopes("all")
Create a client with
flow="pkce", the client credentials inclient_idandclient_secret, the authorization scopes inscopes, and optionallyframework="http.server"to automate the authorization code retrieval process:client_spotify = spotify.WebAPI(client_id=<SPOTIFY_CLIENT_ID>, client_secret=<SPOTIFY_CLIENT_SECRET>, flow="pkce", scopes=scopes, framework="http.server")
If
framework=None, open the authorization URL in a web browser.Log into your Spotify account and authorize Minim by clicking
Agree.If
framework=None, copy and paste the redirect URI into the prompt.
TIDAL API (minim.tidal.API)¶
If you already have a client-only access token, you can provide it and optionally its accompanying refresh token, expiry time, and client credentials to the client as keyword arguments access_token, refresh_token, expiry, client_id, and client_secret, respectively, and skip this section.
First, register a TIDAL application here and jot down the client credentials.
To use the TIDAL API, you can provide the client credentials as keyword arguments client_id and client_secret to the minim.tidal.API constructor:
client_tidal = tidal.API(client_id=<TIDAL_CLIENT_ID>, client_secret=<TIDAL_CLIENT_SECRET>)
or store the client credentials as environment variables TIDAL_CLIENT_ID and TIDAL_CLIENT_SECRET and create a client with no arguments:
client_tidal = tidal.API()
Private TIDAL API (minim.tidal.PrivateAPI)¶
If you already have an access token, you can provide it and optionally its accompanying refresh token, expiry time, and client credentials to the client as keyword arguments access_token, refresh_token, expiry, client_id, and client_secret, respectively, and skip this section.
To use the TIDAL API without user authentication, simply create a client by instantiating a minim.tidal.PrivateAPI object with no arguments:
client_tidal_private = tidal.PrivateAPI()
To use the TIDAL API with user authentication,
Get client credentials from the TIDAL Web Player or the Android, iOS, macOS, and Windows applications by using a web debugging proxy tool to intercept web traffic.
Create a client with the client credentials in
client_idandclient_secretand optionallybrowser=Trueto automatically open a web browser for the authorization flow. Use the authorization code with PKCE flow:client_tidal_private = tidal.PrivateAPI(client_id=<TIDAL_CLIENT_ID>, client_secret=<TIDAL_CLIENT_SECRET>, flow="pkce", browser=True)
if you obtained client credentials from the TIDAL Web Player or the desktop applications, or the device code flow:
client_tidal_private = tidal.PrivateAPI(client_id=<TIDAL_CLIENT_ID>, client_secret=<TIDAL_CLIENT_SECRET>, flow="device", browser=True)
if you obtained client credentials from the Android or iOS applications.
Follow the instructions in the console (
browser=False) or the web browser (browser=True) to log into your TIDAL account and authorize Minim.
Examples¶
Searching for artists¶
Each of the APIs has a search() method that can be used to search for and retrieve information about an artist, such as the EDM group Galantis:
iTunes Search API¶
client_itunes.search("Galantis", entity="musicArtist", limit=1)["results"][0]
Private Qobuz API¶
client_qobuz.search('"Galantis"', limit=1)["artists"]["items"][0]
Spotify Web API¶
client_spotify.search("Galantis", "artist", limit=1)["items"][0]
TIDAL API¶
client_tidal.search("Galantis", "US", type="ARTISTS", limit=1)["artists"][0]
Private TIDAL API¶
client_tidal_private.search("Galantis", type="artist", limit=1)["items"][0]
Searching for tracks¶
The search() methods can also be used to search for and retrieve information about a track, such as “Everybody Talks” by Neon Trees:
iTunes Search API¶
client_itunes.search("Everybody Talks", media="music", limit=1)["results"][0]
Private Qobuz API¶
track_qobuz = client_qobuz.search('"Everybody Talks"', limit=1)["tracks"][
"items"
][0]
track_qobuz
Spotify Web API¶
track_spotify = client_spotify.search("Everybody Talks", "track", limit=1)[
"items"
][0]
track_spotify
TIDAL API¶
client_tidal.search("Everybody Talks", "US", type="TRACKS", limit=1)["tracks"][
0
]
Private TIDAL API¶
track_tidal_private = client_tidal_private.search(
"Everybody Talks", type="track", limit=1
)["items"][0]
track_tidal_private
Creating, modifying, and deleting a personal playlist¶
If the clients are authenticated, you can create and modify user playlists. As an example, we will create a private playlist named “Minim”, make it public, add “Everybody Talks” by Neon Trees to it, and then delete it.
Private Qobuz API¶
playlist_qobuz = client_qobuz.create_playlist(
"Minim", description="A playlist created using Minim.", public=False
)
client_qobuz.update_playlist(playlist_qobuz["id"], public=True)
client_qobuz.add_playlist_tracks(playlist_qobuz["id"], track_qobuz["id"])
playlist_qobuz = client_qobuz.get_playlist(playlist_qobuz["id"])
client_qobuz.delete_playlist(playlist_qobuz["id"])
Spotify Web API¶
playlist_spotify = client_spotify.create_playlist(
"Minim", description="A playlist created using Minim.", public=False
)
client_spotify.change_playlist_details(playlist_spotify["id"], public=True)
client_spotify.add_playlist_items(
playlist_spotify["id"], [f"spotify:track:{track_spotify['id']}"]
)
playlist_spotify = client_spotify.get_playlist(playlist_spotify["id"])
client_spotify.unfollow_playlist(playlist_spotify["id"])
Private TIDAL API¶
playlist_tidal_private = client_tidal_private.create_playlist(
"Minim", description="A playlist created using Minim.", public=False
)
client_tidal_private.set_playlist_privacy(
playlist_tidal_private["data"]["uuid"], True
)
client_tidal_private.add_playlist_items(
playlist_tidal_private["data"]["uuid"], track_tidal_private["id"]
)
playlist_tidal_private = client_tidal_private.get_user_playlist(
playlist_tidal_private["data"]["uuid"]
)
client_tidal_private.delete_playlist(
playlist_tidal_private["playlist"]["uuid"]
)
Audio file handlers¶
from pathlib import Path
from minim.audio import (
Audio,
FLACAudio,
MP3Audio,
MP4Audio,
OggAudio,
WAVEAudio,
)
Minim uses Mutagen to load and edit audio files and FFmpeg to convert between different audio formats. Currently, the most common audio formats, such as AAC, ALAC, FLAC, MP3, Opus, Vorbis, and WAVE, are supported.
Examples¶
Loading and editing audio files¶
To load an audio file, pass the filename as a str or a pathlib.Path object either to the minim.audio.Audio constructor for Minim to automatically detect the audio format:
file = Path().resolve().parents[2] / "tests/data/samples/middle_c.wav"
middle_c = Audio(file)
or the specific class for the audio format if known (in this case, minim.audio.WAVEAudio):
middle_c = WAVEAudio(Path().resolve().parents[2] / "tests/data/samples/middle_c.wav")
For this example, both approaches return a minim.audio.WAVEAudio object:
type(middle_c)
The metadata stored in the audio file can be accessed using dot notation or getattr():
for attr in ["title", "album", "artist", "genre", "codec", "bit_depth"]:
print(f"{attr.capitalize().replace('_', ' ')}: {getattr(middle_c, attr)}")
and edited similarly using dot notation or setattr():
middle_c.title = "Middle C (261.63 Hz)"
middle_c.write_metadata()
If changes are made, don’t forget to write them to file using minim.audio.Audio.write_metadata().
Converting between audio formats¶
Conversion between the supported audio formats is powered by FFmpeg.
To re-encode the previous WAVE audio using the ALAC codec and store it in a MP4 container, use the minim.audio.Audio.convert() method:
middle_c.convert("alac", filename="middle_c_alac")
The call above not only converts the WAVE audio into ALAC audio, but also updates the variable middle_c to now point to a minim.audio.MP4Audio file handler for the new file middle_c.m4a and maintains the metadata:
type(middle_c)
for attr in ["title", "album", "artist", "genre", "codec", "bit_depth"]:
print(f"{attr.capitalize().replace('_', ' ')}: {getattr(middle_c, attr)}")