nimbus
English · Português
A terminal player (Linux and macOS) that streams the music in your Google Drive folders. Access is read-only and nothing is downloaded up front: mpv reads each track in chunks, about two minutes ahead of what is playing.
It also plays music from your own computer: home folder, external drive and USB stick. That part works even without signing in to Google.
The interface and command-line messages are currently in Portuguese.
Requirements
- Python 3.9 or newer
- mpv:
brew install mpvon macOS,sudo apt install mpvon Debian/Ubuntu
The Homebrew installation brings both along.
Installation
Homebrew (macOS and Linux)
brew install allanmedeiros71/tap/nimbus
Homebrew installs mpv and Python along with it. To update: brew upgrade nimbus.
pipx
pipx creates an isolated environment for nimbus and makes the nimbus command available from any folder and any terminal, which shell completion also needs. In this case mpv is installed separately (see Requirements). On PyPI the package is called nimbus-player; the command is still nimbus.
brew install pipx # macOS
sudo apt install pipx # Debian/Ubuntu
pipx ensurepath # adds ~/.local/bin to PATH
pipx install nimbus-player
After pipx ensurepath, open a new terminal so the PATH change takes effect. To update: pipx upgrade nimbus-player.
If you installed an older version from GitHub, switch to the PyPI one: pipx uninstall nimbus && pipx install nimbus-player.
To run the code from the repository (development version): git clone git@github.com:allanmedeiros71/nimbus.git and pipx install -e ./nimbus. With -e, a git pull inside the folder already updates the command; only run pipx install -e --force ./nimbus again if the dependencies change.
Alternative for development: virtual environment
cd nimbus
python3 -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'
In this case the nimbus command only exists while the environment is active: run source .venv/bin/activate in every new terminal first.
Common problems
zsh: command not found: pip: on macOS with Homebrew and on recent Debian/Ubuntu there is no standalonepip, and the system Python does not accept packages. Use pipx (above) or a virtual environment.zsh: permission denied: nimbusorcommand not found: nimbusoutside the project folder: the command was only installed in the venv. Check withtype -a nimbus; if no path like~/.local/bin/nimbusshows up, install with pipx, runpipx ensurepathand open a new terminal.mpv não encontrado(mpv not found): install it withbrew install mpvorsudo apt install mpv.
Google credentials (one time only)
nimbus uses your own OAuth client, so access stays between you and Google.
- Open the Google Cloud Console and create a project (or use an existing one).
- In APIs & Services > Library, enable the Google Drive API.
- In APIs & Services > OAuth consent screen, choose External, and add your email under Test users.
- In APIs & Services > Credentials, click Create credentials > OAuth client ID, type Desktop app.
- Download the JSON and save it as
~/.config/nimbus/client_secret.json.
Then:
nimbus login
The browser opens asking for read permission on Drive (drive.readonly). The token is stored in ~/.config/nimbus/token.json, readable only by your user. nimbus logout deletes the token.
Visual interface
nimbus # opens the interface
nimbus tui "Música/Rock" # opens with this folder already selected
nimbus tui ~/Música # or with a folder on your computer
nimbus tui --offline # without querying MusicBrainz/Cover Art Archive
At the top is the Playback panel: album cover, title • artist, album • genre • year, the playing/paused icon, repeat, shuffle, volume, the position in the queue and a progress bar. Below it, Directories: the folder tree (Meu Drive, Compartilhados comigo and Este computador, i.e. My Drive, Shared with me and This computer) on the left and the contents of the selected folder on the right. Enter on a song plays the whole folder starting from it. To save bandwidth and API calls, a Drive folder is only listed on the right when you press Enter or →/l on it; folders already opened and folders on your computer show up instantly, as you navigate.
| Key | Action |
|---|---|
space |
play / pause |
n / p |
next / previous |
> < (or . ,) |
forward / back 10s |
+ / - |
volume |
m |
mute |
r |
repeat: off → all → one |
s |
shuffle (the current track keeps playing) |
j / k |
down / up |
h / l |
in the tree, collapse the folder or go up / expand the folder (and, if already expanded, move to the list); in the list, go back to the tree / enter the folder |
g / G |
first / last item |
Tab |
switch between tree and list |
Enter |
play the song or enter the subfolder |
? |
help |
q |
quit |
Music on your computer, external drive and USB stick
The Este computador (This computer) root in the tree shows your home folder, mounted external drives and the system disk (/). External drives are looked up where each system mounts them:
- macOS:
/Volumes(the system disk, which shows up there as a link to/, is left out); - Linux:
/media/<user>/…(Ubuntu, Debian),/run/media/<user>/…(Fedora, Arch),/media/…and the mount points in/mnt.
A USB stick plugged in while the interface is open only shows up after reopening nimbus. Hidden folders and files (starting with .) are not shown, and playlists (.m3u, .pls) are not added to the queue. mpv reads the files straight from disk, and the cover comes from the file itself or from an image in the folder, as with Drive.
Without a Google login, nimbus opens with only Este computador.
Cover art and metadata
nimbus uses whatever is available, in this order:
- File tags read by mpv (title, artist, album, genre, year).
- The file name (
01 - Artist - Title.mp3) and the folder name, when tags are missing. - The cover embedded in the MP3 or FLAC. Only the beginning of the file, where the cover lives, is read.
- An image in the same folder (
cover.jpg,folder.png,capa.jpg…). For music on this computer, art subfolders such asCovers/,Artwork/orScans/are searched too, and images that fail to open are skipped. For music on this computer, an image named like a cover (Folder.jpg,cover.jpg,AlbumArt_…_Large.jpg) wins over the embedded cover, and thumbnails such asAlbumArtSmall.jpgcome last. - MusicBrainz to fill in album, year and genre, and the Cover Art Archive for the cover. If the Cover Art Archive does not have the cover or cannot be reached from your network, the public iTunes search and then Deezer's serve as fallbacks. Only artist, title and album are sent. Use
--offlineorNIMBUS_OFFLINE=1to turn this off.
In compilations (folders with tracks by different artists, judging by the file names) the cover comes first from each song's original release on the Cover Art Archive; the embedded cover and the folder image are fallbacks, since they tend to be the same on every track.
Advertising tags (phone numbers, WhatsApp, links, such as "DJ FULANO 62999999999 WHATSAPP") are ignored and the file name takes over; in those files the embedded cover is ignored too.
Each resolved song is saved in ~/.cache/nimbus/tracks.json, keyed by its Drive file ID: next time, the corrected title, artist, album and cover show up instantly, without querying Drive or the internet ("salvo", saved, on the data line). Nothing is written to Drive, which stays read-only. If the internet fails, the track is looked up again next time. Delete tracks.json to redo everything. Writing the corrections into the files themselves is on the backlog.
Covers and responses are cached in ~/.cache/nimbus. The "dados: … · capa: …" (data: … · cover: …) line in the panel shows where each piece came from.
The cover is drawn with colored blocks, which works in any terminal with colors. In terminals with real image support you can ask for higher resolution: NIMBUS_COVER=iterm (WezTerm, iTerm2), NIMBUS_COVER=kitty (kitty, Ghostty), NIMBUS_COVER=sixel (foot, Konsole) or NIMBUS_COVER=auto to detect (in WezTerm and iTerm2, auto picks iterm; in Ghostty and kitty, kitty; inside tmux in any other terminal, blocks). If garbage shows up instead of the cover, go back to the default (NIMBUS_COVER=blocks). NIMBUS_COVER=off hides the cover. To keep a mode, add it to your shell config, for example export NIMBUS_COVER=auto in ~/.zshrc. Inside tmux (3.3 or newer), the iterm and kitty modes need set -g allow-passthrough on in ~/.tmux.conf.
Command line
nimbus ls # root of My Drive
nimbus ls "Música/Rock" # path from My Drive (case doesn't matter)
nimbus ls https://drive.google.com/drive/folders/<id> # folder URL or ID, shared ones included
nimbus play "Música/Rock" # plays the folder in order
nimbus play "Música" -r -s # includes subfolders, random order
nimbus ls ~/Música # folder on your computer
nimbus play /Volumes/PENDRIVE -r # USB stick on macOS
nimbus play /media/$USER/HD/Discos -s # external drive on Linux
nimbus ls "Este computador" # home folder and mounted drives
nimbus ls "Shared with me" # what other people shared with you
nimbus ls "Shared with me/Friend's Records"
nimbus play "Shared with me/Friend's Records" -r
"Compartilhados comigo" (Shared with me) shows up as a folder at the root of nimbus ls. Shared with me, @compartilhados and @shared also work, which is useful if you have a folder of your own with that name. Shared folders can also be opened by URL or ID, like any other.
A path is on your computer when it starts with /, ~, ./ or ../ (file://… also works). Anything else is looked up on Drive, so for a relative folder on your computer use ./Discos, not just Discos.
Folder completion (zsh and bash)
With completion enabled, Tab completes Drive folder names in nimbus ls and nimbus play, one path segment at a time, including inside "Shared with me". Paths on your computer (~/Mú<Tab>, /Volumes/<Tab>) complete too. Case and accents don't need to match.
zsh (default on macOS): add to the end of ~/.zshrc
autoload -Uz compinit && compinit # if your .zshrc doesn't have it yet (oh-my-zsh already does this)
eval "$(nimbus completion zsh)"
bash: add to ~/.bashrc (on macOS, ~/.bash_profile)
eval "$(nimbus completion bash)"
Open a new terminal and test with nimbus ls Mú<Tab>. The eval needs the nimbus command on the PATH, which is why the Homebrew or pipx installation is recommended. Listings are cached for 5 minutes in ~/.cache/nimbus, so a newly created folder may take that long to show up on Tab.
Keys during nimbus play: space pauses, n next, p previous, ←/→ back or forward 10s, +/- volume, q quits.
How it works
drive.pylists folders and files through the Drive API v3 (with support for shared drives, "Shared with me" and shortcuts). Audio is detected by MIME type or by extension.player.pystarts mpv in idle mode and controls it through the JSON IPC socket. Each track is the API URLfiles/<id>?alt=media, with theAuthorization: Bearer …header sent through the socket, never on the command line.local.pylists folders on your computer and finds mounted drives;library.pycombines Drive and the computer and routes each request to the right source. Local items have the IDlocal:<path>.playback.pymanages the queue and asks for a refreshed token before each Drive track. Local files go to mpv by path, with no token.tui.pyis the visual interface (Textual). mpv events arrive on their own thread and the screen only reads the state on a timer, so network and IPC never freeze the interface.metadata.pycombines tags, file name, embedded cover, folder image and MusicBrainz/Cover Art Archive.cli.pyhas the command-line commands and opens the interface when no command is given.completion.pygenerates the completion scripts and answersTabwith the Drive folders.
Useful variables: NIMBUS_CONFIG_DIR changes the configuration folder, NIMBUS_MPV points to another mpv executable, NIMBUS_COVER chooses how to draw the cover (blocks, iterm, kitty, sixel, auto, off) and NIMBUS_OFFLINE=1 turns off internet lookups.
Tests
source .venv/bin/activate # development virtual environment (above)
pytest
The playback and interface tests use a real mpv and a local HTTP server that mimics Drive (it requires the token and answers Range requests).
Releasing a version
Just create and push a tag on main: git tag v0.3.2 && git push origin v0.3.2. The version number comes from the tag itself (via setuptools-scm), so there is no number to edit in the code.
The release workflow runs the tests, publishes nimbus-player to PyPI, creates the GitHub Release with the packages and updates Formula/nimbus.rb in the homebrew-tap repository. The formula is generated by scripts/homebrew_formula.py.
One-time setup:
- PyPI: on pypi.org, Your projects → Publishing → Add a new pending publisher with project
nimbus-player, ownerallanmedeiros71, repositorynimbus, workflowrelease.ymland environmentpypi. - Homebrew: create the public repository
allanmedeiros71/homebrew-tapand a fine-grained token with Contents: read and write on it only; save it as theHOMEBREW_TAP_TOKENsecret in this repository. Without the secret, the workflow publishes to PyPI and only warns that it skipped Homebrew.
Metadata
Release files for nimbus-player 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nimbus_player-0.3.3.tar.gz | 75.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nimbus_player-0.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 126.7 kB
Release files / nimbus_player-0.3.3.tar.gz
| Download URL | nimbus_player-0.3.3.tar.gz |
|---|---|
| Size | 75.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
62dce0fe1f011111fc4d9db01183dbcf21f55e0dcf0275545454671f52f01fea
|
|
BLAKE2b-256 checksum How to use checksums |
c2d5b6a71e4cdc82202af31637553cd73777b909792934585cb47b53775b6dbc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / nimbus_player-0.3.3-py3-none-any.whl
| Download URL | nimbus_player-0.3.3-py3-none-any.whl |
|---|---|
| Size | 51.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0532ada6b687a49419c4ccdaaf777865518b71767aa288a56acb21d9a553ba0f
|
|
BLAKE2b-256 checksum How to use checksums |
c0fa1279d00fa273730124d88764b16ea912cb5d7a7cce56ebf4e68714133d88
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log