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!

  1. Grab a copy of the Minim repository:

    git clone https://github.com/bbye98/minim.git
    
  2. Enter the repository directory:

    cd minim
    
  3. Optional: Create a virtual environment to prevent dependency conflicts.

    Conda

    • Create an environment named minim and 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
      
  4. Install Minim (and required dependencies, if you have not already done so) using pip:

    python -m pip install -e .
    
  5. 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,

  1. Launch a web browser and log into the Spotify Web Player.

  2. Find the sp_dc cookie in your web browser’s storage.

    • For Chromium-based browsers, press F12 to open DevTools and navigate to Application > Storage > Cookies > https://open.spotify.com.

    • For Firefox, press Shift + F9 to open Storage Inspector and nagivate to Storage > Cookies > https://open.spotify.com.

  3. Create a client by instantiating a minim.spotify.PrivateLyricsService object with the sp_dc cookie as a keyword argument:

    client_spotify_lyrics = spotify.PrivateLyricsService(sp_dc=<SPOTIFY_SP_DC>)
    

    or store the sp_dc cookie as an environment variable SPOTIFY_SP_DC and 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,

  1. Get the necessary authorization scopes using spotify.WebAPI.get_scopes():

scopes = spotify.WebAPI.get_scopes("all")
  1. Create a client with flow="pkce", the client credentials in client_id and client_secret, the authorization scopes in scopes, and optionally framework="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")
    
  2. If framework=None, open the authorization URL in a web browser.

  3. Log into your Spotify account and authorize Minim by clicking Agree.

  4. 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,

  1. 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.

  2. Create a client with the client credentials in client_id and client_secret and optionally browser=True to 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.

  3. 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)}")