[mythtv-users] updated nsmn tv_grab_au

Michael Smith myth at immir.com
Wed May 4 02:18:14 UTC 2005


G'day Aussies,

Well, I've finally got my mythbox up and running and found all sorts of
issues with the NMSN tv_grab_au script I posted last month. Attached is
a comprehensive update, which can also be downloaded here:

    <http://immir.com/tv_grab_au>

In brief, features/changes include:

(*) multiple service options; e.g. download a list of chosen channels
from both free-to-air for your region and foxtel/optus listings.

(*) Configuration files for limiting details pages to just a set of
particular channels/titles (specified by a list of strings/regexps like
"Doctor Who", "Simpsons", "S.*T.*N.*G") or everything except for some
list of channels/titles (e.g., "News", "Danoz", "Shopping",
"news.sbs.com.au"). See comments at top of script.

(*) Improved start/stop time processing - I get no errors/conflicts due
to programs crossing midnight anymore (there were serious problems here,
hopefully now fully resolved).

(*) Retries for page requests added again and better error handling in
case of failures (continue to grab data rather than just die).

This one is working flawlessly (AFAIK) in my mythbox (I only download 7
guide pages and 14 details pages per clean run which takes about 1
minute). I'll start thinking about caching fixed details for generic
programs (news, etc).

Cheers,
Michael.


-------------- next part --------------
#!/usr/bin/perl -w

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell

# --- new NMSN Australian TV grabber by Michael 'Immir' Smith...
# --- $Id: tv_grab_au,v 1.25 2005/05/04 02:12:52 michael Exp $
#
# A current version of this script should usually be available here:
#
#   <http://immir.com/tv_grab_au>
#
# pod documentation coming later...
#
# in brief:   --configure           configure the grabber
#             --config-file <file>  use specified config file
#             --list-channels       show subscribed channels and xmltvids
#             --slow                download a details page for every show
#             --fast                opposite of --slow (to override config)
#             --days <n>            days to grab
#             --output <file>       xml output file
#             --ignore-cache        ignore cached information
#
# When run with mythfilldatabase, the config file may end up as
# ~/.mythtv/tv_grab_au.xmltv and this is where xmltvids for channels can
# be modified.
#
# Downloading of details pages can be controlled by the --slow option
# (possibly set by default during configuration) and regular expressions
# contained in the following two files (one regexp per line):
#
# ~/.tv_grab_au-detailed    contains a list of regular expressions matching
#                           "chanid:titles" of shows for which we want details
#                           e.g.:   Doctor Who
#                                   Simpsons
#                                   abc.gov.au:.*Chef
#
# ~/.tv_grab_au-undetailed  regular expressions matching "chanid:titles" to 
#                           skip details downloads
#                           e.g.:   News
#                                   news.sbs.com.au
#
# NB: a match against the detailed list overrides any undetailed matches.
# When changing these files, use the --ignore-cache option to ensure
# all new details are downloaded.
#
# TZ change added as suggested by Greg Boundy.


use strict;
use Getopt::Long;
use LWP::UserAgent;
use Date::Manip;
use File::Path;
use File::Basename;
use Data::Dumper;
use HTML::TreeBuilder;

use XMLTV;
use XMLTV::Ask;
use XMLTV::ProgressBar;
use XMLTV::Config_file;

# --- global parameters/constants

my $conf;           # hashref for configuration parameters
my $channels;       # ref to hash of subscribed channel names to xmltvids
my %chanid;         # mapping from lowercase channel name to xmltv channel id

my $lang = "en";
my $spoofMSID = 1;  # spoof random MSIDs to avoid redirects?
my $output_dir = "/var/local/tv_grab_au";
my $cache_file = "$output_dir/cached.pl";

my $want_details_file = "$ENV{HOME}/.tv_grab_au-detailed";
my $skip_details_file = "$ENV{HOME}/.tv_grab_au-undetailed";
my (@want_details, @skip_details);

my $count_guide  = 0; # global counter for reporting
my $count_detail = 0; # global counter for reporting

# --- various NMSN site URLs
my $NMSN       = "http://tvguide.ninemsn.com.au";
my $GUIDE      = "http://tvguide.ninemsn.com.au/guide";
my $DETAILS    = "http://tvguide.ninemsn.com.au/closeup/default.asp";
my $TVTODAY    = "http://tvguide.ninemsn.com.au/todaytv/default.asp";

my ($Revision)   = '$Revision: 1.25 $' =~ /Revision:\s*(\S+)/;
my $debug = 0;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent   = 1;

# --- Command-line options
my ($opt_configfile, $opt_configure, $opt_list_channels);
my $opt_output = "$output_dir/guide.xml";
my $opt_gui = 1;
my $opt_slow = 0;
my $opt_fast = 0;
my $opt_days = 5;
my $opt_skipcache = 0;

GetOptions( 'days=i'          => \$opt_days,
	    'list-channels'   => \$opt_list_channels,
	    'output=s'        => \$opt_output,
	    'config-file=s'   => \$opt_configfile,
	    'configure'       => \$opt_configure,
	    'gui=s'           => \$opt_gui,
	    'slow'            => \$opt_slow,
	    'fast'            => \$opt_fast,
	    'ignore-cache'    => \$opt_skipcache,
	    'debug'           => \$debug );

XMLTV::Ask::init($opt_gui);

my $config_file = XMLTV::Config_file::filename
  ($opt_configfile, 'tv_grab_au', not $debug );

# --- read and parse configuration --- must do this before configuring
# in case the user has chosen non-default xmltvids for some channels...
if (-r $config_file) {
  local (@ARGV, $/) = ($config_file, undef);
  no warnings 'all';
  eval <>;
  die "error in conf file: $@" if $@ and not $opt_configure;
  print "unable to read configuration file... configuring anyway..." if $@;
}

# --- extract sorted subscribed channel list from config-file hash;
# also compute canonicalised lowercased channel to xmltvid hash
%chanid = map { lc $_, $channels->{$_} } keys %$channels;

# --- are we configuring?
if ($opt_configure) { configure($config_file); exit 0 }

# --- are we just listing channels?
if ($opt_list_channels) {
  print "    $_ -> $channels->{$_}\n" for sort keys %$channels; exit 0;
}

# --- we must be grabbing program information...

my $runtime = time();

# --- explicitly Set Timezone
Date_Init("TZ=$conf->{TZ}");

# --- flag for whether we get individual program details (follow closeup pids)
die "cannot do slow and fast\n" if $opt_slow and $opt_fast;
my $slow = $opt_fast ? 0 : ($opt_slow || $conf->{slow});

# --- override slow for some shows (read list of regexps from file)
if (-f $want_details_file) {
  local (@ARGV) = ($want_details_file);
  chomp(@want_details = <>);
}
if (-f $skip_details_file) {
  local (@ARGV) = ($skip_details_file);
  chomp(@skip_details = <>);
}


if ($debug) {
  print "tv_grab_au revision $Revision\n";
  print "configuration:\n";
  print "    TZ = $conf->{TZ}  slow = $conf->{slow}\n";
  print "    services:\n";
  for my $service (@{$conf->{services}}) {
    print "        $service->{name}: " .
      " region=$service->{region} id=$service->{regionid}\n";
  }
  print "channel list:\n";
  print "    $_ -> $channels->{$_}\n" for keys %$channels;
  print "\n";
}

print fixplural("grabbing $opt_days days into $opt_output\n");

# --- first get cached list of shows from last time (if any)
my ($cached, $newcache);
if (-r $cache_file and not $opt_skipcache) {
  local (@ARGV, $/) = ($cache_file, undef);
  no warnings 'all';
  eval <>;
  die "$@" if $@;
}

# --- now, on with the shows...

# showlists is a hash of refs to arrays holding shows for each channel
# which we use to infer endtimes from starttimes since the rowspan 
# information is generally unreliable (in slow mode)
my %showlists; 

for my $day (0 .. $opt_days-1) {

  my $date = Ymd(DateCalc("today", "+ $day days"));
  my $date6am = ParseDate("6am $date");

  for my $service (@{$conf->{services}}) {

    my $guidedata = get_guide_page($service, dmY($date6am)) or next;
    my $tree = HTML::TreeBuilder->new_from_content($guidedata);

    for ($tree->look_down('_tag' => 'table', 'class' => 'tv')) {

      # extract channel names from the first row of the table 
      # (this row has align=middle and the channels are in bold)
      my @hdr = map { $_->as_text }
	$_->look_down('_tag' => 'tr', 'align' => 'middle')
	  ->look_down('_tag' => 'b');

      my @span = (0) x @hdr;	# rowspans to infer columns
      my $row = 0;		# row number (to compute start times)

      for ($_->look_down('_tag' => 'tr', 'valign' => 'top')) {
	my @idx = grep { $span[$_] == 0 } 0..$#hdr; # columns for this row
	for ($_->look_down('_tag' => 'td', 'class' => 'tv')) {

	  my $col = shift @idx;
	  my $rowspan = $_->attr("rowspan") || 1;
	  $span[$col] = $rowspan;

	  my $channel = $hdr[$col];
	  next unless my $chanid = $chanid{lc $channel}; 

	  my ($e) = $_->content_list;
	  next unless ref($e) eq 'HTML::Element';
	  next unless $e->tag eq 'a';

	  my ($pid) = $e->as_HTML =~ /pid=(\d+)/;
	  next if $pid == 0;  # sometimes null programs at bottom of table
	  my $title = $e->as_text();

	  # --- check (pid, row, rowspan, title) against old cached data
	  my $cache_id = "$date:$pid:$row:$rowspan:$title";
	  if ($cached->{$cache_id}) {
	    $newcache->{$cache_id} = $cached->{$cache_id};
	    push @{ $showlists{$chanid} }, $cached->{$cache_id};
	    next;
	  }

	  # --- compute start and stop times based on row of table and
	  # rowspan --- although this appears problematic for some days
	  my $start = DateCalc($date6am, ($row*5) . " minutes");
	  my $stop  = DateCalc($start, ($rowspan*5) . " minutes");
	  if ($title =~ s/\s*\[\s* (\d+):(\d+) \s* (am|pm) \s*\]\s* //x) {
	    my ($hr, $min, $ampm) = ($1, $2, lc($3));
	    my $hrdm = $hr == 0 ? 12 : $hr;   # Date::Manip convention
	    $min = sprintf "%02d", $min;      # NMSN bug: 1-digit minutes
	    $start = ParseDate("$hrdm:$min$ampm $date");
	    $start = DateCalc($start, "+ 1 day") if $hr < 6 and $ampm eq "am";
	  }

	  ($start .= " $conf->{TZ}") =~ tr/://d;
	  ($stop  .= " $conf->{TZ}") =~ tr/://d;

	  my $show = { 'title'   => [[$title, $lang]],
		       'start'   => $start,
		       'stop'    => $stop,
		       'channel' => $chanid };

	  # --- fill in more details? ---
	  get_closeup_details($show,$pid) if want_details($show); 

	  # --- that's it!
	  push @{ $showlists{$chanid} }, $show;

	  $newcache->{$cache_id} = $show;
	  print Dumper($show) if $debug;
	}
	++ $row;
	@span = map { $_ - 1} @span; # update rowspan counts
      }
    }
    $tree->delete();
  }
}

# --- fill in stop times
for my $channel (keys %showlists) {
    my @shows = @{ $showlists{$channel} };
    for my $i (0 .. @shows-2) {
	$shows[$i]->{stop} = $shows[$i+1]->{start};
    }
}

# --- now write to xml
print "writing xml\n";

my %writer_args = ( encoding => 'ISO-8859-1' );
if ($opt_output) {
  my $fh = new IO::File(">$opt_output")  or die "can't open $opt_output: $!";
  $writer_args{OUTPUT} = $fh;
}

my $writer = new XMLTV::Writer(%writer_args);

$writer->start
  ( { 'source-info-url'    => $NMSN,
      'source-info-name'   => "NMSN TV Guide",
      'generator-info-name' => "XMLTV - tv_grab_au NMSN v$Revision"} );
		
$writer->write_channel( { 'display-name' => [[$_, $lang]],
			  'id' => $chanid{lc $_} } ) for sort keys %$channels;

for my $channel (keys %showlists) {
    $writer->write_programme($_) for @{ $showlists{$channel} };
}

$writer->end();


# --- save seen data
$cached = $newcache;
open(CACHE, "> $cache_file") or die "cannot open $cache_file: $!";
print CACHE Data::Dumper->Dump([$cached], ["cached"]);
close CACHE;


# --- report runtime
$runtime = time() - $runtime;
my $min = int($runtime / 60);
my $sec = $runtime % 60;
printf "tv_grab_au: downloads = %d guide pages, %d detail pages\n",
           $count_guide, $count_detail;
printf "tv_grab_au: finished in %d:%02d\n", $min, $sec;

exit 0; # Game over, man!


# ---------------------------------------------------------------------------
# --- subroutines

# --- determine whether to get details
sub want_details {
  my $show = shift;
  my $title = $show->{title}->[0]->[0];
  my $channel = $show->{channel};
  my $string = "$channel:$title";
  my $want = $slow;
  for my $expr (@skip_details) { $want = 0 if $string =~ /$expr/ }
  for my $expr (@want_details) { $want = 1 if $string =~ /$expr/ }
  return $want;
}

# --- get details from the closeup page for given show
sub get_closeup_details {

  my $show  = shift;
  my $pid   = shift;
  my $title = $show->{title}->[0]->[0];
  my $date  = Ymd($show->{start});

  my $details = get_details_page($pid) or return;

  # --- use HTML::TreeBuilder to parse the details from the page...

  my $tree = HTML::TreeBuilder->new_from_content($details);

  # --- the details are in a two row table: first row is the header
  # ('Time', 'Program'), second row is the body of the table containing
  # the information we want.

  my ($hd,$bd) = $tree->look_down
    ('_tag' => 'table', 'borderColor' => '#003366')->content_list();

  # --- sanity check the header row
  die "the HTML format of the program details page has changed"
    unless $hd->as_text =~ /Time .* Program/x;

  # --- remove breaks and "revert" some markup to simplify parsing
  $_->delete for $bd->look_down('_tag' => 'br');
  $_->replace_with_content for $bd->look_down('_tag' => qr/^(b|font)$/);

  # --- extract (text) content lists of the 2 row cells
  my @td;
  for ($bd->look_down('_tag' => 'table')) {
    push @td, grep { not ref($_) } $_->content_list
      for $_->look_down('_tag' => 'td');
  }
  unless (@td) {
    print "warning: failed to fill in details for '$title' (pid=$pid)\n";
    return
  }

  # --- clean fields up a little
  s/(^\s+|\s+$)//g, s/\((.*?)\)/$1/g for @td;

  # --- Here's an example of the contents of @td at this point:
  # ("7:00 pm", "Southern Cross TEN Capital", "The All Time Greatest
  # Simpsons", "- Cape Feare", "30 mins , Rated: PG", "Genre:",
  # "Cartoon", "Sideshow Bob terrorises Bart after he is paroled
  # from prison.", "" )

  my ($start_time, $channel, $title1,
      $title2, $genre, $desc) = @td[0,1,2,3,6,7];
  my ($duration, $rating) = (split(/\s*,\s*/, $td[4]), "", "");

  # --- is this a channel we know about? is it consistent with the guide?
  $channel = $chanid{lc $channel}; # -- convert to xmltv channel id
  unless ($channel eq $show->{channel}) {
    print "channel mismatch for show: $title";
    next
  }

  # --- now clean up a few things
  $title2 =~ s/^\s*-\s*//;
  $rating =~ s/Rated:\s*//;
  $duration =~ s/mins/minutes/;

  # --- compute start and stop times
  my $start = ParseDate("$start_time $date");
  # $start = DateCalc($start, "+ 1 day") if $start_time =~ /^0?[0-5]:.. am/;
  my $stop = DateCalc($start, "+ $duration");

  # --- append timezone and strip colons
  ($start .= " $conf->{TZ}") =~ tr/://d;
  ($stop  .= " $conf->{TZ}") =~ tr/://d;

  $show->{'title'}     = [[$title1, $lang]];
  $show->{'start'}     = $start;
  $show->{'stop'}      = $stop;
  $show->{'sub-title'} = [[$title2, $lang]]   if $title2;
  $show->{'desc'}      = [[$desc,   $lang]]   if $desc;
  $show->{'category'}  = [[$genre,  $lang]]   if $genre;

}



# --- configure: query for region, services, and channels and write config
sub configure {

  my $config_file = shift;
  my $date = dmY("today");

  my $firstpage;

  XMLTV::Config_file::check_no_overwrite($config_file);

  # --- extract user's ids for channels (if there were any in
  # the config file), add the defaults then clear the channels hash
  $chanid{lc $_} = $channels->{$_} for keys %$channels;
  for (channel_mappings()) {
    my ($name, $id) = / \s* (.+?) \s* : \s* (\S+) /x or next;
    $chanid{lc $name} = $id unless $chanid{lc $name}; # use user's if defined
  }
  $channels = {};
  $conf = {};

  # --- get timezone
  $conf->{TZ} = ask("Please enter your timezone offset (default '+1000') :");
  $conf->{TZ} = '+1000' unless $conf->{TZ} =~ /^ \s* \+\d\d\d\d \s* $/x;

  my @channellist;

  # --- now find list of services - note that this appears to be invariant,
  # so perhaps we should always offer the same list and skip the get?
  {
    my %servicenames = ( free => 1);
    $firstpage = with_progress("getting list of services",
			       sub { get_page("$TVTODAY?channel=free") });
			
    ++$servicenames{$1} while $firstpage =~ /channel=(\w+)/g;
    my @choices = sort keys %servicenames;
    my @flag = ask_many_boolean
      (0, map { "Grab listings for $_ channels" } @choices);

    for (0..$#choices) {
      next unless $flag[$_];
      push @{$conf->{services}}, { name => $choices[$_] };
    }

    # --- now loop over services; find region/regionid and list of
    # channels
    for my $service (@{$conf->{services}}) {
      my ($page, $base);

      if ($service->{name} eq 'free') {
	# --- get list of regions
	my %region;
	my $tree = HTML::TreeBuilder->new_from_content($firstpage);
	for ($tree->look_down('_tag' => 'select', 'name' => 'region')) {
	  for ($_->look_down('_tag' => 'option')) {
	    $region{$_->as_text()} = $_->attr('value');
	  }
	}
	$tree->delete();
	my @choices = sort keys %region;
	$service->{region}
	  = ask_choice("Select your region for free channels",
		       $choices[0], @choices);
	$service->{regionid} = $region{$service->{region}};
	$page = with_progress
	  ( "getting list of channels free service in " . 
	    "$service->{region}", sub { get_guide_page($service, $date) } );

      } else {

	# --- find regionid for service
	($page, $base) = with_progress
	  ( "getting regionid and channels for service $service->{name}",
	    sub { get_content_base("$TVTODAY?channel=$service->{name}") } );
	$service->{region} = "Australia";
	($service->{regionid})
	  = $base =~ /_(\d+).asp/ or die "cannot find regionid";
	# page now has channel list too
      }
    
      # --- now append channels for this service
      my %skip;

      # --- find the channels
      my $tree = HTML::TreeBuilder->new_from_content($page);
      for ($tree->look_down('_tag' => 'table', 'class' => 'tv',
			    'width' => '100%') # only one table of this type
	   ->look_down('_tag' => 'tr')	 # ..first row has channels
	   ->look_down('_tag' => 'b')) {       # ..in bold tags
	my $channel = $_->as_text;
	push @channellist, $channel;
	unless ($chanid{lc $channel}) { # check/define xmltvid
	  my $id = lc($channel);
	  $id =~ s/( ^\s+ | \s+$ | \W )//gx;
	  $id .= ".$service->{name}.au"; # e.g., "foxtel.au", "free.au"
	  print "Warning, unknown channel '$channel', using '$id' as id\n";
	  $chanid{lc $channel} = $id;
	}
      }
      $tree->delete();
    }
  }

  my @select = ask_many_boolean
    (1, map { "subscribe to channel $_  ->  $chanid{lc $_}" } @channellist);
  
  for (0..$#channellist) {
    next unless $select[$_];
    my $name = $channellist[$_];
    $channels->{$name} = $chanid{lc $name};
  }

  my @channels = sort keys %$channels;

  # --- does the user want the slow option turned on by default?
  $conf->{slow} =
    ask_boolean("Show descriptions, ratings, genres and more accurate\n" .
		"time information is available by downloading individual\n" .
		"pages for each show, but this takes a lot longer\n\n" .
		"Do you want this (--slow) option to be on by default?");

  # --- report configuration and ask for confirmation
  my $channel_count = @channels;
  my $services_info;
  for my $service (@{$conf->{services}}) {
    $services_info .= "service: name=$service->{name}, " .
      " region=$service->{region} (id=$service->{regionid})\n";
  }

  die "aborting configuration" unless
    ask_boolean( "Please confirm the following configuration:\n" .
		 "  TZ = $conf->{TZ}\n" .
		 "  $services_info\n" .
		 "  ($channel_count subscribed channels)\n\n" .
		 "[ use the '--list-channels' option for the\n" .
		 "  xmltvids to use in mythtvsetup ]\n\n" .
		 " Continue?\n");

  # --- open config file and write the configuration
  -d dirname($config_file) or mkdir dirname($config_file)
    or die "cannot create directory for $config_file: $!";

  # --- dump as perl code using Data::Dumper
  open(CONF, ">$config_file") or die "cannot write to $config_file: $!";
  print CONF Data::Dumper->Dump([$conf, $channels], ["conf", "channels"]);
  close CONF;

  print "wrote config_file: $config_file\n";
}


# --- we can avoid redirections by spoofing random MSIDs in the URLs
use Digest::MD5 qw{md5_hex};
sub MSID { $spoofMSID ? "&MSID=" . md5_hex(rand) : "" }

# --- get NMSN guide for given date (perversely "ddmmYYYY")
sub get_guide_page {
  my $service = shift;
  my $date = shift;
  my $url = "$GUIDE/${date}_$service->{regionid}.asp?"
    . "channel=$service->{name}" . MSID();
  print "GET_GUIDE_PAGE($url)\n" if $debug;
  my $page = get_page($url) or
    print "Warning: Failed to get program listing for day $date\n";
  ++$count_guide;
  return $page;
}


# --- get NMSN program details for given pid
sub get_details_page {
  my $pid = shift;
  my $url = "$DETAILS?pid=$pid" . MSID();
  print "GET_DETAILS_PAGE($url)\n" if $debug;
  my $page = get_page($url)
    or print "Warning: Failed to get program details for pid $pid\n";
  ++$count_detail;
  return $page;
}


# --- get a page
sub get_page {
  my $url = shift;
  my $page = (get_content_base($url))[0];
  $page =~ s/&nbsp;/ /g;
  return $page;
}


# --- get a page and its base (and report all redirections if debugging)
# we attempt 5 times with 3 second pauses between failures.
my $ua;
sub get_content_base {
  my $url = shift;
  $ua or $ua = LWP::UserAgent->new, $ua->timeout(30);
  my $response;
  for (1..5) {
    $response = $ua->get($url);
    last if $response->is_success;
    sleep 3;
  }
  die "error reading page $url in 5 attempts" unless $response->is_success;
  if ($debug and (my $r = $response)->previous) {
    print "GET_CONTENT_BASE redirection backtrace:\n";
    while ($r) { print "    ", $r->base, "\n"; $r = $r->previous }
  }
  return $response->content, $response->base;
}


# --- show a progress message during call to code (given by closure)
sub with_progress {
  my ($message, $sub) = @_;
  my $bar = new XMLTV::ProgressBar($message, 1);
  my @results = $sub->();
  $bar->update, short_pause(), $bar->finish;
  return wantarray ? @results : $results[0];
}

sub short_pause { select(undef, undef, undef, 0.33) }


# ---------------------------------------------------------------------------
# --- misc/pedantic stuff...

sub fixplural { # hardly seems worth it sometimes... but, standards...
  local $_ = shift;
  s/(\d+) (\s+) (\w+)s (\s)/$1 . $2 . $3 . ($1==1?"":"s") . $4/xe;
  $_
}

sub Ymd { UnixDate($_[0], "%Y%m%d") or die "problem in Ymd($_[0])" }
sub dmY { UnixDate($_[0], "%d%m%Y") or die "problem in dmY($_[0])" }

# ---------------------------------------------------------------------------
# --- here is default the channel list... comments welcome :-)

sub channel_mappings {
  return grep ! /^#/, split "\n", qq{

    # --- Free channels
    ABC NSW                      : nsw.abc.gov.au
    ABC QLD                      : qld.abc.gov.au
    ABC TAS                      : tas.abc.gov.au
    ABC ACT                      : act.abc.gov.au
    ABC2                         : abc2.abc.gov.au

    Channel Seven Sydney         : sydney.seven.com.au
    Channel Seven Queensland     : queensland.seven.com.au
    Prime Southern               : southern.prime.com.au
    CHANNEL SEVEN BRISBANE       : brisbane.seven.com.au

    SBS Sydney                   : sydney.sbs.com.au
    SBS Queensland               : queensland.sbs.com.au
    SBS News                     : news.sbs.com.au
    SBS EASTERN                  : eastern.sbs.com.au

    Network TEN Sydney           : sydney.ten.com.au
    NETWORK TEN BRISBANE         : brisbane.ten.com.au
    Southern Cross TEN Capital    : capital.southerncrossten.com.au
    Southern Cross TEN Queensland : queensland.southerncrossten.com.au

    Channel Nine Sydney          : sydney.nine.com.au
    CHANNEL NINE BRISBANE METRO  : brisbane.nine.com.au
    WIN Television NSW           : nsw.win.com.au
    WIN Television QLD           : qld.win.com.au

    # --- Foxtel

    Arena TV                     : arena.foxtel.com.au
    BBC World                    : bbcworld.foxtel.com.au
    Cartoon Network              : cartoon.foxtel.com.au
    Channel [V]                  : v.foxtel.com.au
    CNBC                         : cnbc.foxtel.com.au
    CNN                          : cnn.foxtel.com.au
    Discovery Channel            : discovery.foxtel.com.au
    FOX News                     : foxnews.foxtel.com.au
    FOX8                         : fox8.foxtel.com.au
    MAX                          : max.foxtel.com.au
    National Geographic Channel  : natgeo.foxtel.com.au
    Nickelodeon                  : nickelodeon.foxtel.com.au
    Showtime                     : showtime.foxtel.com.au
    Showtime 2                   : showtime2.foxtel.com.au
    Sky News                     : skynews.foxtel.com.au
    TV1                          : tv1.foxtel.com.au
    UKTV                         : uktv.foxtel.com.au
    World Movies                 : worldmovies.foxtel.com.au

    A1                           : a1.foxtel.com.au
    ACC                          : acc.foxtel.com.au
    ADULTS ONLY                  : adultsonly.foxtel.com.au
    ANIMAL PLANET                : animalplanet.foxtel.com.au
    ANTENNA PACIFIC              : antennapacific.foxtel.com.au
    ARENA+2                      : arena2.foxtel.com.au
    AURORA                       : aurora.foxtel.com.au
    BLOOMBERG                    : bloomberg.foxtel.com.au
    BOOMERANG                    : boomerang.foxtel.com.au
    CLUB [V]                     : clubv.foxtel.com.au
    CMC                          : cmc.foxtel.com.au
    CRIME & INVESTIGATION NETWORK : crime.foxtel.com.au
    DISCOVERY HEALTH             : health.discovery.foxtel.com.au
    DISCOVERY SCIENCE            : science.discovery.foxtel.com.au
    DISCOVERY TRAVEL & ADVENTURE : travel.discovery.foxtel.com.au
    DISNEY CHANNEL               : disney.foxtel.com.au
    E!                           : e.foxtel.com.au
    ESPN                         : espn.foxtel.com.au
    EUROSPORT NEWS               : eurosportnews.foxtel.com.au
    FOOD                         : food.foxtel.com.au
    FOX CLASSICS                 : classics.foxtel.com.au
    FOX CLASSICS+2               : classics2.foxtel.com.au
    FOX SPORTS 1                 : sports1.foxtel.com.au
    FOX SPORTS 2                 : sports2.foxtel.com.au
    FOX8+2                       : fox82.foxtel.com.au
    FTV                          : ftv.foxtel.com.au
    FUEL                         : fuel.foxtel.com.au
    HALLMARK CHANNEL             : hallmark.foxtel.com.au
    HOW TO                       : howto.foxtel.com.au
    MAIN EVENT                   : mainevent.foxtel.com.au
    MOVIE EXTRA                  : movieextra.foxtel.com.au
    MOVIE GREATS                 : moviegreats.foxtel.com.au
    MOVIE ONE                    : movieone.foxtel.com.au
    MOVIE ONE TAKE 2             : movieonetake2.foxtel.com.au
    MTV                          : mtv.foxtel.com.au
    NICK JNR                     : nickjnr.foxtel.com.au
    OVATION                      : ovation.foxtel.com.au
    RAI INTERNATIONAL            : rai.foxtel.com.au
    SHOWTIME GREATS              : showtimegreats.foxtel.com.au
    SKY RACING                   : skyracing.foxtel.com.au
    TCM                          : tcm.foxtel.com.au
    THE BIOGRAPHY CHANNEL        : biography.foxtel.com.au
    THE COMEDY CHANNEL           : comedy.foxtel.com.au
    THE COMEDY CHANNEL+2         : comedy2.foxtel.com.au
    THE HISTORY CHANNEL          : history.foxtel.com.au
    THE LIFESTYLE CHANNEL        : lifestyle.foxtel.com.au
    THE LIFESTYLE CHANNEL+2      : lifestyle2.foxtel.com.au
    THE WEATHER CHANNEL          : weather.foxtel.com.au
    TV1+2                        : tv12.foxtel.com.au
    TVSN                         : tvsn.foxtel.com.au
    UKTV+2                       : uktv2.foxtel.com.au
    VH1                          : vh1.foxtel.com.au
    W                            : w.foxtel.com.au
  };
}
    



More information about the mythtv-users mailing list