Mac OS X codesigning woes

I just discovered this wonderful bug. Apparently “hdiutil makehybrid” is stripping code signatures in some cases.

I first verify the code signature on an App (a build of Emacs, in this case)—there are no errors:

$ codesign --verify _dmg-build/Emacs.app/
$

I then use “hdiutil makehybrid” to create a disk image out of the directory.

$ hdiutil makehybrid -hfs -hfs-volume-name "Emacs Test" -hfs-openfolder _dmg-build _dmg-build -o /tmp/tmp.dmg

I then mount the created image and run try to verify the signature again—but this time it fails!

$ open /tmp/tmp.dmg
$ codesign --verify /Volumes/Emacs\ Test/Emacs.app/
/Volumes/Emacs Test/Emacs.app/: code object is not signed at all
In subcomponent: /Volumes/Emacs Test/Emacs.app/Contents/MacOS/bin-i386-10_5/grep-changelog

Investigating further, I use “xattr” to list the extended attributes on the “grep-changelog” file. First, the good file:

$ xattr _dmg-build/Emacs.app/Contents/MacOS/bin-i386-10_5/grep-changelog
com.apple.cs.CodeDirectory
com.apple.cs.CodeRequirements
com.apple.cs.CodeSignature

And now the bad file:

$ xattr /Volumes/Test\ Emacs/Emacs.app/Contents/MacOS/bin-i386-10_5/grep-changelog
com.apple.FinderInfo

Yup, all the code signature stuff is completely gone! (The “FinderInfo” stuff is OK, it’s just there as a side effect of mounting the disk image).

I’m not exactly sure how to fix this. Apple recently changed code signing requirements so that 10.9.5 now requires deep signatures (way to change something fundamental in a point release, guys). Also the only thing that correctly makes the deep signatures is Xcode 6 which was released only about 1 week before 10.9.5 was released (way to give advanced warning, guys).

2014-10-03 Update:

I filed a bug with Apple and they suggested I use “hdiutil create -srcfolder” instead of “makehybrid“. This does copy the extended attributes correctly. I had originally not used “create” for two reasons: It didn’t have the “-hfs-openfolder” option and the man page claims that only “makehybrid” makes optimally small filesystems. Turns out that “create -srcfolder” automatically does the same thing as “makehybrid -hfs-openfolder” (though it is not documented in the man page) and in practice the resulting .dmgs are just as small or smaller. Problem solved!

Playstation 4 NW-31250-1 Error

All my Playstation 4 downloads were failing today with “DNS Error” and “NW-31250-1”.

I ran a tcpdump on my router and found this:

15:07:10.389761 00:ee:ff:aa:bb:cc (oui Unknown) > 11:22:33:44:55:66 (oui Unknown), ethertype IPv4 (0x0800), length 109: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 95)
    red-death-router.porkrind.org.domain > 10.0.0.113.49218: [bad udp cksum 0x15cb -> 0xfee5!] 21068 ServFail q: A? ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net. 0/0/0 (67)

That looks promising, a DNS SERVFAIL response to a query for “ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net” (whatever that is).

My router is set up to use Google’s DNS: 8.8.8.8. So a quick test from my computer showed:

$ dig @8.8.8.8 ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net

; <<>> DiG 9.8.3-P1 <<>> @8.8.8.8 ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 8788
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net. IN A

;; Query time: 58 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Wed Jun  4 15:09:39 2014
;; MSG SIZE  rcvd: 67

“status: SERVFAIL”! But when using Level 3’s venerable 4.2.2.2:

$ dig @4.2.2.2 ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net

; <<>> DiG 9.8.3-P1 <<>> @4.2.2.2 ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14680
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net. IN A

;; ANSWER SECTION:
ic.8e0b5f0d.0c2f0f.gs2.sonycoment.loris.llnwd.net. 300 IN A 208.111.144.58

;; Query time: 26 msec
;; SERVER: 4.2.2.2#53(4.2.2.2)
;; WHEN: Wed Jun  4 15:09:26 2014
;; MSG SIZE  rcvd: 83

Aha, it works! So Google’s DNS is broken!

To fix this I set up my PS4’s networking manually and added 4.2.2.2 as a secondary DNS server. Then all my downloads started working again. I didn’t have to delete them and start over, either—just clicked retry and they continued from wherever they were.

Mac OS X 10.9 (Mavericks) and SSH pkcs8 keys

After upgrading to Mavericks (Mac OS X 10.9) I found that ssh-add wasn’t working. After investigating I discovered that the SSH shipped with Mavericks has a regression and doesn’t support pkcs8 keys. Mac OS X 10.8’s SSH supported these keys just fine.

Earlier in the year I had read an article about using pkcs8 formatted keys to encrypt your SSH private keys more strongly. I went ahead and did this because 10.8 (and my Linux machines) supported it just fine. 10.9, however ships with a different SSH. “ssh -V” outputs:

OpenSSH_6.2p2, OSSLShim 0.9.8r 8 Dec 2011

The previous version did not have “OSSLShim”, but rather used OpenSSL. My guess is that Apple replaced OpenSSL with some sort of API shim to another (Apple built?) library that doesn’t have support for pkcs8. Weak.

Anyway, the workaround is to use the openssl command line program to decrypt the key like this:

openssl pkcs8 -in ~/.ssh/id_rsa | ssh-add -

I put that in a file called “~/mavericks_sucks” so that I can just do:

. mavericks_sucks

in the terminal after I boot my computer and then everything works after that.

I’ve submitted a bug to Apple’s bug reporter, but it was marked as a duplicate of bug 14776937 but of course I can’t read bug 14776937 or get status on it because Apple’s whole bug reporting system is a piece of crap. Oh well. Hopefully their stupid shim will support all the features of normal OpenSSL (before 10.10).

Why is there no process viewer in Firefox?

As I sit here right now my laptop is uncomfortably hot and its fans are making a really loud high pitched whine. Something on my computer is spinning the CPU and it’s making me literally hot. I look at the processes and FireFox is taking between 40% and 80% of the CPU! I’m basically sitting idle too. Well, right now I’m typing this message in Firefox, but 5 minutes ago the computer was sitting unattended and it was still going nuts.

Ok, so Firefox is wigging. But why? I can’t find any way to do some introspection. I suspect it’s one or two errant pages sitting in a tight javascript or flash loop. But how can I find out which pages? I have 26 windows open and most windows have multiple tabs open each (one has 30 tabs). Each of them is open for a reason–some unfinished business I have at each page, so I can’t just close them randomly. Besides, that would admit defeat.

What I need is a plug-in that monitors all the javascript and plugins that are running and keeps track of how long each runs. Then I want a unix top-like view of all the open pages and how many resources each is consuming (how much memory, how much cpu time their javascript is taking, etc). Why doesn’t this already exist? Sadly I just don’t have time right now to write such a thing, considering how much I’d have to first learn about the internal workings of Mozilla threads (I assume they are all green threads, because something is running so much my typing has a significant delay every few seconds).

Please, please, please (let me get what I want) won’t somebody write something like this? How do you Mozilla developers work? Is there a Javascript profiler available already? I’ve searched the add-ons before but I can’t find anything like this.

All I know is my lap is getting hot and I can’t do anything about it because I need Firefox open right now and it’s beginning to really annoy me.

Selling out: How a Free Software advocate ended up releasing a shareware program

I write computer programs for a living and quite often I end up writing code for myself at home. Occasionally I get the software into a releasable state and stick it up on my web site. When I had a steady salary I would always just GPL everything I released. Mostly since I didn’t want to support it–I felt that if I tried to hoard the source code I’d be on the spot if someone needed it fixed for them, and I’d feel bad if I couldn’t do it. Giving away the source cleared my conscience since I figured, if you don’t like it, change it yourself.

When I quit my job and started consulting I lost the security that comes from a steady paycheck. I’m making more money now, but it always feels temporary and fleeting. So when I needed to write a piece of software to scratch my own itch I started thinking about selling it instead of releasing it as free software. I know, that seems antithetical to the Free Software movement and, well, it is. My dad laughed out loud when I told him I was releasing a shareware app since I’ve been know to rail about proprietary software before. It’s amazing how a little financial insecurity can change your outlook. Don’t worry, I still donate to the FSF and believe in Free Software.

Mostly, I was very curious about how much money I would make form a piece of shareware. My program was a pretty simple idea–I wanted to have multiple libraries in iTunes for different versions of my music. I have my music all ripped to a central server that transcodes on the fly so it can serve out different file formats at different encoding rates to my various clients. When I’m away from home I stream .mp3’s to my iTunes and .oggs to my xmms. At home I stream .wavs to my iTunes and .flacs to my xmms. If I’m somewhere with not a lot of bandwidth I can stream at a lower bitrate too.

So I wanted to set up my iTunes with a library full of .mp3 streams and separate library with the same songs as .wav streams. I looked around at the existing freeware and shareware solutions and found them all lacking. They all were separate applications that you had to run which I thought was too cumbersome. At this point Apple hadn’t added the option key and iTunes boot trick (which I still think is more cumbersome than my final solution).

What I really wanted was something that fit inside of iTunes so I could switch without launching a separate program. Something that would just be smooth. Since I started programming on the Mac back in the System 7 days I was familiar with the low level carbon calls and I figured I could abuse iTunes plug-in interface to do what I wanted. So I hacked on it for a weekend and got it working. MultiTunes was born.

When I was done I thought MultiTunes was smooth enough to warrant releasing it. So I downloaded a copy of the GPL and was prepping a release when I thought, “wait, these other guys sell theirs. Why can’t I sell mine? How much would I make anyway? Could I make a living selling dorky little shareware apps?” I looked around for people doing shareware but didn’t really find anyone that would divulge their numbers and so I couldn’t really get a handle on how much it would really pay. So in the spirit of experimentation, I decided to release it as shareware and see what happened.

Releasing a program is hard. It always takes 10 times longer than I expect to get everything right. I think I spent more days on the details of the release than I spent getting the program working in the first place. I had to make some artwork for the disk image, a script to create the disk image so I can just to “make release” and not have any manual steps–the more manual steps you have to make, the more errors will occur in the process causing more delays and more overall time spent resulting in less payoff per hour of work. More importantly, the harder it is to release, the more I dread it and refuse to work on the program. So it’s important for me to have the release process be really smooth. I want to edit a version number in exactly one place, edit the changelog, tag the repo and “make release”.

Releasing my first shareware program was doubly hard. How do I get someone to pay me? I looked up merchant accounts and credit card services and realized they were way too much work. Maybe if I was doing this full time it would make sense, but no, I don’t want that. I looked at some shareware services but ultimately decided to just use Paypal and some custom server software to give out registration codes on receipt of payment. This actually didn’t take too long to write–maybe a day or two–and I made sure I wrote it generically enough that I could easily add more programs to sell when I inevitably came up with more brilliant ideas. It’s been 2 years since then and I’ve come up with approximately zero. But the point is, the infrastructure for selling wasn’t too hard, provided I was willing to give up a percentage of my profits to Paypal (which I was).

The next step was deciding on a price. Too high and no one is going to buy it. Too low and I’m essentially throwing away money that people would be willing to give me. I looked around at MultiTunes‘ competitors, as well as other shareware iTunes plug-ins. I thought my solution was smoother than my competitors’ so I thought the price could stand to be higher. I didn’t think I could charge as much as Octiv’s VolumeLogic, which before it was defunct cost $19.99. I asked myself what I was willing to pay for MultiTunes had I not written it and I decided I was willing to pay $10. But I know I’m a cheapskate when it comes to shareware and so I added 50% to that. I also decided to use the cheesy and cliche $0.99 perception trick and so the price I chose was $15.99.

I thought it might be too high, but then again, it’s easier to lower the price if it’s too high but it’s annoying to try to raise the price if you feel it’s too low. In the end I think the price I chose was right, but it’s more of a feeling than a fact backed by hard evidence. The only real way to tell would be to offer it to different people at a different prices and see what happened, but that seemed unfair and hard to set up.

So, I have a nifty program, a infrastructure for selling it, and a price. How much money am I going to make? Well, I’ll write a separate post about that soon, but in the meantime I’ll answer it this way: Not enough to live off of. Certainly not as much as the Delicious Library guys, but then again my program isn’t nearly as cool, sophisticated or good looking. Then again, I wrote it by myself in a weekend or two! And even factoring the time I’ve spent improving it and the email support I provide (and a random telephone support call that I got while visiting my parents for Christmas from a guy who looked up my phone number (!!!)) I think the profit per hour ratio is not too bad. Sadly I didn’t really keep track of the time I worked on it so it’s hard to calculate that number exactly.

Anyway, that’s all for now. I’ll post something soon with specific numbers–profits, piracy rates, etc. The kind of stuff I wished I’d had an idea about when I first started.

Polling is always wrong

I read this article on reddit and while I thought the author was correct that RSS is really a bad design, I think he missed the real underlying reason why. That reason is that polling is always wrong. Always. Really!

RSS ends up fundamentally being a polling technology. Everyone hits your site saying, “hey, you got something new?”

“How about now?”

“Now?”

“Still nothing, huh”

“Ok, Now?”

Over and over it asks, never taking a break. What is the appropriate speed at which it asks? 1 hour? 1 second? Who knows.

It’s not just internet protocols that poll. Polling as a technique is pervasive, hitting every level from the highest to the lowest. Does your OS go out and hit your CD-ROM drive every so often looking for a disc to finally be inserted? Why isn’t there a SCSI command that makes the drive send a command to the computer when a disc is inserted? If there is, why isn’t your OS using it?

What about something simple like the volume slider in your system tray? My friend Jim was using Intel’s PowerTool and discovered the Gnome volume control kept taking the processor out of C3 sleep. Turns out the volume control was waking up constantly and polling ALSA to see if another program had changed the volume behind its back. ALSA even has an API for being notified of the volume change!

Even at the low level of embedded programming there are the same choices. Does your mouse’s microcontroller poll a GPIO to see if you’ve pressed the mouse button, or does the button trigger an interrupt?

Why do people poll?

There’s only one reason. It’s easy. It’s trivial to wrap your head around—You just loop forever, checking for whatever you’re polling for and then sleeping for a little bit. But to do it the right way is more complicated—you need to have to have some infrastructure. You need a way to register for the event you want, which implies you also need some way of getting called back when your event happens. In some cases (like the mouse button), it’s almost as easy to do it the right way as it is to poll. But others, like the RSS case, it’s much harder.

To do RSS properly you’d need some way of getting events pushed to you. One way would be with email, as that infrastructure already exists. But given the amount of disparate email servers and RSS readers, it might be hard to hook your RSS reader to your email. Or you could simulate a callback by having the server hold the connection open until something happens. But given that the connection will eventually time out, that’s really just a degenerate way of polling.

Ah, forget it. Let’s just poll!

The iPhone is not expensive

Every review I read about the iPhone whines that the price is high. So let’s see how high it really is.

An iPhone can be thought of as an iPod Nano plus a phone. Except it’s not quite the same as a Nano. It’s got that screen that’s even bigger than the disk based iPods. And it’s got that cool visual voice mail.

A 4 GB iPod Nano is $199.
A 8 GB iPod Nano is $250.

The 4GB iPhone is $499.
The 8GB iPhone is $599.

A Palm Treo is $99 to $199, an old (refurb) model for $49.
W/ 4GB Nano: $298 to $398, old (refurb) model $248.
W/ 8GB Nano: $349 to $449, old (refurb) model $299.

A Blackberry is $199 to $299, an old model for $49.
W/ 4GB Nano: $398 to $498, old (refurb) model for $248.
W/ 8GB Nano: $449 to $549, old (refurb) model for $299.

All the phone prices are from ATT’s site and are for a 2 year contract, just like the iPhone. The cost per month with data plans is the same regardless of what phone you have so there’s no point worrying about it.

So, depending on what model you buy, you end up paying between $1 and $200 extra for the iPhone. A refurb phone and iPod Nano bundle is between $200 and $300 cheaper than the iPhone. But lets be real here. The iPod is the top of the line right now so it’s not really comparable to an old refurbished phone.

Right now you’re looking at an average of $150 more for an 4GB iPhone and $200 more for an 8GB iPhone. That effectively is the price for the convenience of having both your iPod Nano and smart phone in the same device. And considering the nice screen and some of the cool features of the phone not found anywhere else, that doesn’t seem that expensive to me.

Tivo Desktop on Linux

I tried out TiVo Desktop on my Mac and it was kind of cool. It lets you play music on your TiVo that’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–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!

I installed mdns-scan (thank you Debian) which reports:

+ David Caldwell's Photos on black._tivo-photos._tcp.local
+ David Caldwell's Music on black._tivo-music._tcp.local

I copied /etc/avahi/services/ssh.service to /etc/avahi/services/tivo.service. I then changed the name to “Lovely Music”, “_ssh._tcp” to “_tivo-music._tcp” and picked a port out of the air: 8000. I reloaded the avahi daemon and wham, the “Lovely Music” shows up on the TiVo. Yay!

The rest is just http on port 8000 that spews XML. That should be fairly easy to do…

…. Time Passes ….

And 8 hours later I say, “Fairly easy?”. Actually it turned out to be pretty easy. The main problem I had was that the %$#@! TiVo had died on me and wouldn’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….

Avahi

Turns out mdns-scan doesn’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:

<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<!-- See avahi.service(5) for more information about this configuration file -->
 <service-group>
  <name>Lovely Music</name>
  <service>
    <type>_tivo-music._tcp</type>
    <port>8001</port>
    <txt-record>protocol=http</txt-record>
    <txt-record>path=/tivo.cgi/music</txt-record>
  </service>
</service-group>

The path, <port> and <name> can be anything you want. I’m not sure about protocol.

Here’s the corresponding one for the photo sharing:

<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<!-- See avahi.service(5) for more information about this configuration file -->
<service-group>
  <name>Beautiful Photos</name>
  <service>
    <type>_tivo-photos._tcp</type>
    <port>8001</port>
    <txt-record>protocol=http</txt-record>
    <txt-record>path=/tivo.cgi/photos</txt-record>
  </service>
</service-group>

http server

I set up port 8001 on apache and then wrote tivo.cgi. I wrote it in the least general way possible and so I don’t plan on publishing it–it just wouldn’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…

I could publish my simple test server, if anyone is interested…

Photos

I haven’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’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.

Protocol

The protocol is pretty simple.

The tivo will send a request like this (each CGI parameter is on it’s own line for clarity and horizontal space conservation):

/tivo.cgi/music?Recurse=No
&Filter=x-container%2Ffolder,x-container%2Fplaylist,audio%2F*
&SortOrder=Type,Title
&ItemCount=0
&Details=Basic
&Format=text%2Fxml

The server should respond with something like this:

<?xml version="1.0" encoding="UTF-8" ?>
<TiVoContainer>
  <Details>
    <Title>Top</Title>
    <ContentType>x-container/tivo-music</ContentType>
    <SourceFormat>x-container/tivo-music</SourceFormat>
    <TotalItems>2</TotalItems>
  </Details>
  <ItemStart>0</ItemStart>
  <ItemCount>0</ItemCount>
</TiVoContainer>

The TiVo will then repeat the request with a higher ItemCount:

/tivo.cgi/music?Recurse=No
&Filter=x-container%2Ffolder,x-container%2Fplaylist,audio%2F*
&SortOrder=Type,Title
&ItemCount=2
&Details=Basic
&Format=text%2Fxml

This time the server should give some <Item>s:

<?xml version="1.0" encoding="UTF-8" ?>
<TiVoContainer>
  <Details>
    <Title>Top</Title>
    <ContentType>x-container/tivo-music</ContentType>
    <SourceFormat>x-container/tivo-music</SourceFormat>
    <TotalItems>2</TotalItems>
  </Details>
  <Item>
    <Details>
      <Title>Flac</Title>
      <ContentType>x-container/folder</ContentType>
      <SourceFormat>x-container/folder</SourceFormat>
    </Details>
    <Links>
      <Content>
        <Url>/tivo.cgi/25</Url>
      </Content>
    </Links>
  </Item>
  <Item>
    <Details>
      <Title>dsongs</Title>
      <ContentType>x-container/folder</ContentType>
      <SourceFormat>x-container/folder</SourceFormat>
    </Details>
    <Links>
      <Content>
        <Url>/tivo.cgi/26</Url>
      </Content>
    </Links>
  </Item>
  <ItemStart>0</ItemStart>
  <ItemCount>2</ItemCount>
</TiVoContainer>

The TiVo will now show a menu with 2 items (“Flac”, and “dsongs”). If you select “Flac” it will send a new query the same as the the first but to /tivo.cgi/25?Recurse=No&... etc.

So far I’ve only shown <Item>s that were folders. Here’s what a song looks like:

  <Item>
    <Details>
      <Title>Way Out -></Title>
      <SongTitle>Way Out -></SongTitle>
      <SourceBitRate>320</SourceBitRate>
      <AlbumTitle>The Middle Of Nowhere</AlbumTitle>
      <ArtistName>Orbital</ArtistName>
      <AlbumYear>1999</AlbumYear>
      <ContentType>audio/mpeg</ContentType>
      <SourceFormat>audio/mpeg</SourceFormat>
    </Details>
    <Links>
      <Content>
        <Url>http://10.0.0.1:8080/db/730f0108/01-iTunes-320.mp3</Url>
      </Content>
    </Links>
  </Item>

