Monday, November 24, 2008

Cabletv.sh For Linux or Unix - Final Blog Update

Hey There,

IMPORTANT NOTE: As noted in the title, the poll we put up last week showed that a majority of folks who participated in the blog's poll (72%) would rather that this script's development be moved to my SourceForge page and become a separate project which would mean that we won't be doing any more follow-up posts to this one. I must say that I whole-heartedly agree. The first benefit will be that we won't be continually putting up the same material every Monday. The second, and perhaps even better, benefit will be that I'll be able to work on the script on an entirely different schedule. Once it's freed from the daily-post deadline, I'll be able to completely overhaul it and make it much better than it is (and a whole lot more quickly). I want everyone who's written in with suggestions for improvement to know that I've read all your emails (so far) and am very appreciative of your constructive criticism and creative approaches toward making the script into a fuller and more robust "program." I apologize if I haven't gotten back to you yet (for those of you whom I haven't gotten back to ;), but I promise you that I will. BTW (This is how goofy I am), I just realized, as I was doing some backtracking, that I've posted an entry every day since October 23rd 2006. It's a miracle I still have a job and the love of my family. I'm just wondering how I manage to spend time with them... oh, yeah. I don't sleep ;)

LAST NOTE ON THIS: At this point, I'll need to setup my SourceForge page to include the project and, as soon as it's up, I'll be sure to put a link to it on the blog (In the upper right hand corner, with the policies and "send me a comment" links). I'll probably note it in that day's blog listing, too, but you'll know it's ready as soon as you see that link appear. It should be up sometime this week.

For this week's Monday Linux/Unix bash shell script continuation, we're finishing up the blog version of our cabletv.sh script from last week. If you liked that one, and want to check out other versions, please revisit last week's cabletv.sh script, and that page will link back to all the other versions of the script hosted on this blog. If you can see the Blogger search bar (wrapped around the site and not really part of it), searching for "cable tv" should bring up all of them on one or two pages. As usual, skip the next paragraph if you've read it before. It lists out all our other "bringing the web to your CLI" scripts we've cranked out to date. Starting next week, there will finally be more original content!

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 week's improvements are limited to one (I guess that would just make it a singular "improvement" ;). I received several suggestions about reliance on awk's strftime, which helped us get over the in-sequential time problem, but added some minutes to the output. For the record, this fix isn't included in this version, as problems with formatting need to be completely reworked, but, to answer the question, the quicker way to do this without awk is with Gnu date (and it "must" be Gnu date, as, for instance, Solaris' version of date doesn't allow for the same type of formatting). So, in a version I'm not putting up here because it isn't ready yet, we've changed:

strftime("%x - %I:%M %p", TheTimeFromZap2It/1000)) }'

to:

date -d @$((TheTimeFromZap2It/1000)) "+%x - %I:%M %p"

which is much faster!

Our only improvement for this week's sendoff is that we've switched from using wget to using straight-up telnet, since more people have telnet on their machines (by default) than wget (If you recall, the original reason we went with wget was because we didn't want folks to have to run lynx and/or html2text for no reason). This also results in a slight performance improvement (about 30 seconds), but I'll take it :) So, our URL calls that used to look like this:

wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}"

are now set up this way, using telnet to port 80 and passing an HTTP/1.1 request (Zap2it bumps you if you use HTTP 1.0!), like so:

(echo "GET /tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80

It seems more convoluted (and, technically, I suppose it is), but it works faster. I'm assuming because telnet is so ingrained in Linux and Unix and it saves us the time of loading up another external program that (although it may be "better suited" to the task) can do much more than we need it to and comes with a bit of extra baggage, as would be expected.

Until next time. I look forward to writing something new for you :)

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 listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# TODO switch to enhanced getopt to deal with variable argument switches
# TODO add config file
# TODO check for bad provider code
# TODO rewrite unix to "pretty" time conversion to speed up returns
# ------------------------------
# DONE switch "wget" to "telnet" for more accessibility and a slight speedup
# DONE End Display Time For Previous day when start time is in zero hour
# DONE check for bad zip code
# DONE added optional time increments - up to 3 hours - for display
# DONE tightened up code using indirect variable references
# DONE date may now be entered in quotes numeric or alpha - e.g. 11/9/08 11/09/2008 "Nov 9 2008" "November 9th 2008" etc
# DONE time may now be entered as 10pm 10 (with am/pm defaulting to system time) 10:00am 23:00 etc
# DONE added error checking for invalid date and time entry - stop before querying online guide
# DONE fixed issue with 00:xx time causing script failure
# DONE added extra error checking
# DONE added better formatting of lineup for easier readability - Thanks to Michael Seeley for the suggestion!
# DONE You have entered an invalid ZIP or postal code
# DONE am to pm or pm to am ends up not in chronological order
# DONE 12 to 1 a/p ends up not in chronological order
#

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 "[-s ListingTimeSpan 1 for just now, 2 for an additional half"
echo "hour, 3, 4, 5, 6, in half hour increments, and 7 for 3 hours"
echo "...defaults to 7 since this is the online listing default]"
echo
exit 1
}

