iTunes -- making a script for copying album play counts - applescript

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.

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

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.

print multiple copies of a document Applescripting a printer's app

I'm trying to automate some hard printing.
I already can print one copy of a document accessing the printer's app like this :
tell application "hp LaserJet 2300 series (BDB806)" --my printer's app, could be any other model
print myFile
end tell
This snippet works.
But it becomes tricky when I try to print multiple copies. According to the printer's app dictionnary, I should be able to achieve it by doing :
tell application "hp LaserJet 2300 series (BDB806)"
print myFile with properties {copies:n} -- n being an integer
end tell
But this doesn't work.
Please let me know what I'm doing wrong.
Happens the same to me with this code:
set d to ((path to home folder) as string) & "Desktop:" & "untitled.txt"
set n to 3
tell application "EPSON WP-4595 Series"
print d with properties {copies:n}
end tell
Only one copy is printed.
As far as I know nothing is wrong with the code, maybe something is wrong with AppleScript..
Not very elegant but you could always do
...
repeat n times
print d
end repeat

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