TVDB: better handling of first run
[nonametv.git] / lib / NonameTV / Importer.pm
blob955bd3a5e20eab39a2fe1366b7d29aaa6defdd23
1 package NonameTV::Importer;
3 use strict;
4 use warnings;
6 use File::Copy;
7 use IO::Scalar;
9 use Data::Dumper;
11 use NonameTV qw/CompareArrays/;
13 =head1 NAME
15 NonameTV::Importer
17 =head1 DESCRIPTION
19 Abstract base-class for the NonameTV::Importer::* classes.
21 A package derived from NonameTV::Importer can be used to import
22 data from different datasources into the NonameTV programming
23 database.
25 NonameTV::Importer::*-objects are instantiated from the nonametv.conf
26 configuration file. To instantiate an object, add an entry in the
27 'Importers'-hash. Each entry consists of a hash with configuration
28 parameters for the importer. The following keys are common to all
29 importers:
31 Type - The class-name for the importer, i.e. the instantiated object
32 will be of class NonameTV::Importer::$Type.
34 Channels - The channels that this importer shall import data for. The
35 value shall be another hash with xmltvids as keys and arrays as
36 values. The array shall contain the following data in this order:
38 display_name, grabber_info, sched_lang, empty_ok, def_pty,
39 def_cat, url, chgroup
41 The fields def_pty, def_cat, url, and chgroup are optional and can be
42 omitted.
44 A sample entry for an importer can look like this:
46 Aftonbladet_http => {
47 Type => 'Aftonbladet_http',
48 MaxWeeks => 4,
49 UrlRoot => "http://www.aftonbladet.se/atv/pressrum/tabla",
50 Channels => {
51 "tv7.aftonbladet.se" =>
52 [ "Aftonbladet TV7", "", "sv", 0, "", "", "", "" ],
56 The MaxWeeks and UrlRoot parameters are implemented by the classes
57 deriving from Importer.
59 =head1 METHODS
61 =over 4
63 =cut
65 =item new
67 The constructor for the object. Called with a hashref as the first parameter.
68 This is a ref to the configuration for the object from the nonametv.conf-
69 file. The second parameter is a NonameTV::DataStore object.
71 =cut
73 sub new {
74 my $class = ref( $_[0] ) || $_[0];
76 my $self = { };
77 bless $self, $class;
79 # Copy the parameters supplied in the constructor.
80 foreach my $key (keys(%{$_[1]})) {
81 $self->{$key} = ($_[1])->{$key};
84 $self->{datastore} = $_[2];
86 return $self;
89 =item Import
91 Import is called from the nonametv-import executable. It takes a hashref as
92 the only parameter. The hashref
93 points to a hash with the command-line parameters decoded by Getopt::Long
94 using the $NonameTV::Importer::*::Options arrayref as format specification.
96 =cut
98 sub Import {
99 my( $self, $param ) = @_;
101 $self->ImportData( $param );
104 =item ImportData
106 ImportData is called from Import. It takes a hashref as the only
107 parameter. The hashref points to a hash with the command-line
108 parameters decoded by Getopt::Long using the
109 $NonameTV::Importer::*::Options arrayref as format specification.
111 The ImportData method must be overridden in classes inheriting from
112 NonameTV::Importer.
114 =cut
116 sub ImportData {
117 my( $self, $param ) = @_;
119 die "You must override ImportData in your own class";
122 sub FetchData {
123 my $self = shift;
124 my( $batch_id, $data ) = @_;
126 my $root = "/var/local/nonametv/override";
127 my $code = 0;
128 my $content;
130 if( -f( "$root/new/$batch_id" ) ) {
131 move( "$root/new/$batch_id", "$root/data/$batch_id" );
132 $code = 1;
135 if( -f( "$root/data/$batch_id" ) ) {
136 # Check if the data on site has changed
137 my( $site_content, $site_code ) =
138 $self->FetchDataFromSite( $batch_id, $data );
140 print STDERR "$batch_id New data available for override.\n"
141 if( $site_code );
143 $site_content = undef;
145 # Load data from file
147 local( $/ ) ;
148 open( my $fh, "$root/data/$batch_id" )
149 or die "Failed to read form $root/data/$batch_id: $@";
150 $content = <$fh>;
153 else {
154 ( $content, $code ) = $self->FetchDataFromSite( $batch_id, $data );
155 if( -f( "$root/delete/$batch_id" ) ) {
156 # Delete the old override and force update from site.
157 unlink( "$root/delete/$batch_id" );
158 $code = 1;
162 return ($content, $code);
166 =item ImportFile
168 Import the content from a single file.
170 =cut
172 sub ImportFile {
173 my $self = shift;
174 my( $contentname, $filename, $p ) = @_;
176 my $content;
178 # Load data from file
180 local( $/ ) ;
181 open( my $fh, "$filename" )
182 or die "Failed to read from $filename: $@";
183 $content = <$fh>;
186 return $self->ImportContent( $contentname, \$content, $p );
189 sub UpdateChannels {
190 my $self = shift;
192 return if defined $self->{_ChannelsUpdated};
194 if( not defined( $self->{Channels} ) ) {
195 $self->LoadChannelsFromDb();
197 else {
198 $self->SyncChannelsToDb();
201 $self->{_ChannelsUpdated} = 1;
204 =item ListChannels
206 Return an arrayref with one entry per channnel configured for this
207 grabber. Each entry is a hash with information about the channel.
209 =cut
211 sub ListChannels {
212 my $self = shift;
214 $self->UpdateChannels();
216 return $self->{_ChannelData};
219 sub LoadChannelsFromDb {
220 my $self = shift;
222 $self->{_ChannelData} = $self->{datastore}->FindGrabberChannels(
223 $self->{ConfigName} );
226 sub defdef {
227 my( $value, $default ) = @_;
229 return defined $value ? $value : $default;
232 sub isequal {
233 my( $conf, $db ) = @_;
235 return 0 if $conf->{display_name} ne $db->{display_name};
236 return 0 if $conf->{sched_lang} ne $db->{sched_lang};
237 return 0 if $conf->{empty_ok} ne $db->{empty_ok};
238 return 0 if $conf->{def_pty} ne $db->{def_pty};
239 return 0 if $conf->{def_cat} ne $db->{def_cat};
240 return 0 if defdef($conf->{url}, "") ne defdef( $db->{url}, "");
241 return 0 if defdef($conf->{chgroup}, "") ne defdef($db->{chgroup}, "");
243 return 0 if $conf->{grabber_info} ne $db->{grabber_info};
245 return 1;
248 sub SyncChannelsToDb {
249 my $self = shift;
251 # 1. Convert Channels to _ChannelData. Order by xmltvid.
252 # 2. Iterate through _ChannelData and FindGrabberChannels.
254 $self->{_ChannelData} = [];
256 foreach my $xmltvid (sort keys %{$self->{Channels}}) {
257 my $e = $self->{Channels}->{$xmltvid};
258 my $ce = {
259 xmltvid => $xmltvid,
260 display_name => $e->[0],
261 grabber_info => defdef( $e->[1], "" ),
262 sched_lang => $e->[2],
263 empty_ok => defdef( $e->[3], 0 ),
264 def_pty => defdef( $e->[4], "" ),
265 def_cat => defdef( $e->[5], "" ),
266 url => $e->[6],
267 chgroup => defdef( $e->[7], "" ),
270 push @{$self->{_ChannelData}}, $ce;
273 my $db = $self->{datastore}->FindGrabberChannels( $self->{ConfigName} );
274 my $conf = $self->{_ChannelData};
276 CompareArrays( $conf, $db, {
277 cmp => sub { $_[0]->{xmltvid} cmp $_[1]->{xmltvid} },
278 added => sub {
279 print STDERR "Adding channel info for $_[0]->{xmltvid}\n";
280 $self->AddChannel( $_[0] );
282 deleted => sub {
283 print STDERR "Deleting channel info for $_[0]->{xmltvid}\n";
284 $self->{datastore}->ClearChannel( $_[0]->{id} );
285 $self->{datastore}->sa->Delete( "channels", { id => $_[0]->{id} } );
287 equal => sub {
288 # The channel id only exists in the database.
289 $_[0]->{id} = $_[1]->{id};
291 if( not isequal( $_[0], $_[1] ) ) {
292 print STDERR "Updating channel info for $_[0]->{xmltvid}\n";
293 $self->UpdateChannel( $_[0] );
296 max => { xmltvid => "zzzzzzz" },
297 } );
300 sub UpdateChannel {
301 my $self = shift;
302 my( $cc ) = @_;
304 my $data = {
305 xmltvid => $cc->{xmltvid},
306 display_name => $cc->{display_name},
307 grabber => $self->{ConfigName},
308 grabber_info => $cc->{grabber_info},
309 sched_lang => $cc->{sched_lang},
310 empty_ok => $cc->{empty_ok},
311 def_pty => $cc->{def_pty},
312 def_cat => $cc->{def_cat},
313 url => $cc->{url},
314 chgroup => $cc->{chgroup},
317 $self->{datastore}->sa->Update( 'channels', {id => $cc->{id}}, $data );
320 sub AddChannel {
321 my $self = shift;
322 my( $cc ) = @_;
324 my $data = {
325 xmltvid => $cc->{xmltvid},
326 display_name => $cc->{display_name},
327 grabber => $self->{ConfigName},
328 grabber_info => $cc->{grabber_info},
329 sched_lang => $cc->{sched_lang},
330 empty_ok => $cc->{empty_ok},
331 def_pty => $cc->{def_pty},
332 def_cat => $cc->{def_cat},
333 url => $cc->{url},
334 chgroup => $cc->{chgroup},
335 export => 1,
338 $self->{datastore}->sa->Add( 'channels', $data );
339 my $id = $self->{datastore}->sa->Lookup( 'channels',
340 { xmltvid => $cc->{xmltvid},
341 grabber => $self->{ConfigName} },
342 "id" );
344 $cc->{id} = $id;
347 =head1 CLASS VARIABLES
349 =item $OptionSpec, $OptionDefaults
351 Format specifications and default values for Getopt::Long.
353 our $OptionSpec = [ qw/force-update/ ];
354 our %OptionDefaults = (
355 'force-update' => 0,
359 =head1 COPYRIGHT
361 Copyright (C) 2004-2008 Mattias Holmlund.
363 =cut