Monday, November 3, 2008

Get Cable, Dish and Local TV Listings Using Bash

Hey There,

For this week's Monday Linux/Unix bash shell script, we're following up on what turned out to be a fairly popular script from last week that made it so you could get your local tv listings from the command line with bash. This week's script is an update to include cable and dish TV as well as provide greater coverage of your personal provider's schedule. The next paragraph is another litany of "other scripts we've done before that somewhat resemble this one." Please skip it and move on to paragraph three if you're just interested in today's topic :)

Our previous web-based Bash scripts, in backward chronological order, include our posts on accessing Wikipedia, accessing the Farmer's Almanac, accessing the International Dictionary, checking out the world's weather, spewing out famous quotations on pretty much any subject, doing encyclopedia lookups, accessing the online Thesaurus, translating between different languages and, of course, using the online dictionary.

This script still gets its content from TV Listings At Zap2It.com and accepts a much greater number of arguments than our last attempt. In this version, you can run the entire script manually, like so:

host # ./cabletv.sh

or use any of the flags for the various arguments required at time of execution. Of course we include "-h" for help. See directly below for a picture of your help/usage printout and a manual run up to, but not including, the beginning of the listings:

Click on the picture below and watch the magic happen ;)

cabletv help and manual usage

After that, check out the next picture below, which shows a successful execution and the beginning of the TV listings. Note that the listings now cover a 3 hour time period and include start times for all shows (even those that began before your specified begin time, but happen to end within your time frame):

Click on the picture below and live slightly larger ;)

cabletv usage variations

I'd like to thank everyone who wrote in with comments and suggestions (which have been posted on the original tv script page). You've inspired me to do a better job, but (Lord knows) this script could still use some work. Hopefully you'll find it helpful :)

Of course, later this week, I'll be releasing some wrapper scripts to generate "Favorite Listings," "Only Movie Listings," etc...

SPECIAL NOTE FOR SOME *NIX USERS: If your version of Linux or Unix does not use the Gnu date (available for download as a part of the Gnu coreutils package), the Unix time (seconds since the epoch) extrapolation part of this script won't work correctly.

A few suggestions for getting around having to use Gnu date (available on newer Solaris and some distro's as gdate) include substituting this line of code:

cli_time2=`date -d "$cli_time" +%s`

with either:

1. cli_time2=`perl -e 'print int($cli_time)'` - although this can produce dubious results, or:

2. compiling your own version of gdate with the following code snippet found on The Unix Dummies Question Board:

/* esec.c - display seconds since 00:00:00 UTC, January 1, 1970 */
#include <stdio.h>
#include <time.h>

int main ( void )
{
return ( printf ("%d\n", time (NULL)) );
}


which can be compiled with gcc, like so:

host # gcc -o mygdate esec.c

and used, as a replacement for the above mentioned line of code, like so:

cli_time2=`mygdate "$cli_time"`

Of course, with the understanding that this wouldn't be an issue if the whole program were written in Perl, any suggestions are welcome. If enough interest exists, I'd be happy to rewrite this, in Perl (to make use of the localtime() and TimeLocal() functions), for publication in the near future.

In the meantime, we'll keep it as basic as possible so that anyone with Linux and a shell can find out what's on TV/Cable/Dish without turning on the tube ;)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# cabletv.sh - Get your local regular and HD Tv Cable or Dish listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
#
# TODO You have entered an invalid ZIP or postal code
# TODO am to pm or pm to am ends up not in chronological order
# TODO 12 to 1 a/p ends up not in chronological order
# TODO Deal with special characters
# TODO configuration file
# TODO better error checking
# TODO less hamfisted way of getting 3 hour spread for all channels
#

function usage()
{
echo
echo "Usage: $0 ...all options are optional [-h for help]"
echo "[-z USZipCode] [-p LocalCableOrDishProviderID]"
echo "[-t Time] [-d date] [-n to leave out HD channel info]"
echo
exit 1
}

while getopts t:z:hnd:p: option
do
case $option in
t)
opt_time="$OPTARG"
;;
d)
opt_date="$OPTARG"
;;
z)
opt_zip="$OPTARG"
;;
n)
opt_nohd=1
;;
p)
opt_provider="$OPTARG"
;;
h)
usage
;;
*)
usage
;;
esac
done

wget=/usr/bin/wget
pager=/usr/bin/more

if [ ! $opt_date ]
then
nicedate=`date "+%m/%d/%y"`
else
nicedate=$opt_date
fi


if [ ! $opt_time ]
then
hour=`date "+%H"`
minute=`date "+%M"`
ampm=`date "+%p"`
if [ $hour -lt 12 ]
then
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 AM"
else
nicetime="${hour}:30 AM"
fi
else
let hour=$hour-12
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi
cli_time=${hour}:${minute}${ampm}
else
cli_time=$opt_time
fi

cli_time2=`date -d "$cli_time" +%s`
let fromtime=$cli_time2*1000
let fromtime2=$fromtime+1800000
let fromtime3=$fromtime2+1800000
let fromtime4=$fromtime3+1800000
let fromtime5=$fromtime4+1800000
let fromtime6=$fromtime5+1800000
let fromtime7=$fromtime6+1800000
cli_time_end=`echo $fromtime7|awk '{printf("%20s",strftime("%I:%M %p - %x", $0 / 1000))}'`

if [ ! $opt_nohd ]
then
opt_nohd=0
fi

if [ ! $opt_zip ]
then
echo -n "Enter Zip Code: "
read zip_code
else
zip_code=$opt_zip
fi

if [ ! $opt_provider ]
then
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}" 2>&1|sed 's/<.*&lineupId=\([^&]*\)\">/\1 \c/'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Choose Your Provider/{x;$!d;x;q;};x;$G' -e '/document.write/,$d' -e "s/'/'/" -e 's/&/\&/' -e 's/^[ \t]*//;s/[ \t]*$//'|sed '/\([A-Za-z0-9_][A-Za-z0-9_]*:[-X]\)/ {
N
s/ *\n/ \t/g
}'
echo "Enter Provider ID [e.g. \"IL57303:-\" \"4DTV:-\" \"IL12561:X]\""
read tv_provider
else
tv_provider=$opt_provider
fi

echo
echo "Television Listings for $nicedate from $cli_time to $cli_time_end"
echo
last=1

if [ $opt_nohd -eq 1 ]
then
for x in $fromtime $fromtime2 $fromtime3 $fromtime3 $fromtime4 $fromtime5 $fromtime6
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*$/,+2p'|sed 's/^\([0-9][0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("%20s %-20s", "*** Start",strftime("%x - %I:%M %p",$3 / 1000));print ""}}'
done|sort -t'-' -k1,1n -k2,3n | uniq|while read one two three;do if [ $one -eq $last ];then echo " $three";last=$one;else echo "$one $two $three";last=$one;fi;done|$pager
else
for x in $fromtime $fromtime2 $fromtime3 $fromtime3 $fromtime4 $fromtime5 $fromtime6
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*\.*[0-9]*$/,+2p'|sed -e 's/^\([0-9][0-9]*\.*[0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*\.*[0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("%20s %-20s", "*** Start",strftime("%x - %I:%M %p",$3 / 1000));print ""}}'
done|sort -t'-' -k1,1n -k2,3n | uniq|while read one two three;do if [ "$one" == "$last" ];then echo " $three";last=$one;else echo "$one $two $three";last=$one;fi;done|$pager
fi

exit 0


, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.