Script to duplicate Apple Lossless iTunes library as MP3 files

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:

  1. Select all tracks in playlist Music
  2. 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:

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

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