Apple Music CLI with ChatGPT
It’s always useful to be able to manage things from the terminal, for some of us it’s even a thing about comfort. I used ChatGPT to generate a simple set of alias functions to work as CLI for Apple Music.
The first thing to do would be to define the list of functionalities we are going to be asking ChatGPT to perform for us, these functionalities should be enough to properly handle the basic usage scenarios however providing a proper and extensible format so we can add more complicated tasks over time.
Common functionalities
The basic functionalities I would expect from a sensible music player are:
- Play.
- Pause.
- Skip to next track.
- Go back to previous track.
- Search and play track.
- Display current track’s information.
Mapping of functionalities
The proposal would be for ChatGPT to generate the following mapping for the functions described into aliases:
Functionality | Alias | Usage |
---|---|---|
Play | music-play
| Plays the current track |
Pause | music-pause
| Pauses the current track |
Skip to next track | music-next
| Skips to the next track in queue |
Go back to previous track | music-prev
| Goes back to the previous track in the queue |
Search and play track | music-play-track <track name>
| Searches for <track name> in Apple Music and, if found, plays it
|
Display current track information | music-now-playing
| Shows information about the currently-playing track in Apple Music |
Asking ChatGPT for scripting superpowers!
The prompt I used for generating the tools:
Me: Hey! Can you write me a set of shell aliases for performing normal tasks from the terminal like a CLI for Apple Music?
I had to go back-and-forth for a while with our Chat friend as there were some
issues (explained after the script) regarding the music-now-playing
function
which wasn’t working at the first try. I would have been really surprised if
it generates a perfect working example on the first attempt, honestly.
The resulting script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #!/bin/bash # Apple Music Aliases- @humbertowoody # Creates aliases for Apple Music commands. # Function for playing music function music-play() { osascript -e 'tell application "Music" to play'; } # Function for pausing music function music-pause() { osascript -e 'tell application "Music" to pause'; } # Function for skipping to the next track function music-next() { osascript -e 'tell application "Music" to next track'; } # Function for skipping to the previous track function music-prev() { osascript -e 'tell application "Music" to previous track'; } # Function for searching and playing a music-play-track function music-play-track() { query="$1" osascript -e "tell application \"Music\" set search_results to search playlist \"Library\" for \"$query\" if search_results is not {} then set best_result to item 1 of search_results play best_result end if end tell" } # Function for getting information about the currently playing track function music-now-playing() { # Get the current track properties track_name=$(osascript -e 'tell application "Music" to get name of current track as string' 2>/dev/null || echo "(not present)"); artist_name=$(osascript -e 'tell application "Music" to get artist of current track as string' 2>/dev/null || echo "(not present)"); album_name=$(osascript -e 'tell application "Music" to get album of current track as string' 2>/dev/null || echo "(not present)"); track_number=$(osascript -e 'tell application "Music" to get track number of current track as integer' 2>/dev/null || echo "(not present)"); disc_number=$(osascript -e 'tell application "Music" to get disc number of current track as integer' 2>/dev/null || echo "(not present)"); duration_seconds=$(osascript -e 'tell application "Music" to get duration of current track as integer' 2>/dev/null || echo "(not present)"); play_count=$(osascript -e 'tell application "Music" to get played count of current track as integer' 2>/dev/null || echo "(not present)"); rating=$(osascript -e 'tell application "Music" to get rating of current track as integer' 2>/dev/null || echo "(not present)"); loved=$(osascript -e 'tell application "Music" to get loved of current track as boolean' 2>/dev/null || echo "(not present)"); genre=$(osascript -e 'tell application "Music" to get genre of current track as string' 2>/dev/null || echo "(not present)"); year=$(osascript -e 'tell application "Music" to get year of current track as integer' 2>/dev/null || echo "(not present)"); art_file=$(osascript -e 'tell application "Music" to get location of artwork 1 of current track as string' 2>/dev/null || echo "(not present)"); track_quality=$(osascript -e 'tell application "Music" to get bit rate of current track as integer' 2>/dev/null || echo "(not present)"); sampling_rate=$(osascript -e 'tell application "Music" to get sample rate of current track as integer' 2>/dev/null || echo "(not present)"); # Format the duration as minutes:seconds if [[ -n "$duration_seconds" ]]; then duration_min=$(( duration_seconds / 60 )) duration_sec=$(( duration_seconds % 60 )) duration=$(printf "%d:%02d" $duration_min $duration_sec) else duration="(not present)" fi # Print the current track information with decorations printf "\n%s\n" "--------------------------------------" printf "%s\n" " Now playing" printf "%s\n" "--------------------------------------" printf " %-15s %s\n" "Name:" "$track_name" printf " %-15s %s\n" "Artist:" "$artist_name" printf " %-15s %s\n" "Album:" "$album_name" printf " %-15s %s\n" "Track number:" "$track_number" printf " %-15s %s\n" "Disc number:" "$disc_number" printf " %-15s %s\n" "Duration:" "$duration" printf " %-15s %s\n" "Play count:" "$play_count" printf " %-15s %s\n" "Rating:" "$rating" printf " %-15s %s\n" "Loved:" "$( [[ $loved == true ]] && echo "yes" || echo "no" )" printf " %-15s %s\n" "Genre:" "$genre" printf " %-15s %s\n" "Year:" "$year" printf " %-15s %s\n" "Artwork file:" "$art_file" printf " %-15s %s kbps\n" "Track quality:" "$track_quality" printf " %-15s %s Hz\n" "Sampling rate:" "$sampling_rate" printf "%s\n" "--------------------------------------" } # Helper function to test which track properties are available # This is useful for debugging function music-now-playing-test() { # Define the track properties to test properties=( "name" "artist" "album" "track number" "disc number" "duration" "play count" "rating" "loved" "genre" "year" "artwork location" ) # Test each track property and output the results printf "Music track properties:\n\n" for prop in "${properties[@]}"; do # Get the value of the property prop_value=$(osascript -e "tell application \"Music\" to get ${prop} of current track" 2>/dev/null) # Print the property name and whether it was available if [[ -n "$prop_value" ]]; then printf "%-16s%s\n" "${prop}:" "available" else printf "%-16s%s\n" "${prop}:" "not available" fi done printf "\n" } # Function that prints all aliases with examples function music-help() { echo "music-play: Play music" echo "music-pause: Pause music" echo "music-next: Skip to the next track" echo "music-prev: Skip to the previous track" echo "music-play-track: Search and play a track" echo "music-now-playing: Get information about the currently playing track" } |
The solution involves two things:
function
s to map the commands themselves, that way we cannot get confused with the quote/double-quote dilemma.osascript
, an Apple Script execution engine that, as you can see, receive commands in a really abstract manner, almost plain language1.
The music-now-playing
issues
As you may have observed, the provided script includes a function called
music-now-playing
which pretty-prints in the terminal information from the
currently-playing track.
There’s also a function called music-now-playing-test
, this is because,
depending on Apple Music’s version, there’s no guarantee on the information
available to the scripting engine from Apple.
The idea is for you to use the music-now-playing-test
alias to debug which
features are available in order for you to modify the music-now-playing
function accordingly.
The final usage
These aliases and functions live in my dotfiles repository in GitHub
in which I keep my entire configuration. From there you can infer how I’m using
them, but basically I load them during ZSH initialization from .zshrc
and
source
through each tools/*.sh
file (such as this one:
apple-music-aliases.sh
) and voilá!
This is just a fun perk to have available on the command line. Here you can see
the output of the music-now-playing
alias function:
❯ music-now-playing
Now playing:
Name: Symphony No. 2 in D Major, Op. 36: III. Scherzo. Allegro
Artist: London Symphony Orchestra & Josef Krips
Album: Beethoven: The Complete Symphony Collection
Track number: 7
Disc number: 1
Duration: 3:32
Play count: 2
Rating: 0
Loved: no
Genre: Classical
Year: 1960
Artwork file: (not present)
-
AppleScript is a scripting language created by Apple Inc. that facilitates automated control over scriptable Mac applications. First introduced in System 7, it is currently included in all versions of macOS as part of a package of system automation tools. The term “AppleScript” may refer to the language itself, to an individual script written in the language, or, informally, to the macOS Open Scripting Architecture that underlies the language. – https://en.wikipedia.org/wiki/AppleScript ↩