AppleScript - Get complete information of current iTunes track - macos

I'm trying to return the information of the current iTunes Track, which is playing on my computer.
I can get single informations like this:
tell application "iTunes"
get name of current track
end tell
This returns Thunderstruck for example.
But is it possible that I can get more information of the current track so it returns something like this:
"AC/DC, Thunderstruck, Iron Man 2"
Is this possible? Or do I have to create multiple scripts?
Thanks

To get name, artist and album
tell application "iTunes"
get {name, artist, album} of current track
end tell
The result is a list
To get everything
tell application "iTunes"
get properties of current track
end tell
The result is a record

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

Filling in gaps in itunes

I am new to appplescript and programming in general. would someone be so kind as to look over my code. I know its not in proper applescript syntax yet, as I struggled to find that information.
tell application iTunes
for each track in library playlist{ #all listed tracks, regardless of what files i have. may include dead links
set tr to track name
if file location is missing then search for it at external/Music/iTunes else messagebox(tr no file)
if search has result cut and paste to Music/itunes
check if file now exists else messagebox(tr error)
} end tell
I'll get you started. Here's how you can find the song name and artist of all tracks not found on your computer. You'll have to build on this to do the rest of your stuff.
set missingTracks to {}
tell application "iTunes"
set alltracks to tracks of library playlist 1
repeat with aTrack in alltracks
set theLocation to location of aTrack
set doesExist to my fileExists(theLocation)
if not doesExist then
set thisInfo to {songName:name of aTrack, songArtist:artist of aTrack}
set end of missingTracks to thisInfo
end if
end repeat
end tell
return missingTracks
on fileExists(theLocation)
tell application "Finder"
if exists theLocation then
return true
else
return false
end if
end tell
end fileExists

iTunes AppleScript: query all playlists that contain a certain track

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.

Getting song name of streaming track in iTunes with Applescript

How can I get the name of the song of current track in iTunes when this track is a radio?
I mean the string(s) that appears right below the radio name :)
Quering its name like below gives me the name of the radio (Trance Channel - DIGGITALLY IMPORTED - we can't define it!)but not the song
tell application "iTunes"
set thisTrack to current track
set trackName to the name of thisTrack
set trackTime to thisTrack's time
end tell
which is expected since the info in my library is:
but is there a way to specially deal this streaming tracks? and get their info correctly like iTunes does in the first picture? I know current track is a radio because its time will be missing value and not of the form MM:SS if that helps a bit.
Is this possible?
I looked in the applescript dictionary for iTunes and searched for "stream"...
tell application "iTunes"
current stream title
end tell
It appears that, in iTunes 12.2, a whole variety of interesting things are going on.
current stream title returns missing value when requesting the name of a stream coming from "For You" (e.g. something not in your current music library). name of the current track doesn't exist. For example, I'm listening to "Alternative Gems: 1994" from "For You" right now (Yay- grad school days) and I can't get any information about what is playing. If I go to the album the track is playing from to play something else, missing value and error -1728 on name of current track too.
When listening to Beats 1 radio as per #ivan above, I also get missing value but for name of the current track I get "Beats 1". As #dougscripts points out, the stream title stuff varies all over the map.
Listening to a radio station created via a "For You" seems to give me the correct name of the current track.
So, in short, chaos.
Not all streamers will format the stream data the same, so results from the current stream title property may not be consistent.
Much more hacking and I finally have found a way to get the data directly out from iTunes using the SDK.
This method will check the currentTrack like normal, but when it's detected that the artist is missing (a common understanding when the track is being streamed), we fall down to getting the values from the LCD display using values provided by Accessibility.
#!/usr/bin/env osascript -l JavaScript
/* globals Application */
function main () {
var itunes = new Application('iTunes')
var currentTrack = itunes.currentTrack
var output = {
name: currentTrack.name(),
artist: currentTrack.artist(),
position: itunes.playerPosition()
}
if (currentTrack.artist() === '') {
var app = new Application('System Events')
var itunesProcess = app.applicationProcesses.byName('iTunes')
// Get the text values from the first scrollable area which is the LCD Display
var arr = itunesProcess.windows[0].scrollAreas[0].staticTexts
output.name = arr[0].name()
// Clean up the artist name, as it may contain the Show Name.
output.artist = arr[2].name().replace(' — ' + currentTrack.name(), '')
}
return JSON.stringify(output, null, 2)
}
main()
Example output:
{
"name": "Wild Smooth (Gundam Radar Rip)",
"position": "34:06",
"artist": "MUBLA"
}
Be sure to chmod +x this script.
Note, it requires the calling application to be added to Accessibility Privacy
Another option - given the inconsistency of the output - might be to playback in VLC and query that instead - AppleScript support is pretty limited but at least the source code is available so you know what you can query.
osascript -e 'tell application "VLC" to get name of current item'
After weeks of tearing my hair out trying to figure this out myself.
I have managed to find a very hacky solution to this exact issue.
your not going to like it though, be warned.
In order to get the current playing track even if the audio is being streamed from Beats radio, you're going to have to consider a OCR approach (taking a screencapture, converting the image to text)
The following Ruby code will get you up-and-running with this solution.
It will check the current track, and if the artist field is blank, (which is the case when a track is streamed) then it fall's to the OCR method.
require 'json'
require 'rtesseract'
class CurrentTrack
def self.check
js_command = %Q{var itunes = Application("iTunes");
var currentTrack = itunes.currentTrack;
JSON.stringify({
window_bounds: itunes.windows[0].bounds(),
name: currentTrack.name(),
artist: currentTrack.artist(),
position: itunes.playerPosition()
})
}
command = "osascript -l JavaScript -e '#{js_command}'"
result = `#{command}`
json = JSON.parse(result, symbolize_names: true)
json[:position] = json[:position].to_i
json[:cue] = Time.at(json[:position]).utc.strftime('%H:%M:%S')
if json[:artist] == ''
sc_command = %Q{screencapture -R #{json[:window_bounds][:x]},#{json[:window_bounds][:y].to_i + 30},#{json[:window_bounds][:width]},#{json[:window_bounds][:height]} capture.png}
`#{sc_command}`
image = RTesseract.new("capture.png", processor: 'none')
ocr = image.to_s.split("\n") # Getting the value
unless ocr.first == 'Soulection'
json[:name] = ocr.first
json[:artist] = ocr[1].split(' — ').first
end
end
json.delete :window_bounds
json
end
end
You'll need to install rtesseract to get this working.
Caveats, this script requires the iTunes mini-player window to be visible somewhere on your desktop.

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