// This script will move all files from a given folder on the filesystem that are NOT in your iTunes library to a separate folder where they can be deleted manually // As a side effect, "ghost" files (not existing on filesystem) will be removed from the library // Only these three variables below may need customisation. // WARNING: if you define a music folder that is NOT in your itunes library, it will be erased ENTIRELY!!! (including subfolders!!!) // USE With EXTREME caution and make sure to backup the folder defined by strMusicfolder first var strMusicfolder = "%userprofile%\\Music\\muziektest"; // Location of the folder from which the files to delete var strOutcastfolder = "%userprofile%\\itunesoutcasts"; // location of the folder to wich to backup the files var strOutcastlogfile = "%userprofile%\\itunesoutcasts.log"; // location of the logfile // Define map so we can store the itunes lib in memory and perform some lookups // Source http://snippets.scottwalter.com/tag/javascript/2 function KeyValue( key, value ){ this.key = key; this.value = value; } function Map(){ this.array = new Array(); } Map.prototype.put = function( key, value ){ if( ( typeof key != "undefined" ) && ( typeof value != "undefined" ) ) { this.array[this.array.length] = new KeyValue( key, value ); } } Map.prototype.containsKey = function( key ){ for( var k = 0 ; k < this.array.length ; k++ ) { if( this.array[k].key == key ) { return true; } } return false; } Map.prototype.get = function( key ){ for( var k = 0 ; k < this.array.length ; k++ ) { if( this.array[k].key == key ) { return this.array[k].value; } } return ""; } Map.prototype.remove = function(key){ if( ( typeof key != "undefined" ) ){ for( var k = 0 ; k < this.array.length ; k++ ) { if( this.array[k].key == key ) { this.array.splice(k, 1); } } } } Array.prototype.execute = function(fnc,start) { var a = start; for (var i = this.array.length-1; i > -1; i--) { a = fnc(this.array[i],a); } return a; } Map.prototype.length = function() { return this.array.length; } // Define Filesystem object and shell object var fso = new ActiveXObject("Scripting.FileSystemObject"); var objShell = new ActiveXObject("WScript.Shell"); // Expand "userprofile" to the profille folder of the current user (this to ensure write access in both logfile and target folder) var strMusicfolder = objShell.ExpandEnvironmentStrings(strMusicfolder); var strOutcastfolder = objShell.ExpandEnvironmentStrings(strOutcastfolder); var strOutcastlogfile = objShell.ExpandEnvironmentStrings(strOutcastlogfile); // if the outcast folder doesn't exist, we'll create it. if(fso.FolderExists(strOutcastfolder + "\\")==false){ fso.CreateFolder(strOutcastfolder); } // open the logfile for appending(create it if it doesn't exist) var outcastlogfile = fso.OpenTextFile(strOutcastlogfile,8, true); // log source and target folders loggit('Folder ' + strMusicfolder + ' will be checked'); loggit('Files will be moved to ' + strOutcastfolder); // Load the enitre iTunes library into a dictionary object. var dictFileLocations = loadMusicLibrary(); // Loop over the music folder and delete all files that are NOT in the dictionary object loopOverMusicFolder(strMusicfolder, dictFileLocations); // close the logfile outcastlogfile.Close(); // Function to loop over the music folder and delete all files that are NOT in the dictionary object function loopOverMusicFolder(strFolder, dictFileLocations){ var thisFolder = fso.getFolder(strFolder); // Loop over all files in the current folder var enumFile = new Enumerator(thisFolder.Files); for (; !enumFile.atEnd(); enumFile.moveNext()){ // if the current file is in the iTunesLibrary, just remove it from the dictionary if(dictFileLocations.containsKey(enumFile.item())){ dictFileLocations.remove(enumFile.item()); } else { // else: we need to move them to the outcast folder. Make sure to give them a unique name if they already exist (by appending an order number) var strTargetFile = strOutcastfolder + "\\" + enumFile.item().name; var fileExt = fso.GetExtensionName(enumFile.item()); var fileBaseName = fso.GetBaseName(enumFile.item()); var counter = 0; // Check if the file already exists in the outcast folder while(fso.FileExists(strTargetFile)){ counter++; strTargetFile = strOutcastfolder + "\\" + fileBaseName + "(" + counter + ")." + fileExt; } loggit("Moving outcast file " + enumFile.item() + " to " + strTargetFile); dictFileLocations.remove(enumFile.item()); // Actually move the file to the outcast folder enumFile.item().Move(strTargetFile); } } // Loop over all subfolders in the current folder and perform the same routine recursively var enumSubfolders = new Enumerator(thisFolder.SubFolders); for (; !enumSubfolders.atEnd(); enumSubfolders.moveNext()){ loopOverMusicFolder(enumSubfolders.item(), dictFileLocations); //Recursive } // Finally: if all files in the folder have been deleted: cleanup by deleting the folder as well if(thisFolder.Files.Count==0 && thisFolder.SubFolders.Count==0){ thisFolder.Delete(); } } // This function will load all the locations of the tracks in the Library into a dictionary map. // As a side effect, "ghost" files (not existing on filesystem) will be removed from the library function loadMusicLibrary(){ // Source: http://ottodestruct.com/itunes/update.txt var ITTrackKindFile = 1; var iTunesApp = WScript.CreateObject("iTunes.Application"); var tracks = iTunesApp.LibraryPlaylist.Tracks; var numTracks = tracks.Count; var i; var dictLibraryFiles = new Map(); for (i = 1; i <= numTracks; i++){ var currTrack = tracks.Item(i); // is this a file track? if (currTrack.Kind == ITTrackKindFile){ // yes, does it have an empty location? if (currTrack.Location == ""){ // yes, delete it from the library currTrack.Delete(); } else { // add it to the dictionary dictLibraryFiles.put(currTrack.Location, ""); } } } return dictLibraryFiles; } // Get date and time as a pre-formatted string (dd-MM-yyyy hh:mm ss:nn function getDateTimeString(){ var d = new Date(); var curr_date = d.getDate(); var curr_month = d.getMonth(); curr_month++; var curr_year = d.getFullYear(); var curr_hour = d.getHours(); var curr_min = d.getMinutes(); var curr_sec = d.getSeconds(); var curr_msec = d.getMilliseconds(); return zeroPad(curr_date) + "-" + zeroPad(curr_month) + "-" + zeroPad(curr_year) + " " + zeroPad(curr_hour) + ":" + zeroPad(curr_min) + " " + zeroPad(curr_sec) + ":" + zeroPad(curr_msec) + ":" } // add leading zero's //source: http://sujithcjose.blogspot.com/2007/10/zero-padding-in-java-script-to-add.html function zeroPad(num,count){ if( typeof count == "undefined" ){ count = 1; } var numZeropad = num + ''; while(numZeropad.length < count) { numZeropad = "0" + numZeropad; } return numZeropad; } // shortcut function to log a message with a date-time stamp to the logfile function loggit(message){ outcastlogfile.WriteLine(getDateTimeString() + ":" + message); }