Sunday, July 29, 2007

Automating Windows Media Player with Ruby

My recent article about automating iTunes has resulted in several requests for a similar article about Windows Media Player (WMP). And so, without further ado...

We start by using the win32ole library to connect to the WMPlayer object:


require 'win32ole'
player = WIN32OLE.new('WMPlayer.OCX')

Note that this will not display the WMP user interface. That happens next.

To play a song, call the player object's OpenPlayer method, passing it the path to the song file:

player.OpenPlayer('c:\music\van halen\right now.wma')

The Media Collection

Next, we'll grab the MediaCollection object:

media_collection = player.mediaCollection

To quote Microsoft, "The MediaCollection object represents all the items stored in the Windows Media Player media collection. You will typically query the MediaCollection object to return collections of media items."

The MediaCollection object's getAll method returns a collection of all items in the library:

all_media = media_collection.getAll()

The getByAttribute method can be used to get a collection of all audio files:

audio_media = media_collection.getByAttribute("MediaType", "Audio")

To get a collection of songs by a specific artist, use the getByAuthor method:

sinatra_songs = media_collection.getByAuthor("Frank Sinatra")

You can also get items by Album:

album = media_collection.getByAlbum('Come Fly with Me')

Or by Genre:

jazz_tunes = media_collection.getByGenre('Jazz')

And, of course, you can get a collection of items by name:

songs = media_collection.getByName('Fly Me to the Moon')

Note that this will return a collection of items, even if it contains only one item. You reference a specific item by calling the collection's Item method, passing it a (zero-based) index value. So to get the first item:

first_song = songs.Item(0)

As mentioned earlier, to play a song, call the player object's OpenPlayer method, passing it the song object's sourceURL property. sourceURL is simply the path to the song file:

player.OpenPlayer(first_song.sourceURL)

To add a song to your Media Collection, call the MediaCollection object's Add method, passing it the path and filename:

song = media_collection.Add('C:\music\Just in Time.wma')

To delete a song, from the Media Collection, call the MediaCollection object's Remove method, passing it the song object and the boolean value 'true':

songs = media_collection.getByName('Fly Me to the Moon')
media_collection.Remove(songs.Item(0), true)

You may need to restart the Media Player user interface to see additions and removals.

The PlayLists Collection

PlayLists are managed via the player's PlaylistCollection object:

playlists = player.PlaylistCollection

The PlaylistCollection object has methods similar to the MediaCollection object. Its getAll method returns a collection of all PlayList objects:

all_playlists = playlists.getAll()

Its getByName method returns a collection of PlayList objects by name:

split_enz_playlist = playlists.getByName('Split Enz')

Remember, this returns a collection, even if that collection contains a single item. So to get the first (and possibly only) item, we call the Item method on the returned collection:

split_enz_playlist.Item(0)

A PlayList is a collection of Song items, just like those returned by the MediaCollection object's methods. You can call each item in the collection by index. So to print the name of each song in our playlist:

(0..my_playlist.Count - 1).each do |i|
song = my_playlist.Item(i)
puts song.Name
end

To create a new playlist, call the PlaylistCollection object's newPlaylist method, passing it the name of the new playlist:

playlists = player.PlaylistCollection
playlists.newPlaylist('New Playlist')

This creates a .wpl playlist file. You then have to add the new playlist file to the MediaCollection object, by calling the Add method and passing it the path and name of the newly created PlayList file:

media_collection.Add('D:\Music\My Playlists\New Playlist.wpl')

The path to the playlist file will be defined in the 'Rip music to this location' setting in Media Player's options.

To remove a playlist, call the PlaylistCollection object's Remove method, passing it the name of the PlayList object:

playlists = player.PlaylistCollection
split_enz_playlist = playlists.getByName('Split Enz').Item(0)
playlists.Remove(split_enz_playlist)

To add a song to a playlist, call the PlayList object's appendItem method, passing it the song object:

song = media_collection.getByName('Fly Me to the Moon').Item(0)
playlist = playlists.getByName('Frank & Dino').Item(0)
playlist.appendItem(song)

