Another installment in my series on listening to music is forthcoming and it will mostly be a love letter disguised as a 1000+ word use guide for Plexamp. In the meantime, this is a slightly out of context taste.

My music library lives under the watchful eye of a Plex server. The server pulls down a lot of metadata that makes artist and album pages much more delightful to browse than iTunes/Music.app ever did. I then use Plexamp to listen to music both at and away from home. I really love this setup.

Besides the official Plex and Plexamp clients, there is an unofficial Python Plex library whose goal is to “match all capabilities of the official Plex Web Client”. I’ve been playing with it for a while, it is impressive, and this is the first of many uses I have in mind for it.

Play a random album the simple way

I keep all the albums I own as CDs in a Collection called ‘💿’.1 Sometimes I want to put on some music but I don’t have a specific album in mind and end up scrolling the collection aimlessly.

A common way to deal with this kind of choice paralysis is to let a random picker choose for you. This is very easily done using the Python library.

Here is a simple implementation.

import random
from plexapi.server import PlexServer

BASEURL = 'http://192.168.1.123:32400'
TOKEN = '<insert your own token here>'
plex = PlexServer(BASEURL, TOKEN)

owned = plex.library.section('Music').searchAlbums(collection='💿')
plex.client('KEF').playMedia(random.choice(owned))

Requirements:

  • pip install plexapi
  • Know the local or remote IP address of your server.
  • Your X-Plex-Token for authentication. Here’s how to find it.
  • Know the name of the client that will play the album. This is cool because it doesn’t have to be on the same machine running the script! In my case I have a machine running Plexamp connected to my KEF speakers.

Steps:

  • Import the random library and the PlexServer class.
  • Create an instance of your server with the correct url and token.
  • Get a list of the albums in the ‘💿’ collection.
  • Tell the client to play a random album from that collection.

So that’s amazing and simple. Here’s how you can make it amazing and a little fancy.

Playing a random album the glamorous way

A random choice from a large collection can be jarring. Too decisive, you know? One compromise you can make between that and choosing from the whole collection yourself is to get random to offer you two – or more – candidates to choose from.

The implementation is a bit involved but still pretty straightforward.

#! /usr/local/bin/python3

import random
import requests
import subprocess

from PIL import Image
from io import BytesIO
from plexapi.server import PlexServer
from rich.console import Console

CONSOLE = Console(color_system='truecolor')
BASEURL = 'http://192.168.1.123:32400'
TOKEN = '<insert your own token here>'

plex = PlexServer(BASEURL, TOKEN)
owned = plex.library.section('Music').searchAlbums(collection='💿')

albums = {
    album.title: album
    for album in random.sample(owned, 2)
}

albumart = [
    Image.open(BytesIO(requests.get(candidate.thumbUrl).content)).resize((500, 500), Image.LANCZOS)
    for candidate in albums.values()
]

merged = Image.new('RGBA', (1020, 500))
for ii, art in enumerate(albumart):
    padding = 0 if ii == 0 else 20
    merged.paste(art, (ii*500 + padding, 0))
merged.save('/var/tmp/amp.png')


CONSOLE.print('\n')
subprocess.run(['/Applications/kitty.app/Contents/MacOS/kitty', 'icat', '/var/tmp/amp.png'])

CONSOLE.print('\nPick an album')
choices = ' '.join(['"'+albumtitle+'"' for albumtitle in albums.keys()])
choice = (
    subprocess.check_output(
        f"/usr/local/bin/gum choose {choices} --cursor '  ' --selected.foreground='#7851a9'",
        shell=True,
        encoding='UTF-8')
    .rstrip()
)

plex.client('KEF').playMedia(albums[choice])

CONSOLE.print(
    f'\n  [italic]Now Playing: '
    f'[#9966cc]{albums[choice].title} ({albums[choice].year})[/#9966cc] '
    f'by [#47c1ff]{albums[choice].artist().title}'
    '\n',
    justify='center'
)

Requirements: I wanted to make this aesthetically pleasing, so there are some splurgy requirements.

Steps:

  • Same imports from the simple implementation. Then a few imports for image processing, reading the album art from the server, and pretty printing in the console.
  • Get a list of the albums in the ‘💿’ collection.
  • Get the titles for two random albums, then get the album art for those two albums.
  • Merge the album art into one image. I have to do this because as far as I can tell kitty won’t let me display two images next to each other in the terminal.
  • Tell kitty to display the merged image.
  • Use gum to ask the user which album to play.
  • Tell the client to play the chosen album.
  • Display a “Now Playing” status with the album title, release year, and artist.

Other notes:

  • The characters appearing as in the code are music glyphs in the PragmataPro font I use in kitty. You can see it in the video.
  • Resizing the album art to 500×500 is hardcoded across the script. You could set that as a variable to change later. Also some album art isn’t perfectly square and will look silly when resized to a square. I might deal with that later.
  • It’s more idiomatic to use tempfile to get a path to a temporary filename. You can read more about the tradeoffs here.
  • I wrote this to make it easy to accept the number of albums as an argument instead of that also being hardcoded in the script.
  • You could build this out to also ask the user which collection they want to pick from. I plan to do just that.

And that’s it. Kinda swanky.

  1. This also has music I bought from Bandcamp, Qobuz, Boomkat, etc. But it’s mostly CDs. ↩︎