Tuesday, April 5, 2011

Converting From WMA to MP3

I have a fairly decent sized library of music ripped from CD or flat out bought digital.  The main problem is, as I move towards Android on the phone and because I use my phone as a portable media player, a lot of non-MS software just don't like the ol' WMA.  So, I set out to find a solution.

I found a fine tool ffmpeg.exe and a great script to drive it with PowerShell lifted from this site (my thanks go to Paul for creating the script).  However, I of course have one wrinkle in my folder configuration that causes the script to error and not function:  I have a space within the path to my music folder (probably most of us do).  So I began to read about escaping and dereferencing of PowerShell code within a script.  It turns out its not all that easy, but it is a bit of a touchy issue no matter what you're coding in (e.g. at searching a SQL text field and a name like O'Reilly comes up and screws your SQL because an apostrophe is the end of enclosed text).  After a lot of reading it turns out that putting a single ampersand at the beginning of the text fed to the Invoke-Expression call is all it took.  So here is my version of the script:

(The script assumes a default install location for ffmpeg, and needs a change to the path of Get-ChildItem in the For loop per specific machine config.)
# wma2mp3 conversion powershell script... since my >€600 car stereo does not understand wma....
# dec 2010 version 1.1 Servercare, Paul Weterings

$tool = '"C:\Program Files (x86)\WinFF\ffmpeg.exe"'
$succescounter = $failurecounter = 0

# we simply find all the file objects that and with .wma, and iterate through the folders recursively
# note that we are converting everything in the M: drive here, another example might be:
# (Get-ChildItem "f:\music\" -include *.wma -recurse)

foreach ($child in $(Get-ChildItem "C:\Users\UserName\Music\" -include *.wma -recurse))
{
    $wmaname = $child.fullname
    
    # Since we convert the file, lets give it the appropiate extension, note that the function is case-sensitive
    # so we handle that first
    $wmaname = $wmaname.Replace("WMA","wma")
    $mp3name = $wmaname.Replace("wma","mp3")

    # The argument string that tells ffmpeg what to do...
    $arguments = '-i "' + $wmaname + '" -y -acodec libmp3lame -ab 160k -ac 2 -ar 44100 "' + $mp3name + '"'
    echo "-----------------------------------------------------------------------------------------------------"
    echo "----- Processing: $mp3name"
    #echo "$tool $arguments"
    Invoke-Expression "& $tool $arguments"

    # Lets see what we just converted, did everything go OK?
    $mp3file = get-item $mp3name

    # if conversion went well the mp3 file is larger than 0 bytes, so remove the wma file,
    # otherwise leave the wma file & remove the (zero length) mp3 file
    if ($mp3file.Length -gt 0)
    {
        echo "----- removing $wmaname"
        Remove-Item $wmaname
        $succescounter++
    }
    else
    {
        echo "----- removing $mp3name"
        Remove-Item $mp3name        
        $failurecounter++
    }
}

# We are done, so lets inform the user what the succesrate was.
Echo "Processing completed, $succescounter conversions were succesfull and $failurecounter were not."

The magic line that makes it work with a space in the path is:
Invoke-Expression "& $tool $arguments"

This ran on a fairly beefy desktop for over 14 hours to convert about 5.5k files occupying 34GB.  But chug away it did.  There were some WMA's that couldn't be deleted but wasn't an issue to clean up.

Also, thanks to Darshan for his easy-to-follow post on how to install and use SyntaxHighlighter 3.

No comments:

Post a Comment