/* HauppeTunes.groovy !!!WARNING!!! Files are deleted when finished with (assumes iTunes is set to copy files to its directory)... Renames converts and imports mpg (i.e. audio files as recorded by Hauppauge Nova-T scheduler) to iTunes in Apple Lossless Format or AAC. Requires java, groovy, iTunes & Windows, plus: Groovy COM Scripting support: http://groovy.codehaus.org/COM+Scripting dbPowerAmp: http://www.dbpoweramp.com/ Apple Lossless and AAC CODECs (used by dbPowerAmp): http://www.dbpoweramp.com/codec-central-mp4.htm Nero CODEC pack (used by dbPowerAmp): http://www.nero.com/nerodigital/eng/Nero_Digital_Audio.html iTunes Windows SDK: http://developer.apple.com/sdk/itunescomsdk.html addFileToiTunes.js addFileToiTunes.bat (see end of code) It isn't possible to set dbPowerAmp conversion options such bitrate through the COM interface, therefore you must open the dbPowerAmp conversion dialogue and set them manually, the settings will then be used by default via the COM interface (which this code uses) The file numbers in file names (001 etc.) created by Hauppauge (if present) are replaced by the original file date. TODO (maybe): add option switches for: prompt when finished (default yes) delete source file (default yes) delete target file (default yes) add to iTunes (default yes) sync iPod (default yes) target format (default Apple Lossless) Get rid of the tags used to format the code, they are evil! Written in 2007 by Maurice Walton, www.mauricewalton.com You are free to use this code for any legal purpose, but please credit me and provide a reference to www.mauricewalton.com */ import java.io.* import java.util.* import java.text.* import org.codehaus.groovy.scriptom.ActiveXProxy formatLossless = "Apple Lossless" formatAAC = "Nero Digital Audio (AAC)" m4aTaggingCommand = "\"F:\\Program Files\\dBpowerAMP\\Compression\\Nero Digital Audio (AAC)\\neroaactag.exe\"" aacTaggingCommand = "\"F:\\Program Files\\dBpowerAMP\\Compression\\Nero Digital Audio (AAC)\\neroaactag.exe\"" sourceDirectoryName = "" targetDirectoryName = "" outputFormat = "" // tag values common to all files tagArtist = "BBC Radio" tagComposer = tagArtist tagGenre = "Books & Spoken" tagTrack = "1" tagTotalTracks = "1" tagDisc = "1" tagTotalDiscs = "1" tagComment = "converted from mpg" class FileDateComparator implements Comparator { public int compare(Object o1, Object o2) { int returnVal if (((File) o1).lastModified() > ((File) o2).lastModified()) { returnVal = +1 } else if (((File) o1).lastModified() < ((File) o2).lastModified()) { returnVal = -1 } else { returnVal = 0 } return returnVal } } void convert() { sourceFiles = getFiles() GregorianCalendar gc = new GregorianCalendar() SimpleDateFormat fileNameSDF = new SimpleDateFormat("yyyy-MM-dd") SimpleDateFormat titleNameSDF = new SimpleDateFormat("yyyy/MM/dd") SimpleDateFormat syf = new SimpleDateFormat("yyyy") // process one file at a time as it doesn't really cost much extra time and means that any problem in the chain of events will not effect all files but means all files appear in iTunes in the "right" order for(sourceFile in sourceFiles) { gc.setTimeInMillis(sourceFile.lastModified()) String date = fileNameSDF.format(gc.getTime()) // Build up new file name String targetFileName = sourceFile.getName() // Hauppauge software names files ending "0xx", if this is the case, get rid of the number and replace with the date try { String fileNumberString = targetFileName.substring( targetFileName.lastIndexOf(".") - 3, targetFileName.lastIndexOf(".")) i = Integer.parseInt(fileNumberString); // replace the number with the file date targetFileName = targetFileName.replace(fileNumberString + ".", " (" + date + ")."); } catch (Exception e) { // file name does not end with 3 numeric digits, so leave the file name alone } targetFileName = targetDirectoryName + File.separator + targetFileName targetFile = new File(targetFileName) println("Move " + sourceFile.getAbsolutePath() + " to " + targetFile.getAbsolutePath()) sourceFile.renameTo(new File(targetFileName)) println("Convert") // convert renamed file to Apple Lossless using dbPowerAmp dMCScriptingProxy = new ActiveXProxy("dMCScripting.Converter") // Set My options (Volume norm off, ID Tag Preservation On, No delete Source files) dMCScriptingProxy.VolumeNormalize = false dMCScriptingProxy.PreserveTags = true dMCScriptingProxy.DeleteSourceFiles = false // Set Output Folder dMCScriptingProxy.ConvertToFolder = true dMCScriptingProxy.ToFolder = targetDirectoryName // Add My Files to Convert dMCScriptingProxy.AddFromFile(targetFileName) // Do the conversion // options: (format, No option page, Want Overwrite page, Do Not Want finished, Want Errors) dMCScriptingProxy.GoConversion(outputFormat, true, false, true, false) outputFileName = "" outputFile = new File(outputFileName) if (dMCScriptingProxy.WasConvError == true) { println("conversion error for " + targetFileName) dMCScriptingProxy.release() continue } // build the file specific tag values String tagYear = syf.format(gc.getTime()) String tagTitle = targetFileName.substring(targetFileName.lastIndexOf("\\") +1 ,targetFileName.lastIndexOf(".")) tagTitle = tagTitle.replace("_", " "); // use a different date format in the title (if date is present) if (tagTitle.contains("(" + date + ")")) tagTitle = tagTitle.replace("(" + date + ")","(" + titleNameSDF.format(gc.getTime()) + ")") String tagAlbum = tagTitle; // remove the date from the album title if present if (tagAlbum.contains("(" + titleNameSDF.format(gc.getTime()) + ")")) tagAlbum = tagAlbum.replace("(" + titleNameSDF.format(gc.getTime()) + ")", "").trim() if (outputFormat.equalsIgnoreCase(formatLossless) || outputFormat.equalsIgnoreCase(formatAAC)) { dMCScriptingProxy.release() taggingCommand = m4aTaggingCommand // get name of output file outputFileName = targetFileName.substring(0,targetFileName.lastIndexOf(".")) + ".m4a" outputFile = new File(outputFileName) // build up arguments for setting tag values List arguments = new ArrayList() arguments.add(taggingCommand) arguments.add("\"" + outputFileName + "\"") arguments.add("-meta:title=" + tagTitle) arguments.add("-meta:artist=" + tagArtist) arguments.add("-meta:album=" + tagAlbum) arguments.add("-meta:genre=" + tagGenre) arguments.add("-meta:track=" + tagTrack) arguments.add("-meta:totaltracks=" + tagTotalTracks) arguments.add("-meta:year=" + tagYear) arguments.add("-meta:disc=" + tagDisc) arguments.add("-meta:totaldiscs=" + tagTotalDiscs) arguments.add("-meta:comment=" + tagComment) arguments.add("-meta:composer=" + tagComposer) // Set the tags println("Set tags") synchronousExecute(arguments) } // if there is an output file, copy it into iTunes... if (!outputFileName.equalsIgnoreCase("")) { // finished with mpg file, so remove it println ("removing " + targetFileName) targetFile.delete() // set the converted file date to the date of the original file outputFile.setLastModified(gc.getTimeInMillis()) // add the converted file to iTunes and wait for iTunes to finish with the file println("Add file to iTunes") // UNRELIABLE, so use a batch file and jscript mechanism instead // tried eval'ing jScript, but that was asynchronous too // iTunesProxy = new ActiveXProxy("iTunes.Application") // status = iTunesProxy.LibraryPlaylist.AddFile(outputFileName) // while (status != null && status.InProgress) // Thread.sleep(1000) // iTunesProxy.release() // build up arguments for running jscript via a batch file which seems to be the only // way to synchronously copy into iTunes and be sure output file is finished with before // continuing (so deleting file is done at the right time) List arguments = new ArrayList() arguments.add("cmd") arguments.add("/c") arguments.add("addFileToiTunes.bat") arguments.add("\"" + outputFileName + "\"") synchronousExecute(arguments) println("Add file to iTunes done") // finished with the output file, so remove it println ("removing " + outputFileName) outputFile.delete() } } println("start iPod update") iTunesProxy = new ActiveXProxy("iTunes.Application") status = iTunesProxy.UpdateIPod() try { while (status.InProgress) Thread.sleep(1000) } catch (Exception e) { // we get "VariantChangeType failed" when the status changes, use this "feature" to detect end... // println (e.getMessage()) // ideally this would happen when the update is complete, but making sure the update has started // is good enough (and does not need another jscript file) } iTunesProxy.release() println("iPod update started") } /* * grab all the mpg files from the source directory */ Object getFiles() { List files = new ArrayList() File sourceDirectory = new File(sourceDirectoryName) for(File file : sourceDirectory.listFiles()) { if (file.isFile() && file.canRead() && file.getName().endsWith("mpg")) files.add(file) } // sort file list into ascending date order so they will also be imported into iTunes in ascending date order Collections.sort(files, new FileDateComparator()) return files } void getUserInput() { println("Press Enter to continue") BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)) try { bufferedReader.readLine() } catch (IOException e) { } } void synchronousExecute(arguments) { ProcessBuilder processBuilder = new ProcessBuilder(arguments) processBuilder.redirectErrorStream(true) try { Process process = processBuilder.start() InputStream inputStream = process.getInputStream() InputStreamReader inputStreamReader = new InputStreamReader(inputStream) BufferedReader bufferedReader = new BufferedReader(inputStreamReader) // this code also prevents the script from continuing on without waiting for the program called to finish String line while ((line = bufferedReader.readLine()) != null) { println(line) } } catch (IOException ioe) { ioe.printStackTrace() } } if (args.length < 3) { if (args.length > 0) { for (arg in args) print arg + " " println } else println "No parameters supplied" println "Usage : HauppeTunes [/lossless | /aac]" println "Example: HauppeTunes L:\\MyVideos F:\\ripped /lossless" getUserInput() System.exit(1) } File directory; sourceDirectoryName = args[0] directory = new File(sourceDirectoryName) if (!(directory.exists() && directory.canRead() && directory.isDirectory())) { println "Cannot read from source directory: " + sourceDirectoryName getUserInput() System.exit(2) } targetDirectoryName = args[1] directory = new File(targetDirectoryName) if (!(directory.exists() && directory.canWrite() && directory.isDirectory())) { println "Cannot write to target directory: " + targetDirectoryName getUserInput() System.exit(3) } if (args[2].equalsIgnoreCase("/aac")) outputFormat = formatAAC else if (args[2].equalsIgnoreCase("/lossless")) outputFormat = formatLossless else { println "format not supported: " + args[2] getUserInput() System.exit(4) } iTunesProxy = new ActiveXProxy("iTunes.Application") if (iTunesProxy == null) { println "Can't create iTunes connection" getUserInput() System.exit(5) } iTunesProxy.release() iTunesProxy = null dMCScriptingProxy = new ActiveXProxy("dMCScripting.Converter") if (dMCScriptingProxy == null) { println "Can't create dbPowerAmp connection" getUserInput() System.exit(6) } dMCScriptingProxy.release() dMCScriptingProxy = null convert() getUserInput() /* @rem @rem addFileToiTunes.bat @rem start /wait addFileToiTunes.js %1 @echo Done */ /* * addFileToiTunes.js * Copies the file specified to iTunes and waits for iTunes to complete */ /* arguments = WScript.Arguments; if (arguments.length != 1) { WScript.Echo("Usage: addFileToiTunes.js "); } else { var fileSystemObject = new ActiveXObject("Scripting.FileSystemObject"); var fileName = fileSystemObject.GetAbsolutePathName(arguments(0)); var iTunesApplication = WScript.CreateObject("iTunes.Application"); var mainLibrary = iTunesApplication.LibraryPlaylist; var status = mainLibrary.AddFile(fileName); while (status != null && status.InProgress) { WScript.Sleep(1000); } } */