To remove a song from a playlist, call the PlayList object's removeItem method, passing it the song object:

playlist.removeItem(song)

Playing a playlist is much like playing an individual song. First get the individual PlayList object, then pass its sourceURL value to the player object's OpenPlayer method:

playlist = playlists.getByName('Frank & Dino').Item(0)
player.OpenPlayer(playlist.sourceURL)

And there you have it.

Further details can be found in this Microsoft TechNet article.

As always, feel free to post a comment or send email with questions, comments, or suggestions.

Thanks for stopping by!

Digg my article

Sunday, July 22, 2007

Automating and Managing iTunes with Ruby

We've spent a lot of time on this blog looking at how to get work done with Ruby and Microsoft Office (Excel, Word, Access, Outlook). Let's take a break and go play... with iTunes.

Apple's popular iTunes application for Windows includes a COM interface. This allows you to automate and manage iTunes with Ruby. You can launch the application, search for and play songs, set user interface properties, and manage your iTunes library and PlayLists using Ruby code. Let's take a look...

First, of course, we start by connecting to the iTunes application object, launching iTunes if it isn't already running:


require 'win32ole'
itunes = WIN32OLE.new('iTunes.Application')

Controlling the iTunes User Interface

To place iTunes into MiniPlayer mode (or return it to Full Mode), set the BrowserWindow.MiniPlayer property:

itunes.BrowserWindow.MiniPlayer = true

To toggle the play/pause button, simply call the PlayPause method:

itunes.PlayPause

To increase or decrease sound volume, adjust the SoundVolume property:

itunes.SoundVolume = itunes.SoundVolume + 50
itunes.SoundVolume = itunes.SoundVolume - 25

To go to the previous or next track, call those methods:

itunes.PreviousTrack
itunes.NextTrack

Managing Your iTunes Library

OK, that covers several standard user interface features. Now, let's look at how you can work with your content.

Let's create an instance of the Library, which is a child object of the Application object:

library = itunes.LibraryPlaylist

We'll be working with this LibraryPlaylist object a lot going forward. It provides several methods that will return collections of Track and PlayList objects. For example, calling the Tracks method returns a collection of all tracks in the library:

tracks = library.Tracks

You can then select a song by name from this collection of Track objects:

song = tracks.ItemByName('At Long Last Love')

...and then play the track by calling its Play method:

song.Play

To search the LibraryPlaylist, call its Search method, passing it a string to search for, and an integer that determines what field to search, such as Artist, Album Title, or Track Title:

artist_tracks = library.Search('Sinatra', 2)
album_tracks = library.Search('Come Fly With Me', 3)
title_tracks = library.Search('Fly Me To The Moon', 5)

The Search and Tracks methods return a collection of Track objects, which you could iterate over. We might want to add all track objects in this collection to a ruby array called songs:

songs = []
for track in tracks
songs << track
end

This would then allow us to use Ruby's excellent sort_by method to sort the songs array:

songs = songs.sort_by{|song| [song.Artist, song.Year, song.Album, song.TrackNumber]}

The Track object includes dozens of attributes pertaining to this track. As indicated above, there's Artist, Album, Year, and TrackNumber. But there's also Time (in minutes and seconds), Duration (total seconds), BPM (beats-per-minutes), Composer, Genre, and many more.

You could iterate over the songs collection and write data for each track to a file:

songs.each do |song|
my_file.puts [song.Artist, song.Album, song.Name, song.Time].join("\t")
end

...or add the data to an Excel worksheet or SQLite database.

Working with iTunes PlayLists

To create a new PlayList, call the Application object's CreatePlaylist method, passing it the name of the new PlayList:

playlist = itunes.CreatePlaylist('My Playlist')

To add a song to a PlayList object, first get a Track object by calling the ItemByName method on a Tracks collection:

song = library.Tracks.ItemByName('At Long Last Love')

...then call the PlayList's AddTrack method, passing it the Track object:

playlist.AddTrack(song)

In the iTunes object model, PlayLists are child objects of Source objects. So, to create an array of all PlayList objects, we iterate over each Source object and get each PlayList object:

