Last active
August 5, 2025 20:26
-
-
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`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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