iTunes AppleScript: query all playlists that contain a certain track - macos

Working on a complex AppleScript for iTunes. One task is to accumulate a list of all playlists which contain a given track. I have this track object from somewhere else (a selection or whatever).
Currently, I've got a snippet something like this:
on containingPlaylists(theTrack)
tell application "iTunes"
set librarySource to the source named "Library"
set candidateLists to every user playlist in librarySource
set candidateId to (get id of theTrack)
set matchLists to {}
repeat with candidateList in candidateLists
set matchTracks to (file tracks in candidateList whose id = candidateId)
if (count of matchTracks) > 0 then
copy candidateList to end of matchLists
end if
end repeat
return matchLists
end tell
end containingPlaylists
This works but requires one Apple Event per playlist in the loop, which is expensive (perf) and throws away the intermediate results. What I'd RATHER do is something all in one query:
set matchLists to every playlist in librarySource whose file tracks contain theTrack
But this of course doesn't work (the particular error is "Handler only handles single objects." but not sure if that's insightful). I'm really just not sure if the language/app supports a query like this.
Can anyone confirm/deny/offer any insight? Thanks!

You can use this (work on iTunes 11 and 12):
tell application "iTunes"
set theTrack to item 1 of (get selection)
return user playlists of theTrack
end tell
Updated --
In the AppleScript dictionary:
artwork n [inh. item] : a piece of art within a track |
elements : contained by tracks. So artworks of thisTrack works
track n [inh. item] : playable audio source |
elements : contains artworks; contained by playlists. So playlists of thisTrack works, you can use user playlists of thisTrack
In iTunes.h (ObjC scripting bridge):
#interface iTunesTrack : iTunesItem
- (SBElementArray *) artworks;
it's not possible because playlists is not in the SBElementArray's list.
But I do not know why there is a difference between the AppleScript dictionary and the iTunes.h file.

I too wish a whose clause like that could be used. But alas. Someone else might come up with a better plan, but I'm pretty sure this is how I would find the playlists containing the selected track (it may be the most efficient):
set persisID to persistent ID of selection
set pp to playlists
set playListsWithIt to {}
repeat with p in pp
set tt to (tracks of p whose persistent ID is persisID)
if tt ≠ {} then set playListsWithIt to (playListsWithIt & (id of p))
end repeat
Then I can use those IDs for the next step. This includes, of course, playlists like "Recently Added", which may or may not be what you want; you'd have to put another step in there to 'filter' out such a result.

Related

How can I delete Apple Music tracks from my Library using AppleScript?

My Apple Music library is too big. I want to weed it out by removing a whole load of tracks that I have never listened to. I already did the same thing successfully with playlists but my script isn't working to remove tracks:
tell application "Music"
activate
set mytracks_list to (get the id of (every track whose loved is false and played count is 0 and rating is less than 60))
repeat with mytrack_id in mytracks_list
delete (the track whose id is mytrack_id)
end repeat
end tell
The mytracks_list is populated with no problems. The error message I get is:
error "Can’t get track whose id = item 1 of {130098, [............] }
Am I doing something wrong, and can it be made to work?
P.S. This is what worked for my playlists:
tell application "Music"
activate
set myplaylists_to_delete to (get the name of every playlist whose name does not contain "Adrian" and name does not contain "Loved" and name does not contain "Shazam" and name does not contain "Library" and name is not "Music" and name does not contain "Recent" and name does not contain "5 Stars" and name does not contain "Duo")
repeat with myplaylist in myplaylists_to_delete
delete playlist myplaylist
end repeat
end tell
Did you try:
tell app "Music"
delete every track whose loved is false and played count is 0 and rating is less than 60
end tell
Well-designed, well-implemented “AppleScriptable" apps can usually apply a command to multiple objects; you don’t need to iterate the objects yourself. (Hint: Apple event IPC = RPC + queries, not OOP.)

Getting last encoded file information from Apple Compressor

