#!/usr/bin/perl use Data::Dumper; use Getopt::Long; use XML::Simple; use strict; use warnings; my $output_directory = './'; Getopt::Long::Configure("bundling", "ignorecase_always", "permute"); GetOptions( "target-directory|d=s" => \$output_directory, "dt_fmt=s" => \$RDF::RDFa::Generator::XMLTV::dt_fmt, "d_fmt=s" => \$RDF::RDFa::Generator::XMLTV::d_fmt, "t_fmt=s" => \$RDF::RDFa::Generator::XMLTV::t_fmt, ); my $input_file = shift @ARGV || die "Usage: xmltv2xhtml.pl --target-directory=DIR INPUTFILE\n"; my $xs = XML::Simple->new( ForceArray => [qw(actor subtitles programme channel)], ForceContent => [qw(desc)], KeyAttr => [], ); my $data = $xs->parse_file($input_file); my $channels = {}; my $hours = {}; my $genres = {}; # Loop through channels, reading channel data. foreach my $c (@{ $data->{'channel'} }) { $channels->{$c->{'id'}} = RDF::RDFa::Generator::XMLTV::Channel->new($c); } # Loop through programmes, reading programme data. # Add programmes to schedules. foreach my $p (@{ $data->{'programme'} }) { my $start_hour = substr($p->{'start'}, 0, 10); $hours->{ $start_hour } = RDF::RDFa::Generator::XMLTV::Hour->new($start_hour) unless defined $hours->{ $start_hour }; my $h = $hours->{ $start_hour }; my $g; if (length $p->{'category'}->{'content'}) { $genres->{ $p->{'category'}->{'content'} } = RDF::RDFa::Generator::XMLTV::Genre->new($p->{'category'}->{'content'}) unless defined $genres->{ $p->{'category'}->{'content'} }; $g = $genres->{ $p->{'category'}->{'content'} }; } my $c = $channels->{ $p->{'channel'} }; my $P = RDF::RDFa::Generator::XMLTV::Programme->new($p, $c, $g); $c->add_program($P); $h->add_program($P); $g->add_program($P) if ($g); } my $menu_string = RDF::RDFa::Generator::XMLTV::make_menu({ 'Chronological' => $hours, 'By Channel' => $channels, 'By Genre' => $genres, }); foreach my $schedule (values %$channels) { $schedule->publish($output_directory, $menu_string); } foreach my $schedule (values %$hours) { $schedule->publish($output_directory, $menu_string); } foreach my $schedule (values %$genres) { $schedule->publish($output_directory, $menu_string); } package RDF::RDFa::Generator::XMLTV; BEGIN { our $d_fmt = '%F'; our $dt_fmt = '%F %R'; our $t_fmt = '%R'; } sub make_menu { my $sections = shift; my $rv = "\t\t
\n"; return $rv; } 1; package RDF::RDFa::Generator::XMLTV::Programme; use HTML::Entities qw(encode_entities_numeric); use Data::Dumper; use DateTime; use DateTime::Format::Strptime; use Digest::SHA1 qw(sha1_hex); sub new { my $class = shift; my $self = shift; my $chan = shift; my $genre = shift; $self->{'start'} = DateTime->new( 'year' => $1, 'month' => $2, 'day' => $3, 'hour' => $4, 'minute' => $5, 'second' => $6, 'time_zone' => $7 ) if $self->{'start'} =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\s*(.\d{4})$/; $self->{'stop'} = DateTime->new( 'year' => $1, 'month' => $2, 'day' => $3, 'hour' => $4, 'minute' => $5, 'second' => $6, 'time_zone' => $7 ) if $self->{'stop'} =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\s*(.\d{4})$/; $self->{'duration'} = $self->{'stop'}->subtract_datetime($self->{'start'}); $self->{'chan'} = $chan; $self->{'genre'} = $genre; bless $self, $class; } sub get { my $self = shift; my $field = shift; my @bits = split m#/#, $field; my $it = $self; my $this_bit; while (@bits) { $this_bit = shift @bits; if (ref $it eq 'HASH' || ref $it eq ref $self) { $it = $it->{$this_bit}; } elsif (ref $it eq 'ARRAY') { $it = $it->[0 + $this_bit]; } } no warnings; return "$it"; } sub hget { my $self = shift; return encode_entities_numeric($self->get(@_)); } sub langattr { my $self = shift; my $key = shift; my $lang = $self->get("$key/lang"); return "xml:lang=\"$lang\"" if ($lang); return ''; } sub id { my $self = shift; $self->{'ID'} = sha1_hex($self->{'channel'}.$self->{'start'}->iso8601) unless defined $self->{'ID'}; return $self->{'ID'}; } sub uri { my $self = shift; my $part = shift || ''; # Identifying URIs for the broadcast on a particular channel at a particular # time. These URIs are opaque-looking and non-dereferenceable, but any URI # at all is better than no URI! return 'tag:buzzword.org.uk,2009:tv/' . $self->id . '/' . $part; } sub interval_uri { my $self = shift; $self->{'INTERVAL_URI'} = sprintf( 'http://placetime.com/interval/gregorian/%s%s/%s', $self->{'start'}->iso8601, ($self->{'start'}->time_zone->is_utc ? 'Z' : $self->{'start'}->strtime('%z')), RDF::RDFa::Generator::XMLTV::Programme::DurationHelper::to_iso8601($self->{'duration'})) unless defined $self->{'INTERVAL_URI'}; return $self->{'INTERVAL_URI'}; } sub to_rdfa { my $self = shift; my $showchan = shift || 0; my $showgen = shift || 0; my $css = shift || 'item'; my $hx = shift || 2; my $hxp = $hx + 1; my $hxpp = $hx + 2; my $dt_fmt = $RDF::RDFa::Generator::XMLTV::t_fmt; my $_subtitle = ''; $_subtitle = "