playlists = []
itunes.Sources.each do |source|
source.PlayLists.each do |playlist|
playlists << playlist
end
end

This gives us a collection of PlayList objects that we can now work with:

for playlist in playlists do
puts playlist.Name
end

To select a single PlayList by name:

playlist = itunes.Sources.ItemByName('Library').Playlists.ItemByName('All Sinatra')

To play the first track of a PlayList:

playlist.PlayFirstTrack

To exit the iTunes application, call its Quit method:

itunes.Quit

Further Reading

Further details can be found in this Microsoft TechNet article.

For further examples in Ruby, you might want to check out the itunes-control (itch) library source code.

And for those running iTunes on Mac OS, you may find this blog post by Zack Hobson to be of interest.

I hope you have found this information helpful. As always, post a comment here or send me an email if you have questions, comments, or suggestions.

Thanks for stopping by!


Digg my article

Wednesday, July 18, 2007

Automating Outlook with Ruby: Tasks

In our last episode, we used Ruby to extract appointments data from the Outlook Calendar, delete an appointment, and create a new appointment. Today we'll look at managing Outlook Tasks with Ruby.

Getting Existing Tasks

As usual, we'll use the win32ole library to create a new instance (or connect to a currently running instance) of the Outlook application object:


require 'win32ole'
outlook = WIN32OLE.new('Outlook.Application')

Next we'll get the MAPI namespace:

mapi = outlook.GetNameSpace('MAPI')

Outlook consists of several Folder objects, including Inbox, Tasks, and Calendar. To get a folder object, call the MAPI object's GetDefaultFolder method, passing it an integer representing the folder to return. For the Tasks folder, this number is 13:

tasks = mapi.GetDefaultFolder(13)

The Tasks folder's Items method returns a collection of all tasks, which you can iterate over. The following code prints out some of the most-often used properties for each task:

for task in tasks.Items
puts task.Subject
puts task.Body
puts task.DueDate
puts task.PercentComplete
puts task.Status
puts task.Importance
puts task.LastModificationTime
end

The Status method/property returns an integer, representing one of the following values:

0 -- Not started
1 -- In progress
2 -- Complete
3 -- Waiting on someone else
4 -- Deferred

The Importance method/property returns an integer, representing one of the following values:

0 -- Low
1 -- Normal
2 -- High

Filtering Your Tasks Collection

You can filter your collection of Task items by calling the Items collection's Restrict method, passing it a filter string. The following code gets all 'Order Yankees World Series Tickets' tasks, and then deletes each one:

for task in tasks.Items.Restrict("[Subject] = 'Order Yankees World Series Tickets'")
task.Delete
end

Other possible filters include:

tasks.Items.Restrict("[Status] = 'Not started'")
tasks.Items.Restrict("[Importance] = 'High'")

As illustrated in the above example, to delete a Task, call its Delete method.

Creating New Tasks

To create a new task, call the Outlook Application object's CreateItem method, passing it the number 3, which represents a Task Item. This returns a new Task object:

task = outlook.CreateItem(3)

Then set values for various properties of the new Task object:

task.Subject = 'Send flowers to wife'
task.Body = 'Call flower shop and send roses.'
task.ReminderSet = true
task.ReminderTime = '7/19/2007 12:00 PM'
task.DueDate = '7/20/2007 12:00 PM'
task.ReminderPlaySound = true
task.ReminderSoundFile = 'C:\Windows\Media\Ding.wav'

When you've completed setting property values, call the Task object's Save method:

task.Save

Further details can be found in this Microsoft TechNet article.

As always, feel free to post a comment here or send me email with questions, comments, or suggestions.

Thanks for stopping by!


Digg my article

Saturday, July 14, 2007

Automating Outlook with Ruby: Calendar Appointments

In a previous article, we looked at how to automate Microsoft Outlook to create and send a new email message. This time around, we'll use Ruby to extract appointments data from the Outlook Calendar, delete an appointment, and create a new appointment.

As usual, we'll use the win32ole library to create a new instance (or connect to a currently running instance) of the Outlook application object:


