Tuesday, January 1, 2013

Batch Converting WMA or WAV to MP3 using PowerShell

A family member of mine is a music collector and has had some trouble in the past managing his library. Initially when it was setup he had been using Windows Media Center to rip media in to his collection. Windows Media Center uses Windows Media Player under the hood and is set by default to encode using WMA format. We didn't notice this until several months later. Windows Media Player makes it easy to switch the encoding format but not so easy to re-encode the existing library.

I hate reinventing the wheel, so I went Googling for a batch conversion tool. I looked for a couple of hours installing several candidates. None seemed batch very well, and all but a couple of sourceforge projects were loaded with extra free version garbage. I stumbled on to ffmpeg it is a very versatile command line based encoding tool. I must have been reading some bad or platforms specific examples because they all instructed the use of -acodec libmp3lame as an argument when encoding. This yielded an error indicating that the libmp3library was not a valid encoder. That bogged me down for a trying to find out why this seemingly ubiquitous library was missing or not loading properly. Turns out it was there all along I just needed to use -f mp3 and let the exe figure the rest out.

This is what I was able to put together with some help from a few other examples. I simply recurse through a directory structure and call the ffmpeg.exe using the Invoke-Expression cmdlet. The script example is targeted at .wma files, but it could easily be modified to convert any other format like .wav, .oog or any other ffmpeg supported format. This setup seems to be working well enough; I may consider using something similar to normalize the file tagging with musicbrainz Picard.

Make sure to backup the files you plan on changing before running any batch operation. This script deletes the original .wma file see the "Remove-Item" line. There is no error checking around the EXE call to make sure the conversion completed successfully and that the file is playable. Prior to building this script I staged my changes so I could play around to get the script right.

#Set the path to crawl
$path = 'C:\Documents and Settings\User\My Documents\My Music\Convert'
#The source or input file format
$from = '.wma'
#The encoding bit rate
$rate = '192k'
Get-ChildItem -Path:$path -Include:"*$from" -Recurse | ForEach-Object -Process: { 
        $file = $_.Name.Replace($_.Extension,'.mp3')
        $input = $_.FullName
        $output = $_.DirectoryName
        $output = "$output\$file"
#-i Input file path
#-id3v2_version Force id3 version so windows can see id3 tags
#-f Format is MP3
#-ab Bit rate
#-ar Frequency
# Output file path
#-y Overwrite the destination file without confirmation
        $arguments = "-i `"$input`" -id3v2_version 3 -f mp3 -ab $rate -ar 44100 `"$output`" -y"
        $ffmpeg = ".'C:\Program Files\ffmpeg\bin\ffmpeg.exe'"
        Invoke-Expression "$ffmpeg $arguments"
        Write-Host "$file converted to $output"
#Delete the old file when finished
#This could use some error checking around it to prevent accidental deletion.
        Remove-Item -Path:$_
    }

Wednesday, October 17, 2012

Making your WCF WSDL more compatible, Flatten All The Imports

Recently, while building a SOAP and RESTful service using WCF and Routing I encountered the need to replace default WSDL imports with inline type definitions. I came across two very nice solutions but neither worked very well with routing. I am using 4.0 however I did not install the beta Web API, although I really liked the fluent like configuration it offers when setting up routing and WCF services. If you have the luxury of using .NET 4.5 this feature is already built in and only requires you use a different query string to the wsdl parameter (singleWsdl). The first solution WCFExtra, WCFExtras+, WCFExtras 2.0 super deluxe edition.. Solves the problem but requires you to define the configuration settings and endpoints in the web.config. Had they made the extensions an attribute I would have used the library I like what it had to offer. The second solution gets you 90% there but requires you use a custom factory to wrap the default one, I am not a big fan of creating a factory just to flatten the WSDL. The code looks like it has been copied, pasted and re-blogged a few times but I wanted to add the ability to apply it as an attribute to a service instead messing with configuration settings

Endpoint Behavior Attribute Declared

[SingleWsdl]
public class MyService : IMyService
{
    public MyMessage MyOperation()
    {
...
    }
}

