#!/usr/bin/env booi
# -----------------------------------------------------------------------
# Copyright (C) 2006 Will Uther
# http://www.cse.unsw.edu.au/~willu/
# and Indulis Bernsteins (channeltz patch)
# indulis 2 1 C mail c o m
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# The full text of the GPL license is available here:
# http://www.gnu.org/licenses/gpl.html
#
"""Grab TV guide information from either the Australian Community-based TV
Guide, or the IceTV commercial guide."""
import System
import System.Reflection
import System.Net
import System.Xml from System.Xml
import System.IO
import System.Text.RegularExpressions
import Mono.GetOptions from Mono.GetOptions
class Grabber(Options):
version = "tv_grab_au_reg.boo: Version $Date: 2006-08-05 11:53:35 +1000 (Sat, 05 Aug 2006) $"
[Option("Turn on debugging output.", "verbose")]
public verbose = 0
[Option("The path for the config file.\tDefault: tv_grab_au_reg-conf.xml\tInterpreted relative to the standard config\tdirectory, unless it is absolute or\tstarts with a '.'.", "config-file", 'c')]
public ConfigFileName = "tv_grab_au_reg-conf.xml"
[Option("Write a new default config file.\tAlso prints the path to the new config file.", "configure")]
public NewConfig = false
[Option("Output to chosen file.\tA filename of - indicates standard output.", "output", 'o')]
public OutputFileName = "-"
[Option("Days from today to start grabbing.\tDefault of 0, i.e. start with today.", "offset")]
public start_day_offset = 0
[Option("Select the number of days of\tguide data to grab. Default of 7.", "days", 'd')]
public days = 7
[Option("Force timestamps to have an explicit timezone.\tThis makes the explicit timezone UTC, NOT\tthe local timezone.", "force-explicit-timezone")]
public force_explicit_timezone = false
oldTVGuideURL = false
output = System.Console.Out
epoch = DateTime(1970,1,1)
today = (DateTime.Now - epoch).Days
creds = CredentialCache()
tvguide_channels = []
icetv_channels = []
icetv_conversion = {}
timeRE = Regex("(\\d+)(\\s+\\S+)?", RegexOptions.Compiled)
XML_header = ''
XMLTV_Tag = """
"""
defaultConfigFile = """
"""
def outputChannelTag(provider as string, channel as Hash):
if verbose > 1:
System.Console.Error.WriteLine("processing channel ${channel[provider]} from provider ${provider}.")
if channel.ContainsKey('display'):
System.Console.Error.WriteLine("Display name: ${channel['display']}")
if channel.ContainsKey('local'):
System.Console.Error.WriteLine("Changing XMLTV name to: ${channel['local']}")
if channel.ContainsKey('local'):
channelXMLstring = '\n\t'
else:
channelXMLstring = '\n\t'
if channel.ContainsKey('display'):
channelXMLstring = channelXMLstring + '' + \
channel['display'] + ''
channelXMLstring = channelXMLstring + '\n'
output.Write(channelXMLstring)
if verbose > 2:
System.Console.Error.WriteLine("channel string: ${channelXMLstring}")
def outputProgramme(node as XmlElement, channel as Hash):
if channel.ContainsKey('channeltz'):
channelTimezone = channel['channeltz']
subStr = "$1 " + channelTimezone
startTime = node.GetAttribute('start')
node.SetAttribute('start', timeRE.Replace(subStr, startTime))
endTime = node.GetAttribute('stop')
node.SetAttribute('stop', timeRE.Replace(subStr, endTime))
elif force_explicit_timezone:
startTime = node.GetAttribute('start')
match = timeRE.Match(startTime)
if match.Groups[2].Length == 0:
node.SetAttribute('start', startTime + " +0000")
endTime = node.GetAttribute('stop')
match = timeRE.Match(endTime)
if match.Groups[2].Length == 0:
node.SetAttribute('stop', endTime + " +0000")
if channel.ContainsKey('local'):
node.SetAttribute('channel', channel['local'])
output.Write(node.OuterXml)
output.Write("\n")
if verbose > 4:
System.Console.Error.WriteLine(node.OuterXml)
def outputDayChannel(f as Stream, channel as Hash):
events = XmlTextReader(f)
events.MoveToContent()
while (true):
if events.IsStartElement():
if events.Name == "programme":
nodeDoc = XmlDocument()
nodeDoc.LoadXml(events.ReadOuterXml())
node = nodeDoc.DocumentElement
assert node.GetAttribute('channel') == channel['tvguide']
outputProgramme(node, channel)
elif events.Name == "message":
nodeDoc = XmlDocument()
nodeDoc.LoadXml(events.ReadOuterXml())
node = nodeDoc.DocumentElement
System.Console.Error.WriteLine("Message from server: ${node.InnerText}")
elif not events.Read():
break
elif not events.Read():
break
def grabTVGuideDayChannel(day as int, channel as Hash):
if oldTVGuideURL:
uri = System.UriBuilder("http", "minnie.tuhs.org", 80, "/tivo-bin/tvguide.pl",
"?page=rawstationday&type=view&data=${day}&data2=${channel['tvguide']}").Uri
else:
uri = System.UriBuilder("http", "minnie.tuhs.org", 80, "/tivo-bin/xmlguide.pl",
"?station=${channel['tvguide']}&daynum=${day}").Uri
if verbose > 2:
System.Console.Error.WriteLine("requesting url: ${uri.ToString()}")
try:
req = WebRequest.Create(uri)
req.Credentials = creds
req.PreAuthenticate = true
f= req.GetResponse().GetResponseStream()
outputDayChannel(f, channel)
except e:
System.Console.Error.WriteLine()
System.Console.Error.WriteLine()
System.Console.Error.WriteLine("Error requesting url: ${uri.ToString()}")
System.Console.Error.WriteLine(e)
System.Console.Error.WriteLine()
System.Console.Error.WriteLine("Common errors include bad proxy configuration, and")
System.Console.Error.WriteLine("not setting the username/password correctly in the config file.")
System.Console.Error.WriteLine()
System.Console.Error.WriteLine()
Environment.Exit(1)
def IceTVDate(days_from_today as int):
td = TimeSpan(days_from_today,0,0,0)
day = DateTime.Now + td
return day.ToString("yyMMdd")
def outputIceTVChannel(node as XmlElement, channel as Hash):
if verbose > 1:
System.Console.Error.WriteLine("processing channel ${channel['icetv']} from provider IceTV.")
if channel.ContainsKey('display'):
System.Console.Error.WriteLine("Display name: ${channel['display']}")
if channel.ContainsKey('local'):
System.Console.Error.WriteLine("Changing XMLTV name to: ${channel['local']}")
if channel.ContainsKey('local'):
node.SetAttribute('id', channel['local'])
output.Write(node.OuterXml)
output.Write("\n")
if verbose > 2:
System.Console.Error.WriteLine(node.OuterXml)
def outputIceTVData(f as Stream):
events = XmlTextReader(f)
events.MoveToContent()
while true:
if events.IsStartElement():
if events.Name == "channel":
nodeDoc = XmlDocument()
nodeDoc.LoadXml(events.ReadOuterXml())
node = nodeDoc.DocumentElement
ice_channel = node.GetAttribute('id')
if icetv_conversion.ContainsKey(ice_channel):
channel = icetv_conversion[ice_channel] as Hash
assert ice_channel == channel['icetv']
outputIceTVChannel(node, channel)
elif events.Name == "programme":
nodeDoc = XmlDocument()
nodeDoc.LoadXml(events.ReadOuterXml())
node = nodeDoc.DocumentElement
ice_channel = node.GetAttribute('channel')
if icetv_conversion.ContainsKey(ice_channel):
channel = icetv_conversion[ice_channel] as Hash
assert ice_channel == channel['icetv']
outputProgramme(node, channel)
elif not events.Read():
break
elif not events.Read():
break
def grabIceTVData():
icetv_base_url = 'http://iceguide.icetv.com.au/cgi-bin/epg/iceguide.cgi'
uri = System.Uri(icetv_base_url + "?op=xmlguide" +
'&start_date=' + IceTVDate(start_day_offset) +
'&end_date=' + IceTVDate(start_day_offset+days))
if verbose > 2:
System.Console.Error.WriteLine("requesting url: ${uri.ToString()}")
try:
req = WebRequest.Create(uri)
req.Credentials = creds
req.PreAuthenticate = true
f = req.GetResponse().GetResponseStream()
outputIceTVData(f)
except e:
System.Console.Error.WriteLine()
System.Console.Error.WriteLine()
System.Console.Error.WriteLine("Error requesting url: ${uri.ToString()}")
System.Console.Error.WriteLine(e)
System.Console.Error.WriteLine()
System.Console.Error.WriteLine("Common errors include bad proxy configuration, and")
System.Console.Error.WriteLine("not setting the username/password correctly in the config file.")
System.Console.Error.WriteLine()
System.Console.Error.WriteLine()
Environment.Exit(1)
def grab():
output.Write(XML_header)
output.Write(XMLTV_Tag)
# output channel tags:
for channel in tvguide_channels:
outputChannelTag('tvguide', channel)
if len(icetv_channels) > 0:
grabIceTVData() # output IceTV channels then programs
output.Write("\n\n")
# output tvguide data
for day in range(today + start_day_offset, today + start_day_offset + days):
if verbose > 1:
System.Console.Error.WriteLine("grabbing day ${day} from tvguide")
for channel as Hash in tvguide_channels:
if verbose > 1:
System.Console.Error.WriteLine("grabbing tvguide channel ${channel['tvguide']}")
grabTVGuideDayChannel(day, channel)
output.Write("\n\n\n")
def load_config(cf as StreamReader):
events = XmlTextReader(cf)
events.MoveToContent()
while true:
if events.IsStartElement():
if events.Name == "login":
nodeDoc = XmlDocument()
nodeDoc.LoadXml(events.ReadOuterXml())
node = nodeDoc.DocumentElement
provider = node.GetAttribute('provider')
user = node.GetAttribute('user')
passwd = node.GetAttribute('password')
if provider == 'tvguide':
creds.Add(System.Uri("http://minnie.tuhs.org/"), "basic", NetworkCredential(user, passwd))
elif provider == 'icetv':
creds.Add(System.Uri("http://iceguide.icetv.com.au/"), "basic", NetworkCredential(user, passwd))
else:
System.Console.Error.WriteLine("Unrecognised data provider in login tag:", provider)
elif events.Name == "channel":
nodeDoc = XmlDocument()
nodeDoc.LoadXml(events.ReadOuterXml())
node = nodeDoc.DocumentElement
providerCount = 0
convMap = {}
if node.HasAttribute('display'):
convMap['display'] = node.GetAttribute('display')
if node.HasAttribute('local'):
convMap['local'] = node.GetAttribute('local')
if node.HasAttribute('channeltz'):
convMap['channeltz'] = node.GetAttribute('channeltz')
if node.HasAttribute('tvguide'):
convMap['tvguide'] = node.GetAttribute('tvguide')
tvguide_channels.Add(convMap)
providerCount += 1
if node.HasAttribute('icetv'):
convMap['icetv'] = node.GetAttribute('icetv')
icetv_channels.Add(convMap)
icetv_conversion[convMap['icetv']] = convMap
providerCount += 1
if providerCount == 0:
System.Console.Error.WriteLine("No data provider in config channel tag: ${node.OuterXml}")
Environment.Exit(1)
elif providerCount > 1:
System.Console.Error.WriteLine("Multiple data providers in config channel tag: ${node.OuterXml}")
Environment.Exit(1)
elif not events.Read():
break
elif not events.Read():
break
def main(argv):
if not RunningOnWindows:
ParsingMode = OptionsParsingMode.GNU_DoubleDash
ProcessArgs(argv)
if len(RemainingArguments) != 0:
# print help information and exit:
System.Console.Error.Write("Unrecognised option(s): ")
for opt in RemainingArguments:
System.Console.Error.Write(opt)
System.Console.Error.Write(' ')
System.Console.Error.WriteLine()
System.Console.Error.WriteLine()
DoHelp()
Environment.Exit(2)
if verbose > 0:
System.Console.Error.WriteLine("Verbosity: ${verbose}.")
if days < 1:
System.Console.Error.WriteLine("Cannot grab less than one day of data!")
Environment.Exit(1)
if ConfigFileName.StartsWith('.'):
fullConfigName = Path.Combine(Environment.CurrentDirectory, ConfigFileName)
else:
fullConfigName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ConfigFileName)
if NewConfig:
System.Console.Error.WriteLine("Resetting config file to default: ${fullConfigName}")
using fa = System.IO.StreamWriter(fullConfigName):
fa.Write(defaultConfigFile)
Environment.Exit(0)
if verbose > 1:
System.Console.Error.WriteLine("Reading config file: ${fullConfigName}")
try:
using fb = StreamReader(fullConfigName):
load_config(fb)
except e:
System.Console.Error.WriteLine()
System.Console.Error.WriteLine()
System.Console.Error.WriteLine("Error reading config file: ${fullConfigName}")
System.Console.Error.WriteLine(e)
System.Console.Error.WriteLine("Do you need to run with --config-file or --configure?")
System.Console.Error.WriteLine()
System.Console.Error.WriteLine()
Environment.Exit(1)
if OutputFileName == '-':
output = System.Console.Out
else:
output = System.IO.StreamWriter(OutputFileName)
if verbose > 0:
System.Console.Error.WriteLine("Outputting to file: ${OutputFileName}")
try:
grab()
ensure:
if output != System.Console.Out:
output.Close()
output = System.Console.Out
grabber = Grabber()
grabber.main(argv)
[assembly: AssemblyTitle('tv_grab_au_reg')]
[assembly: AssemblyCopyright("""
Copyright (c) 2006 Will Uther """)]
[assembly: AssemblyDescription("""
This is a script to grab TV guide information from either the
Australian Community-based TV Guide, or the IceTV commercial guide.
Use the 'version' command line option for more details.""")]
[assembly: AssemblyVersion('0.1')]
[assembly: Mono.About("""This script is available from
http://www.cse.unsw.edu.au/~willu/xmltv/tv_grab_au_reg.html
For more information on the sources of guide data, see
http://tvguide.org.au/ and/or http://www.icetv.com.au/
You need to register to get data from either source. The IceTV data is
higher quality, because you pay to have someone update it for you. The
Community data covers more channels and is cheaper.
Configuration File:
The config file is in XML. The config file includes your login information
for each data source, and the list of channels to grab for each data
source. See the default config file for a basic example.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MER-
CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
The full text of the GPL license is available here:
http://www.gnu.org/licenses/gpl.html
The guide data is distributed under the license you agree to when you
register with the data provider (last I checked, either a Creative Commons
license or the IceTV license).
""")]
[assembly: Mono.Author("Will Uther ")]
[assembly: Mono.UsageComplement("\n")]