MythTV  master
twit.tv.pl
Go to the documentation of this file.
1 #!/usr/bin/perl
2 # @(#)$Header: /home/mythtv/mythtvrep/scripts/twit.tv.pl,v 1.34 2015/11/04 11:29:00 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;
35 use warnings;
36 use Getopt::Std;
37 use LWP::Simple;
38 use HTML::TreeBuilder;
39 use HTML::Entities;
40 use XML::TreeBuilder;
41 use Data::Dumper;
42 use Date::Parse;
43 use Date::Format;
44 use DateTime;
45 use Encode;
46 use Storable;
47 use File::stat;
48 use File::Basename;
49 use FindBin '$Bin', '$Script';
50 use lib "$Bin/nv_perl_libs";
51 use mnvcommonsubs;
52 
53 #################################### Settings #################################
54 # Load from config file. May overwrite above.
55 mnvloadconfig(fileparse($Script, '.pl'), "notused");
56 
57 #################################### Globals ##################################
58 my $version = '$Revision: 1.34 $'; $version =~ s/\D*([\d\.]+)\D*/$1/; # rcs tag populated
59 my $command = "twit.tv.pl"; my $commandthumbnail = "twit.tv.png"; my $author = "Auric (modififed by Neoh4x0r)";
60 my $site = 'TWiT.tv';
61 my $description = 'Leo Laporte & Friends';
62 my $baseurl = 'http://wiki.twit.tv';
63 my $feedsurl = 'http://wiki.twit.tv/wiki/TWiT_Show_Feeds';
64 my $baseicon = 'http://wiki.twit.tv/w/images/twitipedialogo.png';
65 my $store = "/tmp/.${site}.diritemsref.store";
66 
67 my $video_small = "_video_small.xml";
68 my $video_large = "_video_large.xml";
69 my $video_hd = "_video_hd.xml";
70 
71 my $video_size = $video_large;
72 
73 my $max_page_count = 1; # 0=disabled ; limit the number of pages parsed for episodes (there are 24 ep/page * num_pages)
74 our ($opt_v, $opt_T, $opt_p, $opt_S);
75 my %diritems;
76 
77 #################################### Site Specific Subs ##########################
78 # Build all vid items for all directories
79 # input hash ref to { "directory name" => [array of anonymous hash's] }
80 # anonymous hash {
81 # 'dirthumbnail' => $icon,
82 # 'title' => $title,
83 # 'mythtv:subtitle' => "",
84 # 'author' => $author,
85 # 'pubDate' => $pubDate,
86 # 'description' => $description,
87 # 'link' => $url,
88 # 'player' => $player,
89 # 'playerargs' => $playerargs,
90 # 'download' => $download,
91 # 'downloadargs' => $downloadargs,
92 # 'media:thumbnailurl' => "",
93 # 'media:contenturl' => $contenturl,
94 # 'media:contentlength' => $length,
95 # 'media:contentduration' => "",
96 # 'media:contentwidth' => "",
97 # 'media:contentheight' => "",
98 # 'media:contentlanguage' => $language,
99 # 'rating' => ""
100 # 'mythtv:country' => ""
101 # 'mythtv:season' => ""
102 # 'mythtv:episode' => ""
103 # 'mythtv:customhtml' => ""
104 # }
105 # Basically this hash ref is what you need to build.
106 # input base url
107 # output items found
108 sub builddiritems {
109 printf "DEBUG:builddiritems called\n";
110  my $diritemsref = shift @_;
111  my $baseurl = shift @_;
112 
113  my $dirurlsref = builddirurls($baseurl);
114  my $vidurlsref = buildvidurls($dirurlsref);
115  my $itemsfound = 0;
116  foreach my $dir (keys(%$vidurlsref)) {
117  my $diritemsfound = 0;
118  my $count = $#{$vidurlsref->{$dir}};
119 # printf "DEBUG:builddiritems:count = '%d'\n", $count;
120  for (my $c = 0; $c <= $count; $c++) {
121  my $found = builditems($diritemsref, $dir, ${$vidurlsref->{$dir}}[$c]);
122  $itemsfound += $found;
123  $diritemsfound += $found;
124  # Skip rest as nothing found so far. (To speed things up)
125  if ($c > 0 && $diritemsfound == 0) {
126  mnvinfomsg(2, "Skipping rest of $dir as nothing found so far");
127  last;
128  }
129  }
130  mnvinfomsg(1, "$dir Items found $diritemsfound");
131  }
132  return $itemsfound;
133 }
134 
135 # Collect url's of all the podcasts
136 # input base url
137 # return hash ref to { "directory name" => "url" }
138 sub builddirurls {
139 printf "DEBUG:builddirurls called\n";
140  my $baseurl = shift@_;
141 
142  my %dirurls;
143 
144 printf "DEBUG:builddirurls:Getting %s\n", $baseurl;
145  mnvinfomsg(1, "Getting $baseurl");
146  # only grab active shows
147  my $content = get($baseurl);
148  unless ($content) {
149  die "Could not retrieve $baseurl";
150  }
151  my $tree = HTML::TreeBuilder->new;
152  eval { $tree->parse($content); };
153  if ($@) {
154  die "$baseurl parse failed, $@";
155  }
156  $tree->eof();
157 
158  my @show_dirs;
159  my @show_urls;
160  my $bReadingActiveShows = 0;
161  my @ptrs;
162  # read the active show TOC
163  my @tmp = $tree->look_down('class', 'toclevel-1 tocsection-1');
164  (@tmp) and push(@ptrs, @tmp);
165  foreach my $ptr (@ptrs) {
166  #printf "DEBUG:builddirurls:ptr = '%s'\n", $ptr->as_trimmed_text();
167  my $ul = $ptr->find_by_tag_name('ul');
168  #printf "DEBUG:builddirurls:ul = '%s'\n", $ul->as_trimmed_text();
169  my @li = $ul->find_by_tag_name('li');
170  foreach my $tag (@li) {
171  #printf "DEBUG:builddirurls:li = '%s'\n", $tag->as_trimmed_text();
172  my @as = $tag->find_by_tag_name('a');
173  foreach my $a (@as) {
174  my $id = mnvcleantext($a->attr('href'));
175  $id =~ s/^.//;
176  #printf "DEBUG:builddirurls:id = '%s'\n", $id;
177  # lookup this id
178  my @ptrs1;
179  # locate the active show by id
180  my @tmp1 = $tree->look_down('class', 'mw-headline', 'id' , $id);
181  (@tmp1) and push(@ptrs1, @tmp1);
182  foreach my $ptr1 (@ptrs1) {
183  # get the show name
184  my $dir = $ptr1->as_trimmed_text();
185  #printf "DEBUG:builddirurls:dir = '%s'\n", $dir;
186  if($dir)
187  {
188  push(@show_dirs, $dir);
189  }
190  }
191  }
192  }
193  }
194  my $show_dirs_size = @show_dirs;
195  printf "DEBUG:builddirurls:found '%d' active shows\n", $show_dirs_size;
196 
197  # find show feeds
198  undef @tmp;
199  undef @ptrs;
200  my $url_count=0;
201  @tmp = $tree->look_down('class', 'external free');
202  (@tmp) and push(@ptrs, @tmp);
203  foreach my $ptr (@ptrs) {
204  my @as = $ptr->find_by_tag_name('a');
205  foreach my $a (@as) {
206  if ( ($url_count + 1) > $show_dirs_size)
207  {
208  last;
209  }
210  my $url = mnvcleantext($a->attr('href'));
211  if ($url && $url =~ /$video_size$/)
212  {
213  my $dir = $show_dirs[$url_count];
214  printf "DEBUG:builddirurls:feed [%d] = '%s' -> '%s'\n", $url_count, $dir, $url;
215  $dirurls{$dir} = $url;
216  $url_count++;
217  }
218  }
219  }
220 
221 
222 
223  (keys(%dirurls)) or die "No urls found";
224 
225  return \%dirurls;
226 }
227 
228 # Collect url's to all vids
229 # input hash ref to { "directory name" => "url" }
230 # return hash ref to { "directory name" => [url] }
231 sub buildvidurls {
232 printf "DEBUG:buildvidurls called\n";
233  my $dirurls = shift @_;
234 
235  my %vidurls;
236 
237  foreach my $dir (sort(keys(%$dirurls))) {
238  # this originally expected a streaming video on page, but we have a direct link to the feed
239  # just return the feed url
240  push(@{$vidurls{$dir}}, $dirurls->{$dir});
241  }
242  return \%vidurls;
243 }
244 
245 # Build all items
246 # input hash ref to { "directory name" => [array of anonymous hash's] }
247 # input "directory name"
248 # input url
249 # output number of items added
250 sub builditems {
251 printf "DEBUG:buildvidurls called\n";
252  my $diritemsref = shift @_;
253  my $dir = shift @_;
254  my $url = shift @_;
255 
256  printf "DEBUG:builditems:%s -> %s\n", $dir, $url;
257  mnvinfomsg(2, "Getting $dir Episode $url");
258  my $content = get($url);
259  unless ($content) {
260  warn "Could not retrieve $url";
261  return 0;
262  }
263  my $tree = XML::TreeBuilder->new;
264  eval { $tree->parse($content); };
265  if ($@) {
266  warn "$url parse failed, $@";
267  return 0;
268  }
269  $tree->eof();
270 
271 
272 
273 
274 
275  my $thumbnail = $tree->find_by_tag_name('channel')->find_by_tag_name('image')->find_by_tag_name('url')->as_trimmed_text();;
276  my $count =0;
277  my @ptrs;
278  my @tmp = $tree->find_by_tag_name('item');
279  (@tmp) and push(@ptrs, @tmp);
280  foreach my $ptr (@ptrs) {
281  #printf "DEBUG:builditems:ptr = %s\n", $ptr->as_trimmed_text();
282  my $title = HTML::Entities::encode($ptr->find_by_tag_name('title')->as_trimmed_text());
283  my $pubdate = $ptr->find_by_tag_name('pubDate')->as_trimmed_text();
284  my $description = HTML::Entities::encode($ptr->find_by_tag_name('description')->as_trimmed_text());
285  my $subtitle = HTML::Entities::encode($ptr->find_by_tag_name('itunes:subtitle')->as_trimmed_text());
286  my $contenturl = $ptr->find_by_tag_name('link')->as_trimmed_text();
287  my $duration = $ptr->find_by_tag_name('itunes:duration')->as_trimmed_text();
288 
289 
290  my $author = $ptr->find_by_tag_name('author')->as_trimmed_text();
291 
292  my $raw_episode = $ptr->find_by_tag_name('comments')->as_trimmed_text();
293  my @words = split /\//, $raw_episode;
294  my $episode = "";
295  foreach my $part (@words)
296  {
297  $episode = $part;
298  }
299 
300  my $epoch = str2time($pubdate);
301  my $dt = DateTime->from_epoch(epoch => $epoch);
302  my $format = '%Y/%m/%d';
303  my $timestamp = $dt->strftime($format);
304  $pubdate = $timestamp;
305 
306 
307 
308  my ($width, $height);
309  my $tmp_contenturl = $contenturl;
310  $tmp_contenturl =~ /_(\d\d\d\d)x(\d\d\d)_/;
311  if ($1 && $2) {
312  $width = $1;
313  $height = $2;
314  }
315  else
316  {
317  $tmp_contenturl = $contenturl;
318  $tmp_contenturl =~ /_(\d\d\d)x(\d\d\d)_/;
319  if ($1 && $2) {
320  $width = $1;
321  $height = $2;
322  }
323  }
324  my $titleresolution = "$pubdate [EP#$episode] - $subtitle";
325  printf "DEBUG:builditems:ptr = [%s]\n", $titleresolution;
326  push(@{$diritemsref->{$dir}}, {
327  'dirthumbnail' => $thumbnail,
328  'title' => $titleresolution,
329  'mythtv:subtitle' => "",
330  'author' => $author,
331  'pubDate' => $pubdate,
332  'description' => $description,
333  'link' => $raw_episode,
334  'player' => mnvgetconfig('player'),
335  'playerargs' => mnvgetconfig('playerargs'),
336  'download' => mnvgetconfig('download'),
337  'downloadargs' => mnvgetconfig('downloadargs'),
338  'media:thumbnailurl' => $thumbnail,
339  'media:contenturl' => $contenturl,
340  'media:contentlength' => "",
341  'media:contentduration' => $duration,
342  'media:contentwidth' => $width,
343  'media:contentheight' => $height,
344  'media:contentlanguage' => "eng",
345  'rating' => "",
346  'mythtv:country' => "usa",
347  'mythtv:season' => "",
348  'mythtv:episode' => $episode,
349  'mythtv:customhtml' => "no"
350  });
351  $count ++;
352  }
353  return $count;
354 }
355 
356 #################################### Main #####################################
357 # If you copy this for another site, hopefully these won't need to changed
358 getopts('vtTp:S:');
359 
360 if ($opt_v) {
361  ($mnvcommonsubs::netvisionver == 23) and print "$site|TS\n";
362  ($mnvcommonsubs::netvisionver > 23) and mnvprintversion($site, $command, $author, $commandthumbnail, $version, $description);
363  exit 0;
364 }
365 
366 my $type; my $page = 1; my $search = "";
367 if ($opt_T) {
368  $type = "tree";
369 } elsif ($opt_S) {
370  $type = "search";
371  $search = $opt_S;
372  ($opt_p) and $page = $opt_p;
373 } else {
374  print STDERR "Must have -T or -S option\n";
375  exit 1;
376 }
377 
378 $SIG{'INT'} = \&mnvcleanexit;
379 $SIG{'HUP'} = \&mnvcleanexit;
380 $SIG{'TERM'} = \&mnvcleanexit;
381 $SIG{'QUIT'} = \&mnvcleanexit;
382 
383 my $diritemsref = \%diritems;
384 my $totalitems = 0; my $filtereditems = 0;
385 my $ss = stat($store);
386 if (($ss) && (time() - $ss->mtime) < mnvgetconfig('cachetime')) {
387  eval { $diritemsref = retrieve($store); };
388  if ($@) {
389  die "Could not load store, $@";
390  }
391  $totalitems = mnvnumresults($diritemsref);
392  mnvinfomsg(1, "Using previous run data");
393 } else {
394 printf "DEBUG: calling builddiritems\n";
395  $totalitems = builddiritems($diritemsref, $feedsurl);
396 printf "DEBUG: called builddiritems : totalitems = %d\n", $totalitems;
397 
398 printf "DEBUG: storing items\n";
399  eval { store($diritemsref, $store); };
400  if ($@) {
401  warn "Could not save store, $@";
402  }
403 printf "DEBUG: stored items\n";
404 }
405 
406 mnvrssheader();
407 print '<channel>
408  <title>'.$site.'</title>
409  <link>'.$baseurl.'</link>
410  <description>'.$description.'</description>'."\n";
411 if ($type eq "search") {
412  $filtereditems = mnvfilter($diritemsref, $search);
413  mnvprintsearch($diritemsref, $page);
414  mnvinfomsg(1, "Total Items match $filtereditems of $totalitems");
415 } else {
416  mnvprinttree($diritemsref, 4);
417  mnvinfomsg(1, "Total Items found $totalitems");
418 }
419 print "</channel>\n";
420 mnvrssfooter();
421 
422 mnvcleanexit 0;