So I'm a novice with AppleScript but learning a lot. I'm trying to create an AppleScript to let me know when Apple Compressor is finished encoding and notify me if it was successful or not. I have the part down about telling when compressor is done and the notification but having difficulty getting the file name and status (complete/fail/cancelled) from Compressor's history log.
Originally tried with UI scripting, but Compressor's window hierarchy list changes with every new encode. So now onto pulling it from the history log, which is in the user folder "Library/Application Support/" folder like this:
And then, the file itself contains the status (whether it was successful or failed) near the bottom of the file like this:
So getting confused about how to A) find the last file and its name, and B) get the value of the last "ElementStatusState" in the document.
This is where I started but get errors when trying to resolve the path to the folder:
tell application "Finder"
set latestFile to (last item of (sort (get files in folder "~/Library/Application Support/Compressor/History/V4" of application "System Events") by name)) as text
set fileName to latestFile's name
end tell
This code throws an error about not being able to make it into the expected file type but also, I can see that It's grabbing a file that's not the last one modified.
In the end I want 2 variables that are 1) theFileName = ie name of encoded movie(s), and 2) theStatus = ie number 4,5, or 6)
Any ideas out there?
You'll want to do something like the script below. This script finds the most recently modified file, then drills down through the plist file to find the various status numbers, and the names of the records they are logged in. I'm assuming that every file Compressor generates has the same structure; if not, you may find you have to alter the script. But this should give you an idea how to do that.
Names and status values are stored in the nameList and valueList variables, in order of depth.
set filePath to POSIX file "~/Library/Application Support/Compressor/History/V4"
-- sort the file list by modification date, then get the last item.
tell application "Finder"
set mostRecentFile to last item of (sort files of folder filePath by modification date) as alias
end tell
set mostRecentFilePath to POSIX path of mostRecentFile
set nameList to {}
set valueList to {}
tell application "System Events"
set plistFile to property list file mostRecentFilePath
tell plistFile
tell (first property list item whose kind is record)
copy value of property list item "ElementInfoName" to end of nameList
copy value of property list item "ElementStatusState" to end of valueList
tell (first property list item whose kind is list)
tell (first property list item whose kind is record)
copy value of property list item "ElementInfoName" to end of nameList
copy value of property list item "ElementStatusState" to end of valueList
tell (first property list item whose kind is list)
tell (first property list item whose kind is record)
copy value of property list item "ElementInfoName" to end of nameList
copy value of property list item "ElementStatusState" to end of valueList
end tell
end tell
end tell
end tell
end tell
end tell
end tell

Any way to improve the performance of this Applescript iTunes Code?

I wrote a script that would identify duplicates in iTunes, find their instances in playlists, and other things which I don't think are relevant. In order to check if a track is a duplicate, I have the following code:
set duplicateSongs to ¬
get (every track of playlist 1 where ¬
(name is equal to (name of currentTrackToCheck as text) and ¬
database ID is not equal to (database ID of currentTrackToCheck as integer) and ¬
time is equal to (time of currentTrackToCheck as text)))
I wrote and tested this against a test library with 1,000 songs and it was speedy. When I tested it against my iTunes library which is about 30,000 songs, then all hell broke loose.
That snippet of code takes 2 minutes to finish processing! Is there any way to make this faster code-wise? I've been reading a lot on AppleScript and I believe using where/whose is the fastest way to filter down results from a query.
Thanks! :-)
It won't affect speed, but I would write it like this...
tell application "iTunes"
set {trackName, trackDatabaseID, trackTime} to {name, database ID, time} of current track
set duplicateSongs to (get every track of playlist 1 whose name = trackName and time = trackTime and database ID ≠ trackDatabaseID)
end tell
Just changed to using the search function and then doing a search within that and it takes less than a second - yay! :-)
on SearchForDuplicates(currentTrackToCheck, iTunesLibrary)
tell application "iTunes"
set duplicateSongs to {}
set preSearch to search iTunesLibrary for (name of currentTrackToCheck as text) only songs
repeat with aTrack in preSearch
tell aTrack
if (name is equal to (name of currentTrackToCheck as text) and ¬
database ID is not equal to (database ID of currentTrackToCheck as integer) and ¬
time is equal to (time of currentTrackToCheck as text)) then
set end of duplicateSongs to aTrack
end if
end tell
end repeat
return duplicateSongs
end tell
end SearchForDuplicates