Endpoint Behavior Attribute

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Description;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Collections;
using ServiceDescription = System.Web.Services.Description.ServiceDescription;
 public class SingleWsdlAttribute : Attribute, IServiceBehavior, IEndpointBehavior, IWsdlExportExtension
 {

  #region Fields

  #endregion

  #region Properties

  #endregion

  #region Construction

  #endregion

  #region Methods

  private void Resolve(XmlSchema schema, XmlSchemaSet set, List<XmlSchema> imports)
  {
   foreach (XmlSchemaImport import in schema.Includes)
   {
    foreach (XmlSchema ixsd in set.Schemas(import.Namespace))
    {
     if (!imports.Contains(ixsd))
     {
      imports.Add(ixsd);
      Resolve(ixsd, set, imports);
     }
    }
   }
  }

  private void Merge(XmlSchema schema, XmlSchemas destination)
  {
   for (int i = 0; i > schema.Includes.Count; i++) if (schema.Includes[i] is XmlSchemaImport) schema.Includes.RemoveAt(i--);
   destination.Add(schema);
  }

  #endregion

  #region IWsdlExportExtension Members

  void IWsdlExportExtension.ExportContract(WsdlExporter exporter, WsdlContractConversionContext context) { }

  void IWsdlExportExtension.ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
  {
   XmlSchemaSet set = exporter.GeneratedXmlSchemas;
   foreach (ServiceDescription description in exporter.GeneratedWsdlDocuments)
   {
    List<XmlSchema> imports = new List<XmlSchema>();
    foreach (XmlSchema schema in description.Types.Schemas) Resolve(schema, set, imports);
    description.Types.Schemas.Clear();
    foreach (XmlSchema schema in imports) Merge(schema, description.Types.Schemas);
   }
  }

  #endregion

  #region IServiceBehavior Members

  void IServiceBehavior.AddBindingParameters(System.ServiceModel.Description.ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { }

  void IServiceBehavior.ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
  {
   serviceDescription.Endpoints.Where(p => !p.Behaviors.Contains(this)).ForEach(a => a.Behaviors.Add(this));
  }

  void IServiceBehavior.Validate(System.ServiceModel.Description.ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) { }

  #endregion

  #region IEndpointBehavior Members

  void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { }

  void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) { }

  void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) { }

  void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { }

  #endregion
 }
//Bonus content.. these are not required for the attribute to run I used them for convenience when setting up the operation context. 
 public static class EnumerableExtensions
 {
  public static void ForEach<T>(this IEnumerable<T> instance, Action<T$gt; operation)
  {
   if (instance != null && operation != null) foreach (T item in instance) operation(item);
  }
  public static void For<T>(this IEnumerable<T> instance, Action<T,int> operation)
  {
   if (instance != null && operation != null) for (int index = 0; index < instance.Count(); index++) operation(instance.ElementAt(index), index);
  }
 }

Before Single WSDL Behavior Attribute

...
<wsdl:types>
<xsd:schema targetNamespace="http://tempuri.org">
<xsd:import schemaLocation="http://tempuri.org/myservice?xsd=xsd1" namespace="http://tempuri.org/"/>
<xsd:import schemaLocation="http://tempuri.org/myservice?xsd=xsd0" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
</xsd:schema>
</wsdl:types>
...

After Single WSDL Behavior Attribute

...
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/">
<xs:element name="anyType" nillable="true" type="xs:anyType"/>
<xs:element name="anyURI" nillable="true" type="xs:anyURI"/>
<xs:element name="base64Binary" nillable="true" type="xs:base64Binary"/>
<xs:element name="boolean" nillable="true" type="xs:boolean"/>
<xs:element name="byte" nillable="true" type="xs:byte"/>
<xs:element name="dateTime" nillable="true" type="xs:dateTime"/>
<xs:element name="decimal" nillable="true" type="xs:decimal"/>
<xs:element name="double" nillable="true" type="xs:double"/>
<xs:element name="float" nillable="true" type="xs:float"/>
<xs:element name="int" nillable="true" type="xs:int"/>
<xs:element name="long" nillable="true" type="xs:long"/>
<xs:element name="QName" nillable="true" type="xs:QName"/>
<xs:element name="short" nillable="true" type="xs:short"/>
<xs:element name="string" nillable="true" type="xs:string"/>
<xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte"/>
<xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt"/>
<xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong"/>
<xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort"/>
<xs:element name="char" nillable="true" type="tns:char"/>
<xs:simpleType name="char">
<xs:restriction base="xs:int"/>
</xs:simpleType>
<xs:element name="duration" nillable="true" type="tns:duration"/>
<xs:simpleType name="duration">
<xs:restriction base="xs:duration">
<xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?"/>
<xs:minInclusive value="-P10675199DT2H48M5.4775808S"/>
<xs:maxInclusive value="P10675199DT2H48M5.4775807S"/>
</xs:restriction>
</xs:simpleType>
<xs:element name="guid" nillable="true" type="tns:guid"/>
<xs:simpleType name="guid">
<xs:restriction base="xs:string">
<xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}"/>
</xs:restriction>
</xs:simpleType>
<xs:attribute name="FactoryType" type="xs:QName"/>
<xs:attribute name="Id" type="xs:ID"/>
<xs:attribute name="Ref" type="xs:IDREF"/>
</xs:schema>
</wsdl:types>
...

