Automating the blogging of images with Windows Live Writer and PowerShell

by Klaus Graefensteiner 19. September 2010 23:00

Introduction

Every once in a wile I am working on a tutorial blog post that involves the publishing of a large number of pictures or screenshots. Finding the right sequence of pictures, labeling them appropriately and then adding them to Windows Live Writer can be quite tedious. Fortunately there is PowerShell and the Windows Live Writer API that helps you automating this task. This is what this blog post is about.

image

Figure 1: Picture series for a how-to clean your pool filter tutorial

Video

The following video shows the script in action.

Windows Live Writer API

The adding of content to Windows Live writer is made possible by the Windows Live Writer API. This API is a 32-bit COM object that is unfortunately not accessible from the 64-bit PowerShell ISE. Please use the 32-bit version of the ISE, if you plan on using this script. You can find more details here.

WLW API COM Wrapper for PowerShell

There was another little problem that I ran into during this experiment. I didn’t know how to cast the COM object in PowerShell to a different COM interface. As a workaround I decided to create a .NET wrapper class for the WLW COM API in Visual Studio 2010 and use this instead of using the COM object directly. Here is the listing of this wrapper class. The complete Visual Studio Project is attached.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace WLWAPIWrapper
{
  public class WLW
  {
    public static void BlogThisImageFileList(string title, string[] imageFilePaths, string comment)
    {
      ValidateTitle(title);
      ValidateComment(comment);
      ValidateImageFilePaths(imageFilePaths);
      
      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      ((WindowsLiveWriterApplicationLib.IWindowsLiveWriterApplication2)myWLW).BlogThisImageFileList(title, imageFilePaths, comment);
    }



    public static void BlogThisHtml(string title, string html)
    {
      ValidateTitle(title);
      ValidateHtml(html);

      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      ((WindowsLiveWriterApplicationLib.IWindowsLiveWriterApplication2)myWLW).BlogThisHtml(title, html);
    }

    public static void BlogThisImageFile(string title, string imageFilePath, string comment)
    {
      ValidateTitle(title);
      ValidateComment(comment);
      ValidatePath(imageFilePath);
      ValidateImageFileExtension(imageFilePath);

      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.BlogThisImageFile(title, imageFilePath, comment);
    }

    public static void BlogThisFeedItem(string feedName, string itemTitle, string itemUrl, string itemContents, string feedHomepage, string author, string authorEmail, string publishDate)
    {
      ValidateTitle(feedName);
      ValidateTitle(itemTitle);
      ValidateUrl(itemUrl);
      ValidateContents(itemContents);
      ValidateUrl(feedHomepage);
      ValidateAuthor(author);
      ValidateEmail(authorEmail);
      ValidateDate(publishDate);
      
      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.BlogThisFeedItem(feedName, itemTitle, itemUrl, itemContents, feedHomepage, author, authorEmail, publishDate);
    }

    public static void BlogThisImageUrl(string title, string url, string comment)
    {
      ValidateTitle(title);
      ValidateUrl(url);
      ValidateUrl(comment);
      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.BlogThisImageUrl(title, url, comment);
    }

    public static void BlogThisLink(string title, string url, string comment)
    {
      ValidateTitle(title);
      ValidateUrl(url);
      ValidateUrl(comment);

      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.BlogThisLink(title, url, comment);
    }

    public static void BlogThisSnippet(string title, string url, string comment, string snippetContents, bool preserveImages)
    {
      ValidateTitle(title);
      ValidateUrl(url);
      ValidateUrl(comment);
      ValidateHtml(snippetContents);

      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.BlogThisSnippet(title, url, comment, snippetContents, preserveImages);
    }

    public static void NewPost()
    {
      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.NewPost();
    }

    public static void OpenPost()
    {
      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.OpenPost();
    }

    public static void ShowOptions(string optionsPage)
    {
      ValidateOptionsPage(optionsPage);

      var myWLW = new WindowsLiveWriterApplicationLib.WindowsLiveWriterApplication();
      myWLW.ShowOptions(optionsPage);
    }

    private static void ValidateImageFilePaths(string[] imageFilePaths)
    {
      if (imageFilePaths == null || imageFilePaths.Length == 0)
      {
        throw new System.InvalidOperationException("There must be at least one valid image path!");
      }
      else
      {
        foreach (var imageFilePath in imageFilePaths)
        {
          ValidatePath(imageFilePath);
          ValidateImageFileExtension(imageFilePath);
        }
      }
    }

    private static void ValidatePath(string path)
    {
      if (String.IsNullOrEmpty(path))
      {
        throw new System.InvalidOperationException("The path parameter is required!");
      }
      else
      {
        if (File.Exists(path) != true)
        {
          throw new System.InvalidOperationException("The file " + path + " does not exist!");
        }
      }
    }

    private static void ValidateImageFileExtension(string path)
    {
      if (String.IsNullOrEmpty(path))
      {
        throw new System.InvalidOperationException("The path parameter is required!");
      }
      else
      {
        FileInfo fi = new FileInfo(path);
        string ext = fi.Extension.ToLower();
        if (ext != ".jpg" && ext != ".jpeg" && ext != ".png" && ext != ".gif")
        {
          throw new System.InvalidOperationException(ext + " is not a valid file extension!");
        }
      }
    }

    private static void ValidateTitle(string title)
    {
      if (String.IsNullOrEmpty(title))
      {
        throw new System.InvalidOperationException("The title parameter is required!");
      }
    }

    private static void ValidateComment(string comment)
    {
      if (String.IsNullOrEmpty(comment))
      {
        throw new System.InvalidOperationException("The comment parameter is required!");
      }
    }

    private static void ValidateHtml(string html)
    {
      if (String.IsNullOrEmpty(html))
      {
        throw new System.InvalidOperationException("The html parameter is required!");
      }
    }

    private static void ValidateEmail(string email)
    {
      if (String.IsNullOrEmpty(email))
      {
        throw new System.InvalidOperationException("The email parameter is required!");
      }
    }

    private static void ValidateUrl(string url)
    {
      if (String.IsNullOrEmpty(url))
      {
        throw new System.InvalidOperationException("The url parameter is required!");
      }
    }

    private static void ValidateAuthor(string author)
    {
      if (String.IsNullOrEmpty(author))
      {
        throw new System.InvalidOperationException("The author parameter is required!");
      }
    }

    private static void ValidateContents(string contents)
    {
      if (String.IsNullOrEmpty(contents))
      {
        throw new System.InvalidOperationException("The contents parameter is required!");
      }
    }

    private static void ValidateDate(string date)
    {
      if (String.IsNullOrEmpty(date))
      {
        throw new System.InvalidOperationException("The date parameter is required!");
      }
    }

    private static void ValidateOptionsPage(string optionsPage)
    {
      if (String.IsNullOrEmpty(optionsPage))
      {
        throw new System.InvalidOperationException("The options page parameter is required!");
      }
    }
  }
}

