Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

Easily download Ch9 videos

, 15 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
I watch and download a lot of videos from Channel 9 and because I’m a developer and always looking for ways to speed up anything I do I searched for a PowerShell script. I eventually found one (can’t remember where) and immediately set it up to download This Week on Channel 9 and Ping Show. Over tim

I watch and download a lot of videos from Channel 9 and because I’m a developer and always looking for ways to speed up anything I do I searched for a PowerShell script. I eventually found one (can’t remember where) and immediately set it up to download This Week on Channel 9 and Ping Show. Over time I have added many other shows, events and series. The initial script I was found with some modifications was

$url=<span style="color: #006080">"http://channel9.msdn.com/Shows/This+Week+On+Channel+9/feed/mp4high"</span>
$rss=invoke-webrequest -uri $url 
$destination=<span style="color: #006080">"Z:\Media\Videos\ch9\This Week on ch9\"
[xml]$rss.Content|foreach{ 
  $_.SelectNodes("</span>rss/channel/item/enclosure<span style="color: #006080">") 
}|foreach{ 
    "</span>Checking $($_.url.split(<span style="color: #006080">"/"</span>)[-1]), we will skip it <span style="color: #0000ff">if</span> it already exists <span style="color: #0000ff">in</span> $($destination)<span style="color: #006080">"
  if(!(test-path ($destination + $_.url.split("</span>/<span style="color: #006080">")[-1]))){ 
    "</span>Downloading: " + $_.url 
    start-bitstransfer $_.url $destination
  } 
}

Because of the amount of shows growing that I was interested in I added another piece to this. On the root level of all my Channel 9 videos I added a PowerShell file that would run all the other PowerShell files that looked like

cd Z:\Media\Videos\ch9

$psScripts = Get-ChildItem -Recurse -Filter <span style="color: #006080">"*.ps1"</span> | ForEach-Object -Process { <span style="color: #0000ff">if</span>($_.Name -ne <span style="color: #006080">"Download All.ps1"</span>){ Write-Text <span style="color: #006080">"$($_.Name)"</span>; . $_.FullName; } }

Write-Output <span style="color: #006080">"Done"</span>

This was working great as I wouldn’t need to go and run multiple files in order to update all my videos. Recently because of all the new stuff that was released because of the announcements  at #VS2013Launch I had to add about 5 new feeds to my collection. This took quite a while as I needed to create a new folder for the show, then get a copy of the first PowerShell script above and then alter the feed url and location to save the videos to. This lead to yet another script, the way I saw it I could either write a script that automates the create of the folder and script or I could do the longer but right thing and start “fresh”. The script I now run takes in an array of the url of the show, series, event or any other type of channel that has an RSS feed, basically everything after http://channel9.msdn.com/. The new and improved script looks like

cls

Write-Output <span style="color: #006080">"Starting"</span>

$feedTypeNameList = $(<span style="color: #006080">"Events/Build/2014"</span>, 
                        <span style="color: #006080">"Shows/PingShow"</span>,
                        <span style="color: #006080">"Shows/This+Week+On+Channel+9"</span>, 
                        <span style="color: #006080">"Series/Microsoft-Research-Luminaries"</span>,
                        <span style="color: #006080">"Shows/Windows-Azure-Friday"</span>,
                        <span style="color: #006080">"Blogs/One-Dev-Minute"</span>,
                        <span style="color: #006080">"Shows/Visual-Studio-Toolbox"</span>,
                        <span style="color: #006080">"Events/Visual-Studio/Launch-2013"</span>,
                        <span style="color: #006080">"Series/Application-Insights-for-Visual-Studio-Online"</span>,
                        <span style="color: #006080">"Series/Visual-Studio-Online"</span>,
                        <span style="color: #006080">"Series/Visual-Studio-Online-Monaco"</span>,
                        <span style="color: #006080">"Series/Visual-Studio-2012-Premium-and-Ultimate-Overview"</span>, 
                        <span style="color: #006080">"Blogs/MadsKristensen"</span>, 
                        <span style="color: #006080">"Blogs/C9Team"</span>, 
                        <span style="color: #006080">"Series/PerfView-Tutorial"</span>,
                        <span style="color: #006080">"Blogs/IE"</span>,
                        <span style="color: #006080">"Shows/Edge"</span>)
$baseSaveLocation = <span style="color: #006080">"N:\s\ch9`$"</span>

$mediaFormat = <span style="color: #006080">"high"</span>
$mediaType = <span style="color: #006080">"mp4"</span>
$fileExtension = <span style="color: #006080">"mp4"</span>

$pathToRemoveInvalidFileNameCharsScript = <span style="color: #006080">"$baseSaveLocation\Remove-InvalidFileNameChars.ps1"</span>

#------------Don<span style="color: #006080">'t edit below here----------------#

. "$pathToRemoveInvalidFileNameCharsScript"
foreach($feedTypeName in $feedTypeNameList)
{
    $channelType = $feedTypeName.Split("/")[0]
    $feedUrl="http://channel9.msdn.com/$($feedTypeName.Trim("/"))/RSS/$($mediaType + $mediaFormat)"
    Write-Output @"
    
    Downloading Feed: $feedUrl

"@
    $rss=invoke-webrequest -uri $feedUrl
    $destination="$baseSaveLocation\$($feedTypeName.Replace("/","\").Trim("\"))\"

    if (!(Test-Path $destination)) {
        New-Item -ItemType directory -Path $destination
    }

    $videos = @()
    [xml]$rss.Content|foreach{ 
        $_.SelectNodes("rss/channel/item")|foreach{ 
            [Array]$array = @($_.SelectSingleNode("enclosure").url,$_.SelectSingleNode("title").InnerText)
            $videos += , $array
        }
    }   
    [Array]::Reverse($videos)
    foreach($video in $videos){ 
        $url = $video[0]
        $title = $video[1]
        if (![string]::IsNullOrEmpty("$url"))
        {
            $fileName = $($url.split("/")[-1])
            $mp4fileName = $($fileName.Replace("." + $fileExtension,"") + "-" + (Remove-InvalidFileNameChars $title.Replace(" ","-")) + ".$fileExtension")
            if ($mp4fileName.Contains("_"))
            {
                $pptxFileName = $mp4fileName.Remove($mp4fileName.LastIndexOf("_")) + ".pptx"
                $pptxFileNameSaveAs = $mp4fileName.Remove($mp4fileName.LastIndexOf("_")) + "-" + (Remove-InvalidFileNameChars $title.Replace(" ","-")) + ".pptx"
            }
            else
            {
                $pptxFileName = $mp4fileName.Remove($mp4fileName.LastIndexOf(".")) + ".pptx"
                $pptxFileNameSaveAs = $mp4fileName.Remove($mp4fileName.LastIndexOf(".")) + "-" + (Remove-InvalidFileNameChars $title.Replace(" ","-")) + ".pptx"
            }

            if (![string]::IsNullOrEmpty("$mp4fileName"))
            {
                "Checking $mp4fileName, we will skip it if it already exists in $($destination)"
                #if we have the file from the previous script rename it or delete it
                if(Test-Path ($destination + $fileName))
                {    
                    if(!(Test-Path ($destination + $mp4fileName))) 
                    { 
                        "Renaming: " + $fileName
                        Rename-Item $($destination + $fileName) $($destination + $mp4fileName)
                    }
                    else
                    {
                        "Deleting: " + $fileName
                        Remove-Item $($destination + $fileName)
                    }
                }
                else
                {
                    #download media if it doesn'</span>t exists
                    <span style="color: #0000ff">if</span>(!(Test-Path ($destination + $mp4fileName))) 
                    {     
                        $dest = $($destination + $mp4fileName)
                        <span style="color: #006080">"Downloading: '$url' to '$dest'"</span>
                        Start-BitsTransfer $url $dest
                    }
                }
            }
            #download pptx <span style="color: #0000ff">if</span> it doesn<span style="color: #006080">'t exists
            if ($channelType -eq "Events") #only attempt to get pptx file for events
            {
                if (![string]::IsNullOrEmpty("$pptxFileName"))
                {
                    "Checking $pptxFileName, we will skip it if it already exists in $($destination)"
                    $dest = $($destination + $pptxFileNameSaveAs)
                    if(!(Test-Path ($dest))) 
                    {
                        $urlToDownload = $url.Remove($url.LastIndexOf("/")) + "/" + $pptxFileName
                        "Downloading: '</span>$urlToDownload<span style="color: #006080">' to '</span>$dest'<span style="color: #006080">"
                        Start-BitsTransfer $urlToDownload $dest -ErrorAction SilentlyContinue
                    }
                }
            }
        }
    }
}

Write-Output "</span>Done"

This script caters for renaming of media files from the old just file name format to including the title in the file name. In order to add this extra script that I used for trimming funny characters from the file names. 

#source - http:<span style="color: #008000">//gallery.technet.microsoft.com/scriptcenter/Remove-Invalid-Characters-39fa17b1</span>

Function Remove-InvalidFileNameChars {
    <#
    .SYNOPSIS
    This <span style="color: #0000ff">is</span> a PowerShell function to remove invalid characters from strings to be used <span style="color: #0000ff">as</span> file names.

    .DESCRIPTION
    The function takes a <span style="color: #0000ff">string</span> parameter called Name and returns a <span style="color: #0000ff">string</span> that has been stripped of invalid file name characters, i.e. *, :, \, /.  The Name parameter will also receive input from the pipeline.

    .PARAMETER Name
    Specifies the file name to strip of invalid characters.

    .INPUTS
    Parameter Name accepts System.String objects from the pipeline.

    .OUTPUTS
    System.String.  Outpus a <span style="color: #0000ff">string</span> <span style="color: #0000ff">object</span>

    .EXAMPLE
    Remove-InvalidFileNameChars -Name <span style="color: #006080">"<This/name\is*an:illegal?filename>"</span>
    PS C:\>Thisnameisanillegalfilename

    .NOTES
    It would be easiest to copy the function from the script file and place it <span style="color: #0000ff">in</span> your profile.  However, you may also dot-source the script to load the function into PowerShell:
    i.e. PS C:\>. .\Remove-InvalidFileNameChars.ps1
    #>

    [CmdletBinding()]

    param([Parameter(Mandatory=$<span style="color: #0000ff">true</span>,
        Position=0,
        ValueFromPipeline=$<span style="color: #0000ff">true</span>, 
        ValueFromPipelineByPropertyName=$<span style="color: #0000ff">true</span>)]
        [String]$Name
    )

    <span style="color: #0000ff">return</span> [RegEx]::Replace($Name, <span style="color: #006080">"[{0}]"</span> -f ([RegEx]::Escape([String][System.IO.Path]::GetInvalidFileNameChars())), <span style="color: #006080">''</span>)
}

Hope this helps others that download Channel 9 videos to watch offline as well Smile.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Gordon W Beeming
Software Developer Derivco
South Africa South Africa
Gordon Beeming is a Software Developer at Derivco in the sunny city of Durban, South Africa. He spends most his time hacking away at the keyboard in Visual Studio or with his family relaxing. He is a Visual Studio ALM Rangers, Visual Studio ALM MVP and Friend of Red Gate. His blog is at 31og.com and you can follow him on Twitter at twitter.com/gordonbeeming

http://31og.com
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150331.1 | Last Updated 15 Apr 2014
Article Copyright 2014 by Gordon W Beeming
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid