How can I use Applescript to remove any songs from myPlaylist if they contain one of the names + artists + years contained in a tab-separated file?
So if track one of myPlaylist is:
'Greensleeves The Scorpions 1965'
and the tab-separated text file contains the line:
Greensleeves The Scorpions 1965
It will delete the track from the playlist. Also, it needs to be the exact title, because some of my song titles have brackets and odd characters in them.
Thanks!
use application "iTunes"
use scripting additions
--------------------------------------------------------------------------------
###USER-DEFINED PROPERTIES: path, playlist
property path : "~/Desktop/trackdelete.list"
property playlist : "myPlaylist"
--------------------------------------------------------------------------------
property text item delimiters : tab
--------------------------------------------------------------------------------
###IMPLEMENTATION
#
#
tell the deleteList
if not (its file exists) then return -1
read
repeat with i from 1 to the length of its list
set [its name, its artist, its year] to ¬
[text item 1, text item 2, text item 3] of ¬
item i of its list
delete (playlistItem's track where ¬
name = deleteList's name and ¬
artist = deleteList's artist and ¬
year = deleteList's year)
end repeat
end tell
--------------------------------------------------------------------------------
###SCRIPT OBJECTS & HANDLERS
#
#
script playlistItem
property playlist : a reference to the playlist named (my playlist)
property track : a reference to every track of my playlist
end script
script deleteList
property application : application "System Events"
property file : a reference to file (my path) of my application
property list : null
property name : null
property artist : null
property year : null
to read
tell AppleScript to read (my file as alias)
set my list to the result's paragraphs
end read
end script
---------------------------------------------------------------------------❮END❯
System info: AppleScript version: "2.7", system version: "10.13.6"
Related
I get the syntax error below with this script. It fails in the line beginning repeat with cell in rows. How can I get the script to compile?
Expected variable name or property but found class name.
-- Declare variables
property albumDescription : ""
-- Display input box to get album description
display dialog "Enter the album description:" default answer ""
set albumDescription to text returned of result
-- Open Excel
tell application "Microsoft Excel"
activate
end tell
-- Set variables for the active sheet and range of cells
tell application "Microsoft Excel"
set sh to active sheet
set rng to range of sh
end tell
-- Loop through each cell in the range, starting from row 2
repeat with cell in rows 2 thru (count of rows of rng) of rng
-- Check if the cell value is not empty
if value of cell is not "" then
-- Update the album description for the row
set value of cell to albumDescription
end if
end repeat
Actually there are three issues:
All terminology which belongs to a specific application (in this case rows, the content of rng and value) must be inside a tell application block.
You cannot use the reserved word cell as a variable name. Well, you can, but you have to wrap the word in pipes (|cell|). However I recommend to use a different name like aCell
range in set rng to... must be used range
-- Declare variables
property albumDescription : ""
-- Display input box to get album description
display dialog "Enter the album description:" default answer ""
set albumDescription to text returned of result
-- Open Excel
activate application "Microsoft Excel"
-- Set variables for the active sheet and range of cells
tell application "Microsoft Excel"
set sh to active sheet
set rng to used range of sh
-- Loop through each cell in the range, starting from row 2
repeat with aCell in rows 2 thru (count of rows of rng) of rng
-- Check if the cell value is not empty
if value of aCell is not "" then
-- Update the album description for the row
set value of aCell to albumDescription
end if
end repeat
end tell
Could anyone offer a way to populate my playlist with songs from a csv file/text file formatted like this:
song title,artist?
I can do it for title alone but can't specify it must have a certain artist.
EDIT: Here is an example of how I'm getting them by titles:
set TheFile to read file "Macintosh HD:Applications:Automator stuff:01b iTunes Scripts:SongList.txt"
tell application "iTunes"
set thePlaylist to playlist "SongList"
try
delete every track of thePlaylist
end try
set MySongs to paragraphs of (TheFile) -- read artist names (separated by newlines) from the file
repeat with AnItem in MySongs -- get all tracks from each artist
set AnItem to (contents of AnItem)
if AnItem is not "" then try -- don't bother with empty names
set MyTracks to (location of file tracks of playlist "Music" whose name is AnItem)
--can also modify the above from "is" to "contains" or "_begins with_"
add MyTracks to thePlaylist
on error errmess -- oopsie (not found, etc)
log errmess -- just log it
end try
end repeat
end tell
OK, figured it out! Couldn't work out how to work around titles with commas in them (which I have a few of), so I ended up using tab separating them instead. So, once I have my tab-separated file, this code did the trick:
set thisTSVFile to (choose file with prompt "Select the CSV file")
readTabSeparatedValuesFile(thisTSVFile)
set theList to readTabSeparatedValuesFile(thisTSVFile)
tell application "iTunes"
set myPlaylist to playlist "Test1"
set sourcePlaylist to playlist "Music"
end tell
repeat with i from 2 to number of items in readTabSeparatedValuesFile(thisTSVFile)
--gets first column
set theName to item 1 of item i of theList
--gets second
set theArtist to item 2 of item i of theList
tell application "iTunes"
duplicate (some track of sourcePlaylist whose name is theName and artist is theArtist) to myPlaylist
end tell
delay 0.1
end repeat
on readTabSeparatedValuesFile(thisTSVFile)
try
set dataBlob to (every paragraph of (read thisTSVFile))
set the tableData to {}
set AppleScript's text item delimiters to tab
repeat with i from 1 to the count of dataBlob
set the end of the tableData to (every text item of (item i of dataBlob))
end repeat
set AppleScript's text item delimiters to ""
return tableData
on error errorMessage number errorNumber
set AppleScript's text item delimiters to ""
error errorMessage number errorNumber
end try
end readTabSeparatedValuesFile
You can use ObjectiveC (or probably Swift too) with XCode to do the heavy lifting (parsing the files) then hit iTunes from there, although it will likely be a lot slower than running in the iTunes process through its script menu.
Here's some ObjectiveC code that gets the current track title; you can adapt the method to suit a more complicated script like populating a playlist.
+(NSString *)getTitle {
return [self runAppleScriptAndReturnResult:#"Tell application \"iTunes\" \nreturn the name of the current track\nend tell"];
}
+(NSString *)runAppleScriptAndReturnResult:(NSString*)script {
NSAppleScript *appleScript=[[NSAppleScript alloc] initWithSource:[NSString stringWithFormat:#"with timeout of 3 seconds\n%#\nend timeout\n", script]];
return [[appleScript executeAndReturnError:nil] stringValue];
}
I've never used AppleScript before, so i'm quite unfamiliar with the language, but i'm doing my best.
Here's what i'm trying to accomplish:
Run a script while selecting a folder filled with .ARW and .JPG files. Iterate through the items in the folder. If the current item is .ARW, iterate through the folder starting from the beginning again. If this nested iteration lands on a file that has the same file name and a JPG extension, label the original ARW file with red.
TLDR: If an ARW file in a folder shares the same filename as a JPG file in the folder, highlight the ARW file in red, otherwise do nothing.
Here's the code i've written so far:
tell application "Finder"
set totalAlias to the entire contents of (selection as alias)
set totalCount to the count of items of totalAlias
set firstName to name of item 1 of totalAlias
set firstExtension to name extension of item 1 of totalAlias
set c to 1
repeat while c ≤ totalCount
set currentAlias to item c of totalAlias
set currentName to name of currentAlias
set currentExtension to name extension of currentAlias
if currentExtension is "ARW" then
set d to 1
set compareFile to currentAlias
set findName to currentName
set findExtension to currentExtension
repeat while d ≤ totalCount
if (name of item d of totalAlias = findName) and (name extension of item d of totalAlias is "JPG") then
tell application "Finder" to set label index of compareFile to 2
end if
set d to (d + 1)
end repeat
end if
set c to (c + 1)
end repeat
end tell
Any thoughts on what's going wrong? I believe it has to do with my IF AND condition.
Try this script:
tell application "Finder"
--Just get all the filenames of the target types:
set allJPG to the name of every file of (entire contents of (selection as alias)) whose name extension = "JPG"
set allARW to the name of every file of (entire contents of (selection as alias)) whose name extension = "ARW"
--Send the two lists to a handler to find all the common names
set targetJPGFiles to my CompareNames(allJPG, allARW)
--Loop through the common names, find the files, set the tags
repeat with eachTarget in targetJPGFiles
set fileToTag to (item 1 of (get every file of (entire contents of (selection as alias)) whose name is (eachTarget as text)))
set label index of fileToTag to 2
end repeat
end tell
targetJPGFiles -- This allows you to see the filenames that SHOULD have been tagged
to CompareNames(jp, pn)
--First, get rid of all the extensions in the ARW files
set cleanARWNames to {}
set neededJPGNames to {}
repeat with eachARWName in pn
set end of cleanARWNames to characters 1 thru -5 of (eachARWName as text) as text
end repeat
--Now, loop through JPG names to find a match
repeat with eachjpgName in jp
set searchName to characters 1 thru -5 of (eachjpgName as text) as text
if cleanARWNames contains searchName then
set end of neededJPGNames to (eachjpgName as text)
end if
end repeat
return neededJPGNames
end CompareNames
It takes a slightly different approach, in that it just compares two lists of filenames only, then goes back, finds the files with the names you want, and does the tagging.
It is based on a script I wrote for another project, so I hope it works for you.
I have never used the label index property in Finder before, and I found through some testing that I could not see the labels until I clicked on the folder after the script ran. All the target files had the correct tag after I did that, though.
I'm trying to write all the song names my iTunes to a txt document. The first issue I had was that I can't seem to correctly loop the operation. Here is my test case with the first 15 songs in my iTunes:
tell application "TextEdit"
make new document
end tell
tell application "iTunes"
set trNameID1 to name of track 1
set trNameID2 to name of track 2
set trNameID3 to name of track 3
set trNameID4 to name of track 4
set trNameID5 to name of track 5
set trNameID6 to name of track 6
set trNameID7 to name of track 7
set trNameID8 to name of track 8
set trNameID9 to name of track 9
set trNameID10 to name of track 10
set trNameID11 to name of track 11
set trNameID12 to name of track 12
set trNameID13 to name of track 13
set trNameID14 to name of track 14
set trNameID15 to name of track 15
tell application "TextEdit"
set text of document 1 to {trNameID1 & "
", trNameID2 & "
", trNameID3 & "
", trNameID4 & "
", trNameID5 & "
", trNameID6 & "
", trNameID7 & "
", trNameID8 & "
", trNameID9 & "
", trNameID10 & "
", trNameID11 & "
", trNameID12 & "
", trNameID13 & "
", trNameID14 & "
", trNameID15} as text
end tell
end tell
When I try to loop it, the txt document only contains the last song name, for instance:
tell application "TextEdit"
make new document
end tell
tell application "iTunes"
set trNum to 1
repeat 15 times
set trNameID to name of track (trNum)
tell application "TextEdit"
set text of document 1 to trNameID & "
"
end tell
end repeat
end tell
This will only output the fifteenth song's name onto the txt document.
I realize that this may be very basic, but I have literally been using applescript for about 48 hours, and I can't seem to figure this out. I would like all of the song names to be in a txt document so I can read and analyze the strings in c++. Does anyone have any ideas?
Also, I'm not sure if there is a way, in AppleScript, to look at the entire iTunes library and see the last song, record that song's id in iTunes, and then make a repeat loop that goes through that id. This way the loop would work for exactly the number of songs that are in the library.
Any ideas would be very much appreciated!
You don't really need a repeat loop at all. You can get track names directly from iTunes. You get it in list format so we just convert that list into a string separating the list items with a return character. Then we write it to TextEdit. So this code optimizes #Michele Percich's code by eliminating the repeat loop and using applescript's text item delimiters to convert the list to a string for use in TextEdit.
tell application "iTunes"
set trackNames to name of every track in (first playlist whose special kind is Music)
end tell
set text item delimiters to return
set trackNames to trackNames as text
set text item delimiters to ""
tell application "TextEdit"
make new document
set text of document 1 to trackNames
end tell
You need to increment the value of trNum variable at the end of your repeat loop:
set trNum to trNum + 1
Or better use a different repeat syntax:
repeat with trNum from 1 to 15
And also to add (and not replace) the track name to the document:
set text of document 1 to text of document 1 & trNameID & return
However, this probably is a better way to do what you want:
tell application "iTunes"
set trackList to ""
set allTracks to every track in (first playlist whose special kind is Music)
repeat with currentTrack in allTracks
set trNameID to name of currentTrack
set trackList to trackList & trNameID & return
end repeat
end tell
tell application "TextEdit"
make new document
set text of document 1 to trackList
end tell
i see you all use the:
tell application "TextEdit"
make new document
set text of document 1 to trackNames
end tell
command
You can use a faster way:
set textlocation to "/users/yourusername/desktop/test.txt"
set Line_1 to "Hello this is line one, if you want more lines just copy > this script and change the variables."
do shell script "echo " & quoted form of Line_1 & " >> " & quoted form of textlocation
You can see in the script the 2 ">>" signs, this will add each textline in a new line in a txt file.
If there is only one ">" the text will replace the other text.
Here is an example:
First with 2 ">>" lines
do shell script "echo Hey this is one line. >> /Users/Yourusername/desktop/Add.txt"
do shell script "echo And this is the second one. >> /Users/Yourusername/desktop/Add.txt"
This script will make a txt file like this:
Hey this is one line.
And this is the second one.
Now with 2 ">" lines
do shell script "echo Hey this is one line > /Users/Zl109819/desktop/Add.txt"
do shell script "echo And this is the second one > /Users/Zl109819/desktop/Add.txt"
This script will make a txt file like this:
And this is the second one.
I've created the following AppleScript for deleting all the selected tracks:
property okflag : false
-- check if iTunes is running
tell application "Finder"
if (get name of every process) contains "iTunes" then set okflag to true
end tell
if okflag then
tell application "iTunes"
if selection is not {} then
repeat with this_track in selection
try
try
set cla to class of this_track
set floc to (get location of this_track)
delete this_track
on error error_message number error_number
display alert error_message message ("Error number: ") & error_number & "."
end try
if cla is file track then
my delete_the_file(floc)
end if
end try
end repeat
end if
end tell
end if
to delete_the_file(floc)
try
-- tell application "Finder" to delete floc
do shell script "mv " & quoted form of POSIX path of (floc as string) & " " & quoted form of POSIX path of (path to trash as string)
on error
display dialog "Track deleted, but could not be moved to trash" buttons {"Hmm"} default button 1 with icon 1
end try
end delete_the_file
It works fine when I select a single item, but when I select more than one I get: "Can't get location of item 2 of selection" (error number -1728). I believe this is because by deleting a track, the script's index into the selection is corrupted.
I thought I'd try making my own list of tracks to be deleted first:
tell application "iTunes"
if selection is not {} then
set to_delete to {}
repeat with this_track in selection
try
set cla to class of this_track
set floc to (get location of this_track)
if cla is file track then
set pair to {this_track, floc}
set to_delete to to_delete & pair
end if
end try
end repeat
repeat with pair in to_delete
set the_track to item 1 of pair
set floc to item 2 of pair
delete the_track
my delete_the_file(floc)
end repeat
end if
end tell
But then I get 'Can't get item 1 of item 1 of selection of application "iTunes".' I think the problem is "this_track" is not an object of class Track, but an item of a selection. How do I get the actual track object from the selection item?
If you don't see the solution, I'll welcome tips on debugging or any other suggestions.
The variable this_track is a reference to an object specifier. You have to use the contents property to get the enclosed object specifier. The same is true for accessing the variable pair in the second loop. See the class reference section on the class reference in the AppleScript language guide.
Another problem exists in the way the list to_delete is being built. The statement set to_delete to to_delete & pair will not produce a list of pairs but a flat list. See the class reference section on the class list in the AppleScript language guide.
Here's a version of your second script, where these bugs have been removed:
tell application "iTunes"
if selection is not {} then
set to_delete to {}
repeat with this_track in selection
try
set cla to class of this_track
set floc to (get location of this_track)
if cla is file track then
set pair to {contents of this_track, floc}
copy pair to end of to_delete
end if
end try
end repeat
repeat with pair in to_delete
set the_track to item 1 of contents of pair
set floc to item 2 of contents of pair
delete the_track
my delete_the_file(floc)
end repeat
end if
end tell