require 'win32ole'
outlook = WIN32OLE.new('Outlook.Application')

Next we'll get the MAPI namespace:

mapi = outlook.GetNameSpace('MAPI')

Outlook consists of several Folder objects, including Inbox, Tasks, and Calendar. To get a folder object, call the MAPI object's GetDefaultFolder method, passing it an integer representing the folder to return. For the Calendar folder, this number is 9:

calendar = mapi.GetDefaultFolder(9)

The calendar folder's Items method returns a collection of all appointments, which you can iterate over. Each appointment object includes dozens of properties. The following code prints out six of the most-often used properties:

calendar.Items.each do |appointment|
puts appointment.Subject
puts appointment.Location
puts appointment.Start
puts appointment.Duration
puts appointment.End
puts appointment.Body
end

To delete an appointment, call its Delete method. For example, the following code locates an appointment based on its Subject value, then deletes it, if found:

calendar.Items.each do |appointment|
if appointment.Subject == 'Punch Bud Selig in the Nose'
appointment.Delete
end
end

To create a new appointment, call the Outlook Application object's CreateItem method, passing it the number 1, which represents a Calendar Item. This returns a new Appointment object:

appointment = outlook.CreateItem(1)

Then set values for various properties of the new Appointment object:

appointment.BusyStatus = 2
appointment.Start = '7/29/2007 11:00 AM'
appointment.Duration = 300
appointment.Subject = 'Baseball Hall of Fame Induction'
appointment.Body = 'Tony Gwynn and Cal Ripken Jr.'
appointment.Location = 'Cooperstown, NY'
appointment.ReminderMinutesBeforeStart = 15
appointment.ReminderSet = true

The BusyStatus value is an integer with the following possible values:

olFree = 0
olTentative = 1
olBusy = 2
olOutOfOffice = 3

The Duration value is an integer representing the appointment length in minutes.

For an all-day event, forget the Duration property and instead set the AllDayEvent property to true:

appointment.AllDayEvent = true

To make an appointment recurring, you'll want to work with the Appointment object's Recurrence child object. You get this object by calling the GetRecurrencePattern method:

recurrence = appointment.GetRecurrencePattern

Now you'll set the RecurrenceType to one of the following values:

0 -- To set an appointment that occurs each and every day.
1 -- To set an appointment that occurs on the same day each week.
2 -- To set an appointment that occurs on the same day each month.
5 -- To set an appointment that occurs on the same day each year.

So, for an appointment that occurs on this day and time (as defined in your Start property above) each week:

recurrence.RecurrenceType = 1

Next, we'll set the PatternStartDate property. This value needs to be on or before the Start value defined above:

recurrence.PatternStartDate = '8/6/2007'

Finally, we set the PatternEndDate property:

recurrence.PatternEndDate = '8/27/2007'

When you've completed setting property values, call the Appointment object's Save method:

appointment.Save

And there you have it. Further details can be found in this Microsoft TechNet article.

Questions? Comments? Suggestions? Post a comment here or send me email.

Thanks for stopping by!


Digg my article

Sunday, July 8, 2007

Using Ruby & WMI to Get Win32 Process Information

In an earlier post, we looked at using Windows Management Instrumentation (WMI) to determine if a "USB Mass Storage Device" is inserted. Today we'll use WMI to get information about the Win32 processes being run.

WMI is installed and already running on all recent versions of Windows, so we'll connect to it using the win32ole library's connect method:


require 'win32ole'
wmi = WIN32OLE.connect("winmgmts://")

Next, we'll call WMI's ExecQuery method to query the Win32_Process table:

processes = wmi.ExecQuery("select * from win32_process")

This returns a collection of all WMI Win32_Process objects.

The Win32_Process class has dozens of properties. You can find a complete list of those properties here. You could also produce a list of these properties by calling the Properties_ method (note the underscore) on one of the Win32_Process objects. This returns a collection of Properties; then call the Name method/property for each of these Property objects. For example:

for process in processes do
for property in process.Properties_ do
puts property.Name
end
break
end

To obtain data about a process, call that Process object's relevant property/method. For example, the following code prints four properties for each process:

for process in processes do
puts "Name: #{process.Name}"
puts "CommandLine: #{process.CommandLine}"
puts "CreationDate: #{process.CreationDate}"
puts "WorkingSetSize: #{process.WorkingSetSize}"
puts
end

That's all for now. Got a question or suggestion? Post a comment or send me email.

Thanks for stopping by!

Digg my article

Monday, July 2, 2007

Querying the Windows Registry for the Default Mail Client

Following up on my post about sending email with MS Outlook, a reader asks, "I wonder how one can mine out the chosen default mail client... Is that somewhere in the Registry?"

It does indeed appear to be in the Windows Registry, and can therefore be extracted using the win32/registry library. I've just started hacking around with this library, but this bit of code seems to do the trick...


require 'win32/registry'

def get_mail_client
Win32::Registry::HKEY_LOCAL_MACHINE.open('Software\Clients\Mail') do |reg|
reg_typ, reg_val = reg.read('')
return reg_val
end
end

mail_client = get_mail_client

...though I am sure it can be improved upon.

On my machine, this returns 'Microsoft Outlook'.

Well, there you have it. As always, post comments or send email with enhancements, questions, or suggestions for future topics.

Thanks for stopping by!


Digg my article

Sunday, July 1, 2007

Automating Outlook with Ruby: Sending Email

Just as we have done with Excel and Word, we can use the win32ole library to automate various tasks in Microsoft Outlook. Let's take a look at how to create an new email message.

Of course, we'll want to require the win32ole library:


require 'win32ole'

Create an instance of the Outlook Application object:

outlook = WIN32OLE.new('Outlook.Application')

Outlook is a single-instance application, so if it is already running, calling the WIN32OLE.new method will behave the same as the WIN32OLE.connect method, returning the running instance.

To create a new item in Outlook, call the Application object's CreateItem method and pass it a numeric value that represents the type of item to create. Valid arguments include:

olMailItem = 0
olAppointmentItem = 1
olContactItem = 2
olTaskItem = 3
olJournalItem = 4
olNoteItem = 5
olPostItem = 6

So, to create a new email message (ie, MailItem), we call the CreateItem method with an argument of 0 (zero):

message = outlook.CreateItem(0)

This returns the newly-created mail item object. We'll proceed by setting values for this MailItem object's properties and calling some of its methods.

To define the subject line of the message, set its Subject value with a string:

message.Subject = 'Double-Header Today'

To define the message body, set the Body value to a string:

message.Body = 'This is the message body.'

Alternatively, for an HTML message body, set the HtmlBody value to an HTML-encoded string.

Define the recipient by setting the To property...

message.To = 'ted.williams@redsox.com'

...or call the Recipients.Add method one or more times:

message.Recipients.Add 'ted.williams@redsox.com'
message.Recipients.Add 'joe.dimaggio@yankees.com'

Got attachments to add? Call the Attachments.Add method one or more times, passing it the path and name of the attachment:

message.Attachments.Add('c:\my_folder\my_file.txt', 1)

The second argument, 1, indicates that this is an attached file, rather than a link to the original file.

You can save the unsent message in your Outbox by calling the Save method...

message.Save

This would then allow you to open and review the message before manually clicking the Send button.

Of course, you can send the message with the Send method:

message.Send

Note that when using the Send method, Outlook may display a security alert that forces the user -- after a forced pause of about five seconds -- to click 'Yes' to allow the message to be sent. I am unaware of a method for avoiding this alert dialog.

UPDATE: I have discovered, but not yet used, a free Outlook add-in from MAPILab that allows the end user to set Outlook security settings. And the related ($99) Security Manager claims to allow you to bypass the Outlook security alerts, with a single line of code. I can't recommend either solution, not having tried them, but mention them here for your information.

That about wraps our show for today. Would you like to see more about Automating Outlook with Ruby? As always, feel free to post a comment here or email me with questions, comments, or suggestions.

Thanks for stopping by!


Digg my article