root/postprocessors/tvdb_augment_data @ 729

Revision 729, 25.6 kB (checked in by lincoln, 6 years ago)

postprocessors/tvdb_augment_data is alive!

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl -w
2
3# tvdb (thetvdb.com / tv.com) XMLTV data augmenter  <ltd@interlink.com.au>
4#
5#  * to be used as a postprocessor for XMLTV data
6#  * uses data from thetvdb.com to augment TV guide data
7#  * this should only be used for non-commercial use.
8#  * can be used in conjunction with 'shepherd' XMLTV reconciler or standalone
9#    (pipe-through)
10#  * no configuration necessary
11#
12#  thanks to Scott Zsori, Paul Taylor, Josh Walters and the other thetvdb.com
13#  folks for providing the data and an easy interface/schema to search for data!
14#
15#  changelog:
16#    0.01 31may07 ltd   initial version
17#                       as per trac ticket #31
18
19use strict;
20
21my $progname = "tvdb_augment_data";
22my $version = "0.01";
23my $mirrorlist_url = 'http://thetvdb.com/interfaces/GetMirrors.php';
24
25use XMLTV;
26use XML::DOM;
27use Getopt::Long;
28use HTML::TreeBuilder;
29use Data::Dumper;
30use Storable;
31use Shepherd::Common;
32
33#
34# some initial cruft
35#
36
37my $script_start_time = time;
38my %stats;
39my $data_cache;
40my $settings_override = { };
41my $d;
42my $parser = new XML::DOM::Parser;
43
44$| = 1;
45
46#
47# parse command line
48#
49
50my $opt = { };
51$opt->{output_file} =           "output.xmltv";
52$opt->{cache_file} =            $progname.".storable.cache";
53$opt->{lang} =                  "en";
54$opt->{debug} =                 0;
55$opt->{min_duration} =          25;     # 25 mins
56$opt->{max_duration} =          140;    # 2 hrs 20 mins
57$opt->{skip_categories} =       "Shopping,Business and Finance,Game Show,News,Parliament,sports,Sport,Weather,live,Education,Movies,Movie,Documentary,Society and Culture";
58$opt->{cache_details_for} =     60;     # cache series details for up to 2 months
59$opt->{cache_title_for} =       90;     # cache title lookups for 3 months
60
61GetOptions(
62        'region=i'              => \$opt->{region},             # ignored
63        'days=i'                => \$opt->{days},               # ignored
64        'offset=i'              => \$opt->{offset},             # ignored
65        'timezone=s'            => \$opt->{timezone},           # ignored
66        'channels_file=s'       => \$opt->{channels_file},      # ignored
67        'config-file=s'         => \$opt->{configfile},         # ignored
68
69        'min_duration=i'        => \$opt->{min_duration},
70        'max_duration=i'        => \$opt->{max_duration},
71        'skip_categories=s'     => \$opt->{skip_categories},
72        'cache_details_for=i'   => \$opt->{cache_details_for},
73        'cache_title_for=i'     => \$opt->{cache_title_for},
74        'dont-augment-desc'     => \$opt->{dont_augment_desc},
75
76        'output=s'              => \$opt->{output_file},
77        'cache-file=s'          => \$opt->{cache_file},
78        'fast'                  => \$opt->{fast},
79        'debug+'                => \$opt->{debug},
80        'lang=s'                => \$opt->{lang},
81        'help'                  => \$opt->{help},
82        'set=s'                 => \$opt->{set},
83        'verbose'               => \$opt->{help},
84        'version'               => \$opt->{version},
85        'ready'                 => \$opt->{ready},
86        'desc'                  => \$opt->{desc},
87        'v'                     => \$opt->{version});
88
89if ($opt->{version} || $opt->{desc} || $opt->{help} || $opt->{ready} ||
90    $opt->{output_file} eq "" || (scalar @ARGV == 0)) {
91        printf "%s v%s\n",$progname,$version;
92        printf "Augments XMLTV data with programme information from thetvdb.com\n" if $opt->{desc};
93        printf "$progname is ready for operation.\n" if ($opt->{ready});
94        printf "No --output file specified.\n" if ($opt->{output_file} eq "");
95        printf "No input XMLTV files specified.\n" if (scalar @ARGV == 0);
96
97        &help if ($opt->{help} || $opt->{output_file} eq "" || (scalar @ARGV == 0));
98        exit(0);
99}
100
101&set_settings if (defined $opt->{set});
102
103# set defaults
104Shepherd::Common::set_default("debug", ((defined $opt->{debug} && $opt->{debug} > 0) ? 2 : 0));
105Shepherd::Common::set_default("retry_delay", 10);
106# Shepherd::Common::set_default("delay", int(rand(4) + 3)) unless (defined $opt->{fast});
107Shepherd::Common::set_default('fake' => 0);
108
109# go go go!
110
111Shepherd::Common::log(sprintf "%s v%s started: %s%soutput %s\n",
112        $progname, $version,
113        ($opt->{fast} ? "fast-override, " : ""),
114        ($opt->{debug} ? "debug enabled, " : ""),
115        ($opt->{output_file}));
116
117&read_cache;
118
119Shepherd::Common::log("Stage 1/5: reading input xmltv files...");
120foreach my $file (@ARGV) {
121        &read_xmltv($file);
122}
123
124&perform_lookups;
125&write_xmltv;
126&write_cache;
127Shepherd::Common::print_stats($progname, $version, $script_start_time, %stats);
128exit(0);
129
130##############################################################################
131
132sub help
133{
134        print<<EOF
135usage: $0 [options] {FILE(s)}
136
137Supported options include:
138  --min_duration={min} ignore programs under {min} duration (default: $opt->{min_duration} min)
139  --max_duration={min} ignore programs over {min} duration (default: $opt->{max_duration} min)
140  --skip_categories={list} don't try to look up programmes in these categories (default: $opt->{skip_categories})
141
142  --dont-augment-desc  don't add IMDb data to programme description,
143                       only update the data fields (default: do)
144
145  --cache_details_for={days}  cache programme details for {days} (def: $opt->{cache_details_for} days)
146  --cache_title_for={days}    cache IMDb URLs for {days} (def: $opt->{cache_title_for} days)
147
148  --lang={lang}        set language to {lang} (default: $opt->{lang})
149  --output={file}      send final XMLTV output to {file} (default: $opt->{output_file})
150  --debug              enable debugging
151  --fast               don't pause between requests to www.imdb.com
152
153  --cache-file={file}  local file to use as our data cache (default: $opt->{cache_file})
154  --no-cache           don't use local cache to reduce network load on www.imdb.com
155
156  --set=(setting):(value) save setting override: (value) 1=enable, 0=disable
157        dont_augment_desc:1/0 (don't / do)
158
159EOF
160;
161}
162
163##############################################################################
164
165sub set_settings
166{
167        &read_cache;
168        my ($setting, $val) = split(/:/,$opt->{set});
169
170        die "--set format is (setting):(value) where value is 0 for disable, 1 for enable.\n"
171          if ((!defined $val) || (($val ne "0") && ($val ne "1")));
172
173        die "unknown '--set' parameter '$setting', see --help for details.\n"
174          if ($setting ne "dont_augment_desc");
175
176        $settings_override->{$setting} = $val;
177        printf "%s: override parameter %s: %s\n", $progname, $setting, ($val eq "0" ? "disabled" : "enabled");
178
179        &write_cache;
180        exit(0);
181}
182
183##############################################################################
184
185sub get_url
186{
187        my %cnf = @_;
188        my ($html_data, $success, $status_msg, $bytes_fetched, $seconds_slept, $failed_attempts) = Shepherd::Common::get_url(%cnf);
189
190        $stats{failed_requests} += $failed_attempts;
191        $stats{slept_for} += $seconds_slept;
192        $stats{bytes_fetched} += $bytes_fetched;
193
194        return undef if ((!$html_data) || (!$success));
195        return $html_data;
196}
197
198##############################################################################
199# populate cache
200
201sub read_cache
202{
203        if (-r $opt->{cache_file}) {
204                my $store = Storable::retrieve($opt->{cache_file});
205                $data_cache = $store->{data_cache};
206                $settings_override = $store->{settings_override};
207
208                foreach my $setting (keys %$settings_override) {
209                        $opt->{$setting} = 1 if ($settings_override->{$setting} != 0);
210                }
211        } else {
212                printf "WARNING: no cache $opt->{cache_file} - ".
213                  "have to fetch all details.\n";
214                &write_cache; # try to write to it - failure will cause an error & barf
215        }
216
217        #
218        # age our caches on startup
219        #
220
221        # age our programme cache on startup
222        foreach my $key (keys %{($data_cache->{prog})}) {
223                my $num_items = 0;
224                foreach my $key2 (keys %{($data_cache->{prog}->{$key})}) {
225                        if ($data_cache->{prog}->{$key}->{$key2}->{expires} < time) {
226                                delete $data_cache->{prog}->{$key}->{$key2};
227                                $stats{removed_prog_from_cache}++
228                        } else {
229                                $num_items++;
230                        }
231                }
232                delete $data_cache->{prog}->{$key} if ($num_items == 0);
233        }
234
235        # age our title cache on startup
236        foreach my $key (keys %{($data_cache->{title})}) {
237                if ($data_cache->{title}->{$key}->{expires} < time) {
238                        delete $data_cache->{title}->{$key};
239                        $stats{removed_title_from_cache}++
240                }
241        }
242}
243
244##############################################################################
245# write out updated cache
246
247sub write_cache
248{
249        my $store;
250        $store->{data_cache} = $data_cache;
251        $store->{settings_override} = $settings_override;
252        Storable::store($store, $opt->{cache_file});
253}
254
255##############################################################################
256
257sub read_xmltv
258{
259        my $filename = shift;
260        $d->{files}++;
261
262        Shepherd::Common::log((sprintf "    parsing: (%d) %s",$d->{files},$filename));
263        $d->{data}->[($d->{files}-1)] = XMLTV::parsefile($filename);
264
265        $d->{progcount}->[($d->{files}-1)] = scalar(@{$d->{data}->[($d->{files}-1)][3]});
266        $stats{programmes} += $d->{progcount}->[($d->{files}-1)];
267}
268
269##############################################################################
270
271sub write_xmltv
272{
273        Shepherd::Common::log("\nStage 5/5: writing output XMLTV into ".$opt->{output_file});
274
275        my %writer_args = ( encoding => 'ISO-8859-1' );
276        my $fh = new IO::File(">".$opt->{output_file}) ||
277          die "can't open $opt->{output_file} for writing: $!";
278        $writer_args{OUTPUT} = $fh;
279
280        my $writer = new XMLTV::Writer(%writer_args);
281        $writer->start( {
282                'source-info-url' => "http://thetvdb.com",
283                'source-info-name' => "$progname $version",
284                'generator-info-name' => "$progname $version"} );
285
286        for (my $i=0; $i < $d->{files}; $i++) {
287                for (my $j = 0; $j < $d->{progcount}->[$i]; $j++) {
288                        my $prog = $d->{data}->[$i][3][$j];
289
290                        my $title, my $subtitle;
291                        $title = lc($prog->{title}->[0]->[0]) if ((defined $prog->{title}) && (defined $prog->{title}->[0]) && (defined $prog->{title}->[0]->[0]));
292                        $subtitle = lc($prog->{'sub-title'}->[0]->[0]) if ((defined $prog->{'sub-title'}) && (defined $prog->{'sub-title'}->[0]) && (defined $prog->{'sub-title'}->[0]->[0]));
293                        my $desc = "";
294
295                        # augment series data if we can
296                        if ((defined $title) && (defined $data_cache->{prog}->{$title}) && (defined $data_cache->{prog}->{$title}->{SERIES})) {
297                                $stats{augmented_prog_series_data}++;
298                                my $series_data = $data_cache->{prog}->{$title}->{SERIES};
299
300                                # description part
301                                my $series_desc = "";
302                                foreach my $field ("Overview", "Status", "FirstAired", "Network", "Genre", "Actors") {
303                                        $series_desc .= "\n ".$field.": ".$series_data->{$field}
304                                          if ((defined $series_data->{$field}) && ($series_data->{$field} ne ""));
305                                }
306                                $desc .= "\nSeries Info:".$series_desc if ($series_desc ne "");
307
308                                # Genre
309                                if ((defined $series_data->{Genre}) && ($series_data->{Genre} ne "")) {
310                                        my $found_genre = 0;
311                                        foreach my $category (@{($prog->{category})}) {
312                                                $found_genre++ if (lc($series_data->{Genre}) eq lc($category->[0]));
313                                        }
314                                        push(@{($prog->{category})},[$series_data->{Genre}]) if (!$found_genre);
315                                }
316
317                                # don't do Actors for now - thetvdb.com has multiple formats for them
318                        }
319
320
321                        # augment episode data if we can
322                        if ((defined $subtitle) && (defined $data_cache->{prog}->{$title}) && (defined $data_cache->{prog}->{$title}->{$subtitle})) {
323                                $stats{augmented_prog_episode_data}++;
324                                my $episode_data = $data_cache->{prog}->{$title}->{$subtitle};
325
326                                # description part
327                                my $episode_desc = "";
328                                foreach my $field ("EpisodeNumber", "EpisodeName", "Overview", "ShowURL", "FirstAired", "GuestStars", "Director", "Writer", "DVD_discid", "DVD_season", "DVD_episodenumber", "DVD_chapter") {
329                                        $episode_desc .= "\n ".$field.": ".$episode_data->{$field}
330                                          if ((defined $episode_data->{$field}) && ($episode_data->{$field} ne ""));
331                                }
332                                $desc .= "\nEpisode Info:".$episode_desc if ($episode_desc ne "");
333
334                                # ShowURL
335                                if ((defined $episode_data->{ShowURL}) && ($episode_data->{ShowURL} ne "")) {
336                                        my $found_url = 0;
337                                        if (defined $prog->{url}) {
338                                                foreach my $url (@{($prog->{url})}) {
339                                                        $found_url++ if (lc($url) eq lc($episode_data->{ShowURL}));
340                                                }
341                                        }
342                                        push (@{($prog->{url})},$episode_data->{ShowURL}) if (!$found_url);
343                                }
344
345                                # don't do GuestStars, Director, Writer for now - thetvdb.com has multiple formats for them
346                        }
347
348                        # should we add any text?
349                        if ((!defined $opt->{dont_augment_desc}) && ($desc ne "")) {
350                                $prog->{desc}->[0]->[0] = "" if (!defined $prog->{desc}->[0]->[0]);
351                                $prog->{desc}->[0]->[0] .= "\n\n" if ($prog->{desc}->[0]->[0] ne "");
352                                $prog->{desc}->[0]->[0] .= "TheTVDB.com augmented data:".$desc;
353                        }
354
355                        Shepherd::Common::cleanup($prog);
356                        $writer->write_programme($prog);
357                }
358        }
359
360        $writer->end();
361}
362
363##############################################################################
364# process all xmltv files
365
366sub perform_lookups
367{
368        $d->{series_lookup_requests} = 0;
369        $d->{episode_lookup_requests} = 0;
370
371        Shepherd::Common::log("\nStage 2/5: processing ".$stats{programmes}." programmes ...");
372        my $prog_count = 0;
373        my $last_updated;
374        for (my $i=0; $i < $d->{files}; $i++) {
375                for (my $j = 0; $j < $d->{progcount}->[$i]; $j++) {
376                        $prog_count++;
377                        if ((!$last_updated) || ((time - $last_updated) > 20) || ($prog_count == $stats{programmes})) {
378                                $last_updated = time;
379                                my $percent_complete = (($prog_count+1) / ($stats{programmes}+1));
380                                my $estimaged_time = ((time - $script_start_time) / $percent_complete);
381                                Shepherd::Common::log((sprintf "  .. at programme %d of %d (%0.1f%%) [%s elapsed] ..",
382                                        $prog_count, $stats{programmes}, ($percent_complete * 100),
383                                        Shepherd::Common::pretty_duration((time - $script_start_time))));
384                        }
385
386                        my $prog = $d->{data}->[$i][3][$j];
387                        my $title, my $subtitle;
388                        $title = $prog->{title}->[0]->[0] if ((defined $prog->{title}) && (defined $prog->{title}->[0]) && (defined $prog->{title}->[0]->[0]));
389                        $subtitle = $prog->{'sub-title'}->[0]->[0] if ((defined $prog->{'sub-title'}) && (defined $prog->{'sub-title'}->[0]) && (defined $prog->{'sub-title'}->[0]->[0]));
390
391                        if ((defined $title) && (include_prog($prog))) {
392                                my $lc_title = lc($title);
393                                &lookup_title_web($title,$prog_count) if (!defined $data_cache->{prog}->{$lc_title});
394
395                                # proceed to episode lookup if we have something in our title cache
396                                if ((defined $data_cache->{title}->{$lc_title}) && ($data_cache->{title}->{$lc_title}->{notfound} == 0)) {
397                                        $subtitle = "" if (!defined $subtitle);
398                                        &lookup_episode($lc_title, $subtitle);
399                                }
400                        }
401                }
402        }
403
404        Shepherd::Common::log("\nStage 3/5: processing ".$d->{series_lookup_requests}." series detail lookup requests ...");
405        &lookup_series_updates if ($d->{series_lookup_requests} > 0);
406
407        Shepherd::Common::log("\nStage 4/5: processing ".$d->{episode_lookup_requests}." episode detail lookup requests ...");
408        &lookup_episode_updates if ($d->{episode_lookup_requests} > 0);
409}
410
411##############################################################################
412# helper routine: returns 1 if we should look up programme, 0 if not
413
414sub include_prog
415{
416        my $prog = shift;
417
418        if ((!defined $prog->{title}) || (!defined $prog->{title}->[0]) || (!defined $prog->{title}->[0]->[0])) {
419                $stats{skipped_due_to_title}++;
420                next;
421        }
422        my $title = $prog->{title}->[0]->[0];
423
424        # skip station close
425        if (($title =~ /^close$/i) || ($title =~ /^station close$/i)) {
426                $stats{skipped_due_to_title}++;
427                return 0;
428        }
429
430        # skip categories
431        if (defined $prog->{category}) {
432                foreach my $prog_category (@{($prog->{category})}) {
433                        foreach my $prog_cat2 (@$prog_category) {
434                                foreach my $skip_category (split(/,/,$opt->{skip_categories})) {
435                                        if (lc($prog_cat2) eq lc($skip_category)) {
436                                                $stats{skipped_due_to_category}++;
437                                                return 0;
438                                        }
439                                }
440                        }
441                }
442        }
443
444        # only lookup if  min_duration < prog_duration > min_duration
445        my $t1 = Shepherd::Common::parse_xmltv_date($prog->{start});
446        my $t2 = Shepherd::Common::parse_xmltv_date($prog->{stop});
447        if ((!$t1) || (!$t2)) {
448                $stats{excluded_couldnt_parse_time}++;
449                return 0;
450        }
451
452        # ensure prog is within duration limits
453        my $prog_duration = (($t2 - $t1) / 60);
454        if (($prog_duration < $opt->{min_duration}) || ($prog_duration > $opt->{max_duration})) {
455                $stats{excluded_prog_duration}++;
456                return 0;
457        }
458
459        return 1;
460}
461
462##############################################################################
463# fill in $d->{tvdb}->{mirrors} with a list of tvdb mirror sites
464
465sub lookup_mirrors
466{
467        my $data = get_url(url => $mirrorlist_url);
468        die "could not gather list of mirrors from $mirrorlist_url\n" if (!$data);
469
470        my $xml_tree = $parser->parse($data);
471        $d->{tvdb}->{num_mirrors} = $xml_tree->getElementsByTagName("Item")->getLength;
472        for (my $i = 0; $i < $d->{tvdb}->{num_mirrors}; $i++) {
473                push(@{($d->{tvdb}->{mirrors})},
474                        $xml_tree->getElementsByTagName("Item")->item($i)->getElementsByTagName("interface")->item(0)->getFirstChild->getNodeValue);
475        }
476        $xml_tree->dispose;
477
478        die "no mirrors found from $mirrorlist_url\n" if ($d->{tvdb}->{num_mirrors} == 0);
479
480        $d->{tvdb}->{mirror} = $d->{tvdb}->{mirrors}[int(rand($d->{tvdb}->{num_mirrors}))];
481        Shepherd::Common::log("    chose mirror ".$d->{tvdb}->{mirror}." for data");
482
483}
484
485##############################################################################
486# find a 'seriesid' associated with this title
487#  (actually just populates $data_cache->{title}->{$lc_title})
488
489sub lookup_title_web
490{
491        my ($title, $prog_count) = @_;
492        my $lc_title = lc($title);
493
494        my $letter = substr($lc_title,0,1);
495        $letter = "OTHER" if ($letter !~ /[a-z]/);
496
497        my $found_title = 0;
498
499        # only lookup if we have passed our caching threshold for this letter...
500        if ((!defined $data_cache->{title}->{"WEB_LOOKUP_".$letter}->{expires}) ||
501            ($data_cache->{title}->{"WEB_LOOKUP_".$letter}->{expires} < time)) {
502                Shepherd::Common::log("    fetching series tables for '".$letter."' ...");
503
504                my $url = "http://thetvdb.com/?tab=listseries&letter=".$letter;
505                my $data = get_url(url => $url);
506                if (!$data) {
507                        Shepherd::Common::log("      ".$url." didn't return any valid data!  skipping...");
508
509                        # try again in 7 days
510                        $data_cache->{title}->{"WEB_LOOKUP_".$letter}->{expires} = time + (7 * 86400);
511                        $stats{failed_title_web_fetch}++;
512                        return;
513                }
514
515                my $expires_in = time + ($opt->{cache_title_for} * 86400);
516                $data_cache->{title}->{"WEB_LOOKUP_".$letter}->{expires} = $expires_in;
517
518                my $tree = HTML::TreeBuilder->new_from_content($data);
519                my $tree_table = $tree->look_down('_tag' => 'table', 'id' => 'listtable');
520
521                foreach my $tree_tr ($tree_table->look_down('_tag' => 'tr')) {
522                        my @tree_td = $tree_tr->look_down('_tag' => 'td');
523
524                        if (((scalar @tree_td) == 3) && ($tree_td[2]->as_text() =~ /^(\d+)$/)) {
525                                my $series_name = lc($tree_td[0]->as_text());
526                                $data_cache->{title}->{$series_name}->{SeriesName} = $tree_td[0]->as_text();
527                                $data_cache->{title}->{$series_name}->{SeriesID} = $tree_td[2]->as_text();
528                                $data_cache->{title}->{$series_name}->{expires} = $expires_in;
529                                $data_cache->{title}->{$series_name}->{notfound} = 0;
530                                $stats{inserted_title_into_cache}++;
531                        }
532                }
533
534                &write_cache;
535                $tree->delete;
536        }
537}
538
539
540##############################################################################
541# find an 'episode' associated with a series
542
543sub lookup_episode
544{
545        my ($lc_title, $subtitle) = @_;
546        my $seriesid = $data_cache->{title}->{$lc_title}->{SeriesID};
547        my $lc_subtitle = lc($subtitle);
548        return if (!defined $seriesid);
549
550        $stats{used_title_cache_item}++;
551
552        my $url = "/GetEpisodes.php?seriesid=".$seriesid."&IncludeSeriesInfo=1";
553        if ($lc_subtitle eq "") {
554                $url .= "&season=1&episode=1";
555                $lc_subtitle = "NONE";
556        } else {
557                $url .= "&episodename=".Shepherd::Common::urlify($lc_subtitle);
558        }
559
560        # return without doing anything if the entry already exists in the cache
561        if (defined $data_cache->{prog}->{$lc_title}->{$lc_subtitle}) {
562                $stats{used_prog_cache_item}++;
563                goto CHECK_EPISODE if ($lc_subtitle ne "NONE");
564                return;
565        }
566
567        Shepherd::Common::log("    fetching series '".$data_cache->{title}->{$lc_title}->{SeriesName}."' ".($lc_subtitle ne "NONE" ? "(episode '$subtitle')" : ""). " ...");
568
569        &lookup_mirrors if (!defined $d->{tvdb}->{mirror});
570        my $data = get_url(url => $d->{tvdb}->{mirror}.$url);
571        if (!$data) {
572                Shepherd::Common::log("   series lookup of ".$d->{tvdb}->{mirror}.$url." failed");
573                $stats{prog_lookup_failed}++;
574                return;
575        }
576
577        my $xml_tree = $parser->parse($data);
578        my $num_items = $xml_tree->getElementsByTagName("Item")->getLength;
579
580        if ($num_items == 1) {
581                $stats{negatively_cached_prog}++;
582               
583                $data_cache->{prog}->{$lc_title}->{$lc_subtitle}->{notfound} = 1;
584                $data_cache->{prog}->{$lc_title}->{$lc_subtitle}->{expires} = time + (($opt->{cache_details_for} / 2) * 86400);
585
586                # on an episode-lookup failure, thetvdb.com doesn't return any SeriesData,
587                # so schedule that for a bulk get.
588                if ((!defined $data_cache->{prog}->{$lc_title}->{SERIES}) &&
589                    (!defined $d->{series_lookup}->{$lc_title})) {
590                        $d->{series_lookup}->{$lc_title} = $seriesid;
591                        $d->{series_lookup_requests}++;
592                }
593                return;
594        }
595
596        my $item = $xml_tree->getElementsByTagName("Item")->item(1);
597
598        # remove existing entries
599        delete $data_cache->{prog}->{$lc_title}->{SERIES} if (defined $data_cache->{prog}->{$lc_title}->{SERIES});
600        delete $data_cache->{prog}->{$lc_title}->{$lc_subtitle} if (defined $data_cache->{prog}->{$lc_title}->{$lc_subtitle});
601
602        # set expiry on these
603        $data_cache->{prog}->{$lc_title}->{SERIES}->{expires} = time + ($opt->{cache_details_for} * 86400);
604        $data_cache->{prog}->{$lc_title}->{$lc_subtitle}->{expires} = time + ($opt->{cache_details_for} * 86400);
605
606        foreach my $field ("SeriesData-Status", "SeriesData-FirstAired", "SeriesData-Network", "SeriesData-Genre", "SeriesData-Actors", "SeriesData-Overview", "id", "SeasonNumber", "EpisodeNumber", "EpisodeName") {
607                my $fieldtag = $item->getElementsByTagName($field)->item(0)->getFirstChild;
608                if (defined $fieldtag) {
609                        if ($field =~ /^SeriesData-(.*)$/) {
610                                $data_cache->{prog}->{$lc_title}->{SERIES}->{$1} = $fieldtag->getNodeValue;
611                        } else {
612                                $data_cache->{prog}->{$lc_title}->{$lc_subtitle}->{$field} = $fieldtag->getNodeValue;
613                        }
614                }
615        }
616        $xml_tree->dispose;
617        $stats{inserted_prog_into_cache}++;
618        &write_cache if (($stats{inserted_prog_into_cache} % 15) == 0);
619
620CHECK_EPISODE:
621        if ((defined $data_cache->{prog}->{$lc_title}->{$lc_subtitle}->{id}) &&
622            (!defined $data_cache->{prog}->{$lc_title}->{$lc_subtitle}->{have_episode_details})) {
623                my $ep;
624                $ep->{id} = $data_cache->{prog}->{$lc_title}->{$lc_subtitle}->{id};
625                $ep->{title} = $lc_title;
626                $ep->{subtitle} = $lc_subtitle;
627
628                push(@{($d->{episode_lookup})}, $ep);
629                $d->{episode_lookup_requests}++;
630        }
631}
632
633##############################################################################
634# used to lookup SeriesData for known (good) seriesid's
635
636sub lookup_series_updates
637{
638        my @id_list = values %{($d->{series_lookup})};
639
640        while ((scalar @id_list) > 0) {
641                Shepherd::Common::log("   ".(scalar @id_list)." remaining...");
642
643                &lookup_mirrors if (!defined $d->{tvdb}->{mirror});
644                my $url = $d->{tvdb}->{mirror}."/SeriesUpdates.php?lasttime=0&idlist=";
645
646                # grab up to 20 at a time
647                foreach my $count (1..20) {
648                        my $id = pop(@id_list);
649
650                        if (defined $id) {
651                                $url .= ',' if ($count > 1);
652                                $url .= $id;
653                        }
654                }
655
656                my $data = get_url(url => $url);
657                if (!$data) {
658                        Shepherd::Common::log("   series detail lookup request of ".$url." failed");
659                        $stats{series_update_lookup_failed}++;
660                        return;
661                }
662
663                my $xml_tree = $parser->parse($data);
664                my $num_items = $xml_tree->getElementsByTagName("Item")->getLength;
665
666                for (my $i=1; $i < $num_items; $i++) {
667                        my $item = $xml_tree->getElementsByTagName("Item")->item($i);
668                        my $namefield = $item->getElementsByTagName("SeriesName")->item(0)->getFirstChild;
669
670                        if (defined $namefield) {
671                                my $name = lc($namefield->getNodeValue);
672
673                                foreach my $field ("Status", "FirstAired", "Network", "Genre", "Actors", "Overview") {
674                                        my $fieldtag = $item->getElementsByTagName($field)->item(0)->getFirstChild;
675                                        if (defined $fieldtag) {
676                                                $data_cache->{prog}->{$name}->{SERIES}->{$field} = $fieldtag->getNodeValue;
677                                        }
678                                }
679
680                                $data_cache->{prog}->{$name}->{SERIES}->{expires} = time + ($opt->{cache_details_for} * 86400);
681                                $stats{inserted_prog_into_cache}++;
682                                &write_cache if (($stats{inserted_prog_into_cache} % 15) == 0);
683                        }
684                }
685
686                $xml_tree->dispose;
687        }
688
689        &write_cache;
690}
691
692##############################################################################
693# used to lookup episode details
694
695sub lookup_episode_updates
696{
697        my $episodes;
698
699        while ((scalar @{($d->{episode_lookup})}) > 0) {
700                Shepherd::Common::log("   ".(scalar @{($d->{episode_lookup})})." remaining...");
701
702                &lookup_mirrors if (!defined $d->{tvdb}->{mirror});
703                my $url = $d->{tvdb}->{mirror}."/EpisodeUpdates.php?lasttime=0&idlist=";
704
705                # grab up to 20 at a time
706                foreach my $count (1..20) {
707                        my $ep = pop(@{($d->{episode_lookup})});
708
709                        if (defined $ep) {
710                                my $id = $ep->{id};
711                                $episodes->{$id}->{title} = $ep->{title};
712                                $episodes->{$id}->{subtitle} = $ep->{subtitle};
713
714                                $url .= ',' if ($count > 1);
715                                $url .= $id;
716                        }
717                }
718
719                my $data = get_url(url => $url);
720                if (!$data) {
721                        Shepherd::Common::log("   epside detail lookup request of ".$url." failed");
722                        $stats{episode_update_lookup_failed}++;
723                        return;
724                }
725
726                my $xml_tree = $parser->parse($data);
727                my $num_items = $xml_tree->getElementsByTagName("Item")->getLength;
728
729                for (my $i=1; $i < $num_items; $i++) {
730                        my $item = $xml_tree->getElementsByTagName("Item")->item($i);
731                        my $id_field = $item->getElementsByTagName("id")->item(0);
732
733                        if ((defined $id_field) && (defined $id_field->getFirstChild)) {
734                                my $id = $id_field->getFirstChild->getNodeValue;
735                                my $title = $episodes->{$id}->{title};
736                                my $subtitle = $episodes->{$id}->{subtitle};
737
738                                foreach my $field ("id", "EpisodeNumber", "EpisodeName", "FirstAired", "GuestStars", "Director", "Writer", "Overview", "ShowURL", "DVD_discid", "DVD_season", "DVD_episodenumber", "DVD_chapter", "IncorrectID") {
739# print "looking for $field in item $i $url\n";
740                                        my $fieldtag = $item->getElementsByTagName($field)->item(0);
741                                        if ((defined $fieldtag) && (defined $fieldtag->getFirstChild)) {
742                                                $data_cache->{prog}->{$title}->{$subtitle}->{$field} = $fieldtag->getFirstChild->getNodeValue;
743                                        }
744                                }
745                                $data_cache->{prog}->{$title}->{$subtitle}->{have_episode_details} = 1;
746                                $data_cache->{prog}->{$title}->{$subtitle}->{expires} = time + (($opt->{cache_details_for} / 2) * 86400);
747
748                                $stats{inserted_episode_into_cache}++;
749                                &write_cache if (($stats{inserted_episode_into_cache} % 15) == 0);
750                        }
751                }
752
753                $xml_tree->dispose;
754        }
755
756        &write_cache;
757}
758
759##############################################################################
Note: See TracBrowser for help on using the browser.