<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Media And Microcode : Get-RecordedTV</title><link>http://blogs.msdn.com/mediaandmicrocode/archive/tags/Get-RecordedTV/default.aspx</link><description>Tags: Get-RecordedTV</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Microcode: Cleaning up DVR with Get-RecordedTV</title><link>http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/21/microcode-cleaning-up-dvr-with-get-recordedtv.aspx</link><pubDate>Mon, 21 Jul 2008 03:51:01 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8759886</guid><dc:creator>JamesBrundage</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/mediaandmicrocode/comments/8759886.aspx</comments><wfw:commentRss>http://blogs.msdn.com/mediaandmicrocode/commentrss.aspx?PostID=8759886</wfw:commentRss><description>&lt;p&gt;The first simple way we can approach cleaning up DVR is by making the assertion that shows you don't want as much you won't record as often.&amp;#160; Suppose you record a movie once, or a game, or the pilot of a really bad TV show.&amp;#160;&amp;#160; After a month or so it's still on your hard drive, taking up space but for some reason avoiding the Windows Media Center's cleanup bots.&lt;/p&gt;  &lt;p&gt;Now that we've got a &lt;a href="http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/21/microcode-cleaning-up-get-recordedtv-with-select-object.aspx"&gt;fairly mature Get-RecordedTV&lt;/a&gt;, we can use it to delete a certain number of files from your collection until we reach a desired amount of freed space.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;function Remove-DVRBySeries($cleanupSize = 1gb, $episodeCount = 1)     &lt;br /&gt;{      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $shows = @()      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; # Pipe the results of Get-RecordedTV so that we can see progress      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $perc = 0 &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; Get-RecordedTV | Foreach-Object {     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; $perc+=5      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if ($perc -gt 100) { $perc = 0 }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Write-Progress &amp;quot;Getting DVR Metadata&amp;quot; &amp;quot;$($_.Series) - $($_.Episode) - $($_.Channel)&amp;quot; -perc $perc      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; $shows+=$_      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $showsbySeries = $shows | Group-Object Series      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; $allSeries = $showsBySeries |      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Where-Object { $_.Count -le $episodeCount} |      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Sort-Object Count      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; foreach ($series in $allSeries)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; foreach ($e in $series.Group) {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; $response =Read-Host &amp;quot;Delete $($e.Series) - $($e.Episode) (Yes/No)&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if ($response -ilike &amp;quot;*Yes*&amp;quot;) {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if ($e.File) {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Write-Progress &amp;quot;Deleting $($e.Series) - $($e.Episode)&amp;quot; `      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;quot;$($e.Size / 1mb) megabytes saved - $(($cleanupSize - $e.Size)/1mb) remaining&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Remove-Item $e.File      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; $cleanupSize-=$e.Size      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } else {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Write-Error &amp;quot;$($e.Series) - $($e.Episode) File Not Found&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } else {      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Write-Progress &amp;quot;Skipping show by user request&amp;quot; &amp;quot;$($e.Series) - $($e.Episode)&amp;quot;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; if ($cleanupSize -le 0) { return }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }      &lt;br /&gt;}&lt;/p&gt;    &lt;p&gt;&amp;#160;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This script will remove shows with one or fewer episodes until it has saved 1gb of space.&amp;#160; If you want to make it save more, use the cleanupSize parameter.&amp;#160; If you want it to check series with more episodes, use the -episodeCount parameter. It will ask to make sure you want to delete each show, and will stop once it has saved you enough space.&lt;/p&gt;  &lt;p&gt;The script above uses a lot of simple PowerShell benefits.&amp;#160; Let's take a closer look at each:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;CleanupSize uses 1gb as its default.&amp;#160; In PowerShell, you can use gb,mb,and kb as numbers, e.g. 1gb, 10mb, or 640kb&lt;/li&gt;    &lt;li&gt;By piping Get-RecordedTV to ForEach-Object, I am able to store the results of Get-RecordedTV as they come in, instead of waiting for all of them to be completed, I can use Write-Progress to display each show as it comes in.&amp;#160; Since I don't know how long it will take, I can just wrap the progress bar around whenever I get to 100%.&lt;/li&gt;    &lt;li&gt;Group-Object will group all of the shows by the value of the series property&lt;/li&gt;    &lt;li&gt;Sort-Object is used to sort the results of Group-Object by the number of items in each group (in our case, the number of recorded episodes of a series)&lt;/li&gt;    &lt;li&gt;Write-Progress is used again to update the user as files are deleted or skipped&lt;/li&gt;    &lt;li&gt;Read-Host displays a prompt and asks for user input&lt;/li&gt;    &lt;li&gt;-ilike is a case insentive like, so $response -ilike will match &amp;quot;Yes&amp;quot;, &amp;quot;yeS', or &amp;quot;Yes Sir&amp;quot;, sadly, the logic will also catch &amp;quot;Yes, I mean No!&amp;quot;&lt;/li&gt;    &lt;li&gt;Write-Error politely informs me that a file cannot be deleted, but does not stop execution of the script (this the&amp;#160; important difference between Write-Error and throw)&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;That's it.&amp;#160; We've brought one train of thought to an end.&amp;#160; With this script, I was able to quickly and easily cleanup almost 40gb of shows that I wasn't watching.&amp;#160; I hope it helps you clean up your hard drive, and I hope that explaining how to get here has increased your understanding of PowerShell scripting and problem solving.&lt;/p&gt;  &lt;p&gt;Hope this helps,&lt;/p&gt;  &lt;p&gt;James&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8759886" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/PowerShell/default.aspx">PowerShell</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Microcode/default.aspx">Microcode</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/DVR/default.aspx">DVR</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Get-RecordedTV/default.aspx">Get-RecordedTV</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Write-Error/default.aspx">Write-Error</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Sort-Object/default.aspx">Sort-Object</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Write-Progress/default.aspx">Write-Progress</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Group-Object/default.aspx">Group-Object</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Remove-DVRBySeries/default.aspx">Remove-DVRBySeries</category></item><item><title>Microcode: Cleaning up Get-RecordedTV with Select-Object</title><link>http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/21/microcode-cleaning-up-get-recordedtv-with-select-object.aspx</link><pubDate>Mon, 21 Jul 2008 02:28:15 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8759683</guid><dc:creator>JamesBrundage</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/mediaandmicrocode/comments/8759683.aspx</comments><wfw:commentRss>http://blogs.msdn.com/mediaandmicrocode/commentrss.aspx?PostID=8759683</wfw:commentRss><description>&lt;p&gt;In a previous post, I introduced &lt;a href="http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/13/microcode-getting-dvr-metadata-with-search-windowsdesktop.aspx"&gt;Get-RecordedTV&lt;/a&gt;, which was built upon another function, &lt;a href="http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/13/microcode-windows-powershell-windows-desktop-search-problem-solving.aspx"&gt;Search-WindowsDesktop&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;The old version of Get-RecordedTV directly returned the properties related to DVR from Windows Desktop Search, with incredibly long names like 'System.RecordedTV.IsRepeatBroadcast'.&amp;#160; In the interests of making future code shorter and explaining one of the free benefits you get with objects in PowerShell (Select-Object), I'm going to start by giving a new Get-RecordedTV.&amp;#160; Instead of giving you the properties directly from Desktop Search, this Get-RecordedTV reinterprets the results to make them easier to read.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;function Get-RecordedTV() {     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Search-WindowsDesktop 'System.Title',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.EpisodeName',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.ChannelNumber',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.StationName',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.IsRepeatBroadcast',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.StationCallsign',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.ProgramDescription',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.IsSAP',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.IsHDContent',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.RecordedTV.RecordingTime',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.Video.FrameHeight',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.Video.FrameWidth',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.Video.EncodingBitrate',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.Media.DateEncoded',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.ParentalRating',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.Size',      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; 'System.ItemPathDisplay' &amp;quot;WHERE System.RecordedTV.ChannelNumber IS NOT NULL&amp;quot; |      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Select-Object @{Name='Series';Expression={$_.'System.Title'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='Episode';Expression={$_.'System.RecordedTV.EpisodeName'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='Description';Expression={$_.'System.RecordedTV.ProgramDescription'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='Channel';Expression={      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;quot;$($_.'System.RecordedTV.ChannelNumber')-$($_.'System.RecordedTV.StationCallsign')-$($_.'System.RecordedTV.StationName')&amp;quot;      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='File';Expression={      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Get-Item $_.'System.ItemPathDisplay' -errorAction SilentlyContinue      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='Size';Expression={$_.'System.Size'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='DateRecorded';Expression={$_.'System.RecordedTV.RecordingTime'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='IsSAP';Expression={$_.'System.RecordedTV.IsSAP'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='IsHD';Expression={$_.'System.RecordedTV.IsHDContent'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='IsRepeatBroadcast';Expression={$_.'System.RecordedTV.IsRepeatBroadcast'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='Width';Expression={$_.'System.Video.FrameWidth'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='Height';Expression={$_.'System.Video.FrameHeight'}},      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @{Name='Rating';Expression={$_.'System.ParentalRating'}}      &lt;br /&gt;}&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This entire function is still one pipeline.&amp;#160; It gets the results from Search-WindowsDesktop, and, as each item arrives, it pipes the results to Select-Object.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://technet.microsoft.com/en-us/library/bb978655(TechNet.10).aspx"&gt;Select-Object&lt;/a&gt; is an incredibly useful PowerShell Cmdlet.&amp;#160; It is used to select one or more objects from a type, and return them in a bag of properties.&amp;#160; While it works great for selecting just a property or two (e.g. Get-Process | Select Name, ID), it can also be used in some interesting other ways.&lt;/p&gt;  &lt;p&gt;The way I use it above is to rename a number of properties.&amp;#160; If you have an parameter in Select-Object that takes the format @{Name='&lt;em&gt;String';Expression={expression}}, &lt;/em&gt;you will create a new property on the object with the value of the expression.&amp;#160; You can refer to something on the current object by using $_ (which, in PowerShell, is pretty commonly the current element in the pipeline).&amp;#160; You can also use this to run methods and return their values as properties.&amp;#160; And, finally, you can actually create a type that is only properties this way, by using a simple other PowerShell coolness, the range operator.&lt;/p&gt;  &lt;p&gt;So, to create one hundred anonymous objects with two random numbers:&lt;/p&gt;  &lt;p&gt;$random=New-Object Random   &lt;br /&gt;1..100 |    &lt;br /&gt;&amp;#160; Select-Object @{Name='Random1';Expression={$random.Next()}},    &lt;br /&gt;&amp;#160; @{Name='Random2';Expression={$random.Next()}}&lt;/p&gt;  &lt;p&gt;People familiar with functional programming might know this sort of thing as a &lt;a href="http://blogs.msdn.com/ericwhite/pages/Tuples-and-Anonymous-Types.aspx"&gt;Tuple&lt;/a&gt;, but knowing the correct terminology doesn't help the usefulness sink in all that much to me.&lt;/p&gt;  &lt;p&gt;In the case of Get-RecordedTV, the first use is pretty obvious: it reduces the amount of typing for each property name and makes it easier to identify.&amp;#160; The second advantage is more subtle.&amp;#160; By re-interpreting the return type from Search-WindowsDesktop to be a bunch of properties that the system decides to a bunch of properties that I choose, it means that I can make the dependency on Search-WindowsDesktop removable.&amp;#160; In doing so, I hurt anyone that has already built scripts around Get-RecordedTV, because they have to change their scripts to handle a new signature for the anonymous type, because the change subtracted types.&lt;/p&gt;  &lt;p&gt;Subtractive changes break backwards compatibility, but in this case it frees me from the stack that I used to depend on.&amp;#160; This is a wonderful flexibility of writing in an interpreted language, and can be valuable because you can use it to make a flexible dependency instead of a fixed one.&amp;#160; With a flexible dependency, if I find out that they have turned Windows Desktop Search off, or I find a better mechanism down the line, I can replace my dependency on Search-WindowsDesktop with another technology.&lt;/p&gt;  &lt;p&gt;I could write an interface to express the data I want to get, but that's overkill for my situation.&amp;#160; If I write an interface, then I have to have a real type already loaded, and I've swapped one dependency (on Search-WindowsDesktop) for two ( the type that implements the interface and the interface type ) and have essential hard-bolted a dependency on my interface to any application that wants to use the data.&lt;/p&gt;  &lt;p&gt;Anonymous types allow you to get most of the benefits of interfaces with a flexible dependency.&amp;#160; As long as you keep the bag of properties the same, you can build upon scripts that return anonymous properties.&amp;#160; You can use this to coerce data into forms that are easier to remember, and to summarize data in simpler forms.&amp;#160; You will also find that a number of cmdlets within PowerShell (Select-Object, Sort-Object, and Group-Object) work amazingly well with bags of properties.&lt;/p&gt;  &lt;p&gt;In a later posts, we'll use this base of code and these cmdlets to prune the collection, and demonstrate how the use of property bags and the exploration into Windows Desktop Search leads to a variety of wonderful side effects.&lt;/p&gt;  &lt;p&gt;Hope this Helps,&lt;/p&gt;  &lt;p&gt;James Brundage [MSFT]&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8759683" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/PowerShell/default.aspx">PowerShell</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Microcode/default.aspx">Microcode</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Get-RecordedTV/default.aspx">Get-RecordedTV</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Select-Object/default.aspx">Select-Object</category></item><item><title>Microcode: Getting DVR MetaData with Search-WindowsDesktop</title><link>http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/13/microcode-getting-dvr-metadata-with-search-windowsdesktop.aspx</link><pubDate>Mon, 14 Jul 2008 00:35:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8729859</guid><dc:creator>JamesBrundage</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/mediaandmicrocode/comments/8729859.aspx</comments><wfw:commentRss>http://blogs.msdn.com/mediaandmicrocode/commentrss.aspx?PostID=8729859</wfw:commentRss><description>&lt;P&gt;&lt;A href="http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/13/microcode-windows-powershell-windows-desktop-search-problem-solving.aspx" mce_href="http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/13/microcode-windows-powershell-windows-desktop-search-problem-solving.aspx"&gt;In the previous post&lt;/A&gt;, i explained that I was trying to find a way to get at the information about my TV shows, and introduced you to some of the thought process that went into finding out information about my DVR.&amp;nbsp; I then solved the most important portion of my DVR problem (extracting out the metadata) in a way that was flexible enough to apply to be used for other potential projects, like Getting Photo Metadata or Getting Contact Metadata.&lt;/P&gt;
&lt;P&gt;Here's the Search-WindowsDesktop function again:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;function Search-WindowsDesktop($fields, $filter) &lt;BR&gt;{ &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $connection = New-Object -comObject "ADODB.Connection" &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $recordSet = New-Object -comObject "ADODB.RecordSet" &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $null = $connection.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';") &amp;gt; $null &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $ofs = "," &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $query = "SELECT $fields" &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $query += " FROM SYSTEMINDEX $filter" &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $null = $recordSet.Open($query, $connection) &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ($recordSet.EOF) { return } &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $recordSet.MoveFirst() &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (-not $recordSet.EOF) {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $result = New-Object Object&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach ($field in $fields) { &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $result | Add-Member NoteProperty $field $recordSet.Fields.Item($field).Value &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $result &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $recordSet.MoveNext() &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $null = $recordSet.Close() &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $null = $connection.Close() &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $connection = $null &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $recordSet = $null &lt;BR&gt;} &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I also introduced the way I think solve problems by looking at the smaller problems they contained.&amp;nbsp; My starting point was that I was trying to prune my Windows Media Center dvrms files.&amp;nbsp; I could have gone about getting rid of Windows Media Center files in a less elegant way, but I would have regretted it.&amp;nbsp; For instance, I could have pruned the files just on size, but I would have Lost &lt;EM&gt;Lost.&amp;nbsp; &lt;/EM&gt;But going down the path of getting metadata lets me do more related tasks.&lt;/P&gt;
&lt;P&gt;I strongly believe that whenever problem solving, it is key to examine not just the problems that you are working with, but to look at the related problems.&amp;nbsp; The root problem in this case was that dvrms files are my appetite for video on demand are both large enough to flood my disk.&amp;nbsp; If I pruned my collection on size, not only would I have lost &lt;EM&gt;Lost&lt;/EM&gt;, but I would only have the option of losing the files forever.&amp;nbsp; By getting the DVR metadata, I open up the door to a number of more promising paths:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;I can back up certain shows to other hard drives&lt;/LI&gt;
&lt;LI&gt;If I can find a way to remove commercials&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;I can burn a DVD of the files&lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;If I can find the right API,&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;I can convert the files to another file format&lt;/LI&gt;&lt;/UL&gt;&lt;/UL&gt;
&lt;P&gt;Getting the metadata solves the problem in a better and more flexible way than does chopping by file size.&amp;nbsp; &lt;A href="http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/13/microcode-windows-powershell-windows-desktop-search-problem-solving.aspx" mce_href="http://blogs.msdn.com/mediaandmicrocode/archive/2008/07/13/microcode-windows-powershell-windows-desktop-search-problem-solving.aspx"&gt;Researching it&lt;/A&gt; also exposed me to other cool things that I can do, and give me a reference to use while creating my DVR Metadata script.&amp;nbsp; Using PowerShell, I tried running queries one by one for each of the properties on the &lt;A href="http://www.microsoft.com/technet/scriptcenter/topics/desktop/wdsprops.mspx#E6HAE" mce_href="http://www.microsoft.com/technet/scriptcenter/topics/desktop/wdsprops.mspx#E6HAE"&gt;Recorded TV&lt;/A&gt; reference to discover which ones work and determined if the return values were helpful.&lt;/P&gt;
&lt;P&gt;Here's the result of my efforts:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;function Get-RecordedTV() { &lt;BR&gt;&amp;nbsp; Search-WindowsDesktop 'System.Title', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.EpisodeName', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.ChannelNumber', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.StationName', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.IsRepeatBroadcast', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.StationCallsign', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.ProgramDescription', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.IsSAP', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.IsHDContent', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.RecordingTime', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.Video.FrameHeight', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.Video.FrameWidth', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.Video.EncodingBitrate', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.Media.DateEncoded', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.ParentalRating', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.Size', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.ItemURL', &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'System.RecordedTV.RecordingTime' "WHERE System.RecordedTV.ChannelNumber IS NOT NULL" &lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;I know it really doesn't look like it, but that can be written as a one-liner.&amp;nbsp; By using an existing system and writing a generic function, I was able to write this Get-RecordedTV function simply by providing the data I needed to my Search-WindowsDesktop function.&lt;/P&gt;
&lt;P&gt;Since the Search-WindowsDesktop function returns objects with property names, it is possible to use a number of built-in PowerShell commands to help you look through the data.&lt;/P&gt;
&lt;P&gt;Here are the Cmdlets you can use to explore objects:&lt;/P&gt;
&lt;TABLE class="" cellSpacing=0 cellPadding=2 width=873 border=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=198&gt;Group-Object&lt;/TD&gt;
&lt;TD class="" vAlign=top width=673&gt;Creates groups of objects on a type or expression&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=198&gt;Select-Object&lt;/TD&gt;
&lt;TD class="" vAlign=top width=673&gt;Selects properties from an object or make new ones on the fly &lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=198&gt;Sort-Object&lt;/TD&gt;
&lt;TD class="" vAlign=top width=673&gt;Sort a list of objects by a property&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=198&gt;Where-Object (or ?)&lt;/TD&gt;
&lt;TD class="" vAlign=top width=673&gt;Filters the objects by an expresson&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=198&gt;Foreach-Object (or %)&lt;/TD&gt;
&lt;TD class="" vAlign=top width=673&gt;Performs an action on each of the input objects&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;One quick thing I can see this way is what TV station I watch the most:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;Get-RecordedTV | Group-Object 'System.RecordedTV.StationCallSign' | Sort Count -Descending&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Another thing I can do with a simple change is group by Parental Rating instead&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;Get-RecordedTV | Group-Object 'System.ParentalRating' | Sort Count -Descending&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;And of course, I can sort on the size and the time recorded:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;Get-RecordedTV | sort 'System.RecordedTV.RecordingTime' -descending&lt;/P&gt;
&lt;P&gt;Get-RecordedTV | sort 'System.Size' -descending&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Now I have the potential to build a much more flexible TV pruning solution.&amp;nbsp; In a future post, I'll show how you can resolve the ItemURL to a real file and, finally, how to prune the files you don't want by any criteria you'd like.&lt;/P&gt;
&lt;P&gt;Hope this helps,&lt;/P&gt;
&lt;P&gt;James Brundage&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8729859" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Windows+Media+Center/default.aspx">Windows Media Center</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/MetaData/default.aspx">MetaData</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Windows+Desktop+Search/default.aspx">Windows Desktop Search</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/PowerShell/default.aspx">PowerShell</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Microcode/default.aspx">Microcode</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/DVR/default.aspx">DVR</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Get-RecordedTV/default.aspx">Get-RecordedTV</category><category domain="http://blogs.msdn.com/mediaandmicrocode/archive/tags/Search-WindowsDesktop/default.aspx">Search-WindowsDesktop</category></item></channel></rss>