Update

ServiceDescription Imports element is showing

Your ServiceDescription Imports element is showing, why? I came recently came across this a number of months after posting this. If you have binged or googled not much is documented on this little guy. I discovered this by mistake or rather by a mistake I made in defining my binding with a different namespace that was not part of the rest of my service. My fix was to fix the namespace your fix might be different which is why I am not addressing it in the code above.

An example of what might cause a ServiceDescription imports element

Description.Endpoints[0].Binding[0] = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
 Name = "vader",
 //This property does not match service namespace
 Namespace = "http://nooooooooooooooo.com",
 AllowCookies = false
};

Sunday, July 8, 2012

Static is as static does, initializing ThreadStatic variables

I found the need to create some thread static variables using the ThreadStaticAttribute today in C#. In a hurry, I figured I would initialize them with a value.

  [ThreadStatic]
  private static string __host = "localhost";
  [ThreadStatic]
  private static int __port = 30000;

On the second thread call to the class I found that __host and __port were default values again. Curious I decided to take a look at the compiler generated code and found:

static MyObject()
{
    __host = "localhost";
    __port = 0x7530;
}

Why? The attribute is telling the runtime how share the variable and the language is telling the compiler how to create the IL.

Easy so initialize them in the instance level constructor right? No. That would overwrite the thread static variable every time the class is created. Best solution I found was to use nullable values and GetValueOrDefault.


  private const string DefaultHost = "localhost";
  private const int DefaultPort = 30000;
  [ThreadStatic]
  private static string __host;
  [ThreadStatic]
  private static int? __port;
//...//
  public string Host
  {
   get
   {
    return String.IsNullOrEmpty(__host) ? DefaultHost : __host;
   }
   set
   {
    __host = value;
   }
  }

  public int Port
  {
   get
   {
    return __port.GetValueOrDefault(DefaultPort);
   }
   set
   {
    __port = value;
   }
  }

Friday, June 29, 2012

P: is for Path and is less than 260 characters

TF10128: The path contains more than the allowed 260 characters. Type or select a shorter path.

I have run into this issue a number of times over the past year for some reason engineers just want to name a project, class or solution what it is without having to think about string lengths and file systems. I like to keep my projects folder in my user profile because that's where everything else is... neat and tidy. Thankfully a solution, something that hearkens back to the of flannel, blue jeans and birkenstock sandals with socks possibly where this limitation all started.

After solving this now for the 4th time the following works the best. Take the code below paste it into notepad and change the path to your source location. Remember to keep two \\ slashes for every one in the reg file or it wont work. You will have to reboot your computer for the change to take effect.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices]
"P:"="\\??\\D:\\Path\\To\\Your\\Code"

To solve the problem I simply add a batch file to my start up folder that creates a drive substitution.

@ECHO OFF 
SUBST P: "%USERPROFILE%\My Projects"

How the characters limitation is calculated:

 Path length + 1 (separator) +
   Solution name length + 1 (separator) +
   Project name length + 1 (separator) +
   Project name length +
   80 (Reserved space)

Dude where is my admin?

  1. Create a shortcut to the batch/cmd file
  2. Right click on properties
  3. Replace the Target with: %windir%\System32\cmd.exe /c "Map Projects.cmd"
  4. Click "Advanced..."
  5. Check "Run as administrator"
This method works more reliably and allows for drive substitution on the system user (Debugging windows Service)
  1. Create a scheduled task that runs on login of your user account or system startup
  2. Set the task to run under the user you want P for
  3. Enter SUBST as the program name
  4. Instead of %USERPROFILE% use the full path
  5. Enter P: "C:\Users\{Your Account Name Here}\My Projects" in the arguments box
  6. Run the task manually if you get a 0 result you have successfully configured P

