1 package NonameTV
::DataStore
::Helper
;
6 use NonameTV
::Log qw
/d p w f/;
10 NonameTV::DataStore::Helper
14 Alternative interface to the datastore for NonameTV. Usable for Importers
15 that receive data where each programme entry does not contain a stop-time
18 The typical calling-sequence is
40 The constructor for the object. Called with a NonameTV::DataStore object
41 and a timezone-string as parameters. If the timezone is omitted,
42 "Europe/Stockholm" is used.
44 After creating the object, you can set DETECT_SEGMENTS:
46 $dsh->{DETECT_SEGMENTS} = 1;
48 This means that the Datastore::Helper will look for programs that seem
49 to belong together, i.e. they have been split into two with another
50 program in between. The algorithm looks for two identical (same title,
51 description and episode) programs with another program between them.
52 If such programs are found, they will be marked with the last part
53 of the episode number as 0/2 and 1/2.
59 my $class = ref( $_[0] ) || $_[0];
65 $self->{timezone
} = $_[2] || "Europe/Stockholm";
67 $self->{DETECT_SEGMENTS
} = 0;
80 Called by an importer to signal the start of a batch of updates.
81 Takes two parameters: one containing a string that uniquely identifies
82 a set of programmes (a batch_id) and the channel_id for the channel
83 that this data is for. The channel_id is a numeric index into the
91 my( $batch_id, $channel_id ) = @_;
93 $self->{batch_id
} = $batch_id;
94 $self->{channel_id
} = $channel_id;
96 $self->{lasttime
} = undef;
97 $self->{save_ce
} = undef;
98 $self->{curr_date
} = undef;
100 $self->{programs
} = [];
101 $self->{ds
}->StartBatch( $batch_id );
106 Called by an importer to signal the end of a batch of updates.
107 Takes two parameters:
109 An integer containing 1 if the batch was processed
110 successfully, 0 if the batch failed and the database should
111 be rolled back to the contents as they were before StartBatch was called.
112 and -1 if the batch should be rolled back because it has not changed.
114 A string containing a log-message to add to the batchrecord. If success==1,
115 then the log-message is stored in the field 'message'. If success==0, then
116 the log-message is stored in abort_message. If success==-1, the log message
117 is not stored. The log-message can be undef.
123 my( $self, $success, $log ) = @_;
125 if( scalar( @
{$self->{programs
}} ) > 0 ) {
126 $self->CommitPrograms();
129 $self->{ds
}->EndBatch( $success, $log );
134 Signal the start of a new day. Takes two parameters, a date in the
135 format "yyyy-mm-dd" and an optional time in the format "hh:mm".
137 The time is used to signal the earliest possible starttime of the
138 first programme of the day. If the first programme added via
139 AddProgramme after StartDate has a time that is earlier than the time
140 in StartDate, then it is assumed that the programme actually starts a
143 $dsh->StartDate( "2008-01-01", "06:00" );
144 $dsh->AddProgramme( {
145 start_time => "01:45",
149 The program in the example will get start-time "2008-01-02 01:45".
156 my( $date, $time ) = @_;
158 if( scalar( @
{$self->{programs
}} ) > 0 ) {
159 $self->CommitPrograms();
162 d
"StartDate: $date";
163 my( $year, $month, $day ) = split( '-', $date );
164 $self->{curr_date
} = DateTime
->new(
171 time_zone
=> $self->{timezone
} );
173 if( $self->{curr_date
} < DateTime
->today->subtract( days
=> 31 ) )
175 w
"StartDate called with old date, " .
176 $self->{curr_date
}->ymd("-") . ".";
178 if( defined( $time ) )
180 $self->{lasttime
} = $self->create_dt( $self->{curr_date
}, $time );
184 $self->{lasttime
} = undef;
187 $self->{programs
} = [];
192 Called by an importer to add a programme for the current batch.
193 Takes a single parameter containing a hashref with information
194 about the programme. The hashref does NOT need to contain an end_time.
196 $dsh->AddProgramme( {
197 start_time => "08:00",
198 title => "Morgon-tv",
199 description => "Morgon i TV-soffan",
202 If the start_time is less than the end_time of the last programme (or
203 the start_time if no end_time was specified), AddProgramme assumes that
204 the date should be increased by one day.
213 # print "AddProgramme: $ce->{start_time} $ce->{title}\n";
215 if( not defined( $self->{curr_date
} ) )
217 confess
"Helper $self->{batch_id}: You must call StartDate before AddProgramme";
220 my $start_time = $self->create_dt( $self->{curr_date
},
222 if( defined( $self->{lasttime
} ) and ($start_time < $self->{lasttime
}) )
226 # Have to wrap add days => 1 in an eval, since the resulting time
227 # may not exist (due to daylight savings).
229 $new_start_time = $start_time->clone()->add( days
=> 1 );
232 if( not defined $new_start_time ) {
233 w
"Failed to add one day to start_time, adding 24h instead.";
234 $new_start_time = $start_time->clone()->add( hours
=> 24 );
237 my $dur = $new_start_time - $self->{lasttime
};
238 my( $days, $hours ) = $dur->in_units( 'days', 'hours' );
242 # By adding one day to the start_time, we ended up with a time
243 # that is less than 20 hours after the lasttime. We assume that
244 # this means that adding a day is the right thing to do.
245 $self->{curr_date
}->add( days
=> 1 );
246 $start_time = $new_start_time;
250 # By adding one day to the start_time, we ended up with a time
251 # that is more than 20 hours after the lasttime. This probably means
252 # that the start_time hasn't wrapped into a new day, but that
253 # there is something wrong with the source-data and the time actually
254 # moves backwards in the schedule.
255 if( not $self->{ds
}->{SILENCE_END_START_OVERLAP
} )
257 w
"Improbable program start " .
258 $start_time->ymd . " " . $start_time->hms . " skipped";
263 $ce->{start_time
} = $start_time->clone();
265 $self->{lasttime
} = $start_time->clone();
267 if( defined( $ce->{end_time
} ) )
269 my $stop_time = $self->create_dt( $self->{curr_date
},
271 if( $stop_time < $self->{lasttime
} )
273 $stop_time->add( days
=> 1 );
274 $self->{curr_date
}->add( days
=> 1 );
276 $ce->{end_time
} = $stop_time->clone();
277 $self->{lasttime
} = $stop_time->clone();
288 $ce->{start_time
}->set_time_zone( "UTC" );
289 $ce->{start_time
} = $ce->{start_time
}->ymd('-') . " " .
290 $ce->{start_time
}->hms(':');
292 if( defined( $ce->{end_time
} ) )
294 $ce->{end_time
}->set_time_zone( "UTC" );
295 $ce->{end_time
} = $ce->{end_time
}->ymd('-') . " " .
296 $ce->{end_time
}->hms(':');
299 $ce->{channel_id
} = $self->{channel_id
};
301 push @
{$self->{programs
}}, $ce;
307 # Max Programs Between
310 if( $self->{DETECT_SEGMENTS
} ) {
311 my $p = $self->{programs
};
312 for( my $i=0; $i < scalar(@
{$p}) - 2; $i++ ) {
313 for( my $j=$i+2; $j<$i+2+$MPB && $j < scalar( @
{$p} ); $j++ ) {
314 if( programs_equal
( $p->[$i], $p->[$j] ) ) {
315 # print "Segments found: $p->[$i]->{title}\n";
316 $p->[$i]->{episode
} = defined( $p->[$i]->{episode
} ) ?
317 $p->[$i]->{episode
} . " 0/2" : ". . 0/2";
318 $p->[$j]->{episode
} = defined( $p->[$j]->{episode
} ) ?
319 $p->[$j]->{episode
} . " 1/2" : ". . 1/2";
325 foreach my $ce (@
{$self->{programs
}}) {
326 $self->{ds
}->AddProgramme( $ce );
329 $self->{programs
} = [];
335 my( $date, $time ) = @_;
337 # print $date->ymd('-') . " $time\n";
339 my $dt = $date->clone();
341 my( $hour, $minute, $second ) = split( ":", $time );
343 # Don't die for invalid times during shift to DST.
345 $dt->set( hour
=> $hour,
350 if( not defined $res )
352 w
$dt->ymd('-') . " $hour:$minute: $@";
354 w
"Adjusting to $hour:$minute";
355 $dt->set( hour
=> $hour,
366 return 1 if (not defined($s1)) and (not defined($s2));
367 return 0 if not defined( $s1 );
368 return 0 if not defined( $s2 );
373 my( $ce1, $ce2 ) = @_;
375 return 0 unless str_eq
($ce1->{title
}, $ce2->{title
});
376 return 0 unless str_eq
($ce1->{description
}, $ce2->{description
});
377 return 0 unless str_eq
($ce1->{episode
}, $ce2->{episode
});