Skip to content

Instantly share code, notes, and snippets.

@squatto
Last active August 5, 2025 20:26
Show Gist options
  • Save squatto/4af0f674829584484ee78956092fc872 to your computer and use it in GitHub Desktop.
Save squatto/4af0f674829584484ee78956092fc872 to your computer and use it in GitHub Desktop.
Listen for macOS "now playing" media change events and send notifications when they occur using either a BetterTouchTool trigger or `terminal-notifier`
#!/usr/bin/env bash
# Description: This script listens for macOS "now playing" media change events
# and sends notifications when they occur using either a BetterTouchTool trigger or terminal-notifier
#
# Requirements:
# - macOS
# - media-control - https://github.com/ungive/media-control
# - jq - https://jqlang.org/download/
# - If using a BetterTouchTool trigger:
# - BetterTouchTool - https://folivora.ai/
# - wget - https://www.gnu.org/software/wget/
# - If not using a BetterTouchTool trigger:
# - terminal-notifier - https://github.com/julienXX/terminal-notifier
#
# Optional:
# - base64 - for decoding artwork data - https://www.gnu.org/software/coreutils/
# - imgcat - for displaying artwork in the terminal - https://iterm2.com/documentation-images.html
#
# Usage: Run the script in the background to listen for media changes and send notifications.
#
# Author: Scott Carpenter - https://github.com/squatto
# License: MIT License
# Latest Version: https://gist.github.com/squatto/4af0f674829584484ee78956092fc872
########################################################################################################
# settings/variables
########################################################################################################
# use a BetterTouchTool trigger to show the notification when media changes?
# if false, the notification will be sent with the terminal-notifier command
use_btt_trigger=true
## IMPORTANT: If you are using a BetterTouchTool trigger, you MUST enable the webserver in BetterTouchTool
# - In BetterTouchTool, go to Settings > Web Server and enable the webserver
# - Do not enable the HTTPS option, as this script does not support it
# - You can also change the host, port, and shared secret, or use the defaults
## IMPORTANT: If you are using a BetterTouchTool trigger, you MUST set the following variables:
### BetterTouchTool webserver settings
#
# These are set with environment variables, or you can set them here directly by overriding the defaults.
#
# The BTT_WEBSERVER_HOST, BTT_WEBSERVER_PORT, and BTT_WEBSERVER_SHARED_SECRET environment variables
# can be set in your shell environment or in your .bash_profile, .zshrc, etc.
btt_webserver_host=${BTT_WEBSERVER_HOST:-"127.0.0.1"}
btt_webserver_port=${BTT_WEBSERVER_PORT:-12345}
btt_webserver_shared_secret=${BTT_WEBSERVER_SHARED_SECRET:-""}
### UUID for BetterTouchTool trigger to run when media changes
#
# You can find the UUID in BetterTouchTool by right-clicking the trigger and selecting "Copy Selected Item UUID".
#
# The BTT_NOW_PLAYING_TRIGGER_UUID environment variable
# can be set in your shell environment or in your .bash_profile, .zshrc, etc.
btt_trigger_uuid=${BTT_NOW_PLAYING_TRIGGER_UUID:-""}
########################################################################################################
# check requirements
########################################################################################################
if [[ "$(uname)" != "Darwin" ]]; then
echo "⚠️ ERROR: This script is designed to run on macOS only!"
exit 1
fi
if [ "$use_btt_trigger" = true ]; then
if ! [ -d "/Applications/BetterTouchTool.app" ] && ! brew ls --cask bettertouchtool &> /dev/null; then
echo "⚠️ ERROR: BetterTouchTool is not installed!"
echo "- Install: brew install bettertouchtool"
echo "- More info: https://folivora.ai/"
exit 1
fi
if ! [ -x "$(command -v wget)" ]; then
echo "⚠️ ERROR: wget is not installed!"
echo "- Install: brew install wget"
echo "- More info: https://www.gnu.org/software/wget/"
exit 1
fi
if [ -z "$btt_trigger_uuid" ]; then
echo "⚠️ ERROR: BetterTouchTool trigger UUID is not set!"
echo "- Option 1: Set the BTT_NOW_PLAYING_TRIGGER_UUID environment variable to the UUID of your BetterTouchTool trigger."
echo "- Option 2: Set the btt_trigger_uuid variable in the script to the UUID of your BetterTouchTool trigger."
echo "- You can find the UUID in BetterTouchTool by right-clicking the trigger and selecting 'Copy Selected Item UUID'."
exit 1
fi
else
if ! [ -x "$(command -v terminal-notifier)" ]; then
echo "⚠️ ERROR: terminal-notifier is not installed!"
echo "- Install: brew install terminal-notifier"
echo "- More info: https://github.com/julienXX/terminal-notifier"
exit 1
fi
fi
if ! [ -x "$(command -v jq)" ]; then
echo "⚠️ ERROR: jq is not installed!"
echo "- Install: brew install jq"
echo "- More info: https://jqlang.org/download/"
exit 1
fi
if ! [ -x "$(command -v media-control)" ]; then
echo "⚠️ ERROR: media-control is not installed!"
echo "- brew tap ungive/media-control && brew install media-control"
echo "- More info: https://github.com/ungive/media-control"
exit 1
fi
# check if base64 and imgcat are available for displaying artwork
has_base64=$(command -v base64)
has_imgcat=$(command -v imgcat)
########################################################################################################
# watch for media changes
########################################################################################################
if [ -n "$btt_webserver_shared_secret" ]; then
shared_secret_param="&shared_secret=${btt_webserver_shared_secret}"
else
shared_secret_param=""
fi
echo "🔊 Listening for media changes..."
echo "Press Ctrl+C to stop."
echo
while read -r line; do
if jq -e ".diff == false" <<< "$line" > /dev/null; then
artist=$(jq -r .payload.artist <<< "$line" | awk '{$1=$1;print}')
title=$(jq -r .payload.title <<< "$line" | awk '{$1=$1;print}')
if [ -n "$title" ] && [ "$title" != "null" ]; then
echo "---------------------------------------------------------------------------------------------------------"
echo
echo -e "\033[2m[$(date +'%m/%d/%Y %I:%M:%S%p')]\033[0m 🔊 \033[4m\033[1mNow Playing:\033[0m \033[1m$artist\033[0m – $title"
if [ "$use_btt_trigger" = true ]; then
# send the notification using a BetterTouchTool trigger
# encode the artist and title for URL usage
encoded_artist=$(echo -n "$artist" | jq -Rr @uri)
encoded_title=$(echo -n "$title" | jq -Rr @uri)
# set the "now playing" variables in BetterTouchTool webserver
wget -qO- "http://${btt_webserver_host}:${btt_webserver_port}/set_string_variable/?variable_name=now_playing_title&to=${encoded_title}${shared_secret_param}" &> /dev/null
wget -qO- "http://${btt_webserver_host}:${btt_webserver_port}/set_string_variable/?variable_name=now_playing_artist&to=${encoded_artist}${shared_secret_param}" &> /dev/null
# trigger the BetterTouchTool action
wget -qO- "http://${btt_webserver_host}:${btt_webserver_port}/execute_assigned_actions_for_trigger/?uuid=${btt_trigger_uuid}${shared_secret_param}" &> /dev/null
else
# send the notification using terminal-notifier
# get the bundle identifier of the currently playing app
# this is useful for macOS notifications to show the correct app icon
bundleId=$(jq -r .payload.bundleIdentifier <<< "$line")
terminal-notifier \
-group now-playing \
-title "Now Playing" \
-subtitle "$artist" \
-message "$title" \
-sender "$bundleId"
fi
fi
fi
# if they have base64 and imgcat, and artwork data is available, display it using imgcat
if [ -n "$has_base64" ] && [ -n "$has_imgcat" ] && jq -e .payload.artworkData <<< "$line" >/dev/null; then
echo
jq -r .payload.artworkData <<< "$line" | base64 -d | imgcat
echo
fi
done < <(media-control stream --debounce=1000)
########################################################################################################
# Recommended BetterTouchTool Trigger Setup
#
# If you want to use a BetterTouchTool trigger, follow the instructions below to set it up.
#
# Once you have created the trigger in BetterTouchTool, copy the UUID of the trigger
# and set it in the btt_trigger_uuid variable above.
# You can copy the UUID in BetterTouchTool by right-clicking the trigger and selecting "Copy Selected Item UUID".
########################################################################################################
## PREFERRED METHOD: Copy and paste the following JSON directly into BetterTouchTool to create the trigger automatically
: '
[
{
"BTTActionCategory" : 0,
"BTTTriggerType" : 643,
"BTTTriggerTypeDescriptionReadOnly" : "Named Trigger: Show Now Playing HUD",
"BTTTriggerClass" : "BTTTriggerTypeOtherTriggers",
"BTTPredefinedActionType" : 366,
"BTTPredefinedActionName" : "Empty Placeholder",
"BTTTriggerName" : "Show Now Playing HUD",
"BTTAdditionalActions" : [
{
"BTTActionCategory" : 0,
"BTTIsPureAction" : true,
"BTTTriggerClass" : "BTTTriggerTypeOtherTriggers",
"BTTPredefinedActionType" : 254,
"BTTPredefinedActionName" : "Show HUD Overlay",
"BTTHUDActionConfiguration" : "{\"BTTActionHUDBlur\":true,\"BTTActionHUDBackground\":null,\"BTTIconConfigImageHeight\":100,\"BTTActionHUDPosition\":4,\"BTTActionHUDDetail\":\"\",\"BTTActionHUDDuration\":5,\"BTTActionHUDPadding\":5,\"BTTActionHUDDisplayToUse\":0,\"BTTIconConfigImageWidth\":100,\"BTTActionHUDSlideDirection\":2,\"BTTActionHUDHideWhenOtherHUDAppears\":true,\"BTTActionHUDAttributedTitle\":\"{\\\\rtf1\\\\ansi\\\\ansicpg1252\\\\cocoartf2822\\n\\\\cocoatextscaling0\\\\cocoaplatform0{\\\\fonttbl\\\\f0\\\\fnil\\\\fcharset0 SFPro-Bold;\\\\f1\\\\fnil\\\\fcharset0 SFPro-Regular;}\\n{\\\\colortbl;\\\\red255\\\\green255\\\\blue255;\\\\red255\\\\green255\\\\blue255;}\\n{\\\\*\\\\expandedcolortbl;;\\\\csgray\\\\c100000;}\\n\\\\pard\\\\tx560\\\\tx1120\\\\tx1680\\\\tx2240\\\\tx2800\\\\tx3360\\\\tx3920\\\\tx4480\\\\tx5040\\\\tx5600\\\\tx6160\\\\tx6720\\\\pardirnatural\\\\qc\\\\partightenfactor0\\n\\n\\\\f0\\\\b\\\\fs40 \\\\cf2 \\\\uc0\\\\u55357 \\\\u56586 \\\\{now_playing_artist\\\\}\\\\\\n\\\\pard\\\\tx560\\\\tx1120\\\\tx1680\\\\tx2240\\\\tx2800\\\\tx3360\\\\tx3920\\\\tx4480\\\\tx5040\\\\tx5600\\\\tx6160\\\\tx6720\\\\pardirnatural\\\\qc\\\\partightenfactor0\\n\\n\\\\f1\\\\b0\\\\fs36 \\\\cf2 \\\\{now_playing_title\\\\}}\",\"BTTActionHUDWidth\":650,\"BTTActionHUDBorderWidth\":0,\"BTTActionHUDTitle\":\"\",\"BTTActionHUDHeight\":110}"
}
]
}
]
'
## ALTERNATIVE METHOD: Follow the instructions below to create the trigger manually in BetterTouchTool
#
# Create a new trigger in BetterTouchTool with the following settings:
# * Trigger Section: "Automations & Named & Other Triggers"
# * Trigger Type: "Reusable Named Trigger"
# * Trigger Name: "Show Now Playing HUD"
# * Action: "Show HUD Overlay" with settings:
# - Message Contents: Format however you would like, making sure to include the following variables:
# {now_playing_artist}
# {now_playing_title}
# - Message Font Size: 20
# - Message Font Color: White or #FFFFFF (255,255,255 RGB)
# - Example: "🔊 {now_playing_artist} - {now_playing_title}"
# - HUD Overlay Duration: 5 seconds
# - Enable "Hide immediately after another HUD is being presented." option
# - Display On: Show On All Screens
# - Animation: Slide Down
# - Position: Bottom Right
# - HUD Width: 650
# - HUD Height: 110
# - Padding: 5
# - Background Color: Black/White preset
# - Enable Option: "Mimic macOS default HUD background style"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment