{"id":19,"date":"2007-02-20T07:37:29","date_gmt":"2007-02-20T07:37:29","guid":{"rendered":"http:\/\/porkrind.org\/missives2\/?p=19"},"modified":"2010-04-20T02:46:35","modified_gmt":"2010-04-20T09:46:35","slug":"tivo-desktop-on-linux","status":"publish","type":"post","link":"https:\/\/porkrind.org\/missives\/tivo-desktop-on-linux\/","title":{"rendered":"Tivo Desktop on Linux"},"content":{"rendered":"<p>I tried out TiVo Desktop on my Mac and it was kind of cool. It lets you play music on your TiVo that&#8217;s streamed from your computer. But my mac already streams music from my Linux server and TiVo desktop wanted to serve up real mp3s and not re-broadcast streams&#8211;so none of my music would play. I found some articles and discovered the TiVo Desktop is just a http daemon plus some mdns stuff. Sounds easy enough!<\/p>\n<p>I installed mdns-scan (thank you Debian) which reports:<\/p>\n<blockquote>\n<pre>+ David Caldwell's Photos on black._tivo-photos._tcp.local\r\n+ David Caldwell's Music on black._tivo-music._tcp.local\r\n<\/pre>\n<\/blockquote>\n<p>I copied \/etc\/avahi\/services\/ssh.service to \/etc\/avahi\/services\/tivo.service. I then changed the name to &#8220;Lovely Music&#8221;, &#8220;_ssh._tcp&#8221; to &#8220;_tivo-music._tcp&#8221; and picked a port out of the air: 8000. I reloaded the avahi daemon and wham, the &#8220;Lovely Music&#8221; shows up on the TiVo. Yay!<\/p>\n<p>The rest is just http on port 8000 that spews XML. That should be fairly easy to do&#8230;<\/p>\n<h2>&#8230;. Time Passes &#8230;.<\/h2>\n<p>And 8 hours later I say, &#8220;Fairly easy?&#8221;. Actually it turned out to be pretty easy. The main problem I had was that the %$#@! TiVo had died on me and wouldn&#8217;t play any music, not even from the official TiVo Desktop. I had to reset it to make it work again. Of course, I kept thinking it was something I was doing wrong and so I kept implementing more and more of the protocol. For 4 hours. Grrr&#8230;.<\/p>\n<h2>Avahi<\/h2>\n<p>Turns out mdns-scan doesn&#8217;t give enough info to really get it working. I used avahi-discover which dumped the txt records that were needed. In the end my avahi services file looked like this:<\/p>\n<blockquote>\n<pre>&lt;?xml version=\"1.0\" standalone='no'?&gt;&lt;!--*-nxml-*--&gt;\r\n&lt;!DOCTYPE service-group SYSTEM \"avahi-service.dtd\"&gt;\r\n&lt;!-- See avahi.service(5) for more information about this configuration file --&gt;\r\n &lt;service-group&gt;\r\n  &lt;name&gt;Lovely Music&lt;\/name&gt;\r\n  &lt;service&gt;\r\n    &lt;type&gt;_tivo-music._tcp&lt;\/type&gt;\r\n    &lt;port&gt;8001&lt;\/port&gt;\r\n    &lt;txt-record&gt;protocol=http&lt;\/txt-record&gt;\r\n    &lt;txt-record&gt;path=\/tivo.cgi\/music&lt;\/txt-record&gt;\r\n  &lt;\/service&gt;\r\n&lt;\/service-group&gt;\r\n<\/pre>\n<\/blockquote>\n<p>The <tt>path<\/tt>, <tt>&lt;port&gt;<\/tt> and <tt>&lt;name&gt;<\/tt> can be anything you want. I&#8217;m not sure about <tt>protocol<\/tt>.<\/p>\n<p>Here&#8217;s the corresponding one for the photo sharing:<\/p>\n<blockquote>\n<pre>&lt;?xml version=\"1.0\" standalone='no'?&gt;&lt;!--*-nxml-*--&gt;\r\n&lt;!DOCTYPE service-group SYSTEM \"avahi-service.dtd\"&gt;\r\n&lt;!-- See avahi.service(5) for more information about this configuration file --&gt;\r\n&lt;service-group&gt;\r\n  &lt;name&gt;Beautiful Photos&lt;\/name&gt;\r\n  &lt;service&gt;\r\n    &lt;type&gt;_tivo-photos._tcp&lt;\/type&gt;\r\n    &lt;port&gt;8001&lt;\/port&gt;\r\n    &lt;txt-record&gt;protocol=http&lt;\/txt-record&gt;\r\n    &lt;txt-record&gt;path=\/tivo.cgi\/photos&lt;\/txt-record&gt;\r\n  &lt;\/service&gt;\r\n&lt;\/service-group&gt;\r\n<\/pre>\n<\/blockquote>\n<h2>http server<\/h2>\n<p>I set up port 8001 on apache and then wrote <tt>tivo.cgi<\/tt>. I wrote it in the least general way possible and so I don&#8217;t plan on publishing it&#8211;it just wouldn&#8217;t be of much use without specific perl data files that I already had lying around. They actually are for the Philips Streamium server that I heavily modified. And so I wanted to share them instead of putting a whole bunch of work into reinventing the interface&#8230;<\/p>\n<p>I could publish my simple test server, if anyone is interested&#8230;<\/p>\n<h2>Photos<\/h2>\n<p>I haven&#8217;t looked too much at the photo sharing yet, but it looks exactly like the music sharing at first glance. In fact, you can see in the avahi configs I pointed photos and music to the same tivo.cgi. Only I don&#8217;t really check for \/music or \/photo yet and so a couple times I accidentally selected the photo option from the TiVo menu and it came up with a list of my music folders in the thumbnails (with the correct names and everything). So I think it really is the same.<\/p>\n<h2>Protocol<\/h2>\n<p>The protocol is pretty simple.<\/p>\n<p>The tivo will send a request like this (each CGI parameter is on it&#8217;s own line for clarity and horizontal space conservation):<\/p>\n<blockquote>\n<pre>\/tivo.cgi\/music?Recurse=No\r\n&amp;Filter=x-container%2Ffolder,x-container%2Fplaylist,audio%2F*\r\n&amp;SortOrder=Type,Title\r\n&amp;ItemCount=0\r\n&amp;Details=Basic\r\n&amp;Format=text%2Fxml\r\n<\/pre>\n<\/blockquote>\n<p>The server should respond with something like this:<\/p>\n<blockquote>\n<pre>&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt;\r\n&lt;TiVoContainer&gt;\r\n  &lt;Details&gt;\r\n    &lt;Title&gt;Top&lt;\/Title&gt;\r\n    &lt;ContentType&gt;x-container\/tivo-music&lt;\/ContentType&gt;\r\n    &lt;SourceFormat&gt;x-container\/tivo-music&lt;\/SourceFormat&gt;\r\n    &lt;TotalItems&gt;2&lt;\/TotalItems&gt;\r\n  &lt;\/Details&gt;\r\n  &lt;ItemStart&gt;0&lt;\/ItemStart&gt;\r\n  &lt;ItemCount&gt;0&lt;\/ItemCount&gt;\r\n&lt;\/TiVoContainer&gt;\r\n<\/pre>\n<\/blockquote>\n<p>The TiVo will then repeat the request with a higher ItemCount:<\/p>\n<blockquote>\n<pre>\/tivo.cgi\/music?Recurse=No\r\n&amp;Filter=x-container%2Ffolder,x-container%2Fplaylist,audio%2F*\r\n&amp;SortOrder=Type,Title\r\n&amp;ItemCount=2\r\n&amp;Details=Basic\r\n&amp;Format=text%2Fxml\r\n<\/pre>\n<\/blockquote>\n<p>This time the server should give some <tt>&lt;Item&gt;<\/tt>s:<\/p>\n<blockquote>\n<pre>&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt;\r\n&lt;TiVoContainer&gt;\r\n  &lt;Details&gt;\r\n    &lt;Title&gt;Top&lt;\/Title&gt;\r\n    &lt;ContentType&gt;x-container\/tivo-music&lt;\/ContentType&gt;\r\n    &lt;SourceFormat&gt;x-container\/tivo-music&lt;\/SourceFormat&gt;\r\n    &lt;TotalItems&gt;2&lt;\/TotalItems&gt;\r\n  &lt;\/Details&gt;\r\n  &lt;Item&gt;\r\n    &lt;Details&gt;\r\n      &lt;Title&gt;Flac&lt;\/Title&gt;\r\n      &lt;ContentType&gt;x-container\/folder&lt;\/ContentType&gt;\r\n      &lt;SourceFormat&gt;x-container\/folder&lt;\/SourceFormat&gt;\r\n    &lt;\/Details&gt;\r\n    &lt;Links&gt;\r\n      &lt;Content&gt;\r\n        &lt;Url&gt;\/tivo.cgi\/25&lt;\/Url&gt;\r\n      &lt;\/Content&gt;\r\n    &lt;\/Links&gt;\r\n  &lt;\/Item&gt;\r\n  &lt;Item&gt;\r\n    &lt;Details&gt;\r\n      &lt;Title&gt;dsongs&lt;\/Title&gt;\r\n      &lt;ContentType&gt;x-container\/folder&lt;\/ContentType&gt;\r\n      &lt;SourceFormat&gt;x-container\/folder&lt;\/SourceFormat&gt;\r\n    &lt;\/Details&gt;\r\n    &lt;Links&gt;\r\n      &lt;Content&gt;\r\n        &lt;Url&gt;\/tivo.cgi\/26&lt;\/Url&gt;\r\n      &lt;\/Content&gt;\r\n    &lt;\/Links&gt;\r\n  &lt;\/Item&gt;\r\n  &lt;ItemStart&gt;0&lt;\/ItemStart&gt;\r\n  &lt;ItemCount&gt;2&lt;\/ItemCount&gt;\r\n&lt;\/TiVoContainer&gt;\r\n<\/pre>\n<\/blockquote>\n<p>The TiVo will now show a menu with 2 items (&#8220;Flac&#8221;, and &#8220;dsongs&#8221;).  If you select &#8220;Flac&#8221; it will send a new query the same as the the first but to <tt>\/tivo.cgi\/25?Recurse=No&amp;...<\/tt> etc.<\/p>\n<p>So far I&#8217;ve only shown <tt>&lt;Item&gt;<\/tt>s that were folders. Here&#8217;s what a song looks like:<\/p>\n<blockquote>\n<pre>  &lt;Item&gt;\r\n    &lt;Details&gt;\r\n      &lt;Title&gt;Way Out -&gt;&lt;\/Title&gt;\r\n      &lt;SongTitle&gt;Way Out -&gt;&lt;\/SongTitle&gt;\r\n      &lt;SourceBitRate&gt;320&lt;\/SourceBitRate&gt;\r\n      &lt;AlbumTitle&gt;The Middle Of Nowhere&lt;\/AlbumTitle&gt;\r\n      &lt;ArtistName&gt;Orbital&lt;\/ArtistName&gt;\r\n      &lt;AlbumYear&gt;1999&lt;\/AlbumYear&gt;\r\n      &lt;ContentType&gt;audio\/mpeg&lt;\/ContentType&gt;\r\n      &lt;SourceFormat&gt;audio\/mpeg&lt;\/SourceFormat&gt;\r\n    &lt;\/Details&gt;\r\n    &lt;Links&gt;\r\n      &lt;Content&gt;\r\n        &lt;Url&gt;http:\/\/10.0.0.1:8080\/db\/730f0108\/01-iTunes-320.mp3&lt;\/Url&gt;\r\n      &lt;\/Content&gt;\r\n    &lt;\/Links&gt;\r\n  &lt;\/Item&gt;\r\n<\/pre>\n<\/blockquote>\n<p>Note that the URL can point anywhere&#8211;to another port on my server in this case (running Apache::MP3 plus some transcoding extensions I wrote&#8211;that&#8217;s why the directory is labeled &#8220;Flac&#8221; but we&#8217;re pulling mp3s out of it).<\/p>\n<p>I don&#8217;t show it in this case, but there are also <tt>&lt;Duration&gt;<\/tt> and <tt>&lt;MusicGenre&gt;<\/tt> tags. I don&#8217;t know if they are order dependent or not, but I copied the TiVoDesktop server which puts them after <tt>&lt;SongTitle&gt;<\/tt> and <tt>&lt;AlbumYear&gt;<\/tt> respectively.<\/p>\n<p>The next thing that can happen is if you scroll through a list that has more than 8 items in it. When it needs to get the next set of items it issues something like this:<\/p>\n<blockquote>\n<pre>\/tivo.cgi\/22?Recurse=No\r\n&amp;Filter=x-container%2Ffolder,x-container%2Fplaylist,audio%2F*\r\n&amp;SortOrder=Type,Title\r\n&amp;AnchorItem=%2Ftivo.cgi%2F3\r\n&amp;AnchorOffset=-1\r\n&amp;ItemCount=1\r\n&amp;Details=Basic\r\n&amp;Format=text%2Fxml\r\n<\/pre>\n<\/blockquote>\n<p>So they added 2 new parameters: <tt>AnchorItem<\/tt> and <tt>AnchorOffset<\/tt>.<\/p>\n<p><tt>&lt;rant&gt;<\/tt>Whoever made up this part of the protocol was drunk. Notice that <tt>AnchorItem<\/tt> is given as a URL instead of an offset. This means if you have a list of <tt>&lt;Item&gt;<\/tt>s you have to search through them to find the URL of the AnchorItem. You could use a hash table to efficiently look for it but then you still need the original list so you can get the next 7 items. So for no good reason (that I can tell) this part of the protocol complicates your life. <tt>&lt;\/rant&gt;<\/tt><\/p>\n<p>Anyway, once you&#8217;ve found which <tt>&lt;Item&gt;<\/tt> the <tt>AnchorItem<\/tt> is referring to, you need to iterate <tt>AnchorItem<\/tt> by <tt>AnchorOffset<\/tt> + 1. Yes, +1. That&#8217;s why they almost always pass -1 as <tt>AnchorOffset<\/tt>. Lovely.<\/p>\n<p>One other &#8220;interesting&#8221; thing: <tt>ItemCount<\/tt> can be negative. If you scroll up through a list it&#8217;ll start sending you negative counts. I deal with this like this:<\/p>\n<blockquote>\n<pre>if ($count &lt; 0) {\r\n    $start += $count;\r\n    $count = -$count;\r\n}\r\n<\/pre>\n<\/blockquote>\n<p>I also then make sure to clip <tt>$start<\/tt> and <tt>$count<\/tt> so they are within the list of items on that container.<\/p>\n<p>When you select a track to play the TiVo will ask for details on just that item like this:<\/p>\n<blockquote>\n<pre>\/tivo.cgi\/4?Command=QueryItem\r\n&amp;Url=http%3A%2F%2F10.0.0.1%3A8080%2Fdb%2F730f0108%2F01-iTunes-320.mp3\r\n<\/pre>\n<\/blockquote>\n<p>The response looks like this:<\/p>\n<blockquote>\n<pre>&lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?&gt;\r\n&lt;TiVoItem&gt;\r\n  &lt;Item&gt;\r\n    &lt;Details&gt;\r\n      &lt;Title&gt;Way Out -&gt;&lt;\/Title&gt;\r\n      &lt;SongTitle&gt;Way Out -&gt;&lt;\/SongTitle&gt;\r\n      &lt;SourceBitRate&gt;320&lt;\/SourceBitRate&gt;\r\n      &lt;AlbumTitle&gt;The Middle Of Nowhere&lt;\/AlbumTitle&gt;\r\n      &lt;ArtistName&gt;Orbital&lt;\/ArtistName&gt;\r\n      &lt;AlbumYear&gt;1999&lt;\/AlbumYear&gt;\r\n      &lt;ContentType&gt;audio\/mpeg&lt;\/ContentType&gt;\r\n      &lt;SourceFormat&gt;audio\/mpeg&lt;\/SourceFormat&gt;\r\n    &lt;\/Details&gt;\r\n    &lt;Links&gt;\r\n      &lt;Content&gt;\r\n        &lt;Url&gt;http:\/\/10.0.0.1:8080\/db\/730f0108\/01-iTunes-320.mp3&lt;\/Url&gt;\r\n      &lt;\/Content&gt;\r\n    &lt;\/Links&gt;\r\n  &lt;\/Item&gt;\r\n&lt;\/TiVoItem&gt;\r\n<\/pre>\n<\/blockquote>\n<p>That&#8217;s just a standard <tt>&lt;Item&gt;<\/tt> wrapped in a <tt>&lt;TiVoItem&gt;<\/tt> tag.<\/p>\n<p>That is enough to set up a hierarchy, have the TiVo be able to maneuver around and play music. I wanted shuffle though so I enabled it in the TiVo expecting it to just jump around in the folders randomly. Nope. It sends this:<\/p>\n<blockquote>\n<pre>\/tivo.cgi\/1?Recurse=Yes\r\n&amp;Filter=audio%2F*\r\n&amp;SortOrder=Random\r\n&amp;RandomSeed=1172006919\r\n&amp;RandomStart=http%3A%2F%2F10.0.0.1%3A8080%2Fdb%2F0511ce12%2F02-iTunes-320.mp3\r\n&amp;AnchorItem=http%3A%2F%2F10.0.0.1%3A8080%2Fdb%2F5f103b07%2F04-iTunes-320.mp3&amp;ItemCount=1\r\n&amp;Details=Optimal\r\n&amp;Format=text%2Fxml\r\n<\/pre>\n<\/blockquote>\n<p><tt>RandomSeed<\/tt> and <tt>RandomStart<\/tt> are constant once you&#8217;ve started some music playing with shuffle enabled on the TiVo. Essentially It sends you the last song it played in <tt>AnchorItem<\/tt> and expects you to return a new song to play in <tt>&lt;TiVoItem&gt;<\/tt> form. For now I just key off of the <tt>SortOrder<\/tt> parameter and give a random item from the folder I&#8217;m in. This isn&#8217;t a good shuffle on small playlists since it will repeat songs while ignoring others, but it was simple.<\/p>\n<p>In fact, the implementation I made only does the bare minimum to work. I ignore most of the parameters passed in (like <tt>Details<\/tt> and <tt>Filter<\/tt>) but it works just fine (for now).<\/p>\n<h2>Debugging<\/h2>\n<p>What helped me was pointing towards the TiVo Desktop server on my Mac and submitting test queries. I&#8217;d also always had the apache access.log open in my Emacs so I could see what the TiVo was trying to ask me. And if I didn&#8217;t understand I&#8217;d recreate the query on the Mac to see what it would do. I&#8217;ll try to post more later, especially if there is some interest.<\/p>\n<h2>Conclusion<\/h2>\n<p>I&#8217;m happy I did this. It isn&#8217;t complete but it good enough for me for the time being. It&#8217;s been working all day (I&#8217;m listening to music out of my substandard TV speakers as I write this).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I tried out TiVo Desktop on my Mac and it was kind of cool&#8230; I found some articles and discovered the TiVo Desktop is just a http daemon plus some mdns stuff. Sounds easy enough!<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-19","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts\/19","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/comments?post=19"}],"version-history":[{"count":4,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts\/19\/revisions"}],"predecessor-version":[{"id":161,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts\/19\/revisions\/161"}],"wp:attachment":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/media?parent=19"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/categories?post=19"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/tags?post=19"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}