(new Soapbox())->shout(array_map('strtoupper', $opinions)); //Shaun's blog

Me, elsewhere

Miscellaneous public code

A PHP API client for Reddit

I don't tweet much

XMPP chat
(Pidgin, Miranda, Swift, etc.)

Perfect is the enemy of good enough.

LocalStorage kills another site, or: Working around Zap2it's new interface

Posted January 06, 2018 by shaun

I hate when a redesign gets in the way of a good thing.

Zap2it.com has been my choice for TV listings for many years. They briefly rebranded as "Screener" in 2016, with a new domain that broke my bookmarks and a bright yellow theme that broke my eyeballs. That venture didn't go very far; the Zap2it name returned after a few months, at which point I updated my TV listings bookmark (again) and went on with my life.

Tonight I went to see what's on TV and my trusty bookmark took me to a 404 page. Compounding matters, visiting the main Zap2it.com page showed me a totally blank "TV Listings Grid" section. This is the unfortunate hallmark of a website that won't work properly if LocalStorage is disabled.

I normally keep LocalStorage turned off, because it presents a bundle of privacy issues and I've found that the simplest way to deal with them is to reject the feature altogether. When I find a site like Zap2it that refuses to work with dom.storage_enabled set to false, sometimes I'll enable the feature temporarily and examine what's happening under the hood. My experience is that most sites demanding LocalStorage fall into one of two categories:

  1. Overzealous LocalStorage: dozens or even hundreds of key/value pairs are stored, most of which aren't read or used by the application. The site is essentially offloading analytics data storage onto its visitors for some later, unknown purpose.

  2. Gratuitous LocalStorage: a handful of small key/value pairs are stored, a use case that doesn't require LocalStorage at all. This breaks the site for visitors with DOM storage disabled, while providing no tangible benefit to the developer.

The new Zap2it interface falls into the second category. The site places the following tuples into the browser's LocalStorage:

Zap2it.overlay.shownOverlay: true
Zap2it.provider.search.result: {"providerResults": //JSON containing my local TV providers
Zap2it.gridScroll.scrollTop: null
Zap2it.gridpos.gridpos: null
Zap2it.providerData.provider: {"lineupId:" //JSON identifying the TV provider I chose

That's it. I suppose if I signed up for an account, it would probably store my username and a session ID, too, but there's nothing fancy going on here. This could all be stored in traditional cookies, which are easier for users to manage, without crippling the UI for the privacy-conscious audience.

While I was poking at the site, I found a workaround to keep getting the listings, which itself requires a bit of a hack.

The new Zap2it listings grid has a "Print" button. That link generates a searchable PDF of the full TV listings table, and accepts all of its parameters directly from the query string, no LocalStorage required. Getting here is fairly easy:

  • Temporarily enable LocalStorage
  • Go to the Zap2it front page
  • Dismiss the tutorial dialog
  • Enter your ZIP code
  • Choose your TV provider
  • When the grid appears, click "Print" in the upper right
  • Copy the PDF's URL, which contains the parameters for your local listings
  • Disable LocalStorage again

Of course, no good workaround comes without a challenge.

The PDF generator URL contains a time= parameter that holds the epoch timestamp corresponding to the most recent half-hour programming block; that is, it points to either :00 or :30 of the current hour. This tells the Zap2it application where to start the TV listings grid. I can't bookmark that directly or else I'm always going to see the listings for January 6, 2018. But this is a necessary element: omitting it results in a 502 Gateway Timeout error. Lacking a user-supplied start time, Zap2it is probably trying to crunch every TV program my provider has ever shown.

As a workaround to the workaround, I wrote a little PHP script that calculates the correct epoch timestamp, embeds it into the URL, and then redirects me to the Zap2it PDF. I've removed some unneeded query string parameters and broken the URL down for readability.

$epoch = time() - ((date('i') % 30) * 60);

$url = 'https://tvlistings.Zap2it.com/api/printGrid?timespan=3'
        //Provider ID, twice
        . '&lineupId=DITV640'
        . '&headendId=DITV640'
        . '&country=USA'
        . "&time={$epoch}"
        . '&dstStart=2018-03-11T02:00Z'
        . '&dstEnd=2018-11-04T02:00Z'
        . '&dstOffset=-360&stdOffset=-360';

header("Location: $url");

Now that's something I can bookmark! Time will tell if I have to update the Daylight Saving start and end times, but tweaking a script once a year is much more palatable to me than keeping LocalStorage turned on.

Recent articles

📰 Caveat with Vantec SATA/IDE to USB 2.0 Adapter and Macrium software

📰 Jay Niffley, Man of Mystery

📰 Compiling Doxygen on FreeBSD without LaTeX and Ghostscript

📰 Introducing Snuze, a PHP client for the Reddit API

📰 jisusaiche: Java's installer telemetry

📰 BIND client log error "query_find: query_getdb failed"

📰 Resolving "The lang/perl5.24 port has been deleted: Has expired" portmaster error

📰 Armagaddon2 interim fix for Firefox 56 and other old versions

📰 Strange DNS queries: qname "miep", qtype ANY

📰 Undeliverable as addressed: A massive broken spam campaign?

📰 Using WITH_META_MODE and ccache for FreeBSD build boosts

📰 Resolving subversion error E000013: Unable to create pristine install stream

📰 Enhancements to SmokePing's AnotherDNS probe

📰 Generating vanity DNSSEC key tags

📰 DDoS involving forged packets from

▲ Back to top | Permalink to this page