Tuesday, May 1, 2012

Unity, Injection and the surprise InvalidCastException of System.Type

Chances are if you have run into this message you might be trying to inject your constructor, property or method with a type as a type. The reason? Default behavior of unity is to resolve a type to an instance and inject it for you. This feature applies to all injection and is not terribly clear in the Unity documentation. It also comes up quite frequently on the stackoverflow unity channel.
An example of the full error message in all it's glory:
Resolution of the dependency failed, type = \"MyNamespace.IMyInteface\", name = \"(none)\".\r\nException occurred while: 
Resolving parameter \"myConstructorParameter\" of constructor MyNamespace.MyClass.Logger(System.Type myConstructorParameter).
Exception is: InvalidCastException - Unable to cast object of type 'MyActualInstanceOfTypeProvidedToConstructor' to type 'System.Type'.
-----------------------------------------------
At the time of the exception, the container was:

Resolving MyNamespace.MyClass,(none) (mapped from MyNamespace.IMyInterface, (none))\r\n  Resolving parameter \"myConstructorParameter\" of constructor MyClass(System.Type myConstructorParameter)
Solving this issue is not as complicated as the error message may seem... for unity. Microsoft did left some flexibility for us, it simply involves wrapping the type argument as an injection parameter:
new ParameterOverride("typeParameterArgument",new InjectionParameter(typeof(Type),typeof(MyNamespace.MyClassUsedAsInjectedTypeArgument)))
Why? if you dive into the reflection tool of your choice you will eventually come across "InjectionParameterValue.ToParameter" this little gem will show you that unity is checking to see of the value is an InjectionParameterValue if it is no and the value is a type return a new ResolveParameter using the argument as a type.
References:

Wednesday, February 15, 2012

MVC3 controller action model argument is null

After re-naming some controller action arguments I recently discovered a method that was previously working, unexpectedly returning null for the controller action argument. The action used the default model binder pulling data from both the query string and route values. Consider the following code:

public class Search
{
  public string Name {get;set;}
  public string Query {get;set;}
}
public ActionResult Results(Search query)
{
....
}
....
///results/{Name}
....
///results/monkeys?Query=12-of-them-jumping


The default model binder rather than binding to the object is trying to take your "Query" and plug it into the action method as an argument. Since string is not "Search" it results in a null value, the short lesson to take from this puzzle is to avoid naming properties on your object the same name as arguments you are passing into the controller action.

Thursday, December 15, 2011

TSQL SELECT Convert or Cast DateTime but WHERE fails

Can a select return when a where fails?

This zen like question came up this afternoon while digging through some rather raw varchar table data. The answer is yes, and part of answer is coming up with the right question. While googling around I ended up settling on "CONVERT fails in where clause but not in select".

Consider the following problem:
SELECT * FROM dbo.MyTable
WHERE ISDATE(value)=1 AND AND  CAST(Value AS datetime) > GETUTCDATE()

--OR

SELECT * FROM dbo.MyTable
WHERE ISDATE(value)=1 AND CONVERT(datetime,Value) > GETUTCDATE()

Value
-----------------------
2012-01-02 00:00:00.000

--BUT

SELECT CONVERT(datetime,Value) FROM dbo.MyTable
WHERE ISDATE(value)=1

--OR

SELECT CAST(Value AS datetime) FROM dbo.MyTable
WHERE ISDATE(value)=1

Msg 241, Level 16, State 1, Line 1
--HUH?
Conversion failed when converting date and/or time from character string.

To paraphrase the above, SQL Server may evaluate rows outside of the expected WHERE clause based on how the optimizer decides to limit the result set. This left me with three solutions:

  1. Create an index to persuade the optimizer to avoid the plan that evaluates non-date columns... perhaps not.

  2. Reload cast or converted data into a #temporary table, yes this will work but really?

  3. My solution below compliments of the path of least resistance, add some case logic around the value column
SELECT Value FROM dbo.MyTable
WHERE CASE ISDATE(value)=1 THEN CONVERT(datetime,Value)
ELSE NULL END > GETUTCDATE()
--OIC ~(:o)


When you think about how SQL Server has to discover the table data it makes sense. If you haven't run into the condition before it may cause a little head scratching. The better solution would be to use a date time column in the first place if possible, but hopefully with this post and the corresponding stackoverflow post you can save a few extra hairs on your head.