BTT cannot detect the media currently playing

Thank you for this Andreas! I'm testing this and so far it is working great. I just have the following questions:

  1. Is it expected BTTCurrentlyPlaying to show 0, when in reality media is playing
  2. BTTCurrentlyPlayingApp is empty
  3. BTTNowPlayingInfoAlbum is empty

I see that there's new var BTTNowPlayingInfoSequoia which contains all of the info now in 1 var, but how to extract only isPlaying for example or appName? chatGPT tried with JXA, but it was only able to do it in the terminal not in BTT :smiley: .

// nowplaying.js
ObjC.import("stdlib");

const btt = Application("BetterTouchTool");
const json = btt.get_string_variable("BTTNowPlayingInfoSequoia");
const info = JSON.parse(json);

console.log("Title: " + info.title);
console.log("Artist: " + info.artist);
console.log("Is Playing: " + info.isPlaying);
console.log("Duration: " + info.duration + " seconds");

I've tried to replace console.log with return inside a function, but without success.

All info from that should be available via the standard variables, you shouldn't need to extract yourself. If that doesn't work, I'll need to check!
Which media app are you testing with?

I'm using browsers - Firefox and Safari

and the BTTNowPlayingInfoSequoia contains a album? (I thought browsers never set albums)

Ah but I see that I forgot to populate BTTCurrentlyPlayingApp and maybe also the playing state. Will fix now.

1 Like

No album, I've just said that BTTNowPlayingInfoAlbum it is empty.

but does BTTNowPlayingInfoSequoia contain an album or is it also empty there?

It is empty. Sorry if I was not clear in my explanation.

5.360 should fix the playing state and app. Unfortunately Apple's notarization service is broken right now, so I can't upload it at the moment.

Amazing, thank you for the quick turnaround.

I've just tested Version: 5.362 (2025050702) and it is again working as expected. Thank you for this!

I've just noticed the following:

BTTCurrentlyPlayingApp != BTTNowPlayingInfoSequoia.appName
BTTNowPlayingInfoDuration != BTTNowPlayingInfoSequoia.duration

Is this expected? I think BTTNowPlayingInfoSequoia.bundleIdentifier is also useful piece of info, but as the the name of BTTCurrentlyPlayingApp suggests I guess it should be == BTTNowPlayingInfoSequoia.appName

If you have few spear minutes can you let me know what is the easiest way to extract the data from BTTNowPlayingInfoSequoia - this is out of curiosity. I guess I can run some external script to get the data and then tell BTT to get the data, but I guess there's more elegant/intelligent way.

5.363 might resolve this (uploading).
(Problem is the process that is playing is com.apple.WebKit.GPU, but that's a sub-process of Safari in this case - however it's always named the same even for webviews in other apps.). I'm now returning the parent processes bundle identifier if available.

If you'd like to parse a JSON like in BTTNowPlayingInfoSequoia, you'd use JSON.parse like in your ChatGPT example, but you need to return it as a string, not log it. You could also use BTT's standard Java Script, that would be more performant than JXA, but if you want to use JXA do it like this:

const btt = Application("BetterTouchTool");
const json = btt.get_string_variable("BTTNowPlayingInfoSequoia");
const info = JSON.parse(json);

info.artist

or a concatenated string:

const btt = Application("BetterTouchTool");
const json = btt.get_string_variable("BTTNowPlayingInfoSequoia");
const info = JSON.parse(json);

`${info.artist} - ${info.title}`

Example in standard Java Script (not JXA):

async function someJavaScriptFunction() {
    let sequoia = await get_string_variable({variableName: "BTTNowPlayingInfoSequoia"});
	let json = JSON.parse(sequoia);
	return json.title;
}

A-M-A-Z-I-N-G!

Really appreciate this!

Let's hope this keeps working for a bit, but it's pretty uncertain ;-(
(at least in 15.5 RC it continues to work)

1 Like

So happy for this update, thanks @Andreas_Hegenberg !

Now, when I run this query:

osascript -e 'tell application "BetterTouchTool" to get_string_variable "BTTNowPlayingInfoSequoia"' | jq

I get this response:

{
  "isPlaying": false,
  "title": "Watch: OpenAI CEO Sam Altman, other executives give opening statements at Senate AI hearing",
  "artist": "CBS News",
  "album": "",
  "duration": 1211.021,
  "appName": "Google Chrome",
  "bundleIdentifier": "com.google.Chrome",
  "artworkIdentifier": "9affac1ae431f941"
}

First, my question:

What does artworkIdentifier refer to?

Now, my requests:

Would it be possible for the result to also include additional information? NOTE: I know that a lot has changed since macOS 15.4 but I'll ask anyway.

  1. current_timestamp – type: float – previously obtained using kMRMediaRemoteNowPlayingInfoTimestamp
  2. playbackRate – type: float – previously obtained using kMRMediaRemoteNowPlayingInfoPlaybackRate

Relevant link: GitHub - kirtan-shah/nowplaying-cli: macOS command-line utility for retrieving currently playing media

I could, however retrieving that info got way more expensive with 15.4, thus I would recommend to rely on BTT’s change detection and not call for manual updates too often. These variables you metioned sound quite dynamic and probably only make sense when updated continuously, correct?

The artwork identifier I don’t know yet, I had hoped I could somehow get the artwork using that but that’s something I haven’t figured out yet (for spotify and music I’ll soon provide the artwork in a variable)

the BTTNowPlayingInfoSequoia variable is only updated by BTT if it recognizes a track or pause-state change in the playing media

1 Like

I get the current timestamp using an automation like this:


The variable custom_var_test_title which BTT is "monitoring", in the 1st screenshot, is being set from a script running in a floating menu every X seconds, this means that the current timestamp will only be updated if new song started playing. I guess it is not ideal, but it gets the job done.

1 Like

that works - however unfortunately BTT can not detect the start of a new track immediately anymore on 15.4, it may take a few seconds for it to do so.

Yeah, few seconds differences are not an issue for me.

Sorry, haven't managed to get this working. Is there something aside definining a CAG with a condition BTTCurrentlyPlaying == "" and triggering this javascript?

async function someJavaScriptFunction() {
    let sequoia = await get_string_variable({variableName: "BTTNowPlayingInfoSequoia"});
	let json = JSON.parse(sequoia);
	return json.title;
}

Getting SyntaxError: JSON Parse error: Unexpected identifier "undefined".

if possible don’t query the „ BTTNowPlayingInfoSequoia“ directly, better use the standard variables like

BTTNowPlayingInfoTitle, BTTNowPlayingInfoArtist etc.. As soon as a number variable that starts with BTTNowPlaying is queried it will start the media observation

You could also do it like this

async function someJavaScriptFunction() {
    let startObservation = await get_number_variable({variableName: "BTTCurrentlyPlaying"});

    let sequoia = await get_string_variable({variableName: "BTTNowPlayingInfoSequoia"});
	let json = JSON.parse(sequoia);
	return json.title;
}

I'll add some more robust checks to automatically start the media observation with the next version.

//edit 5.382 (uploading) should also start the observation when querying only BTTNowPlayingInfoSequoia

1 Like