while getopts t:z:hs:nd: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"
;;
s)
opt_timespan="$OPTARG"
;;
h)
usage
;;
*)
usage
;;
esac
done

## FEEL FREE TO EDIT HERE - If the path's are wrong, or you use less, rather than more, you can change that here.
telnet=/usr/bin/telnet
pager=/usr/bin/more
## OK, THAT'S ENOUGH OF THAT ;)

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

if [ ! $opt_timespan ]
then
opt_timespan=7
elif [ $opt_timespan -lt 1 -o $opt_timespan -gt 7 ]
then
usage
fi

if [ ! $opt_time ]
then
hour=`date "+%I"`
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
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi
if [ $hour == "00" ]
then
cli_time=${hour}:${minute}
else
cli_time=${hour}:${minute}${ampm}
fi
else
cli_time=$opt_time
fi

date_ok=`date -d "$cli_time" +%s 2>&1`
if [[ "$date_ok" =~ "invalid date" ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi

ampm=`echo $opt_time|egrep 'a|p|m' 2>&1`
result2=$?
if [ $result2 -eq 0 ]
then
date_ok2=`echo $opt_time|egrep 'a|p'|grep m 2>&1`
result3=$?
if [[ $result3 -ne 0 ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi
fi

cli_time2=`date -d "$cli_time" +%s`
let fromtime1=$cli_time2*1000
let fromtime2=$fromtime1+1800000
let fromtime3=$fromtime2+1800000
let fromtime4=$fromtime3+1800000
let fromtime5=$fromtime4+1800000
let fromtime6=$fromtime5+1800000
let fromtime7=$fromtime6+1800000
if [ ! $opt_timespan ]
then
opt_timespan=7
fi

timespan_relay="$fromtime1"
timespan_counter=2
timespan_limit=$opt_timespan

while [ $timespan_limit -gt 1 ]
do
nextup_time=$(eval "echo \$$(echo fromtime${timespan_counter})")
timespan_relay="$timespan_relay $nextup_time"
let timespan_counter=$timespan_counter+1
let timespan_limit=$timespan_limit-1
done

from_timespan=$(eval "echo \$$(echo fromtime${opt_timespan})")
cli_time_end=`echo $from_timespan|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

(echo "GET /tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 2>&1|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' 2>&1|grep "You have entered an invalid ZIP or postal code" >/dev/null 2>&1
zip_ok=$?

if [ $zip_ok -eq 0 ]
then
echo
echo "INVALID ZIP CODE ENTERED: $opt_zip"
echo
usage
fi

if [ ! $opt_provider ]
then
(echo "GET /tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 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 $timespan_relay
do
(echo "GET /tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 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("*** Start - %s", $3);print ""}}'
done|sort -t'-' -k1,1n -k2,2n | uniq|while read one two;do if [ $one -eq $last ]; then echo "$one $two"|awk '{aline=$NF;$NF="";printf("%s %s\n", $0, strftime("%x - %I:%M %p", aline/1000)) }'|while read one two three;do echo " $three";done;last=$one;else echo "$one $two"|awk '{aline=$NF;$NF="";printf("%s %s\n", $0, strftime("%x - %I:%M %p", aline/1000))}'|while read one two three;do echo " $one $two";echo " $three";done;last=$one;fi;done|$pager
else
for x in $timespan_relay
do
(echo "GET /tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 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("*** Start - %s", $3);print ""}}'
done|sort -t'-' -k1,1n -k2,2n | uniq|while read one two three;do three_one=`echo "$three"|sed -e 's/.*\*\*\* Start - //' -e 's/^[ \t]*//;s/[ \t]*$//'`; let three_two=$three_one/1000; three_three=`echo "$three"|sed 's/^\(.*\*\*\* Start - \).*/\1/'`;three_four=`echo $three_two|awk '{printf("%s", strftime("%x - %I:%M %p", $0)) }'`;if [ $one -eq $last ];then echo " $three_three $three_four";last=$one;else echo "$one $two";echo " $three_three $three_four";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.