(Updated with live project download here)
Windows Media Center in Windows Vista (Home Premium and Ultimate) provides a wide range of services for viewing all kinds of media. One media is TV. There is a complete EPG maintained for you in Media Centre - the possibilities of what you could do with it, if you could get at it, are very exciting.
There is good news and bad news here. The good news is that you can quite easily get to the EPG database using the functionality Windows Media Centre uses to do so. The bad news is that the terms of use for the EPG data are very restrictive. My advice is to refer to these terms of use before proceeding with any EPG based projects.
Given the legal issues lets just look at the published and documented technology for accessing the EPG on your Windows Media Centre machine.
The EPG database is a sub-set of SQL database functionality. Contained in the ehepg.dll assembly - to be found in the Windows\ehome directory - is a SQL Lite implementation which once you get past the SQL Lite naming, pretty much the same as the SQL functionality we're used to:
using Microsoft.MediaCenter.TV.Scheduling;
...
SqlLiteConnection conn = new SqlLiteConnection();
string path = GetCurrentEpgFile();
conn.Init(path, true);
conn.Open();
DateTime start = DateTime.Now;
System.Console.WriteLine("loading...");
DataSet ds = new DataSet();
SqlLiteCommand scmd = (SqlLiteCommand)conn.CreateCommand();
scmd.CommandText = "select a.Title, a.Description,b.StartTime,c.servicename,c.callsign from programs a, scheduleentries b, services c
Where a.title like '" + textBox1.Text + "' and a.identifier = b.program and b.service = c.identifier";
scmd.CommandType = CommandType.Text;
SqlLiteDataAdapter adapter = new SqlLiteDataAdapter();
adapter.SelectCommand = scmd;
adapter.Fill(ds);
The above code segment is quite obvious - we open the database and run a query against tables within it. The gotcha is finding the database file! You see the EPG is updated frequently by Windows Media Centre. It copies down a new database file each time. So you need to locate not just one database file but the latest database file. This is easily achieved and nicely implemented by a short piece of code by Casey Chestnut:
public static String GetCurrentEpgFile()
{
String strEpgFolder = Environment.GetEnvironmentVariable("PROGRAMDATA") + "\\Microsoft\\eHome\\EPG";
String[] strFiles = Directory.GetFiles(strEpgFolder, "*.sdf");
String strFile = (strFiles.Length > 0 ? strFiles[0] : String.Empty);
foreach (String s in strFiles)
{
if (File.GetCreationTime(s) > File.GetCreationTime(strFile))
strFile = s;
}
return strFile;
}
Casey's Simple Guide project provided much of the answers to my questions while I was writing my own Guide Search and Record Requester application. Casey's project also includes a very information database diagram:
I was able to work very successfully from this diagram to formulate the query commands I required.
The next challenge for my project was to use the Click-To-Record scheduling function which requests the recording of a TV programme to Windows Media Centre. The core of the request is an XML file containing the request constraints - ie the details of the programme or series you want to record. The MSDN documentation has both very little and broken examples regarding this file format. So I found myself debugging through the inner exceptions of the createschedulerrequest method several times before I got it sorted. The key is that the metadata element containing the request description isn't in the namespace - so just delete it. There is a Click to Record wizard in the Windows Media Centre SDK 5.3 - but this failed to load timezone data for me so I could never use it to create sample request files.
In the end, I opted for a simple search and replace strategy against a 'working' click to record file. Replacing the Programme Title, Station call sign and start time data to produce individual recording requests.
try
{
fsXml = new FileStream("example.c2r", FileMode.Open, FileAccess.Read);
readerXml = XmlReader.Create(fsXml);
}
catch (Exception ex)
{
Console.WriteLine("example.xml could not be read");
Console.WriteLine(ex.Message);
return;
}
XmlDocument dx = new XmlDocument();
dx.Load(readerXml);
dx.InnerXml = dx.InnerXml.Replace("PROGRAMTITLE", x.Cells[0].Value.ToString());
dx.InnerXml = dx.InnerXml.Replace("STATION", x.Cells[4].Value.ToString());
dx.InnerXml = dx.InnerXml.Replace("PROGRAMTIME", XmlConvert.ToString(Convert.ToDateTime(x.Cells[2].Value.ToString()), "yyyy-MM-ddTHH:mm:ss"));
dx.Save("Request.xml");
This works well enough but only covers one of the several possibilities apparently available for scheduling recording. Exercise for the reader to work out how to record series etc.
Once a click to record request has been created it is easy to submit:
EventSchedule scheduler;
ScheduleRequest request = null;
CreateScheduleRequestResult result;
try
{
scheduler = new EventSchedule();
result = scheduler.CreateScheduleRequest(readerXml, ConflictResolutionPolicy.AllowConflict, "API Request ", out request);
MessageBox.Show("Result: " + result.ToString(), "Record request", MessageBoxButtons.OK);
}
You then have to wait for Windows Media Centre to find the programme in the EPG (again) and determine if there are any recording conflicts - it won't for example record the same programme twice on the same occasion.
For me this project is the first step for integrating my Media Centre into a number of digital lifestyle services which include being able to ask my household robot, A1DW, when a programme is on and to record it if needed. I'm also working on a conversation bot for Windows Live Messenger which will enable me to instruct my Windows Media Centre directly from any number of IM clients to do the same.
More on these projects as they surface. For now the example code for this post is available here.
A short video talking through the code is available: