2010/01/07

Speeding up podcasts within iTunes

What started out as a simple Superuser question became a long trek to find a solution.  The only answer at the time pointed me to do batch processing through Audacity, but I was trying to avoid automating the GUI as much as possible.  Although I know how to make use of AutoIT and AutoHotKey to automate a GUI application, it’s not the approach I wanted to take with this.  The machine I sync my iPod and iTunes with I use quite a bit so I don’t want to have miscellaneous windows popping up on me and worrying about my current activity accidentally overriding what the script is doing.  Yes, using the iTunes COM SDK causes iTunes to open up if it isn’t already, but I’m okay with that since I keep iTunes open most of the time to update my podcasts so it doesn’t affect me very much.

So all that being said, as you probably guessed since I actually posted this, I came up with a solution that doesn’t involve AutoIT/AutoHotKey.  Doing a little digging I found an alternative to Audacity (which only operates through a GUI) called SOX (which is command line driven).  In a similar vein that Audacity suffers, they can’t distribute compiled applications that work with MP3’s because of the wonderful licensing of the format.  Thankfully SOX is open source, and others have already dealt with this issue, so I’ll make use of their outstanding work.  If you really want to do the work yourself, take a look at the steps on this Code Project article.  I attempted the steps, but ran into a number of issues when trying to compile everything myself.  It may have been my very rusty C++ skills, but who knows.  So instead, the author of that article published the output of the steps and that’s what I downloaded and made use of (the sox.zip link).  It is a number of versions behind the current version of SOX, but for my needs it’s fine.  To follow what I’ve done, download that file as well, and put it somewhere on your machine.

The next step was creating a Powershell script to get all the podcast tracks that iTunes has downloaded and modify them.  Not sure why, but this appears only only work in Powershell v2, so I accomplished that using the following script (note the line pointing to where Sox.exe exists), which I called “”SpeedUpPodcastsIniTunes.ps1”:

 

# Comment that will be applied to all podcast files that will be updated.
$modificationComment = "::Modified By Powershell Script::"

# Location where sox.exe exists on your machine.
$soxFile = "C:\Path\To\Sox.exe"

# The file types to modify. Make sure that Sox.exe can handle the file types
$extension = ".mp3"

# This is a list of all the podcasts that should have every file modified. This is an
# opt-in process for each podcast. The podcast names are case sensitive.
# Format should be like the following:
# ... = "Podcast 1", "Podcast 2", "Podcast 3", ...
#$podcastsToAffect = ".NET Rocks!", "RunAs Radio", "The Thirsty Developer - Podcast"
$podcastsToAffect = "The Thirsty Developer - Podcast",
".NET Rocks!",
"RunAs Radio",
"Hanselminutes",
"Herding Code"

# The tempo speed is how fast to speed up the podcast. A value of 1.0 is the same speed
# it's currently at. 1.5 is 150% faster, so the overall length would be 66% of what it
# currently is.
$tempoSpeed = "1.5"


$itunes = new-object -com itunes.application
if($itunes -ne $null)
{
Write-Host "iTunes is running..."
# Sources.Kind == 1 (ITSourceKindLibrary)
$itunesLibrary = $itunes.Sources | Where-Object { $_.Kind -eq 1 }
Write-Host "Retrieving Podcasts"
$podcastsPlaylist = $itunesLibrary.Playlists | Select-Object $_ | Where-Object { [string]::Compare($_.Name, "podcasts", $True) -eq 0 }
# Tracks.Kind == 1 (ITTrackKindFile)
Write-Host "Filtering Podcasts"
$downloadedTracks = $podcastsPlaylist.Tracks | Where-Object { ($_.Kind -eq 1) -and ($_.Podcast -eq $True) -and ($podcastsToAffect -contains $_.Album) } | Select-Object $_ | Where-Object { (([string]::IsNullOrEmpty($_.Lyrics) -eq $True) -or ($_.Lyrics.Contains($modificationComment) -ne $true)) -and ([System.IO.Path]::GetExtension($_.Location) -eq $extension) }

Write-Host "Processing..."
$downloadedTracks | ForEach-Object {
if($_ -ne $null){
$trackLocation = $_.Location;
$currentLyrics = [string]::Empty
if([string]::IsNullOrEmpty($_.Lyrics) -ne $True){
$currentLyrics = $_.Lyrics
}

$tempFile = [System.IO.Path]::GetTempFileName()
# GetTempFileName() actually creates the file, which we don't need
[System.IO.File]::Delete($tempFile)
# Need to give the temp file an appropriate extension because Sox.exe requires it.
$tempFile = $tempFile + $extension

Write-Host Converting `( $_.Name`)
& $soxFile $trackLocation $tempFile tempo $tempoSpeed
Write-Host Done!

# Need to delete the current file because Move() doesn't let you overwrite the file.
[System.IO.File]::Delete($trackLocation)
[System.IO.File]::Move($tempFile, $trackLocation)

# Update the Lyrics for tracking the files that have been changed.
$currentLyrics = $currentLyrics + "`r`n" + $modificationComment
$_.Lyrics = $currentLyrics

$_.UpdateInfoFromFile()
}
}

# clean up memory
[void][System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$iTunes)
}


You’ll notice that I’m making use of the Lyrics section of the podcast to track if it’s been modified by this script before.  I initially was using the Comments, but apparently some of the podcasts actually fill in that information and there’s a limit of 256 characters on the field.  My first trial runs ended up with a few “Cannot change the Comment” errors being thrown by the COM interop because of that limitation.  So far I haven’t run into an issue with the Lyrics section.



The final step is setting up a scheduled task to run this script on a schedule.  For the command on the scheduled task itself, it looks like this:



C:\Windows\system32\windowspowershell\v1.0\powershell.exe –NoProfile –NonInteractive “C:\Utils\Scripts\SpeedUpPodcastsIniTunes.ps1”



And the Start In directory is set to `C:\Windows\system32\windowspowershell\v1.0`.



So far it seems to fit my need pretty well.  It does have a noticeable lag when it’s filtering the podcasts, but not too bad.



NOTE: The first time you run this, it’s advisable to delete all but the most recent track for a podcast.  It does take several minutes to convert the files, so even a handful of them can take a good deal of time.

No comments: