OK, I had promised to share some code to make the TiVo Now Playing screen available from an ASP.Net page, so here goes…
First off, I learned a lot of the TiVo access details by lurking in the forums over at http://www.tivocommunity.com/. Props to the guys and gals over there.
As of TiVo OS version 7.1, TiVo actually runs a web server inside. Its true – just point your browser at your DVR’s IP address and you’ll see a basic web page. Not only that, it also exposes some of its basic details via XML.
Processing XML in .Net can be accomplished a whole host of different ways, but for generating a web page, datasets and XSLT of my two favorites.
With a little playing around and a little Googling, we can come up with a few basic facts:
- Fact #1 – All of TiVo’s feeds are based off of a single main URL - https:///TiVoConnect?Command=QueryContainer
- Fact #2 – At the current time, only the now playing list can be accessed through the web server. The to-do list and season passes just aren’t there.
- Hurdle #1 – TiVo’s SSL is self certified. That means that any .Net code we write is going to have to have a certificate policy (hint, hint) to trust the XML feed from the machine.
- Hurdle #2 – The TiVo web server prompts for a username and password to access the XML feed. We’ll have to know how to supply these network credentials (another hint).
- Hurdle #3 – The now playing feed has a node called “Details” at two different levels, which xsd.exe doesn’t like.
- Hurdle #4 –The XML file has dates formatted as Hex. Why? I don’t know, but they are. I’m sure there’s a way, but hex to date conversion in XSLT sounds ugly.
So this should be enough info to get down to work. I’m going to choose the dataset method because it comes more naturally to me than XSLT, so we won’t even tackle hurdle #4
The general steps of the application will be something like this:
- Make a request to the TiVo for the now playing XML data we want.
- Accept TiVo’s certificate.
- Get authenticated using our username and password.
- Retrieve the XML response stream the TiVo provides.
- Put the XML into a dataset.
- Bind that dataset to a grid on our web form.
Piece of cake, right? For the purposes of this example I’m going to leave out a bunch of stuff that you’ll probably want to consider like caching, and displaying the amount of recording time left, etc.
First, let’s tackle the certificate issue.
The System.Net namespace has an interface called ICertificatePolicy. The interface has a couple of methods, but the one we want is a Boolean called CheckValidation result. Implementing this will let us override the default security policy set be the Framework. In this case, we’re only making a request to a single source, the TiVo, which we trust. So we can always return true, thusly…
Public Class TivoPolicy
Implements ICertificatePolicy
Public Function CheckValidationResult(ByVal srvPoint As ServicePoint, _
ByVal cert As X509Certificate, ByVal request As WebRequest, _
ByVal certificateProblem As Integer) _
As Boolean Implements ICertificatePolicy.CheckValidationResult
'Return True to force the certificate to be accepted.
Return True
End Function
End Class
Requests to Internet URI’s by .Net apps are controlled by the System.Net.ServicePointManager. We can set the certificate policy of the ServicePointManager to our policy right before our request to the TiVo and Hurdle #1 will be cleared:
System.Net.ServicePointManager.CertificatePolicy = New TivoPolicy
Next up is creating the actual request. There’s lots of tutorials on screen scraping and the like, so lets just keep it simple… (sorry for the wrapping)
Dim wr As WebRequest = _
WebRequest.Create("https://192.168.1.3/TiVoConnect" & _
"?Command=QueryContainer&Container=/NowPlaying&Recurse=Yes")
Before we make this request we need to attach the username and password to the WebRequest. If you’ve ever password protected a webservice in ASP.Net (poor man’s WSE), you’re probably familiar with the NetworkCredential class. It pretty simple; you just instantiate it with the username and password and you’ve got it. We’ll tell our WebRequest to use the info we supply, and that it should send the credentials with the request, and Hurdle #2 is done. (Don’t forget to put your Media Access Key as the password.)
wr.PreAuthenticate = True
wr.Credentials = New NetworkCredential("tivo", "")
To actually get our hands on the XML and do something useful with it, we’ll get the response from our request and put it into a StreamReader. We’ve already decided that we want this in a dataset, so lets go ahead and put that into a string.
Dim wresponse As WebResponse = wr.GetResponse()
Dim reader As StreamReader = New StreamReader(wresponse.GetResponseStream())
Dim str As String = reader.ReadToEnd
Dim sr As New StringReader(str)
Now, we can create the schema for the dataset.
Open up https:///TiVoConnect?
Command=QueryContainer&Container=%2FNowPlaying&Recurse=Yes
in your browser and save the output to a local file. Now open the file in VS.Net. See that top level right below ? Change that to or something similar and save. Now open up a command prompt and run xsd.exe against it to generate an XSD file. If you’ve renamed that pesky details node to something else, it won’t complain. Add the xsd file you just created to your web project. Hurdle #3 cleared.
Finally, we’re left with a pretty simple exercise of loading the StringReader into our dataset using the schema that we specified.
Dim ds As New DataSet
ds.ReadXmlSchema(Server.MapPath("TivoConnect.xsd"))
ds.ReadXml(sr, XmlReadMode.ReadSchema)
Now that we have a dataset, drag a datagrid onto your form and bind away.
But wait there’s more…
Remember those Hex dates? I was trying to forget them too, but in order to get the date that a program was recorded we need to have a conversion function. Unless you can do them in your head, but then way are you reading my blog? J
I chose to put this in the ItemDataBound event of the datagrid. It could certainly be done other ways, but since the XML file also need some cleanup for file size, program length, etc. so creating a helper function seem like an easy way.
To be honest, I just took a known date and reverse engineered. There’s amazingly little info on the web about doing this.
Protected Function ParseHexDate(ByVal HexDate As String) As String
HexDate = HexDate.Replace("0x", "")
HexDate = "&H" & HexDate
HexDate = DateAdd(DateInterval.Second, Val(HexDate) - 14398, _
CDate("1/1/1970 12:00AM")).ToString
Return HexDate
End Function
What’s 14398? Beats the heck out of me. Around 120 hours I think. ;)
One last question – why would anyone want to do this if TiVo has it built in? A few reasons – a) because its fun, b) because TiVo doesn’t tell how much time is left on the hard drive, and c) because I want to download my videos to a PC that doesn’t have TiVo Desktop installed.
Anyway, that about wraps it up. I’ll post some basic example code to download.