Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

oddharsh

macrumors newbie
Original poster
Jul 2, 2021
4
0
Hey,

I'm looking to optimize this applescript, ideally with an idle handler, though I haven't been able to make that work all that well. Any help would be greatly appreciated. Thanks!
AppleScript:
local trackID
tell application "Spotify" to activate -- launches spotify

repeat while application "Spotify" is running -- the actual script portion
    try
        tell application "Spotify" to set trackID to id of current track -- grab track ID
        if (offset of "ad" in trackID) = 9 then relaunch() -- checks if the track is an ad and relaunches if it is
    end try
    delay 0.5 -- polls every .5 seconds. A more efficient alt would be to run this when the song changes, though that's going to be more complex.
end repeat

on relaunch() -- quit, then relaunch and play
    tell application "Spotify" to quit
    repeat until application "Spotify" is not running -- smarter alt to fixed delay
        delay 0.05
    end repeat
    tell application "Spotify"
        launch
        repeat until application "Spotify" is running -- smarter alt to fixed delay
            delay 0.05
        end repeat
        play
    end tell
end relaunch
 
Last edited:
For the idle handler, the script needs to be saved as a stay-open application. The app’s run handler will be executed, and when finished the idle handler will be repeatedly called, with the number it returns being the number of seconds before it is called again.
 
For the idle handler, the script needs to be saved as a stay-open application. The app’s run handler will be executed, and when finished the idle handler will be repeatedly called, with the number it returns being the number of seconds before it is called again.
Thanks for your response! This is what I have for the version with an idle and quit handler, but it still doesn't seem to quit in the same manner as a normal application.
AppleScript:
on run
    local trackID, playerState
    tell application "Spotify" to activate -- opens spotify
    repeat while application "Spotify" is running -- better open tester
        try
            tell application "Spotify" -- grabbing data from current track to id it
                set trackID to id of current track
                set playerState to (player state as string)
            end tell
            if playerState = "playing" then
                if (offset of "ad" in trackID) = 9 then relaunch()
                delay 0.5 -- polls every 0.5 while playing
            else
                idle
            end if
        end try
    end repeat
    quit
end run

on relaunch() -- quit, then relaunch and play
    tell application "Spotify" to quit
    repeat until application "Spotify" is not running -- smarter alt to fixed delay
        delay 0.05
    end repeat
    tell application "Spotify"
        launch
        repeat until application "Spotify" is running -- smarter alt to fixed delay
            delay 0.05
        end repeat
        play
    end tell
end relaunch

on idle --idle handler with a longer poll time for when spotify is paused
    return 5
    -- unsure about how to make it go back to the run handler when playerState = "playing"
end idle

on quit --WIP quit handler, need to figure out how to make this trigger on command + Q
    --tell application "Spotify" to quit
    continue quit
    return
end quit
 
In a stay-open application, the run handler is normally used to set stuff up, then after that is completed the idle handler will take over and continually repeat, so it doesn't look like you are letting it work. The system is what calls the idle handler (via a timer), and in the idle handler you would be performing tasks and setting a return value, which is what sets the time before it is run again.

Note that if you are performing a continual repeat statement, you will be blocking the user interface (menus, etc) by not allowing the system time to process events. I don't have Spotify, but if it has a notification when the player state changes, that might simplify things a bit.
 
ah okay so the idle handler would have the bit that gets looped over (and the 0.5s delay) rather than the 5s delay? In terms of the spotify user interface, I'm not sure what you mean by your second point, as the spotify ui functions normally when the script is running. The bit that doesn't work is quitting the script with command Q rather than having to force quit the script. I've somewhat worked around that by making the script terminate when spotify closes. My hope with the quit handler was to rectify this by letting the script app quit like a normal application (when I use the apple menu or command Q)
 
Here's my second go at the version with the idle handler, the only thing missing is the slower polling when spotify's paused. As you said, quitting in the normal sense seems to work perfectly now. Thank you!
AppleScript:
local trackID
tell application "Spotify" to activate -- launches spotify
on idle
    try
        tell application "Spotify" to set trackID to id of current track -- grab track ID
        if (offset of "ad" in trackID) = 9 then relaunch() -- checks if the track is an ad and relaunches if it is
    end try
    if application "Spotify" is not running then quit
    return 0.5 -- polls every .5 seconds.
end idle

on quit
    tell application "Spotify" to quit
    continue quit
    return
end quit

on relaunch() -- quit, then relaunch and play
    tell application "Spotify" to quit
    repeat until application "Spotify" is not running -- smarter alt to fixed delay
        delay 0.05
    end repeat
    tell application "Spotify"
        launch
        repeat until application "Spotify" is running -- smarter alt to fixed delay
            delay 0.05
        end repeat
        play
    end tell
end relaunch
 
The system needs an application to let it have a shot at handling events. If the app doesn’t do that (regardless of the programming language), events pile up in the queue and you get the spinning beach ball. Cocoa applications are typically written around background tasks and event loops, so they tend to stay responsive.

In contrast, a lot of AppleScript applications, especially if they are just wrappers around a script, aren’t written to be event-driven, so all it takes is something like a repeat loop to make the UI unresponsive. In your earlier script, the continual chain of repeat loops prevented the system from handling the menu or keypress to quit. Spotify would be running with its own event loop, so it wouldn’t necessarily be affected by the AppleScript application.

The idle handler is called by the system and uses a repeating timer rather than a delay, so in between runs it isn’t really doing anything. This gives the system time to do other application things like handle the user interface, so the idle handler is a cheap way to break things up. The value returned by the idle handler determines how long to wait until it is run again, so code in the handler can be used to change the time, for example from the state of the player.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.