Hi
I’m thinking of writing a script to duplicate my iTunes Music Library which is in Apple Lossless format to MP3 format. Essentially this would work the same as connecting an iPod and using the convert higher bitrate to 128kbps feature in iTunes, except that I would be converting/duplicating to a folder on my hard drive and would have more flexibility in terms of format for conversion and bitrate.
As I see it the script would need to do the following:
- Select all tracks in playlist Music
- Check if track already exists in MP3 folder with same modification date and:
a If yes, do nothing
b If same track with different modification date convert and replace track (so that tag changes are transferred)
c If track doesn’t exist convert track
So essentially it would be processing one track at a time, and I’m wondering how quickly the script would be able to process a large library (20,000+ tracks). Of course it would take a fair bit of time if there’s a lot of changes, as the conversion takes time. But say I’ve only changed/added a few tracks to the library. Would it be able to process the unchanged tracks quickly? Of course I could make a smart playlist for tracks with a modification date since the last time I ran the script, but I’d rather not have do this if possible.
Also how would the script identify the corresponding MP3 track? Is there a unique identifier for each track in the iTunes library or would this be done by comparing the same folder structure and file names?
And is there anything else I haven’t considered in terms of the smooth operation of this script?
Thanks
Nick
Hi, Nick.
Step 1 should be avoided, where possible. You don’t need a selection. You need reference lists. There are unique IDs for each track and”not including the actual conversion time”it shouldn’t take very long to compare the items… probably a matter of seconds, dependent on your machine.
This will give you a 3 item list of your MP3 data. You can compare the items of this list against a similar list you create of the source files and then delete by ID.
tell application "iTunes"
set MP3s to (tracks whose kind is "MPEG audio file")'s {name, modification date, id}
end tell
Thanks. The aim would be for the converted tracks to not be in the iTunes Library. So the script would be moving the resulting files to a folder structure based upon the tags, e.g. MP3 Music/[Album Artist]/[Album]. Does this mean the reference list method won’t work?
If so I am then thinking that comparing the iTunes library (or the folder structure of the iTunes music library) to the folder structure of the MP3 files is the only way to achieve what I am looking for.
I.e. search for each track, based on Name, in MP3 Music/[Album Artist]/[Album]. Then:
(i) if track doesn’t exist then convert.
(ii) if modification date of original file is more recent than modification of MP3 file, convert and replace
(iii) else do nothing
I think it would be faster if iTunes managed both sets of files in separate playlists, but, if you need to do it this way, my sample below should give you two 3-item lists of tracks common to both music folders. I’d compare the dates by looping through the 2nd item and then create a new list of mismatches, which would be the files to convert/delete.
--assumed is your non-iTunes music folder, "Music2", lives on the desktop
tell application "Finder"
set music2list to (folder "Music2"'s entire contents's files whose name is in ((get folder ("" & (path to music folder) & "iTunes:iTunes Music:")'s entire contents's files's name)))'s {name, modification date, it}
set iTunesMusiclist to (folder ("" & (path to music folder) & "iTunes:iTunes Music:")'s entire contents's files whose name is in ((get folder "Music2"'s entire contents's files's name)))'s {name, modification date, it}
end tell
Thanks. I tested the first script you posted on my 25000 Apple Lossless Music Library and it took 5 minutes to generate the list.
I was curious as to how the script would make the comparisons between the lists, and as I can see that the id is different for the converted tracks I am presuming it must be by name. Given that my iTunes Music Library folder is organised by iTunes in subfolders and some tracks have the same name, then would I not need the lists to include the artist name and album name as well to make sure that the correct tracks are being compared?
Just to say that, in the absence of knowing how to use the reference list method suggested above I am working on an alternative (it seems that there’s always more than one way to do these things). I am still interested in your method if it will work better. The script below does everything I want it to do with the following caveats:
-
I want to be able to delete MP3 files that have been previously converted using the script, but which no longer exist in the iTunes library, because I may delete tracks I no longer want in iTunes and would like there to be an exact match between the iTunes library and the MP3 folder). I guess it would have to be an additional section of script which compares each file by name in the folder/subfolder in the iTunes Music Library with the equivalent folders in the target music folder, deleting any files found in the target music folder that don’t exist in the iTunes Music folders, but I’m not sure if this is the best/only method, and how I would go about scripting this.
-
Because I need to specify the conversion twice (once if no file exists in target folder and once if it does but with an older modification date) I am wondering about using a subroutine method here. I tried doing so, but no conversions happened even though some should have happened. I wonder if this is because the relevant variables aren’t picked up properly by the script when it’s a subroutine. I can leave things as they are, just thought it would be good to make the script neater.
tell application "iTunes"
activate
set sel to selection
set encoderBackup to name of current encoder
set this_folder to "Macintosh HD:Users:nick:Desktop:music:" as text
set current encoder to encoder "MP3 Encoder"
repeat with this_track in sel
set trackComposer to this_track's composer
set trackAlbum to this_track's album
set trackName to this_track's name
tell application "Finder"
set path1 to "Macintosh HD:Users:nick:Desktop:music:" & trackComposer as text
if folder path1 exists then
{}
else
make new folder at this_folder with properties {name:trackComposer}
end if
set path2 to this_folder & trackComposer & ":" & trackAlbum as text
if folder path2 exists then
{}
else
make new folder at path1 with properties {name:trackAlbum}
end if
if exists file (path2 & ":" & trackName & ".mp3") then
set file2 to (path2 & ":" & trackName & ".mp3") as alias
set modDate to modification date of file2
set modDate2 to this_track's modification date
if modDate is greater than modDate2 then
{}
else
tell application "iTunes"
try -- skip on failure
set new_track to item 1 of (convert this_track)
set loc to new_track's location
set dbid to new_track's database ID
delete artworks of new_track
-- move the file to new location
do shell script "mv " & (quoted form of POSIX path of loc) & " " & (quoted form of POSIX path of path2 as string)
-- delete the track
delete new_track
end try
end tell
end if
else
tell application "iTunes"
try -- skip on failure
set new_track to item 1 of (convert this_track)
set loc to new_track's location
set dbid to new_track's database ID
delete artworks of new_track
-- move the file to new location
do shell script "mv " & (quoted form of POSIX path of loc) & " " & (quoted form of POSIX path of path2 as string)
-- delete the track
delete new_track
end try
end tell
end if
end tell
end repeat
set current encoder to encoder encoderBackup
display dialog "done"
end tell
Another update. I have now worked out the second issue, so that I can use a handler to specify the conversion and call it in the script when necessary. I am not sure where to start though with the first issue, i.e. how to delete any MP3 files in the destination folders/subfolders, that don’t exist as m4a files in the source (iTunes) folders/subfolders (presumably this would have to be by generating a list of files of in each folder and comparing file names, but I am not sure how to go about this - would I do it folder by folder, or just generate one long list of all the files in the folders/subfolders?). Here’s the revised script:
tell application "iTunes"
activate
set sel to selection
set encoderBackup to name of current encoder
set this_folder to "Macintosh HD:Users:nick:Desktop:music:" as text
set current encoder to encoder "MP3 Encoder"
repeat with this_track in sel
set trackComposer to this_track's composer
set trackAlbum to this_track's album
set trackName to this_track's name
tell application "Finder"
set path1 to "Macintosh HD:Users:nick:Desktop:music:" & trackComposer as text
if not (folder path1 exists) then
make new folder at this_folder with properties {name:trackComposer}
end if
local path2
set path2 to this_folder & trackComposer & ":" & trackAlbum as text
if not (folder path2 exists) then
make new folder at path1 with properties {name:trackAlbum}
end if
if exists file (path2 & ":" & trackName & ".mp3") then
set file2 to (path2 & ":" & trackName & ".mp3") as alias
set modDate to modification date of file2
set modDate2 to this_track's modification date
if not (modDate is greater than modDate2) then
my convertTrack(this_track, path2)
end if
else
my convertTrack(this_track, path2)
end if
end tell
end repeat
set current encoder to encoder encoderBackup
display dialog "done"
end tell
on convertTrack(this_track, path2)
tell application "iTunes"
try -- skip on failure
set new_track to item 1 of (convert this_track)
set loc to new_track's location
set dbid to new_track's database ID
delete artworks of new_track
-- move the file to new location
do shell script "mv " & (quoted form of POSIX path of loc) & " " & (quoted form of POSIX path of path2 as string)
-- delete the track
delete new_track
end try
end tell
end convertTrack
Is your iTunes music folder in the default artist>album>song arrangement or are you using the same composer structure you’ve created in the secondary music folder?
Thanks.
I now have a script that does everything I want, including deleting superfluous tracks from the destination folder, except where there are characters in the track name in iTunes that aren’t supported in filenames in Finder (i.e. ? / :). This means that all tracks which contain these characters in the name are re-converted every time I run the script.
Is there a way I wonder of adapting the script so that when it is checking to see if the track already exists in the destination folder, it replaces these characters in the track name with an _ (which is what happens in Finder) for the purposes of the comparison?
Nick
tell application "iTunes"
activate
set sel to every track of playlist "1 music library"
set encoderBackup to name of current encoder
set this_folder to "Macintosh HD:Users:nick:Desktop:music:" as text
set current encoder to encoder "MP3 Encoder"
repeat with this_track in sel
set trackComposer to this_track's composer
set trackAlbum to this_track's album
set trackName to this_track's name
tell application "Finder"
set path1 to "Macintosh HD:Users:nick:Desktop:music:" & trackComposer as text
if not (folder path1 exists) then
make new folder at this_folder with properties {name:trackComposer}
end if
local path2
set path2 to this_folder & trackComposer & ":" & trackAlbum as text
if not (folder path2 exists) then
make new folder at path1 with properties {name:trackAlbum}
end if
if exists file (path2 & ":" & trackName & ".mp3") then
set file2 to (path2 & ":" & trackName & ".mp3") as alias
set modDate to modification date of file2
set modDate2 to this_track's modification date
if not (modDate is greater than modDate2) then
my convertTrack(this_track, path2)
end if
else
my convertTrack(this_track, path2)
end if
end tell
end repeat
set current encoder to encoder encoderBackup
display dialog "done"
end tell
on convertTrack(this_track, path2)
tell application "iTunes"
try -- skip on failure
set new_track to item 1 of (convert this_track)
set loc to new_track's location
-- move the file to new location
do shell script "mv " & (quoted form of POSIX path of loc) & " " & (quoted form of POSIX path of path2 as string)
-- delete the track
delete new_track
end try
end tell
end convertTrack