PowerShell script

The PowerShell script is straight forward. Use-Add type to load the wrapper assembly. Get a list of picture files, sort them appropriately and then call the function that starts WLW and adds the pictures to it.

$Images = Get-ChildItem -Path "C:\Users\Klaus\Schreibtisch\BlogPosts\InWork\Installing new Haward DE 4800 filter elements" -Filter *.JPG

$Images = $Images | Sort-Object -Property "Name"

#50 Images is Windows Live Writer cut-off. If you add more than 50 images you don't get a choice between photo album and inline pictures when adding an image list.
$ImagePaths = $Images | Select-Object -First 50 | ForEach-Object { $_.FullName }
$ImagePaths


Add-Type -Path "C:\Users\Klaus\SVN\PS\BlogKebab\WLW\BlogThisImageFileList\WLWAPIWrapper\bin\Debug\WLWAPIWrapper.dll" -PassThru
[WLWAPIWrapper.WLW]::BlogThisImageFileList("Hello World From PowerShell", $ImagePaths ,  "Just Curious");

Download

The Visual Studio project for the WLW API wrapper and the PowerShell script can be downloaded here: BlogThisImageFileList.zip

Ausblick

The PowerShell script in this article uses only one of the many automation features of Windows Live Writer. Next I will explore the other options like publishing html snippets, image URLs and links. Stay tuned!

Tags: , , , ,

Windows Live Writer | Automation | Tips & Tricks | PowerShell | Blogging | Blog Kebab

About Klaus Graefensteiner

I like the programming of machines.

Add to Google Reader or Homepage

LinkedIn FacebookTwitter View Klaus Graefensteiner's profile on Technorati
Klaus Graefensteiner

Klaus Graefensteiner
works as Developer In Test and is founder of the PowerShell Unit Testing Framework PSUnit. More...

Open Source Projects

PSUnit is a Unit Testing framwork for PowerShell. It is designed for simplicity and hosted by Codeplex.
BlogShell is The tool for lazy developers who like to automate the composition of blog content during the writing of a blog post. It is hosted by CodePlex.

Administration

About

Powered by:
BlogEngine.Net
Version: 1.6.1.0

License:
Creative Commons License

Copyright:
© Copyright 2013, Klaus Graefensteiner.

Disclaimer:
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Theme design:
This blog theme was designed and is copyrighted 2013 by Klaus Graefensteiner

Rendertime:
Page rendered at 5/19/2013 8:23:07 PM (PST Pacific Standard Time UTC DST -7)