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


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 and put it on one of my local servers. It figures out the correct epoch timestamp, embeds it into the URL, and then redirects me there. I've removed some unneeded query string parameters and broken the URL down for readability.

<?php
$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");
exit;

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

📰 Resolving portmaster error "pkg-static: automake-1.16.1 conflicts with automake-wrapper-20131203"

📰 Resolving LibreNMS error "RuntimeException: The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths"

📰 Unusual HTTP POST traffic from 75.108.75.42

📰 1.1.1.1: Fast, but not so accurate (yet)

📰 autodiscover.xml as an Indicator of Attack

📰 Blocking Facebook's Tracking and Surveillance: A Comprehensive Approach

📰 Let's Encrypt Readies for Certificate Transparency with Embedded SCTs

📰 Evaluating DNSBL Effectiveness with Postfix Logs

📰 Russian/Ukrainian Referer Spam Campaign IPs

📰 Resolving subversion error E145001: Node has unexpectedly changed kind

📰 Installing PHP 7.2 with pthreads on CentOS 6

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

▲ Back to top | Permalink to this page