Note that the URL can point anywhere–to another port on my server in this case (running Apache::MP3 plus some transcoding extensions I wrote–that’s why the directory is labeled “Flac” but we’re pulling mp3s out of it).

I don’t show it in this case, but there are also <Duration> and <MusicGenre> tags. I don’t know if they are order dependent or not, but I copied the TiVoDesktop server which puts them after <SongTitle> and <AlbumYear> respectively.

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:

/tivo.cgi/22?Recurse=No
&Filter=x-container%2Ffolder,x-container%2Fplaylist,audio%2F*
&SortOrder=Type,Title
&AnchorItem=%2Ftivo.cgi%2F3
&AnchorOffset=-1
&ItemCount=1
&Details=Basic
&Format=text%2Fxml

So they added 2 new parameters: AnchorItem and AnchorOffset.

<rant>Whoever made up this part of the protocol was drunk. Notice that AnchorItem is given as a URL instead of an offset. This means if you have a list of <Item>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. </rant>

Anyway, once you’ve found which <Item> the AnchorItem is referring to, you need to iterate AnchorItem by AnchorOffset + 1. Yes, +1. That’s why they almost always pass -1 as AnchorOffset. Lovely.

One other “interesting” thing: ItemCount can be negative. If you scroll up through a list it’ll start sending you negative counts. I deal with this like this:

if ($count < 0) {
    $start += $count;
    $count = -$count;
}

I also then make sure to clip $start and $count so they are within the list of items on that container.

When you select a track to play the TiVo will ask for details on just that item like this:

/tivo.cgi/4?Command=QueryItem
&Url=http%3A%2F%2F10.0.0.1%3A8080%2Fdb%2F730f0108%2F01-iTunes-320.mp3

The response looks like this:

<?xml version="1.0" encoding="UTF-8" ?>
<TiVoItem>
  <Item>
    <Details>
      <Title>Way Out -></Title>
      <SongTitle>Way Out -></SongTitle>
      <SourceBitRate>320</SourceBitRate>
      <AlbumTitle>The Middle Of Nowhere</AlbumTitle>
      <ArtistName>Orbital</ArtistName>
      <AlbumYear>1999</AlbumYear>
      <ContentType>audio/mpeg</ContentType>
      <SourceFormat>audio/mpeg</SourceFormat>
    </Details>
    <Links>
      <Content>
        <Url>http://10.0.0.1:8080/db/730f0108/01-iTunes-320.mp3</Url>
      </Content>
    </Links>
  </Item>
</TiVoItem>

That’s just a standard <Item> wrapped in a <TiVoItem> tag.

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:

/tivo.cgi/1?Recurse=Yes
&Filter=audio%2F*
&SortOrder=Random
&RandomSeed=1172006919
&RandomStart=http%3A%2F%2F10.0.0.1%3A8080%2Fdb%2F0511ce12%2F02-iTunes-320.mp3
&AnchorItem=http%3A%2F%2F10.0.0.1%3A8080%2Fdb%2F5f103b07%2F04-iTunes-320.mp3&ItemCount=1
&Details=Optimal
&Format=text%2Fxml

RandomSeed and RandomStart are constant once you’ve started some music playing with shuffle enabled on the TiVo. Essentially It sends you the last song it played in AnchorItem and expects you to return a new song to play in <TiVoItem> form. For now I just key off of the SortOrder parameter and give a random item from the folder I’m in. This isn’t a good shuffle on small playlists since it will repeat songs while ignoring others, but it was simple.

In fact, the implementation I made only does the bare minimum to work. I ignore most of the parameters passed in (like Details and Filter) but it works just fine (for now).

Debugging

What helped me was pointing towards the TiVo Desktop server on my Mac and submitting test queries. I’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’t understand I’d recreate the query on the Mac to see what it would do. I’ll try to post more later, especially if there is some interest.

Conclusion

I’m happy I did this. It isn’t complete but it good enough for me for the time being. It’s been working all day (I’m listening to music out of my substandard TV speakers as I write this).

Last Modified on: Dec 31, 2014 18:59pm