Is there a way to use "Variable Value Did Change" with some of the internal vars, like "BTTNowPlayingInfoTitle" for example? I've tried with only "BTTNowPlayingInfoTitle" as Variable name, also with "{BTTNowPlayingInfoTitle}" but it does not work. The "workaround" is to assign BTTNowPlayingInfoTitle to a persistent variable and the I can use this new var with "Variable Value Did Change".
The use-case is to detect any change, no matter what was previous value.
Hey @Andreas_Hegenberg, I'm using the custom persistent variable atm, but since the script which is updating it is being executed every 8 sec, this causes an automation (under "Automations & Named & Other Triggers"), which is depended on this var, to be executed every 8 sec, which I think should not happen since the variable value has not changed during these 8 seconds. Is this correct?
you mean the variable doesn't change but the variable did change trigger is still executed? That would be weird, there is a check for whether the variable value is equal to the new one before triggering a variable did change trigger
A script executed every 8 sec in a floating menu is doing: set_persistent_string_variable "custom_var_menubarx_url" to songTitle, which is working fine.
Under "Automations & Named & Other Triggers", I have:
ah true, when updating via script, it didn't check for equality so currently your script would need to check that. I have changed this in 4.953 alpha (uploading), it should now be the same for any way to set a variable.
I think 4.952 aplha, broke something related to the playing variables, since for example the following script no longer works (script result is: "script completed without result"):
on itemScript(itemUUID)
tell application "BetterTouchTool"
set playing_app to get_string_variable "BTTCurrentlyPlayingApp"
end tell
return playing_app
end itemScript
Hey @Andreas_Hegenberg, trigger works (I can see it in "Recently used"), but the action "Run Real JavaScript" it seems it is not being executed. This javascript should set_persistent_string_variable with name view_count, which however is not being set/updated. The trigger for this automation is if "BTTNowPlayingInfoTitle" value did change.
If I run the script manually (from BTT) it is working.
The script is javascript + applescript which is checking tabs in safari, the weird thing is that if I bring safari as active window/app the javascript is being executed (trigger also is shown in "recently used", when safari becomes active like BTTNowPlayingInfoTitle is changed which is not true).
I hope that I was able to explain it somewhat understandable.
weird, can you post the script or does it contain sensitive data?
It sounds a bit like there is some sort of exception when it's executed while Safari is not active
async function itemScript(itemUUID) {
// AppleScript to get the matching URL from Safari
const appleScript = `
set matchingURL to ""
tell application "Safari"
repeat with w in windows
repeat with t in tabs of w
set tabName to name of t
if tabName ends with " - YouTube" then
set tabName to text 1 thru -11 of tabName
end if
tell application "BetterTouchTool"
set nowPlayingTitle to get_string_variable "BTTNowPlayingInfoTitle"
end tell
if tabName is equal to nowPlayingTitle then
set matchingURL to URL of t
exit repeat
end if
end repeat
if matchingURL is not "" then exit repeat
end repeat
end tell
return matchingURL
`;
// Execute the AppleScript and get the URL
const scriptCommand = `osascript -e '${appleScript.replace(/'/g, "'\\''")}'`;
const matchingURL = await runShellScript({ script: scriptCommand });
// Proceed with the rest of your logic using the matching URL
const baseUrl = matchingURL.match(/(https:\/\/www\.youtube\.com\/watch\?v=[\w-]+)/)[0];
const command = `curl -s ${baseUrl} | /opt/homebrew/bin/ggrep -o -P "(?<=shortViewCount\\\":\\\{\\\"simpleText\\\":\\\").+?(?=\\\"\\\},\\\"originalViewCount)";`;
try {
// Execute the shell command
let viewCount = await runShellScript({ script: command });
if (!viewCount) {
// Define another command to run if viewCount is empty
const fallbackCommand = `curl -s ${baseUrl} | /opt/homebrew/bin/ggrep -o -P "(?<=extraShortViewCount\\\":\\\{\\\"simpleText\\\":\\\").+?(?=\\\"\\\},\\\"entityKey)";`;
// Execute the fallback command
viewCount = await runShellScript({ script: fallbackCommand });
}
viewCount = viewCount.trim(); // Clean up the result
// Set the variable to store the view count. This is used then in floating menu ViewCount and script:
// /Users/myuser/Library/CloudStorage/OneDrive/Documents/btt/scripts/youtube_views_message.js
await set_persistent_string_variable({ variableName: "view_count", to: viewCount });
// Retrieve the stored view count
const retrievedViewCount = await get_string_variable({ variableName: "view_count" });
// Return the retrieved view count
return retrievedViewCount;
} catch (error) {
console.error("Error:", error);
return "Error";
}
}
I'm aware that it is not the best approach, also I've tried to used JXA, but without success, so I ended up with this.
btw, if I change songs fast enough (meaning that BTTNowPlayingInfoTitle is changing as well, which should trigger the javascrip action), it is working even if safari is not active.
Play/Pause (executed with almost no delay) is also triggering the automation and the script.
I have't tried a lot, but maybe try this (two less indirections by getting the variable via javascript, and replacing the runShellScript with runAppleScript. The path from BTT JS => Apple Script => BTT Apple Script Function => BTT JS => Shell Script is super complex
async function itemScript(itemUUID) {
// AppleScript to get the matching URL from Safari
let nowPlayingTitle = await get_string_variable({variableName: "BTTNowPlayingInfoTitle"});
const appleScript = `
set matchingURL to ""
tell application "Safari"
repeat with w in windows
repeat with t in tabs of w
set tabName to name of t
if tabName ends with " - YouTube" then
set tabName to text 1 thru -11 of tabName
end if
if tabName is equal to "${nowPlayingTitle}" then
set matchingURL to URL of t
exit repeat
end if
end repeat
if matchingURL is not "" then exit repeat
end repeat
end tell
return matchingURL
`;
// Execute the AppleScript and get the URL
let matchingURL = await runAppleScript(appleScript);
if(!matchingURL) {return "no url";}
// Proceed with the rest of your logic using the matching URL
const baseUrl = matchingURL.match(/(https:\/\/www\.youtube\.com\/watch\?v=[\w-]+)/)[0];
const command = `curl -s ${baseUrl} | /opt/homebrew/bin/ggrep -o -P "(?<=shortViewCount\\\":\\\{\\\"simpleText\\\":\\\").+?(?=\\\"\\\},\\\"originalViewCount)";`;
try {
// Execute the shell command
let viewCount = await runShellScript({ script: command });
if (!viewCount) {
// Define another command to run if viewCount is empty
const fallbackCommand = `curl -s ${baseUrl} | /opt/homebrew/bin/ggrep -o -P "(?<=extraShortViewCount\\\":\\\{\\\"simpleText\\\":\\\").+?(?=\\\"\\\},\\\"entityKey)";`;
// Execute the fallback command
viewCount = await runShellScript({ script: fallbackCommand });
}
if (!viewCount) {
throw new Error("xx");
}
viewCount = viewCount.trim(); // Clean up the result
// Set the variable to store the view count (stubbed as a placeholder function)
await set_persistent_string_variable({ variableName: "view_count", to: viewCount });
// Retrieve the stored view count (stubbed as a placeholder function)
const retrievedViewCount = await get_string_variable({ variableName: "view_count" });
// Return the retrieved view count
return retrievedViewCount;
} catch (error) {
console.error("Error:", error);
return "Error" + error;
}
}
maybe looking at the logs can help. If you quit BTT and call this terminal command BTT will write every scripting function call (e.g. set_persistent_string_variable) to the logs in ~/Library/Application Support/BetterTouchTool/Logs