This file is indexed.

/usr/share/perl5/FlashVideo/Site/Pbs.pm is in get-flash-videos 1.25.98-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# Part of get-flash-videos. See get_flash_videos for copyright.
package FlashVideo::Site::Pbs;

use strict;
use warnings;
use FlashVideo::Utils;
use FlashVideo::JSON;
use Data::Dumper;

=pod

Programs that work:
    - http://video.pbs.org/video/1623753774/
    - http://www.pbs.org/video/1623753774/
    - http://www.pbs.org/video/circus-born-to-be-circus/
    - http://www.pbs.org/video/2365612568/
    - http://www.pbs.org/video/american-experience-bonnie-clyde-preview/
    - http://www.pbs.org/show/tunnel/
    - http://www.pbs.org/show/remember-me/

Programs that don't work yet:
    - http://www.pbs.org/wgbh/pages/frontline/woundedplatoon/view/
    - http://www.pbs.org/wgbh/roadshow/rmw/RMW-003_200904F02.html

TODO:
    - subtitles

=cut

our $VERSION = '0.04';
sub Version() { $VERSION; }

sub find_video {
  our %opt;
  my ($self, $browser, $embed_url, $prefs) = @_;
  
  # check for redirect. PBS redirects old URLs to their most recent structures.
  my $status = $browser->status();
  if (($status >= 300) && ($status < 400)) {
    my $location = $browser->response()->header('Location');
    if (defined $location) {
      info "Redirected to $location\n";
      my $redirecturl = URI->new_abs($location, $browser->base());
      $browser->get($redirecturl);
        return $self->find_video($browser, $redirecturl, $prefs);
    }
  }

  # looking for the media ID which is needed to get the video player configuration
  my ($media_id) = $embed_url =~ m[http://(?:video|www)\.pbs\.org/videoPlayerInfo/(\d+)]x;
  debug("media id found in URL") if (defined $media_id);
  unless (defined $media_id) {
    debug("media id not found in URL");
    ($media_id) = $browser->uri->as_string =~ m[
      ^http://(?:video|www)\.pbs\.org/video/(.+)
    ]x;
    debug("media id found in URI") if (defined $media_id);
  }
  unless (defined $media_id) {
    debug("media id not found in URI");
    ($media_id) = $browser->content =~ m[
      http://(?:video|www)\.pbs\.org/widget/partnerplayer/(\d+)
    ]x;
    debug("media id found in partner player link") if (defined $media_id);
  }
  unless (defined $media_id) {
    debug("media id not found in partner player link");
    ($media_id) = $browser->content =~ m[
      /embed-player[^"]+\bepisodemediaid=(\d+)
    ]x;
    debug("media id found in embedded player reference") if (defined $media_id);
  }
  unless (defined $media_id) {
    debug("media id not found in embedded player reference");
    ($media_id) = $browser->content =~ m[var videoUrl = "([^"]+)"];
    debug("media id found in a pbs_video_id tag") if (defined $media_id);
  }
  unless (defined $media_id) {
    debug("media id not found in a javascript videoURL variable");
    ($media_id) = $browser->content =~ m[pbs_video_id_\S+" value="([^"]+)"];
    debug("media id found in a pbs_video_id tag") if (defined $media_id);
  }
  unless (defined $media_id) {
    debug("media id not found in a pbs_video_id tag");
    my ($pap_id, $youtube_id) = $browser->content =~ m[
      \bDetectFlashDecision\ \('([^']+)',\ '([^']+)'\);
    ]x;
    if ($youtube_id) {
      debug "Youtube ID found, delegating to Youtube plugin\n";
      my $url = "http://www.youtube.com/v/$youtube_id";
      require FlashVideo::Site::Youtube;
      return FlashVideo::Site::Youtube->find_video($browser, $url, $prefs);
    }
    debug("media id not found in a YouTube tag");
  }

  # pbs.org uses redirects all over the place
  $browser->allow_redirects;

  if (! defined $media_id) {
    debug ("...scanning for list of multiple videos");
  
    my @possible_videos = $browser->content =~ m{<a href=['"](/video/.+/)['"][^>]*>([^<]+)</a>}g;
    if (@possible_videos) {
      if (!$opt{yes}) {
        print "There are " . scalar(@possible_videos)/2 . " videos referenced, please choose:\n";
        my $count;
        for (my $i = 0; $i < $#possible_videos; $i += 2) {
          my $item = $i/2;
          my $item_title = $possible_videos[$i+1];
          # remove extraeous line feeds and white space
          $item_title =~ s/[\r\n]*//g;
          $item_title =~ s/^ *//;
          $item_title =~ s/ *$//;
          print "$item - $item_title\n";
        }

        print "\nWhich video would you like to use?: ";
        chomp(my $chosen_item = <STDIN>);    
        $chosen_item *= 2;
        if ($possible_videos[$chosen_item]) {
          my $chosen_url = "http://www.pbs.org" . $possible_videos[$chosen_item];
          $browser->get($chosen_url);
          return $self->find_video($browser, $chosen_url, $prefs);
        }
      }
      else
      {
        info "There were " . scalar(@possible_videos)/2 . " referenced videos, but you used the yes option.";
        info "Re-run without the yes option to select one.";
      }
    }
  }

  #
  die "Couldn't find media_id\n" unless defined $media_id;
  debug "media_id: $media_id\n";
  
  # PBS returns the player configuration as a javascript variable
  # extract the embedded javascript and extract the PBS.playerConfig variable
  my @scriptags =  $browser->content() =~/<script[^>]*>(.+?)<\/script>/sig;
  my $script;
  my $pbsdata;
  local $/ = "\r\n";
  foreach $script (@scriptags)
  {
     if ($script =~ /PBS.playerConfig/si) {
       ($pbsdata) = $script =~ /PBS.playerConfig += +([^;]*);/s;
       # change ' to " for the json parser
       $pbsdata =~ s/'/"/g;
       $pbsdata =~ s/([a-zA-Z_]+) *: /"$1" : /g;
       debug $pbsdata;
       last;
     }
  }
# Parse the json structure
  my $result = from_json($pbsdata);
  debug Data::Dumper::Dumper($result);
  die "Could not extract video player info.\n   Video may not be available.\n"
     unless ref($result) eq "HASH";
  
  # Get the video's id and the metadata source url and type
  my $video_id = $result->{id};
  die "Could not extract video id" unless $video_id;
  debug "video id is: $video_id\n";
  
  my $metaurl = $result->{embedURL};
  die "Could not extract video metadata source" unless $metaurl;
  debug "video metadata source is: $metaurl\n";
  
  my $metatype = $result->{embedType};
  die "Could not extract video metadata type" unless $metatype;
  debug "video metadata source is: $metatype\n";
  
  my $query = $metaurl . $metatype . $video_id;
 
  my $account = $prefs->account("pbs.org", <<EOT);
If you set up a PBS account, you can access high definition videos.
The pbs.org login is the email address you registered at pbs.org.
See the documentation, i.e man netrc, for how to configure ~/.netrc
and skip continual prompting for account credentials. Example:
   machine pbs.org
   login myemail\@xyzzy.net
   password xxxxxxx
NOTE: if the login is set to 'no', standard definition will be downloaded.

EOT

  my $pbs_uid;
  my $pbs_station;

  # log into PBS if user has provided their credentials
  if ($account->username and $account->username ne 'no' and $account->password) {
   # get the pbs.ord login page and fill in the login form
   $browser->get('https://account.pbs.org/oauth2/authorize/?scope=account&redirect_uri=http://www.pbs.org/login/&response_type=code&client_id=LXLFIaXOVDsfS850bnvsxdcLKlvLStjRBoBWbFRE');
   if (! $browser->success()) {
     debug $browser->content();
     die "Could not access login page" unless $browser->success();
   }
   
   # fill in the login form with the users credentials
   $browser->form_number(1);
   $browser->field('email', $account->username);
   $browser->field('password', $account->password);
   
   # submit the login request
   $browser->submit();
   if ($browser->success()) {
   
      # login successful, but need to extract some cookie values to retrieve
      # high definition video
   
      foreach my $cookie (split /\n/, $browser->cookie_jar->as_string()) {
         my @tokens = split /; |: /, $cookie;
         my ($cname, $cvalue) = split /=/, $tokens[1];
         $pbs_uid = $cvalue if $cname eq 'pbs_uid';
         $pbs_station = $cvalue if $cname eq 'pbsol.station';
         debug "cookie name = $cname, value = $cvalue"
      }
   
      debug "setting pbs_uid=$pbs_uid and callsign=$pbs_station";
      info "using pbs.org account " . $account->username . " to retrieve high definition videos";
      # format query to get high definition video details in JSON
      $query = $query . '/?uid=' . $pbs_uid;
      
      } else {
         info "\n*** pbs.org login failed ***\ncorrect your login and password\nwill retrieve standard definition video.\n";
         # format query to get standard definition video details in JSON
         # $query = $query . '/?callsign=KCTS&callback=video_info&format=jsonp&type=portal';
      }
   
  } else {
   info "no pbs login credentials, will retrieve standard definition video.";
   # format query to get standard definition video details in JSON
   # $query = $query . '/?callsign=KCTS&callback=video_info&format=jsonp&type=portal';
  }
  
  info "Downloading video metadata";
  $browser->get($query);
  die "Could not get video metadata" unless $browser->success();
  
  # PBS returns the video metadata as a javascript variable
  # extract the embedded javascript and extract the PBS.videoData variable
  @scriptags =  $browser->content() =~/<script[^>]*>(.+?)<\/script>/sig;
  $script = "";
  $pbsdata = "";
  local $/ = "\r\n";
  foreach $script (@scriptags)
  {
     if ($script =~ /PBS.videoData/si) {
       ($pbsdata) = $script =~ /PBS.videoData += +([^;]*);/s;
       # change ' to " for the json parser
       $pbsdata =~ s/'/"/g;
       # PBS computes the number of chapters in the javascript.
       # We don't care, so replace it with an integer
       # so that the json parser does not fail.
       $pbsdata =~ s/: *chapters *,/: 4,/g;
       debug $pbsdata;
       last;
     }
  }
# Parse the json structure
  $result = from_json($pbsdata);
  debug Data::Dumper::Dumper($result);
  die "Could not extract video metadata.\n   Video may not be available.\n"
     unless ref($result) eq "HASH";
  
  # Get the video's title and urs source
  my $title = $result->{program}->{title} . " " . $result->{title};
  die "Could not extract video title" unless $title;
  debug "title is: $title\n";
  
  my $urs = $result->{recommended_encoding}->{url};
  die "Could not extract video urs" unless $urs;
  debug "urs extracted\n";
  
  # format another query to get video url in JSON  
  $query = $urs . '?format=json';
  
  info "Downloading video details";
  $browser->get($query);
  die "Could not get video details" unless $browser->success();
  
  # Content is JSON fomatted
  $result = from_json($browser->content());
  
  # Get the video's url source
  my $url = $result->{url};
  die "Could not extract video url. Possibly it is no longer available." unless $url;
  debug "found PBS video: $media_id @ $url";
  
  # get the scheme and filetype to determine appropriate downloader
  my ($scheme, $filetype) = $url =~ m[(^\w+):.*\.(\w+)$];
     debug "scheme is: $scheme";
     debug "file type is: $filetype";  
  
  if ($scheme =~ m[^rtmp]) {
  # pbs.org has not moved all videos from flash to hls
  # use rtmpdump for backward compatibility
     my $playpath;
     ($playpath) = $url =~ m[(\w+:*:videos.*$)];
     debug "playpath is: $playpath";
     debug "using rtmp downloader";
     return {
       rtmp    => $url,
       playpath => $playpath,
       flashVer => 'LNX 11,2,202,481',
       flv     => title_to_filename($title, $filetype),
     };
  } elsif ($scheme =~ m[^http] and $filetype eq "m3u8") {
      debug "using hls downloader";
      return {
         downloader => "hls",
         flv        => title_to_filename($title, "mp4"),
         args       => { hls_url => $url, prefs => $prefs }
      };
  } elsif ($scheme =~ m[^http] and $filetype eq "mp4") {
      return $url, title_to_filename($title, $filetype);
  } else {
      die "Video is in unknown scheme or format. Run with debug and report problem";
  }
}

1;