MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
twit.tv.pl
Go to the documentation of this file.
1 #!/usr/bin/env perl
2 # @(#)$Header: /home/mythtv/mythtvrep/scripts/twit.tv.pl,v 1.32 2010/07/24 23:28:11 mythtv Exp $
3 # Auric 2010/01/10 http://web.aanet.com.au/auric/
4 #
5 # MythNetvision Grabber Script for TWiT.tv site.
6 #
7 # If you want to alter any of the default settings.
8 # Create/Change $HOME/.mythtv/MythNetvision/userGrabberPrefs/twit.tv.cfg
9 # Format of file
10 # player=mplayer
11 # playerargs=-fs -zoom %MEDIAURL%
12 #
13 # Some settings you can have in this are
14 # Print info/progress message: 0 - off, 1 - low ,2 - high
15 # mnvinfo
16 # Info messages go to: 0 = stderr, filename = filename
17 # mnvinfoop
18 # External player to use
19 # player
20 # Args to external player %MEDIAURL% will be replaced with content url
21 # playerargs
22 # External download to use
23 # download
24 # Args to external download %MEDIAURL% will be replaced with content url
25 # downloadargs
26 # A network player like a flash or html5 html. TODO 0.24 May not be approved
27 # netplayer
28 # Type flash or html5
29 # netplayertype
30 # Seconds to cache results Default 72000
31 # cachetime
32 #
33 ################################################################################
34 use strict;
36 use Getopt::Std;
37 use LWP::Simple;
38 use HTML::TreeBuilder;
39 use HTML::Entities;
40 use Data::Dumper;
41 use Date::Parse;
42 use Date::Format;
43 use Encode;
45 use File::stat;
46 use File::Basename;
47 use FindBin '$Bin', '$Script';
48 use lib "$Bin/nv_perl_libs";
50 
51 #################################### Settings #################################
52 # Load from config file. May overwrite above.
53 mnvloadconfig(fileparse($Script, '.pl'), "notused");
54 
55 #################################### Globals ##################################
56 my $version = '$Revision: 1.32 $'; $version =~ s/\D*([\d\.]+)\D*/$1/; # rcs tag populated
57 my $command = "twit.tv.pl"; my $commandthumbnail = "twit.tv.png"; my $author = "Auric";
58 my $site = 'TWiT.tv';
59 my $description = 'Leo Laporte & Friends';
60 my $baseurl = 'http://twit.tv';
61 my $baseicon = 'http://twit.tv/sites/all/themes/twit/img/logo.gif';
62 my $store = "/tmp/.${site}.diritemsref.store";
63 our ($opt_v, $opt_T, $opt_p, $opt_S);
65 
66 #################################### Site Specific Subs ##########################
67 # Build all vid items for all directories
68 # input hash ref to { "directory name" => [array of anonymous hash's] }
69 # anonymous hash {
70 # 'dirthumbnail' => $icon,
71 # 'title' => $title,
72 # 'mythtv:subtitle' => "",
73 # 'author' => $author,
74 # 'pubDate' => $pubDate,
75 # 'description' => $description,
76 # 'link' => $url,
77 # 'player' => $player,
78 # 'playerargs' => $playerargs,
79 # 'download' => $download,
80 # 'downloadargs' => $downloadargs,
81 # 'media:thumbnailurl' => "",
82 # 'media:contenturl' => $contenturl,
83 # 'media:contentlength' => $length,
84 # 'media:contentduration' => "",
85 # 'media:contentwidth' => "",
86 # 'media:contentheight' => "",
87 # 'media:contentlanguage' => $language,
88 # 'rating' => ""
89 # 'mythtv:country' => ""
90 # 'mythtv:season' => ""
91 # 'mythtv:episode' => ""
92 # 'mythtv:customhtml' => ""
93 # }
94 # Basically this hash ref is what you need to build.
95 # input base url
96 # output items found
97 sub builddiritems {
98  my $diritemsref = shift @_;
99  my $baseurl = shift @_;
100 
101  my $dirurlsref = builddirurls($baseurl);
102  my $vidurlsref = buildvidurls($dirurlsref);
103  my $itemsfound = 0;
104  foreach my $dir (keys(%$vidurlsref)) {
105  my $diritemsfound = 0;
106  for (my $c = 0; $c <= $#{$vidurlsref->{$dir}}; $c++) {
107  my $found = builditems($diritemsref, $dir, ${$vidurlsref->{$dir}}[$c]);
108  $itemsfound += $found;
109  $diritemsfound += $found;
110  # Skip rest as nothing found so far. (To speed things up)
111  if ($c > 0 && $diritemsfound == 0) {
112  mnvinfomsg(2, "Skipping rest of $dir as nothing found so far");
113  last;
114  }
115  }
116  mnvinfomsg(1, "$dir Items found $diritemsfound");
117  }
118  return $itemsfound;
119 }
120 
121 # Collect url's of all the podcasts
122 # input base url
123 # return hash ref to { "directory name" => "url" }
124 sub builddirurls {
125  my $baseurl = shift@_;
126 
127  my %dirurls;
128 
129  mnvinfomsg(1, "Getting $baseurl");
130  my $content = get($baseurl);
131  unless ($content) {
132  die "Could not retrieve $baseurl";
133  }
134  my $tree = HTML::TreeBuilder->new;
135  eval { $tree->parse($content); };
136  if ($@) {
137  die "$baseurl parse failed, $@";
138  }
139  $tree->eof();
140 
141  my @ptrs;
142  my $tmp = $tree->look_down('class', 'leaf first');
143  ($tmp) and push(@ptrs, $tmp);
144  my @tmp = $tree->look_down('class', 'leaf');
145  (@tmp) and push(@ptrs, @tmp);
146  foreach my $ptr (@ptrs) {
147  my @as = $ptr->find_by_tag_name('a');
148  foreach my $a (@as) {
149  my $dir = $a->as_trimmed_text();
150  $dirurls{$dir} = mnvcleantext($baseurl.$a->attr('href'));
151  }
152  }
153  #print STDERR Dumper(%dirurls);
154  (keys(%dirurls)) or die "No urls found";
155 
156  return \%dirurls;
157 }
158 
159 # Collect url's to all vids
160 # input hash ref to { "directory name" => "url" }
161 # return hash ref to { "directory name" => [url] }
162 sub buildvidurls {
163  my $dirurls = shift @_;
164 
165  my %vidurls;
166 
167  foreach my $dir (sort(keys(%$dirurls))) {
168  mnvinfomsg(1, "Getting $dir $dirurls->{$dir}");
169  my $content = get($dirurls->{$dir});
170  unless ($content) {
171  warn "Could not retrieve $dirurls->{$dir}";
172  next;
173  }
174  my $tree = HTML::TreeBuilder->new;
175  eval { $tree->parse($content); };
176  if ($@) {
177  warn "$dirurls->{$dir} parse failed, $@";
178  next;
179  }
180  $tree->eof();
181 
182  # Not used anywhere.
183  #my $dirdesc;
184  #my $ptr = $tree->look_down('class', 'podcast-description');
185  #($ptr) and $dirdesc = $ptr->as_trimmed_text();
186 
187  my @ptrs;
188  my $tmp = $tree->look_down('class', 'podcast-number current');
189  ($tmp) and push(@ptrs, $tmp);
190  my @tmp = $tree->look_down('class', 'podcast-number');
191  (@tmp) and push(@ptrs, @tmp);
192  foreach my $urlp (@ptrs) {
193  push(@{$vidurls{$dir}}, mnvcleantext($baseurl.$urlp->attr('href')));
194  }
195  }
196  #print STDERR Dumper(%vidurls);
197  return \%vidurls;
198 }
199 
200 # Build all items
201 # input hash ref to { "directory name" => [array of anonymous hash's] }
202 # input "directory name"
203 # input url
204 # output number of items added
205 sub builditems {
206  my $diritemsref = shift @_;
207  my $dir = shift @_;
208  my $url = shift @_;
209 
210  mnvinfomsg(2, "Getting $dir Episode $url");
211  my $content = get($url);
212  unless ($content) {
213  warn "Could not retrieve $url";
214  return 0;
215  }
216  my $tree = HTML::TreeBuilder->new;
217  eval { $tree->parse($content); };
218  if ($@) {
219  warn "$url parse failed, $@";
220  return 0;
221  }
222  $tree->eof();
223 
224  my @links;
225  my @as = $tree->find_by_tag_name('a');
226  foreach my $a (@as) {
227  $a->as_trimmed_text() =~ /Download Video/ or next;
228  $a->attr('href') =~ /^http:.*video.*mp4$/ and push(@links, mnvcleantext($a->attr('href')));
229  }
230  (@links) or return 0;
231 
232  my $title = ""; my $pubDate = ""; my $desc = "";
233  my @ptrs;
234  my $tmp = $tree->look_down('class', 'podcast-number current');
235  ($tmp) and push(@ptrs, $tmp);
236  my @tmp = $tree->look_down('class', 'podcast-number');
237  (@tmp) and push(@ptrs, @tmp);
238  my $ptr;
239  foreach my $tmp (@ptrs) {
240  my $testurl = $tmp->attr('href');
241  $url =~ /http:.*${testurl}/ and $ptr = $tmp and last;
242  }
243  if ($ptr) {
244  $title = mnvcleantext($ptr->attr('title'));
245  $ptr = $ptr->parent();
246  my $ptr2 = $ptr->look_down('class', 'podcast-date');
247  if ($ptr2) {
248  my $time = str2time($ptr2->as_trimmed_text());
249  $pubDate = time2str("%a, %d %b %Y 00:00:00 GMT", $time);
250  $pubDate = mnvcleantext($pubDate);
251  }
252  $ptr2 = $ptr->find_by_tag_name('p');
253  ($ptr2) and $desc = mnvcleantext($ptr2->as_trimmed_text());
254  }
255  ($title) or return 0;
256 
257  my $icon = $baseicon;
258  $ptr = $tree->look_down('class', 'imagecache imagecache-coverart');
259  ($ptr) and $icon = mnvcleantext($ptr->attr('src'));
260 
261  my $duration = "";
262  $ptr = $tree->look_down('class', 'running-time');
263  ($ptr) and my $tmpdur = $ptr->as_trimmed_text();
264  if ($tmpdur =~ s/Running time: //) {
265  my $hours = 0; my $mins = 0; my $secs = 0;
266  my $count = $tmpdur =~ s/(:)/$1/g;
267  if ($count == 1) {
268  ($mins, $secs) = split(':', $tmpdur);
269  } elsif ($count == 2) {
270  ($hours, $mins, $secs) = split(':', $tmpdur);
271  } else {
272  goto NODURATION;
273  }
274  $tmpdur = ($hours * 60 * 60) + ($mins * 60) + $secs;
275  ($tmpdur > 0) and $duration = mnvcleantext($tmpdur);
276  }
277 NODURATION:
278 
279  my $country = "";
280 
281  my $count = 0;
282  foreach my $contenturl (@links) {
283  my ($width, $height, $titleresolution);
284  $contenturl =~ /_(\d\d\d)x(\d\d\d)_/;
285  if ($1 && $2) {
286  $width = $1;
287  $height = $2;
288  }
289  if (mnvgetconfig('resolution')) {
290  $titleresolution = $title;
291  if ((mnvgetconfig('resolution') eq "high") && ($width < 750)) {
292  mnvinfomsg(1, "Skipping $contenturl due to wrong resolution");
293  next;
294  }
295  if ((mnvgetconfig('resolution') eq "low") && ($width >= 750)) {
296  mnvinfomsg(1, "Skipping $contenturl due to wrong resolution");
297  next;
298  }
299  } else {
300  $titleresolution = "$title (${width}x${height})";
301  }
302  my $link = $url;
303  if ((mnvgetconfig('netplayer')) && ($contenturl)) {
304  if (mnvistype(mnvgetconfig('netplayertype'), $contenturl)) {
305  my $encodedtitle = decode_entities($title);
306  $encodedtitle = mnvURLEncode($encodedtitle);
307  $link = mnvcleantext(mnvgetconfig('netplayer')."?title=${encodedtitle}&videofile=").$contenturl;
308  } else {
309  mnvinfomsg(1, "Not ".mnvgetconfig('netplayertype')." $contenturl");
310  }
311  }
312  push(@{$diritemsref->{$dir}}, {
313  'dirthumbnail' => $icon,
314  'title' => $titleresolution,
315  'mythtv:subtitle' => "",
316  'author' => "twit.tv",
317  'pubDate' => $pubDate,
318  'description' => $desc,
319  'link' => $link,
320  'player' => mnvgetconfig('player'),
321  'playerargs' => mnvgetconfig('playerargs'),
322  'download' => mnvgetconfig('download'),
323  'downloadargs' => mnvgetconfig('downloadargs'),
324  'media:thumbnailurl' => $icon,
325  'media:contenturl' => $contenturl,
326  'media:contentlength' => "",
327  'media:contentduration' => $duration,
328  'media:contentwidth' => $width,
329  'media:contentheight' => $height,
330  'media:contentlanguage' => "",
331  'rating' => "",
332  'mythtv:country' => $country,
333  'mythtv:season' => "",
334  'mythtv:episode' => "",
335  'mythtv:customhtml' => "no"
336  });
337 
338  mnvinfomsg(2, "Added $title");
339  $count ++;
340  }
341  return $count;
342 }
343 
344 #################################### Main #####################################
345 # If you copy this for another site, hopefully these won't need to changed
346 getopts('vtTp:S:');
347 
348 if ($opt_v) {
349  ($mnvcommonsubs::netvisionver == 23) and print "$site|TS\n";
350  ($mnvcommonsubs::netvisionver > 23) and mnvprintversion($site, $command, $author, $commandthumbnail, $version, $description);
351  exit 0;
352 }
353 
354 my $type; my $page = 1; my $search = "";
355 if ($opt_T) {
356  $type = "tree";
357 } elsif ($opt_S) {
358  $type = "search";
359  $search = $opt_S;
360  ($opt_p) and $page = $opt_p;
361 } else {
362  print STDERR "Must have -T or -S option\n";
363  exit 1;
364 }
365 
366 $SIG{'INT'} = \&mnvcleanexit;
367 $SIG{'HUP'} = \&mnvcleanexit;
368 $SIG{'TERM'} = \&mnvcleanexit;
369 $SIG{'QUIT'} = \&mnvcleanexit;
370 
371 my $diritemsref = \%diritems;
373 my $ss = stat($store);
374 if (($ss) && (time() - $ss->mtime) < mnvgetconfig('cachetime')) {
375  eval { $diritemsref = retrieve($store); };
376  if ($@) {
377  die "Could not load store, $@";
378  }
379  $totalitems = mnvnumresults($diritemsref);
380  mnvinfomsg(1, "Using previous run data");
381 } else {
382  $totalitems = builddiritems($diritemsref, $baseurl);
383  eval { store($diritemsref, $store); };
384  if ($@) {
385  warn "Could not save store, $@";
386  }
387 }
388 
389 mnvrssheader();
390 print '<channel>
391  <title>'.$site.'</title>
392  <link>'.$baseurl.'</link>
393  <description>'.$description.'</description>'."\n";
394 if ($type eq "search") {
395  $filtereditems = mnvfilter($diritemsref, $search);
396  mnvprintsearch($diritemsref, $page);
397  mnvinfomsg(1, "Total Items match $filtereditems of $totalitems");
398 } else {
399  mnvprinttree($diritemsref, 4);
400  mnvinfomsg(1, "Total Items found $totalitems");
401 }
402 print "</channel>\n";
403 mnvrssfooter();
404