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
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.