Weekend Scripter: A Cool Music Explorer Script

Weekend Scripter: A Cool Music Explorer Script

  • Comments 5
  • Likes

 

Microsoft Scripting Guy Ed Wilson here. I don’t know about you, but I have thousands of music files on my computer. In fact, I have exactly 4,018 music files. I used this Windows PowerShell command to count them:

PS E:\Music> (dir -Include *.mp3,*.wma -Recurse | Measure-Object).count
4018
PS E:\Music>

One problem with having 4,018 music files on my computer is that at times I am not sure what I want to listen to, or even what a particular song might sound like. Whereas the Zune software is really cool and has several nice modes for playing music, at times I prefer a bit more automated operation. Unfortunately, there are no Zune HD cmdlets available—at least none that I know of.

Luckily, Windows PowerShell has access to the .NET Framework, and using some basic Windows PowerShell techniques and an easy-to-use .NET Framework class, a pretty cool music explorer script can be easily crafted.

The system.windows.media.mediaplayer .NET Framework class is just the tool that we need. Actually, the class name is mediaplayer, and it is found in the system.windows.media .NET Framework namespace. Namespaces in the .NET Framework work like folders that group similar items together, or like DNS domain names that group computers into domains. The MSDN .NET Framework documentation makes fascinating reading, and the fact that the classes are grouped into namespaces makes it easy to peruse. I often spend entire weekends browsing the namespaces, selecting interesting classes, and trying them out in Windows PowerShell scripts. The system.windows.media .NET Framework namespace documentation is shown in the following image.

Image of system.windows.media .NET Framework namespace documentation

I just click through the classes until I see something that sounds cool. I then read about it, and try it out. Often, there are useful code samples that illustrate using the classes; unfortunately, there are almost no code samples in Windows PowerShell. A few of the Microsoft PowerShell MVPs such as Thomas Lee have added useful samples written in Windows PowerShell for some of the major .NET Framework classes (these show up in the discussion section at the very bottom of the page).

The mediaplayer class is not immediately available. The first thing to do is to load the assembly that contains the class. MSDN tells you in which assembly a class resides, and that is what you will use with the Add-Type Windows PowerShell cmdlet. The mediaplayer class resides in an assembly called presentationCore. To load the presentationCore assembly, use the command shown here:

Add-Type -AssemblyName presentationCore

After the assembly has been loaded, it is time to create an instance of the mediaplayer class. To do this, you will need to look up the mediaplayer constructor on MSDN. Luckily, we do not need to pass anything to New-Object when we create the instance of the mediaplayer class. Sometimes, .NET Framework classes require one or more parameters to be supplied when creating a new instance of the class, and therefore it is always a good idea to review the constructor details in MSDN before attempting to create the class. Because the mediaplayer class requires no parameters, the code to create the class is simple and is shown here:

$mediaPlayer = New-Object system.windows.media.mediaplayer

The path variable is used to store the path to the music files I wish to gather, and the Get-ChildItem cmdlet is used to collect those files. The script retrieves both MP3 and WMA files and stores the returned system.io.fileinfo objects in the $files variable, as shown here:

$path = "E:\Music\classical\Luciano Pavarotti"
$files = Get-ChildItem -path $path -include *.mp3,*.wma -recurse

After the collection of files is stored in the $files variable, the Foreach statement is used to walk through the collection. Inside the loop, each file is referenced by the $file variable. This is shown here:

foreach($file in $files)
{

The basename property was added in Windows PowerShell 2.0 to the fileinfo class, and it is the file name minus the file extension. This is used to display an indicator of the song that is being played and is shown here:

"Playing $($file.BaseName)"

The fullname property from the fileinfo class contains the complete path to the file. This path is converted to a system.uri .NET Framework class, and is passed to the open method of the mediaplayer class. One of the system.uri constructors accepts a string. When accessing a .NET Framework class, the name can be placed inside square brackets and the Word system left off. This gives us [uri]. The constructor is then supplied to it directly. This is illustrated here:

PS C:\> [uri]"E:\Music\classical\Luciano Pavarotti\Live 1961-1966\01.wma"


AbsolutePath   : E:/Music/classical/Luciano%20Pavarotti/Live%201961-1966/01.wma
AbsoluteUri    : file:///E:/Music/classical/Luciano%20Pavarotti/Live%201961-1966/01.
                
wma
Authority      :
Host           :
HostNameType   : Basic
IsDefaultPort  : True
IsFile         : True
IsLoopback     : True
IsUnc          : False
LocalPath      : E:\Music\classical\Luciano Pavarotti\Live 1961-1966\01.wma
PathAndQuery   : E:/Music/classical/Luciano%20Pavarotti/Live%201961-1966/01.wma
Port           : -1
Query          :
Fragment       :
Scheme         : file
OriginalString : E:\Music\classical\Luciano Pavarotti\Live 1961-1966\01.wma
DnsSafeHost    :
IsAbsoluteUri  : True
Segments       : {/, E:/, Music/, classical/...}
UserEscaped    : False
UserInfo       :



PS C:\>

Because the open method of the mediaplayer class accepts a system.uri, I do not need to worry about which property to supply. The mediaplayer class is smart enough to choose what I need from the incoming object. A subexpression is used on the fullname property of the fileinfo class to retrieve the string path to the file. This portion of the script is shown here:

$mediaPlayer.open([uri]"$($file.fullname)")

The remainder of the script is really easy. I call the play method from the mediaplayer class, use the Start-Sleep Windows PowerShell cmdlet to pause the script for 10 seconds, and then call the stop method to halt playing the tune. I then loop around to the next file in the $files collection. This is shown here:

$mediaPlayer.Play()
 
Start-Sleep -Seconds 10
 
$mediaPlayer.Stop()
}

The complete PlayMedia.ps1 script is shown here.

PlayMedia.ps1

Add-Type -AssemblyName presentationCore
$mediaPlayer = New-Object system.windows.media.mediaplayer
$path = "E:\Music\classical\Luciano Pavarotti"
$files = Get-ChildItem -path $path -include *.mp3,*.wma -recurse
foreach($file in $files)
{
"Playing $($file.BaseName)"
 
$mediaPlayer.open([uri]"$($file.fullname)")
 
$mediaPlayer.Play()
 
Start-Sleep -Seconds 10
 
$mediaPlayer.Stop()
}

When the script runs, each file name is displayed in either the Windows PowerShell console or the Windows PowerShell ISE output pane, depending on how you launched the script.

We invite you to follow us on Twitter and Facebook. If you have any questions, send email to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Now I'm satisfied!

    while ($true) {

    $mediaPlayer.Open([uri]"$($music[(Get-Random -min 0 -max ($music.Count-1))].fullname)") # play random file!

    $mediaPlayer.Play()

    "Playing => $mediaPlayer.Source.LocalPath"

    Start-Sleep -seconds 2 # need to wait for mediaPlayer to determine file duration

    Start-Sleep -seconds $mediaPlayer.NaturalDuration.TimeSpan.TotalSeconds

    }

  • Cool!

  • "I often spend entire weekends browsing the namespaces, selecting interesting classes, and trying them out in Windows PowerShell scripts"

    And here I thought my weekends we fun :)

  • When I try to run this, I get "The calling thread cannot access this object because a different thread owns it"

  • Why don't you just create a playlist? Then you can let Windows Media Player handle the rest, like shuffle etc. Spares you a Powershell Console while playing:

    [System.Reflection.Assembly]::LoadWithPartialName("System.Web");

    $f = '\\1.1.1.250\music\_playlists\All Except XMas.wpl';

    '<?wpl version="1.0"?>' | set-content $f;

    '<smil>' | add-content $f;

    '    <head>' | add-content $f;

    '        <meta name="Generator" content="Jan Kooy: Powershell Script" />' | add-content $f;

    '        ' | add-content $f;

    '    </head>' | add-content $f;

    '    <body>' | add-content $f;

    '        <seq>' | add-content $f;

    get-childitem '\\1.1.1.250\music\mp3' -recurse | foreach-object {

       if ($_.gettype().name -ieq 'fileinfo') {

           if (-not $_.fullname.tolower().contains('xmas')) {

               if ($_.extension -eq '.flac' -or $_.extension -eq '.mp3' -or $_.extension -eq '.m4a') {

    '            <media src="'+[system.web.httputility]::htmlencode($_.fullname)+'" />';

               };

           };

       };

    }  | add-content $f;

    '        </seq>' | add-content $f;

    '    </body>' | add-content $f;

    '</smil>' | add-content $f;