Moved from macOs 15.8.1 to 27.0.1 and now my osascript runs dog slow

Years ago I used the Last.fm website to keep track of the music I played. It was great because it kept track of music that I played from iTunes and when I connected my iPhone to my computer. The Last.fm software could determine which songs I played on my phone and the software would update the last.fm website.

I don't using streaming sites because I don't believe in paying to rent music. I have all my music on my phone and I want to keep track of what I played each month and hence each year.

When iTunes went away my iPhone was now mounted under Finder. The Last.fm software could no longer see what songs I played on my phone. So I looked into using osascript to read the Music app and write out each song to a flat file. I would do this after connecting my phone and after playing songs from Music (on the actual computer). I would then do a diff of the two database files to create a counts file. (This is a pretty brute force way of doing things. I get that.) The thing it it took less than 2 minutes to accomplish so I was satisfied with it. A typical flat file resulted over 20,000 lines. Now that I have moved to macOs 27 this is taking too long (less than 2 minutes to hours).

So, could some one help and see how I could improve this script, I would appreciate it.

Also, I am open with getting the songs I played another way if it is simpler.

#!/usr/bin/osascript

set p to "./"
set a to POSIX file p
set filepath to (a as «class utf8») & "out.txt"
set openfile to open for access file filepath with write permission

tell application "Music"
    set allTracks to tracks
    repeat with index from 1 to the count of allTracks
        set theTrack to item index of allTracks
        get {artist, album, name, played date, played count, time, media kind} of theTrack
        set theArtist to artist of theTrack
        if (theArtist is not "NPR" and theArtist is not "") and (theArtist is not "MusicRadar.com") then
            set theAlbum to album of theTrack
            if (theAlbum is not "KEXP Video of the Week") then
                set mediakind to media kind of theTrack
                if mediakind as text is "song" then
                    set theArtist to my makePretty(theArtist)
                    set theName to name of theTrack
                    set lastPlayDate to played date of theTrack
                    set thePlayCount to played count of theTrack
                    set theTime to time of theTrack
                    set theLine to theArtist & " -;- " & theAlbum & " -;- " & theName & " -;- " & lastPlayDate & " -;- " & thePlayCount & " -;- " & theTime
                    write theLine & "
" to openfile
                end if
            end if
        end if
    end repeat
end tell
close access openfile

on makePretty(theArtist)
    set myName to theArtist
    if myName is "some artist" then
        set myName to "some new artist"
    end if
    return myName
end makePretty

Answered by DTS Engineer in 912149022

Thanks for including the whole script. It made this easy to measure.

Your export-and-diff approach doesn't need to change. Nearly all of the time goes to Apple events. Your script sends many Apple events to Music for every track. The get {...} line sends one event per property, seven in all, and the script never uses its result. Each property you read afterward sends another event. That's up to 14 events per track, or roughly 280,000 for a 20,000-track library. On my Mac with macOS 27.0.1, each Apple event took about 17 ms, whether it went to Music, Finder, or System Events. At that rate the events alone take over an hour.

Asking Music for each property of every track at once needs only one event per property. This version does that, then runs the same filters and writes the same lines without sending any more events:

#!/usr/bin/osascript

set p to "./"
set a to POSIX file p
set filepath to (a as «class utf8») & "out.txt"
set openfile to open for access file filepath with write permission

-- One Apple event per property for the whole library, instead of several per track.
tell application "Music"
    set {theArtists, theAlbums, theNames, theDates, theCounts, theTimes, theKinds} to ¬
        {artist, album, name, played date, played count, time, media kind} of every track
end tell

-- Everything below runs inside the script, without sending Apple events.
repeat with i from 1 to count of theNames
    set theArtist to item i of theArtists
    if (theArtist is not "NPR" and theArtist is not "") and (theArtist is not "MusicRadar.com") then
        set theAlbum to item i of theAlbums
        if (theAlbum is not "KEXP Video of the Week") then
            set mediakind to item i of theKinds
            if mediakind as text is "song" then
                set theArtist to makePretty(theArtist)
                set theLine to theArtist & " -;- " & theAlbum & " -;- " & (item i of theNames) & " -;- " & (item i of theDates) & " -;- " & (item i of theCounts) & " -;- " & (item i of theTimes)
                write theLine & linefeed to openfile
            end if
        end if
    end if
end repeat
close access openfile

on makePretty(theArtist)
    set myName to theArtist
    if myName is "some artist" then
        set myName to "some new artist"
    end if
    return myName
end makePretty

On my library, your original took 9.4 seconds and this version took 0.2 seconds, and the two out.txt files were byte-for-byte identical. Tracks that were never played come back with missing value for played date. The output shows "missing value" for them, the same as your original. My library is much smaller than yours, so I haven't measured the bulk request with 20,000 tracks.

If the per-event time matters to other scripts you run, please consider filing a bug report about the change from macOS 15.8.1. Include your timings from both releases. Bug Reporting: How and Why? has tips on creating your bug report.

Thanks for including the whole script. It made this easy to measure.

Your export-and-diff approach doesn't need to change. Nearly all of the time goes to Apple events. Your script sends many Apple events to Music for every track. The get {...} line sends one event per property, seven in all, and the script never uses its result. Each property you read afterward sends another event. That's up to 14 events per track, or roughly 280,000 for a 20,000-track library. On my Mac with macOS 27.0.1, each Apple event took about 17 ms, whether it went to Music, Finder, or System Events. At that rate the events alone take over an hour.

Asking Music for each property of every track at once needs only one event per property. This version does that, then runs the same filters and writes the same lines without sending any more events:

#!/usr/bin/osascript

set p to "./"
set a to POSIX file p
set filepath to (a as «class utf8») & "out.txt"
set openfile to open for access file filepath with write permission

-- One Apple event per property for the whole library, instead of several per track.
tell application "Music"
    set {theArtists, theAlbums, theNames, theDates, theCounts, theTimes, theKinds} to ¬
        {artist, album, name, played date, played count, time, media kind} of every track
end tell

-- Everything below runs inside the script, without sending Apple events.
repeat with i from 1 to count of theNames
    set theArtist to item i of theArtists
    if (theArtist is not "NPR" and theArtist is not "") and (theArtist is not "MusicRadar.com") then
        set theAlbum to item i of theAlbums
        if (theAlbum is not "KEXP Video of the Week") then
            set mediakind to item i of theKinds
            if mediakind as text is "song" then
                set theArtist to makePretty(theArtist)
                set theLine to theArtist & " -;- " & theAlbum & " -;- " & (item i of theNames) & " -;- " & (item i of theDates) & " -;- " & (item i of theCounts) & " -;- " & (item i of theTimes)
                write theLine & linefeed to openfile
            end if
        end if
    end if
end repeat
close access openfile

on makePretty(theArtist)
    set myName to theArtist
    if myName is "some artist" then
        set myName to "some new artist"
    end if
    return myName
end makePretty

On my library, your original took 9.4 seconds and this version took 0.2 seconds, and the two out.txt files were byte-for-byte identical. Tracks that were never played come back with missing value for played date. The output shows "missing value" for them, the same as your original. My library is much smaller than yours, so I haven't measured the bulk request with 20,000 tracks.

If the per-event time matters to other scripts you run, please consider filing a bug report about the change from macOS 15.8.1. Include your timings from both releases. Bug Reporting: How and Why? has tips on creating your bug report.

Thank you so much. My run time of the script went from a minute 40 something to just over a minute for almost 30,000 entries in Music.