iTunes -- making a script for copying album play counts

I'm trying to write a script which would let me copy the playcounts of one version of an album to another based on the title of the tracks.
Basically, scenario would be that I have the album on my computer along with all the playcounts. Then I rerip the original CD with higher quality (previously my quality preference was very low).
Now I want to automatically copy the playcounts of my old crappy quality rips to the new high quality ones.
I adopted a script from Doug Adams to try to do this, but when I try to run it it just gives me "A descriptor type mismatch occurred." without any indication as to the line where the problem is.
I've never used Apple Script before; does anybody know where the problem could be?
global thismany
tell application "iTunes"
if selection is not {} then
set sel to selection
repeat with t in sel
set t to contents of t
if class of t is file track or class of t is URL track then
if played count of t is 0 then
set thismany to 0
repeat with t2 in sel
set t2 to contents of t2
if class of t2 is file track or class of t2 is URL track then
if title of t is equal to title of t2 and t2 is not t then
set thismany to played count of t2
exit repeat
end if
end if
end repeat
set played count of t to (thismany as integer)
if (thismany as integer) is 0 then
try
set played date of t to missing value
end try
end if
end if
end if
end repeat
else -- no track selected
tell me to message_and_cancel("No tracks selected.")
end if
end tell
I figured this out, turns out the problem was that there is no such thing as a title of a track, the property is called the name of the track.
If anybody is interested, the full script can now be found here.

Error: Can't get some «class» of {«class», «class», ...}

I'm writing an Applescript for use in iTunes in which at some point I want to select any track from a list of tracks, but the way I expected it to work gives an error. Here's the code:
tell application "iTunes"
set thePlaylist to the first playlist whose name is "Missing track count"
-- ...
-- populate a list of strings: albumList
-- ...
repeat with albumName in the albumList
set theAlbum to (the tracks of thePlaylist whose album is albumName)
display dialog "Found " & (count theAlbum) & " tracks in the album"
set aTrack to some track of theAlbum -- ERROR OCCURS HERE
end repeat
end tell
The error I get when I execute the script from within iTunes is:
Can't get some «class cTrk» of {«class cFlT» id 16112 of «class cUsP» id 15982 of «class cSrc» id 65 of application "iTunes", ... etc}
Now, I don't really see why it doesn't work, although I guess it must have something to do with the fact that the items in theAlbum are file tracks from a user playlist from the source from the iTunes application instead of 'just' tracks. Can anyone help me out here?
In this example I use some item instead of some track, which works OK.
tell application "iTunes"
set thePlaylist to the first playlist
set x to (the tracks of thePlaylist)
set aTrack to some item in x
end tell
results in
URL track id 87 of library playlist id 82 of source id 64 of application "iTunes"
Since all the items in your example inherit from track, I don't know why it doesn't work, but it doesn't.
theAlbum is a list, not a playlist, so it doesn't have track elements; it only has items.
The documentation on lists, where it states "You can also refer to indexed list items by class." is incomplete and thus misleading. It seems you can only do this with to built-in classes. From what I can glean, here's why:
Object specifiers (2) are based on key-value coding. A specifier might identify a property (an object attribute or a to-one relationship) or element (a to-many relationship). In the example, we're dealing with elements. To handle elements, the underlying Objective-C class must implement a collection accessor pattern. That is, it must implement at least -<key>, or -countOf<Key> and -objectIn<Key>AtIndex: (it can, of course, implement all of them). The list class does this for a set number of Applescript classes (if you peeked at the ObjC source for the list class, you'd find methods like countOfApplication and -objectInNumberAtIndex:). It could conceivably support arbitrary element object specifiers with an appropriate -doesNotRecognizeSelector: handler, but lists don't appear to have been implemented this way. Since lists don't have -track, -countOfTrack or -objectInTrackAtIndex: handlers, they can't deal with a specifier such as "first track of trackList".

Resources