Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock

Hi,

Please unblock get-iplayer/2.87-2. Sorry that this is a little late up to
the starting gate.

get-iplayer is for browsing the BBC iPlayer archives with free software. It
consumes various feeds and can display schedules, download programmes and
stream live data.

A few weeks ago the BBC radically changed the way the feeds are produced,
rendering get-iplayer effectively useless. Upstream has made a number of
changes to suit, of which I've cherry picked the ones that restore basic
functionality, but left out nicities like thumbnail substitution. The
patches are unforunately invasive but necessary.

The bug fixed is severity:important.

Without an unblock, the alternative is to remove get-iplayer altogether. My
intention is to ship this working version in Jessie, and augment it with
the other missing features in backports later.

Debdiff attached.

 changelog                     |    8 
 patches/cache-search.patch    | 1030 ++++++++++++++++++++++++++++++++++++++++++
 patches/data-feeds.patch      |  221 +++++++++
 patches/future-schedule.patch |   81 +++
 patches/live-tv.patch         |   18 
 patches/series                |    4 
 6 files changed, 1362 insertions(+)

-- 
Jonathan Wiltshire                                      j...@debian.org
Debian Developer                         http://people.debian.org/~jmw

4096R: 0xD3524C51 / 0A55 B7C5 1223 3942 86EC  74C3 5394 479D D352 4C51

diff -Nru get-iplayer-2.87/debian/changelog get-iplayer-2.87/debian/changelog
--- get-iplayer-2.87/debian/changelog   2014-10-20 20:39:26.000000000 +0100
+++ get-iplayer-2.87/debian/changelog   2014-12-04 23:22:09.000000000 +0000
@@ -1,3 +1,11 @@
+get-iplayer (2.87-2) unstable; urgency=medium
+
+  * Fix feed fetch, parse and search across all channels after invasive
+    changes by the BBC. All patches cherry-picked from upstream
+    (Closes: #767331)
+
+ -- Jonathan Wiltshire <j...@debian.org>  Thu, 04 Dec 2014 23:21:16 +0000
+
 get-iplayer (2.87-1) unstable; urgency=medium
 
   * New upstream release (Closes: #743905, 623179)
diff -Nru get-iplayer-2.87/debian/patches/cache-search.patch 
get-iplayer-2.87/debian/patches/cache-search.patch
--- get-iplayer-2.87/debian/patches/cache-search.patch  1970-01-01 
01:00:00.000000000 +0100
+++ get-iplayer-2.87/debian/patches/cache-search.patch  2014-12-04 
23:22:09.000000000 +0000
@@ -0,0 +1,1030 @@
+From: dinkypumpkin <dinkypump...@gmail.com>
+Date: Thu, 30 Oct 2014 21:54:17 +0000
+Subject: Partial restoration of cache and search functions
+Origin: 
http://git.infradead.org/get_iplayer.git/commit/8220a6bf04a83f49f75d4996b6222ef3fd5d9d8b
+        
http://git.infradead.org/get_iplayer.git/commit/0c1e7ab3fb92656613dfe7811a31d559224957cc
+Last-Update: 2014-12-03
+
+--- get-iplayer-2.87.orig/get_iplayer
++++ get-iplayer-2.87/get_iplayer
+@@ -227,6 +227,12 @@
+       refreshinclude  => [ 1, "refreshinclude|refresh-include=s", 'Config', 
'--refresh-include <string>', "Include matched channel(s) when refreshing cache 
(regex or comma separated values)"],
+       refreshexclude  => [ 1, 
"refreshexclude|refresh-exclude|ignorechannels=s", 'Config', '--refresh-exclude 
<string>', "Exclude matched channel(s) when refreshing cache (regex or comma 
separated values)"],
+       refreshfuture   => [ 1, "refreshfuture|refresh-future!", 'Config', 
'--refresh-future', "Obtain future programme schedule when refreshing cache 
(between 7-14 days)"],
++      refreshfeeds    => [ 1, "refreshfeeds|refresh-feeds=s", 'Config', 
'--refresh-feeds <string>', "Alternate source for programme data.  Valid 
values: 'schedule'"],
++      refreshfeedsradio       => [ 1, 
"refreshfeedsradio|refresh-feeds-radio=s", 'Config', '--refresh-feeds-radio 
<string>', "Alternate source for radio programme data.  Valid values: 
'schedule'"],
++      refreshfeedstv  => [ 1, "refreshfeedstv|refresh-feeds-tv=s", 'Config', 
'--refresh-feeds-tv <string>', "Alternate source for TV programme data.  Valid 
values: 'schedule'"],
++      refreshlimit    => [ 1, "refreshlimit|refresh-limit=n", 'Config', 
'--refresh-limit <integer>', "Number of days of programmes to cache. Only 
applied with --refresh-feeds=schedule. Makes cache updates VERY slow. Default: 
7 Min: 1 Max: 30"],
++      refreshlimitradio       => [ 1, 
"refreshlimitradio|refresh-limit-radio=n", 'Config', '--refresh-limit-radio 
<integer>', "Number of days of radio programmes to cache. Only applied with 
--refresh-feeds=schedule. Makes cache updates VERY slow. Default: 7 Min: 1 Max: 
30"],
++      refreshlimittv  => [ 1, "refreshlimittv|refresh-limit-tv=n", 'Config', 
'--refresh-limit-tv <integer>', "Number of days of TV programmes to cache. Only 
applied with --refresh-feeds=schedule. Makes cache updates VERY slow. Default: 
7 Min: 1 Max: 30"],
+       skipdeleted     => [ 1, "skipdeleted!", 'Config', "--skipdeleted", 
"Skip the download of metadata/thumbs/subs if the media file no longer exists. 
Use with --history & --metadataonly/subsonly/thumbonly."],
+       update          => [ 2, "update|u!", 'Config', '--update, -u', "Update 
get_iplayer if a newer one exists"],
+       webrequest      => [ 1, "webrequest=s", 'Config', '--webrequest 
<urlencoded string>', 'Specify all options as a urlencoded string of 
"name=val&name=val&..."' ],
+@@ -6996,7 +7002,7 @@
+ use IO::Socket;
+ use LWP::ConnCache;
+ use LWP::UserAgent;
+-use POSIX qw(mkfifo);
++use POSIX qw(mkfifo strftime);
+ use strict;
+ use Time::Local;
+ use URI;
+@@ -7009,24 +7015,16 @@
+ sub index_max { return 9999 }
+ sub channels {
+       return {
+-              'bbcone'                        => 'BBC One',
+-              'bbctwo'                        => 'BBC Two',
+-              'bbcthree'                      => 'BBC Three',
+-              'bbcfour'                       => 'BBC Four',
+-              'bbcnews'                       => 'BBC News',
+-              'bbcnews24'                     => 'BBC News',
++              'bbc_one'                       => 'BBC One',
++              'bbc_two'                       => 'BBC Two',
++              'bbc_three'                     => 'BBC Three',
++              'bbc_four'                      => 'BBC Four',
+               'cbbc'                          => 'CBBC',
+               'cbeebies'                      => 'CBeebies',
+-              'parliament'                    => 'BBC Parliament',
+-              'bbcwebonly'                    => 'BBC Web Only',
+-              'bbchd'                         => 'BBC HD',
+-              'bbcalba'                       => 'BBC Alba',
+-              'categories/news/tv'            => 'BBC News',
+-              'categories/sport/tv'           => 'BBC Sport',
+-              'categories/signed'             => 'Signed',
+-              'categories/audiodescribed'     => 'Audio Described',
+-              'popular/tv'                    => 'Popular',
+-              'highlights/tv'                 => 'Highlights',
++              'bbc_news24'            => 'BBC News',
++              'bbc_parliament'        => 'BBC Parliament',
++              'bbc_alba'                      => 'BBC Alba',
++              'bbc_webonly'           => 'BBC Web Only',
+       };
+ }
+ 
+@@ -7176,143 +7174,53 @@
+       }
+ }
+ 
+-
+-
+-# Usage: Programme::tv->get_links( \%prog, 'tv' );
+-# Uses: %{ channels() }, \%prog
+-sub get_links {
+-      shift; # ignore obj ref
++sub get_links_aod {
++      my $self = shift;
+       my $prog = shift;
+       my $prog_type = shift;
++      return 1 if $prog_type ne "radio";
+       # Hack to get correct 'channels' method because this methods is being 
shared with Programme::radio
+-      my %channels = %{ main::progclass($prog_type)->channels_filtered( 
main::progclass($prog_type)->channels() ) };
+-      my $channel_feed_url = 'http://feeds.bbc.co.uk/iplayer'; # 
/$channel/list
++      my %channels = %{ main::progclass($prog_type)->channels_filtered( 
main::progclass($prog_type)->channels_aod() ) };
+       my $bbc_prog_page_prefix = 'http://www.bbc.co.uk/programmes'; # /$pid
+-      my $thumbnail_prefix = 'http://www.bbc.co.uk/iplayer/images/episode';
+-      my $xml;
+-      my $feed_data;
+-      my $res;
+-      main::logger "INFO: Getting $prog_type Index Feeds\n";
+       # Setup User agent
+       my $ua = main::create_ua( 'desktop', 1 );
+-
+       # Download index feed
+-      # Sort feeds so that category based feeds are done last - this makes 
sure that the channels get defined correctly if there are dups
+-      my @channel_list;
+-      push @channel_list, grep !/(categor|popular|highlights|bbchd)/, keys 
%channels;
+-      push @channel_list, grep  /categor/, keys %channels;
+-      push @channel_list, grep  /popular/, keys %channels;
+-      push @channel_list, grep  /highlights/, keys %channels;
+-      push @channel_list, grep  /bbchd/, keys %channels;
+-      for ( @channel_list ) {
+-
+-              my $url = "${channel_feed_url}/$_/list/limit/400";
+-              main::logger "DEBUG: Getting feed $url\n" if $opt->{verbose};
+-              $xml = main::request_url_retry($ua, $url, 3, '.', "WARNING: 
Failed to get programme index feed for $_ from iplayer site\n");
++      my @channel_list = keys %channels;
++      for my $channel_id ( @channel_list ) {
++              my $url = 
"http://www.bbc.co.uk/radio/aod/availability/${channel_id}.xml";;
++              main::logger "\nDEBUG: Getting feed $url\n" if $opt->{verbose};
++              my $xml = main::request_url_retry($ua, $url, 3, '.', 
"\nWARNING: Failed to get programme index feed for $channels{$channel_id}\n");
+               decode_entities($xml);
+-              
+-              # Feed as of August 2008
+-              #        <entry>
+-              #          <title type="text">Bargain Hunt: Series 18: 
Oswestry</title>
+-              #          <id>tag:feeds.bbc.co.uk,2008:PIPS:b0088jgs</id>
+-              #          <updated>2008-07-22T00:23:50Z</updated>
+-              #          <content type="html">
+-              #            &lt;p&gt;
+-              #              &lt;a 
href=&quot;http://www.bbc.co.uk/iplayer/episode/b0088jgs?src=a_syn30&quot;&gt;
+-              #                &lt;img 
src=&quot;http://www.bbc.co.uk/iplayer/images/episode/b0088jgs_150_84.jpg&quot; 
alt=&quot;Bargain Hunt: Series 18: Oswestry&quot; /&gt;
+-              #              &lt;/a&gt;
+-              #            &lt;/p&gt;
+-              #            &lt;p&gt;
+-              #              The teams are at an antiques fair in Oswestry 
showground. Hosted by Tim Wonnacott.
+-              #            &lt;/p&gt;
+-              #          </content>
+-              #          <category term="Factual" />
+-              #          <category term="Guidance" />
+-              #          <category term="TV" />
+-              #          <link rel="via" 
href="http://www.bbc.co.uk/iplayer/episode/b0088jgs?src=a_syn30"; 
type="text/html" title="Bargain Hunt: Series 18: Oswestry" />
+-              #       </entry>
+-              #
+-
+-              ### New Feed
+-              #  <entry>
+-              #    <title type="text">House of Lords: 02/07/2008</title>
+-              #    <id>tag:bbc.co.uk,2008:PIPS:b00cd5p7</id>
+-              #    <updated>2008-06-24T00:15:11Z</updated>
+-              #    <content type="html">
+-              #      <p>
+-              #       <a 
href="http://www.bbc.co.uk/iplayer/episode/b00cd5p7?src=a_syn30";>
+-              #         <img 
src="http://www.bbc.co.uk/iplayer/images/episode/b00cd5p7_150_84.jpg"; 
alt="House of Lords: 02/07/2008" />
+-              #       </a>
+-              #      </p>
+-              #      <p>
+-              #       House of Lords, including the third reading of the 
Health and Social Care Bill. 1 July.
+-              #      </p>
+-              #    </content>
+-              #    <category term="Factual" scheme="urn:bbciplayer:category" 
/>
+-              #    <link rel="via" 
href="http://www.bbc.co.uk/iplayer/episode/b00cd5p7?src=a_syn30"; 
type="application/atom+xml" title="House of Lords: 02/07/2008">
+-              #    </link>
+-              #  </entry>
+-
+-              ### Newer feed (Sept 2009)
+-              #  <entry>
+-              #    <title type="text">BBC Proms: 2009: Prom 65: Gustav Mahler 
Jugend Orchester</title>
+-              #    <id>tag:feeds.bbc.co.uk,2008:PIPS:b00mgw03</id>
+-              #    <updated>2009-09-05T03:29:07Z</updated>
+-              #    <content type="html">
+-              #      &lt;p&gt;
+-              #        &lt;a 
href=&quot;http://www.bbc.co.uk/iplayer/episode/b00mgw03/BBC_Proms_2009_Prom_65_Gustav_Mahler_Jugend_Orchester/&quot;&gt;
+-              #          &lt;img 
src=&quot;http://node1.bbcimg.co.uk/iplayer/images/episode/b00mgw03_150_84.jpg&quot;
 alt=&quot;BBC Proms: 2009: Prom 65: Gustav Mahler Jugend Orchester&quot; /&gt;
+-              #        &lt;/a&gt;
+-              #      &lt;/p&gt;
+-              #      &lt;p&gt;
+-              #        The Gustav Mahler Youth Orchestra perform works by 
Mahler, Richard Strauss and Ligeti.
+-              #      &lt;/p&gt;
+-              #    </content>
+-              #    <category term="Music" />
+-              #    <category term="Classical" />
+-              #    <category term="TV" />
+-              #    <link rel="alternate" 
href="http://www.bbc.co.uk/iplayer/episode/b00mgw03/BBC_Proms_2009_Prom_65_Gustav_Mahler_Jugend_Orchester/";
 type="text/html" title="BBC Proms: 2009: Prom 65: Gustav Mahler Jugend 
Orchester">
+-              #      <media:content>
+-              #        <media:thumbnail 
url="http://node1.bbcimg.co.uk/iplayer/images/episode/b00mgw03_150_84.jpg"; 
width="150" height="84" />
+-              #      </media:content>
+-              #    </link>
+-              #    <link rel="self" 
href="http://feeds.bbc.co.uk/iplayer/episode/b00mgw03"; 
type="application/atom+xml" title="Prom 65: Gustav Mahler Jugend Orchester" />
+-              #    <link rel="related" 
href="http://www.bbc.co.uk/programmes/b007v097/microsite"; type="text/html" 
title="BBC Proms" />
+-              #  </entry>
+-
+-
+               # Parse XML
+-
+               # get list of entries within <entry> </entry> tags
+-              my @entries = split /<entry>/, $xml;
++              my @entries = split /<entry/, $xml;
+               # Discard first element == header
+               shift @entries;
+-
+-              main::logger "INFO: Got ".($#entries + 1)." programmes\n" if 
$opt->{verbose};
++              main::logger "\nINFO: Got ".($#entries + 1)." programmes for 
$channels{$channel_id}\n" if $opt->{verbose};
++              my $now = time();
+               foreach my $entry (@entries) {
+                       my ( $title, $name, $episode, $episodetitle, 
$nametitle, $episodenum, $seriesnum, $desc, $pid, $available, $channel, 
$duration, $thumbnail, $version, $guidance );
+-                      
+-                      my $entry_flat = $entry;
+-                      $entry_flat =~ s/\n/ /g;
+-
+-                      # <id>tag:bbc.co.uk,2008:PIPS:b008pj3w</id>
+-                      $pid = $1 if $entry =~ m{<id>.*PIPS:(.+?)</id>};
+-
+-                      # <title type="text">Richard Hammond's Blast Lab: 
Series Two: Episode 11</title>
+-                      # <title type="text">Skate Nation: Pro-Skate 
Camp</title>
+-                      $title = $1 if $entry =~ 
m{<title\s*.*?>\s*(.*?)\s*</title>};
+-
++                      my ($start, $available) = ($1, $2) if $entry =~ 
m{<availability\s+start="(.*?)"\s+end="(.*?)"};
++                      next if ! ( $start || $available );
++                      if ( $start ) {
++                              my $xstart = Programme::get_time_string( $start 
);
++                              next if $xstart > $now;
++                      }
++                      if ( $available ) {
++                              my $xavailable = Programme::get_time_string( 
$available );
++                              next if $xavailable < $now;
++                      }
++                      my $vpid = $1 if $entry =~ m{pid="(.+?)">};
++                      $pid = $1 if $entry =~ m{<pid>(.+?)</pid>};
++                      $duration = $1 if $entry =~ m{duration="(.*?)"};
++                      $title = $1 if $entry =~ m{<title>(.*?)</title>};
+                       # determine name and episode from title
+                       ( $name, $episode ) = 
Programme::bbciplayer::split_title( $title );
+-
+-                      # Get the title from the atom link refs only to 
determine the longer episode name
+-                      $episodetitle = $1 if $entry =~ 
m{<link\s+rel="self"\s+href="http.+?/episode/.+?"\s+type="application/atom\+xml"\s+title="(.+?)"};
+-                      $nametitle = $1 if $entry =~ 
m{<link\s+rel="related"\s+href="http.+?/programmes/.+?"\s+type="text/html"\s+title="(.+?)"};
+-
++                      $episodetitle = $episode;
++                      $nametitle = $name;
+                       # Extract the seriesnum
+                       my $regex = 'Series\s+'.main::regex_numbers();
+                       $seriesnum = main::convert_words_to_number( $1 ) if 
"$name $episode" =~ m{$regex}i;
+-
+                       # Extract the episode num
+                       my $regex_1 = 'Episode\s+'.main::regex_numbers();
+                       my $regex_2 = '^'.main::regex_numbers().'\.\s+';
+@@ -7323,79 +7231,20 @@
+                       } elsif ( $episodetitle =~ m{$regex_2}i ) {
+                               $episodenum = main::convert_words_to_number( $1 
);
+                       }
+-
+                       # Re-insert the episode number if the episode text 
doesn't have it
+                       if ( $episodenum && $episodetitle =~ /^\d+\./ && 
$episode !~ /^(.+:\s+)?\d+\./ ) {
+                               $episode =~ s/^(.+:\s+)?(.*)$/$1$episodenum. 
$2/;
+                       }
+-
+-                      #<p>    House of Lords, including the third reading of 
the Health and Social Care Bill. 1 July.   </p>    </content>
+-                      $desc = $1 if $entry =~ 
m{<p>\s*(.*?)\s*</p>\s*</content>};
+-                      $desc =~ s|[\n\r]| |g;
+-                      # Remove unwanted html tags
+-                      $desc =~ s!</?(br|b|i|p|strong)\s*/?>!!gi;
+-
+-                      # Parse the categories into hash
+-                      # <category term="Factual" />
+-                      my @category;
+-                      for my $line ( grep /<category/, (split /\n/, $entry) ) 
{
+-                              push @category, $1 if $line =~ 
m{<category\s+term="(.+?)"};
+-                      }
+-                      # strip commas - they confuse sorting and spliting later
+-                      s/,//g for @category;
+-
++                      $desc = $1 if $entry =~ m{<synopsis>(.*?)</synopsis>};
+                       # Extract channel
+-                      $channel = $channels{$_};
+-                      # Add HD as category
+-                      push @category, 'HD' if $channel eq 'BBC HD';
+-
++                      $channel = $channels{$channel_id};
+                       main::logger "DEBUG: '$pid, $name - $episode, 
$channel'\n" if $opt->{debug};
+-
+                       # Merge and Skip if this pid is a duplicate
+                       if ( defined $prog->{$pid} ) {
+-                              main::logger "WARNING: '$pid, 
$prog->{$pid}->{name} - $prog->{$pid}->{episode}, $prog->{$pid}->{channel}' 
already exists (this channel = $channel)\n" if $opt->{verbose};
+-                              # Since we use the 'Signed' (or 'Audio 
Described') channel to get sign zone/audio described data, merge the categories 
from this entry to the existing entry
+-                              if ( $prog->{$pid}->{categories} ne join(',', 
sort @category) ) {
+-                                      my %cats;
+-                                      $cats{$_} = 1 for ( @category, split 
/,/, $prog->{$pid}->{categories} );
+-                                      main::logger "INFO: Merged categories 
for $pid from $prog->{$pid}->{categories} to ".join(',', sort keys %cats)."\n" 
if $opt->{verbose};
+-                                      $prog->{$pid}->{categories} = join(',', 
sort keys %cats);
+-                              }
+-
+-                              # If this a popular or highlights programme 
then add these tags to categories
+-                              my %cats;
+-                              $cats{$_} = 1 for ( @category, split /,/, 
$prog->{$pid}->{categories} );
+-                              $cats{Popular} = 1 if $channel eq 'Popular';
+-                              $cats{Highlights} = 1 if $channel eq 
'Highlights';
+-                              $prog->{$pid}->{categories} = join(',', sort 
keys %cats);
+-
+-                              # If this is a dupicate pid and the channel is 
now Signed then both versions are available
+-                              $version = 'signed' if $channel eq 'Signed';
+-                              $version = 'audiodescribed' if $channel eq 
'Audio Described';
+-                              # Add version to versions for existing prog
+-                              $prog->{$pid}->{versions} = join ',', 
main::make_array_unique_ordered( (split /,/, $prog->{$pid}->{versions}), 
$version );
+                               next;
+                       }
+-
+-                      # Set guidance based on category
+-                      $guidance = 'Yes' if grep /guidance/i, @category;
+-
+-                      # Check for signed-only or audiodescribed-only version 
from Channel
+-                      if ( $channel eq 'Signed' ) {
+-                              $version = 'signed';
+-                      } elsif ( $channel eq 'Audio Described' ) {
+-                              $version = 'audiodescribed';
+-                      } else {
+-                              $version = 'default';
+-                      }
+-
+-                      # Default to 150px width thumbnail;
+-                      my $thumbsize = $opt->{thumbsizecache} || 150;
+-                      my $thumbnail = $1 if $entry =~ 
m{<media:thumbnail.*?url="(.*?)"};
+-                      my $suffix = 
Programme::bbciplayer->thumb_url_suffixes->{ $thumbsize };
+-                      if ( $thumbnail !~ /$suffix/ ) {
+-                              $thumbnail =~ s/_\d+_\d+\.jpg/$suffix/;
+-                      }
++                      $version = 'default';
++                      my @category;
+                       # build data structure
+                       $prog->{$pid} = main::progclass($prog_type)->new(
+                               'pid'           => $pid,
+@@ -7407,203 +7256,408 @@
+                               'desc'          => $desc,
+                               'guidance'      => $guidance,
+                               'available'     => 'Unknown',
+-                              'duration'      => 'Unknown',
++                              'duration'      => $duration || 'Unknown',
+                               'thumbnail'     => $thumbnail,
+                               'channel'       => $channel,
+                               'categories'    => join(',', sort @category),
+                               'type'          => $prog_type,
+-                              'web'           => 
"${bbc_prog_page_prefix}/${pid}.html",
++                              'web'           => 
"${bbc_prog_page_prefix}/${pid}",
+                       );
+-
+               }
+       }
++}
+ 
+-      # Get future schedules if required
+-      # http://www.bbc.co.uk/cbbc/programmes/schedules/this_week.xml
+-      # http://www.bbc.co.uk/cbbc/programmes/schedules/next_week.xml
+-      if ( $opt->{refreshfuture} ) {
+-              # Hack to get correct 'channels' method because this methods is 
being shared with Programme::radio
+-              my %channels = %{ 
main::progclass($prog_type)->channels_filtered( 
main::progclass($prog_type)->channels_schedule() ) };
+-              # Only get schedules for real channels
+-              @channel_list = keys %channels;
+-              for my $channel_id ( @channel_list ) {
+-                      my @schedule_feeds = (
+-                              
"http://www.bbc.co.uk/${channel_id}/this_week.xml";,
+-                              
"http://www.bbc.co.uk/${channel_id}/next_week.xml";,
+-                      );
+-                      for my $url ( @schedule_feeds ) {
+-                              main::logger "DEBUG: Getting feed $url\n" if 
$opt->{verbose};
+-                              $xml = main::request_url_retry($ua, $url, 3, 
'.', "WARNING: Failed to get programme schedule feed for $channel_id from 
iplayer site\n");
+-                              decode_entities($xml);
+-              
+-                              # <broadcast is_repeat="1" is_blanked="0">
+-                              #   <pid>p00l44r8</pid>
+-                              #   <start>2011-10-24T00:45:00+01:00</start>
+-                              #   <end>2011-10-24T01:15:00+01:00</end>
+-                              #   <duration>1800</duration>
+-                              #   <programme type="episode">
+-                              #     <pid>b016c73c</pid>
+-                              #     <position>3</position>
+-                              #     <title>Episode 3</title>
+-                              #     <short_synopsis>With team captains Noel 
Fielding and Phill Jupitus, and a surprise special guest host.</short_synopsis>
+-                              #     <media_type>audio_video</media_type>
+-                              #     <duration>2100</duration>
+-                              #     <display_titles>
+-                              #       <title>Never Mind the Buzzcocks</title>
+-                              #       <subtitle>Series 25, Episode 
3</subtitle>
+-                              #     </display_titles>
+-                              #     
<first_broadcast_date>2011-10-17T22:00:00+01:00</first_broadcast_date>
+-                              #     <ownership>
+-                              #       <service type="tv" id="bbc_two" 
key="bbctwo">
+-                              #         <title>BBC Two</title>
+-                              #       </service>
+-                              #     </ownership>
+-                              #     <programme type="series">
+-                              #       <pid>b015skhy</pid>
+-                              #       <title>Series 25</title>
+-                              #       <position>25</position>
+-                              #       
<expected_child_count>12</expected_child_count>
+-                              #       
<first_broadcast_date>2011-10-03T22:00:00+01:00</first_broadcast_date>
+-                              #       <programme type="brand">
+-                              #         <pid>b006v0dz</pid>
+-                              #         <title>Never Mind the 
Buzzcocks</title>
+-                              #         <position />
+-                              #         <expected_child_count />
+-                              #         
<first_broadcast_date>2007-06-20T22:00:00+01:00</first_broadcast_date>
+-                              #         <ownership>
+-                              #           <service type="tv" id="bbc_two" 
key="bbctwo">
+-                              #             <title>BBC Two</title>
+-                              #           </service>
+-                              #         </ownership>
+-                              #       </programme>
+-                              #     </programme>
+-                              #     
<available_until>2011-10-30T23:29:00Z</available_until>
+-                              #     
<actual_start>2011-10-17T22:30:00+01:00</actual_start>
+-                              #     
<is_available_mediaset_pc_sd>1</is_available_mediaset_pc_sd>
+-                              #     <is_legacy_media>0</is_legacy_media>
+-                              #     <media format="video">
+-                              #       <expires>2011-10-30T23:29:00Z</expires>
+-                              #       <availability>11 days left to 
watch</availability>
+-                              #     </media>
+-                              #   </programme>
+-                              # </broadcast>
+-
+-                              # get list of entries within <broadcast> 
</broadcast> tags
+-                              my @entries = split /<broadcast[^s]/, $xml;
+-                              # Discard first element == header
+-                              shift @entries;
+-                              main::logger "INFO: Got ".($#entries + 1)." 
programmes\n" if $opt->{verbose};
+-                              my $now = time();
+-                              foreach my $entry (@entries) {
+-                                      my ( $title, $channel, $name, $episode, 
$episodetitle, $nametitle, $seriestitle, $episodenum, $seriesnum, $desc, $pid, 
$available, $duration, $thumbnail, $version, $guidance );
+-
+-                                      my $entry_flat = $entry;
+-                                      $entry_flat =~ s/\n/ /g;
+-
+-                                      $pid = $1 if $entry =~ 
m{<programme\s+type="episode">.*?<pid>\s*(.+?)\s*</pid>};
+-
+-                                      $episode = $1 if $entry =~ 
m{<programme\s+type="episode">.*?<title>\s*(.*?)\s*</title>};
+-                                      $nametitle = $1 if $entry =~ 
m{<programme\s+type="brand">.*?<title>\s*(.*?)\s*</title>.*?</programme>};
+-                                      $seriestitle = $1 if $entry =~ 
m{<programme\s+type="series">.*?<title>\s*(.*?)\s*</title>.*?</programme>};
+-
+-                                      # Set name
+-                                      if ( $nametitle && $seriestitle ) {
+-                                              $name = "$nametitle: 
$seriestitle";
+-                                      } elsif ( $seriestitle && ! $nametitle 
) {
+-                                              $name = $seriestitle;
+-                                      # Fallback to episade name if the BBC 
missed out both Series and Name
+-                                      } elsif ( ( ! $seriestitle ) && ! 
$nametitle ) {
+-                                              $name = $episode;
+-                                      } else {
+-                                              $name = $nametitle;
+-                                      }
+-
+-                                      # Extract the seriesnum
+-                                      my $regex = 
'Series\s+'.main::regex_numbers();
+-                                      $seriesnum = 
main::convert_words_to_number( $1 ) if $seriestitle =~ m{$regex}i;
+-
+-                                      # Extract the episode num
+-                                      my $regex_1 = 
'Episode\s+'.main::regex_numbers();
+-                                      my $regex_2 = 
'^'.main::regex_numbers().'\.\s+';
+-                                      if ( $episode =~ m{$regex_1}i ) { 
+-                                              $episodenum = 
main::convert_words_to_number( $1 );
+-                                      } elsif ( $episode =~ m{$regex_2}i ) {
+-                                              $episodenum = 
main::convert_words_to_number( $1 );
+-                                      }
+ 
+-                                      # extract desc
+-                                      if ( $entry =~ 
m{<long_synopsis>\s*(.+?)\s*</long_synopsis>} ) {
+-                                              $desc = $1;
+-                                      } elsif ( $entry =~ 
m{<medium_synopsis>\s*(.+?)\s*</medium_synopsis>} ) {
+-                                              $desc = $1;
+-                                      } elsif ( $entry =~ 
m{<short_synopsis>\s*(.+?)\s*</short_synopsis>} ) {
+-                                              $desc = $1;
+-                                      };
+-                                      # Remove unwanted html tags
+-                                      $desc =~ 
s!</?(br|b|i|p|strong)\s*/?>!!gi;
+-
+-                                      $duration = $1 if $entry =~ 
m{<duration>\s*(.+?)\s*</duration>};
+-                                      $available = $1 if $entry =~ 
m{<start>\s*(.+?)\s*</start>};
+-
+-                                      # Extract channel nice name
+-                                      $channel = $channels{$channel_id};
+-
+-                                      main::logger "DEBUG: '$pid, $name - 
$episode, $channel'\n" if $opt->{debug};
+-
+-                                      # Merge and Skip if this pid is a 
duplicate
+-                                      if ( defined $prog->{$pid} ) {
+-                                              main::logger "WARNING: '$pid, 
$prog->{$pid}->{name} - $prog->{$pid}->{episode}, $prog->{$pid}->{channel}' 
already exists (this channel = $channel)\n" if $opt->{verbose};
+-                                              # Update this info from 
schedule (not available in the usual iplayer channels feeds)
+-                                              $prog->{$pid}->{duration} = 
$duration;
+-                                              $prog->{$pid}->{episodenum} = 
$episodenum if ! $prog->{$pid}->{episodenum};
+-                                              $prog->{$pid}->{seriesnum} = 
$seriesnum if ! $prog->{$pid}->{seriesnum};
+-                                              # don't add this as some progs 
are already available
+-                                              #$prog->{$pid}->{available} = 
$available;
+-                                              next;
++sub get_links_ion {
++      my $self = shift;
++      my $prog = shift;
++      my $prog_type = shift;
++      # Hack to get correct 'channels' method because this methods is being 
shared with Programme::radio
++      my %channels = %{ main::progclass($prog_type)->channels_filtered( 
main::progclass($prog_type)->channels() ) };
++      my $bbc_prog_page_prefix = 'http://www.bbc.co.uk/programmes'; # /$pid
++      # Setup User agent
++      my $ua = main::create_ua( 'desktop', 1 );
++      # Download index feed
++      my @channel_list;
++      @channel_list = sort keys %channels;
++      for my $channel_id ( @channel_list ) {
++              my @ranges;
++              push @ranges, ('a-z', '0-9');
++              if ( $prog_type eq 'tv' ) {
++                      push @ranges, ('signed_a-z', 'signed_0-9');
++              }
++              for my $range ( @ranges ) {
++                      my $cond = $range;
++                      my $op = "letters";
++                      my $cat;
++                      if ( $cond =~ /signed_/ ) {
++                              $cond =~ s/signed_//;
++                              $cat = "category/signed";
++                      }
++                      my $url = 
"http://www.bbc.co.uk/iplayer/ion/atoz/format/xml/service_type/${prog_type}/masterbrand/$channel_id/$op/$cond/$cat";;
++                      main::logger "\nDEBUG: Getting feed $url\n" if 
$opt->{verbose};
++                      my $xml = main::request_url_retry($ua, $url, 3, '.', 
"\nWARNING: Failed to get programme index feed for $channels{$channel_id} - 
$cond $cat\n");
++                      decode_entities($xml);
++                      # Parse XML
++                      # get list of entries within <entry> </entry> tags
++                      my @entries = split /<episode>/, $xml;
++                      # Discard first element == header
++                      shift @entries;
++                      main::logger "\nINFO: Got ".($#entries + 1)." 
programmes for $channels{$channel_id} - $cond $cat\n" if $opt->{verbose};
++                      foreach my $entry (@entries) {
++                              my ( $title, $name, $episode, $episodetitle, 
$nametitle, $episodenum, $seriesnum, $desc, $pid, $available, $channel, 
$duration, $thumbnail, $version, $guidance );
++                              $pid = $1 if $entry =~ 
m{</passionsite_title>.*?<id>(.+?)</id>}s;
++                              $duration = $1 if $entry =~ 
m{<duration>(.*?)</duration>};
++                              $title = $1 if $entry =~ 
m{<complete_title>(.*?)</complete_title>};
++                              # determine name and episode from title
++                              ( $name, $episode ) = 
Programme::bbciplayer::split_title( $title );
++                              $episodetitle = $1 if $entry =~ 
m{<short_synopsis>.*?<title>(.*?)</title>}s;
++                              $nametitle = $name;
++                              # Extract the seriesnum
++                              my $regex = 'Series\s+'.main::regex_numbers();
++                              $seriesnum = main::convert_words_to_number( $1 
) if "$name $episode" =~ m{$regex}i;
++                              # Extract the episode num
++                              my $regex_1 = 
'Episode\s+'.main::regex_numbers();
++                              my $regex_2 = '^'.main::regex_numbers().'\.\s+';
++                              if ( "$name $episode" =~ m{$regex_1}i ) { 
++                                      $episodenum = 
main::convert_words_to_number( $1 );
++                              } elsif ( $episode =~ m{$regex_2}i ) {
++                                      $episodenum = 
main::convert_words_to_number( $1 );
++                              } elsif ( $episodetitle =~ m{$regex_2}i ) {
++                                      $episodenum = 
main::convert_words_to_number( $1 );
++                              }
++                              # Re-insert the episode number if the episode 
text doesn't have it
++                              if ( $episodenum && $episodetitle =~ /^\d+\./ 
&& $episode !~ /^(.+:\s+)?\d+\./ ) {
++                                      $episode =~ 
s/^(.+:\s+)?(.*)$/$1$episodenum. $2/;
++                              }
++                              $desc = $1 if $entry =~ 
m{<short_synopsis>(.*?)</short_synopsis>};
++                              my @category;
++                              my @lines = split /<category>/, $entry;
++                              shift @lines;
++                              for my $line ( @lines ) {
++                                      push @category, $1 if $line =~ 
m{<title>(.*?)</title>};
++                              }
++                              # strip commas - they confuse sorting and 
spliting later
++                              s/,//g for @category;
++                              # Extract channel
++                              $channel = $1 if $entry =~ 
m{<masterbrand_title>(.*?)</masterbrand_title>};
++                              main::logger "DEBUG: '$pid, $name - $episode, 
$channel'\n" if $opt->{debug};
++                              # Merge and Skip if this pid is a duplicate
++                              if ( defined $prog->{$pid} ) {
++                                      main::logger "WARNING: '$pid, 
$prog->{$pid}->{name} - $prog->{$pid}->{episode}, $prog->{$pid}->{channel}' 
already exists (this channel = $channel)\n" if $opt->{verbose};
++                                      # Since we use the 'Signed' (or 'Audio 
Described') channel to get sign zone/audio described data, merge the categories 
from this entry to the existing entry
++                                      if ( $prog->{$pid}->{categories} ne 
join(',', sort @category) ) {
++                                              my %cats;
++                                              $cats{$_} = 1 for ( @category, 
split /,/, $prog->{$pid}->{categories} );
++                                              main::logger "INFO: Merged 
categories for $pid from $prog->{$pid}->{categories} to ".join(',', sort keys 
%cats)."\n" if $opt->{verbose};
++                                              $prog->{$pid}->{categories} = 
join(',', sort keys %cats);
+                                       }
+-
++                                      # If this is a duplicate pid and the 
channel is now Signed then both versions are available
++                                      $version = 'signed' if grep /Sign 
Zone/, @category;
++                                      $version = 'audiodescribed' if grep 
/Audio Described/, @category;
++                                      # Add version to versions for existing 
prog
++                                      $prog->{$pid}->{versions} = join ',', 
main::make_array_unique_ordered( (split /,/, $prog->{$pid}->{versions}), 
$version );
++                                      next;
++                              }
++                              $guidance = $1 if $entry =~ 
m{<has_guidance>(.*?)</has_guidance>};
++                              if ( $guidance ) {
++                                      $guidance = "Yes";
++                              } else {
++                                      $guidance = undef;
++                              }
++                              # Check for signed-only or audiodescribed-only 
version from category
++                              if ( grep /Sign Zone/, @category ) {
++                                      $version = 'signed';
++                              } elsif ( grep /Audio Described/, @category ) {
++                                      $version = 'audiodescribed';
++                              } else {
+                                       $version = 'default';
+-
+-                                      # Default to 150px width thumbnail;
+-                                      my $thumbsize = $opt->{thumbsizecache} 
|| 150;
+-                                      my $thumbnail = $1 if $entry =~ 
m{<media:thumbnail.*?url="(.*?)"};
+-                                      if ( $thumbnail ) {
+-                                              my $suffix = 
Programme::bbciplayer->thumb_url_suffixes->{ $thumbsize };
+-                                              if ( $thumbnail !~ /$suffix/ ) {
+-                                                      $thumbnail =~ 
s/_\d+_\d+\.jpg/$suffix/;
+-                                              }
+-                                      }
+-
+-                                      # Don't create this prog instance if 
the availablity is in the past 
+-                                      # this prevents programmes which never 
appear in iPlayer from being indexed
+-                                      next if Programme::get_time_string( 
$available ) < $now;
+-
+-                                      # build data structure
+-                                      $prog->{$pid} = 
main::progclass($prog_type)->new(
+-                                              'pid'           => $pid,
+-                                              'name'          => $name,
+-                                              'versions'      => $version,
+-                                              'episode'       => $episode,
+-                                              'seriesnum'     => $seriesnum,
+-                                              'episodenum'    => $episodenum,
+-                                              'desc'          => $desc,
+-                                              'available'     => $available,
+-                                              'duration'      => $duration,
+-                                              'thumbnail'     => $thumbnail,
+-                                              'channel'       => $channel,
+-                                              'type'          => $prog_type,
+-                                              'web'           => 
"${bbc_prog_page_prefix}/${pid}.html",
+-                                      );
+                               }
+-                      }               
++                              # Default to 150px width thumbnail;
++                              my $thumbsize = $opt->{thumbsizecache} || 150;
++                              my $image_template_url = $1 if $entry =~ 
m{<image_template_url>(.*?)</image_template_url>};
++                              my $recipe = 
Programme::bbciplayer->thumb_url_recipes->{ $thumbsize };
++                              my $thumbnail = $image_template_url;
++                              $thumbnail =~ s/\$recipe/$recipe/;
++                              # build data structure
++                              $prog->{$pid} = 
main::progclass($prog_type)->new(
++                                      'pid'           => $pid,
++                                      'name'          => $name,
++                                      'versions'      => $version,
++                                      'episode'       => $episode,
++                                      'seriesnum'     => $seriesnum,
++                                      'episodenum'    => $episodenum,
++                                      'desc'          => $desc,
++                                      'guidance'      => $guidance,
++                                      'available'     => 'Unknown',
++                                      'duration'      => $duration || 
'Unknown',
++                                      'thumbnail'     => $thumbnail,
++                                      'channel'       => $channel,
++                                      'categories'    => join(',', sort 
@category),
++                                      'type'          => $prog_type,
++                                      'web'           => 
"${bbc_prog_page_prefix}/${pid}",
++                              );
++                      }
++              }
++      }
++}
+ 
++
++# Usage: Programme::tv->get_links( \%prog, 'tv' );
++# Uses: %{ channels() }, \%prog
++sub get_links {
++      my $self = shift; # ignore obj ref
++      my $prog = shift;
++      my $prog_type = shift;
++      my $feeds = lc($opt->{"refreshfeeds".${prog_type}} || 
$opt->{"refreshfeeds"});
++      main::logger "INFO: Getting $prog_type Index Feeds (this may take a few 
minutes)\n";
++      if ( $feeds eq 'schedule' ) {
++              return $self->get_links_schedule($prog, $prog_type, 0);
++      } else {
++              if ( $prog_type eq 'radio' ) {
++                      return $self->get_links_aod($prog, $prog_type);
++              }
++              elsif ( $prog_type eq 'tv' ) {
++                      return $self->get_links_ion($prog, $prog_type);
+               }
+       }
++
++      if ( $opt->{refreshfuture} ) {
++              $self->get_links_schedule($prog, $prog_type, 1);
++      }
++      
+       main::logger "\n";
+       return 0;
+ }
+ 
+ 
++# get cache info for programmes from schedule
++sub get_links_schedule {
++      my $self = shift;
++      my $prog = shift;
++      my $prog_type = shift;
++      my $future = shift;
++      my %channels = %{ main::progclass($prog_type)->channels_filtered( 
main::progclass($prog_type)->channels_schedule() ) };
++      my @channel_list = sort keys %channels;
++      my @schedule_dates;
++      my $limit = 0;
++      my $limit_days = $opt->{"refreshlimit".${prog_type}} || 
$opt->{"refreshlimit"};
++      $limit_days = 30 if $limit_days > 30;
++      if ( $limit_days ) {
++              my $now = time();
++              $limit = $now - $limit_days * 86400;
++              my ($limit_weeks, $rem) = (int $limit_days / 7, $limit_days % 
7);
++              $limit_weeks++ if $rem && $limit_weeks;
++              for (my $i = $limit_weeks; $i >= 0; $i -= 1) {
++                      my $then = $now - ($i * 7) * 86400;
++                      my $year = (gmtime($then))[5];
++                      my $week = strftime( "%W", gmtime($then) );
++                      push @schedule_dates, sprintf("%04d/w%02d", $year+1900, 
++$week);
++              }
++      } else {
++              if ( $future ) {
++                      @schedule_dates = ( "this_week", "next_week" );
++              } else {
++                      @schedule_dates = ( "last_week", "this_week" );
++              }
++      }
++      for my $channel_id ( @channel_list ) {
++              for my $schedule_date ( @schedule_dates ) {
++                      my $url = 
"http://www.bbc.co.uk/${channel_id}/${schedule_date}.xml";;
++                      my $rc = $self->get_links_schedule_page($prog, 
$prog_type, $channels{$channel_id}, $future, $url, $limit);
++                      if ( $rc ) {
++                              main::logger("\nWARNING: Failed to get 
programme schedule feed for $channel_id from iplayer site\n");
++                      }
++              }
++      }
++}
++
++# get cache info from schedule page
++sub get_links_schedule_page {
++      my $self = shift;
++      my $prog = shift;
++      my $prog_type = shift;
++      my $channel = shift;
++      my $future = shift;
++      my $url = shift;
++      my $limit = shift;
++      my $bbc_prog_page_prefix = 'http://www.bbc.co.uk/programmes'; # /$pid
++      my $ua = main::create_ua( 'desktop', 1 );
++      main::logger "DEBUG: Getting feed $url\n" if $opt->{debug};
++      my $xml = main::request_url_retry($ua, $url, 3, '.', "\nWARNING: Failed 
to download programme schedule $url\n");
++      return 1 if ! $xml;
++      decode_entities($xml);
++              
++      # <broadcast is_repeat="0" is_blanked="0">
++      #       <pid>p0290kxs</pid>
++      #       <start>2014-10-31T11:00:00Z</start>
++      #       <end>2014-10-31T11:45:00Z</end>
++      #       <duration>2700</duration>
++      #       <programme type="episode">
++      #               <pid>b04n8rx0</pid>
++      #               <position>10</position>
++      #               <title>Episode 10</title>
++      #               <short_synopsis>Council officers deal with home owners 
living on top of deadly waste.</short_synopsis>
++      #               <media_type>audio_video</media_type>
++      #               <duration>2700</duration>
++      #               <image>
++      #                       <pid>p028r5jx</pid>
++      #               </image>
++      #               <display_titles>
++      #                       <title>Call the Council</title>
++      #                       <subtitle>Series 2, Episode 10</subtitle>
++      #               </display_titles>
++      #               
<first_broadcast_date>2014-10-31T11:00:00Z</first_broadcast_date>
++      #               <ownership>
++      #                       <service type="tv" id="bbc_one" key="bbcone">
++      #                               <title>BBC One</title>
++      #                       </service>
++      #               </ownership>
++      #               <programme type="series">
++      #                       <pid>b04mlq1k</pid>
++      #                       <title>Series 2</title>
++      #                       <position>2</position>
++      #                       <image>
++      #                               <pid>p028r5jx</pid>
++      #                       </image>
++      #                       <expected_child_count>10</expected_child_count>
++      #                       
<first_broadcast_date>2014-10-20T11:00:00+01:00</first_broadcast_date>
++      #                       <ownership>
++      #                               <service type="tv" id="bbc_one" 
key="bbcone">
++      #                                       <title>BBC One</title>
++      #                               </service>
++      #                       </ownership>
++      #                       <programme type="brand">
++      #                               <pid>b04mlpdd</pid>
++      #                               <title>Call the Council</title>
++      #                               <position/>
++      #                               <image>
++      #                                       <pid>p028r5jx</pid>
++      #                               </image>
++      #                               <expected_child_count/>
++      #                               
<first_broadcast_date>2014-05-19T11:30:00+01:00</first_broadcast_date>
++      #                               <ownership>
++      #                                       <service type="tv" id="bbc_one" 
key="bbcone">
++      #                                               <title>BBC One</title>
++      #                                       </service>
++      #                               </ownership>
++      #                       </programme>
++      #               </programme>
++      #               <available_until>2014-12-03T07:45:00Z</available_until>
++      #               <actual_start>2014-10-31T11:45:00Z</actual_start>
++      #               
<is_available_mediaset_pc_sd>1</is_available_mediaset_pc_sd>
++      #               <is_legacy_media>0</is_legacy_media>
++      #               <media format="video">
++      #                       <expires>2014-12-03T07:45:00Z</expires>
++      #                       <availability>1 month left to 
watch</availability>
++      #               </media>
++      #       </programme>
++      # </broadcast>  
++
++      # get list of entries within <broadcast> </broadcast> tags
++      my @entries = split /<broadcast[^s]/, $xml;
++      # Discard first element == header
++      shift @entries;
++      main::logger "\nINFO: Got ".($#entries + 1)." programmes for 
$channel\n" if $opt->{verbose};
++      my $now = time();
++      foreach my $entry (@entries) {
++              my ( $title, $name, $episode, $episodetitle, $nametitle, 
$seriestitle, $episodenum, $seriesnum, $desc, $pid, $available, $duration, 
$thumbnail, $version, $guidance, $descshort, $start );
++              $start = $1 if $entry =~ m{<start>\s*(.+?)\s*</start>};
++              next if ! $start;
++              my $xstart = Programme::get_time_string( $start );
++              next if $future &&  $xstart < $now;
++              next if ! $future && $xstart >= $now;
++              next if ! $future && $limit && $xstart < $limit;
++              $available = $1 if $entry =~ 
m{<available_until>\s*(.+?)\s*</available_until>};
++              my $availability = $1 if $entry =~ 
m{<availability>.*?(Available).*?</availability>}i;
++              next if ! ( $available || $availability );
++              if ( $available ) {
++                      my $xavailable = Programme::get_time_string( $available 
);
++                      next if $xavailable < $now;
++              }
++              $pid = $1 if $entry =~ 
m{<programme\s+type="episode">.*?<pid>\s*(.+?)\s*</pid>};
++              $episode = $1 if $entry =~ 
m{<programme\s+type="episode">.*?<title>\s*(.*?)\s*</title>};
++              $nametitle = $1 if $entry =~ 
m{<programme\s+type="brand">.*?<title>\s*(.*?)\s*</title>.*?</programme>};
++              $seriestitle = $1 if $entry =~ 
m{<programme\s+type="series">.*?<title>\s*(.*?)\s*</title>.*?</programme>};
++              # Set name
++              if ( $nametitle && $seriestitle ) {
++                      $name = "$nametitle: $seriestitle";
++              } elsif ( $seriestitle && ! $nametitle ) {
++                      $name = $seriestitle;
++              # Fallback to episode name if the BBC missed out both Series 
and Name
++              } elsif ( ( ! $seriestitle ) && ! $nametitle ) {
++                      $name = $episode;
++              } else {
++                      $name = $nametitle;
++              }
++              # Extract the seriesnum
++              my $regex = 'Series\s+'.main::regex_numbers();
++              $seriesnum = main::convert_words_to_number( $1 ) if 
$seriestitle =~ m{$regex}i;
++              my $series_position = $1 if $entry =~ 
m{<programme\s+type="series">.*?<position>\s*(.+?)\s*</position>};
++              $seriesnum ||= $series_position;
++              # Extract the episode num
++              my $regex_1 = 'Episode\s+'.main::regex_numbers();
++              my $regex_2 = '^'.main::regex_numbers().'\.\s+';
++              if ( $episode =~ m{$regex_1}i ) { 
++                      $episodenum = main::convert_words_to_number( $1 );
++              } elsif ( $episode =~ m{$regex_2}i ) {
++                      $episodenum = main::convert_words_to_number( $1 );
++              }
++              my $episode_position = $1 if $entry =~ 
m{<programme\s+type="episode">.*?<position>\s*(.+?)\s*</position>};
++              $episodenum ||= $episode_position;
++              # extract desc
++              if ( $entry =~ m{<long_synopsis>\s*(.+?)\s*</long_synopsis>} ) {
++                      $desc = $1;
++              } elsif ( $entry =~ 
m{<medium_synopsis>\s*(.+?)\s*</medium_synopsis>} ) {
++                      $desc = $1;
++              } elsif ( $entry =~ 
m{<short_synopsis>\s*(.+?)\s*</short_synopsis>} ) {
++                      $desc = $1;
++              };
++              # Remove unwanted html tags
++              $desc =~ s!</?(br|b|i|p|strong)\s*/?>!!gi;
++              $duration = $1 if $entry =~ m{<duration>\s*(.+?)\s*</duration>};
++              # Extract channel nice name
++              # $channel = $channels{$channel_id};
++              main::logger "DEBUG: '$pid, $name - $episode, $channel'\n" if 
$opt->{debug};
++              # Merge and Skip if this pid is a duplicate
++              if ( defined $prog->{$pid} ) {
++                      main::logger "WARNING: '$pid, $prog->{$pid}->{name} - 
$prog->{$pid}->{episode}, $prog->{$pid}->{channel}' already exists (this 
channel = $channel)\n" if $opt->{verbose};
++                      # Update this info from schedule (not available in the 
usual iplayer channels feeds)
++                      $prog->{$pid}->{duration} = $duration;
++                      $prog->{$pid}->{episodenum} = $episodenum if ! 
$prog->{$pid}->{episodenum};
++                      $prog->{$pid}->{seriesnum} = $seriesnum if ! 
$prog->{$pid}->{seriesnum};
++                      # don't add this as some progs are already available
++                      #$prog->{$pid}->{available} = $available;
++                      next;
++              }
++              $version = 'default';
++              # thumbnail options
++              # 
http://ichef.bbci.co.uk/programmeimages/p01m1x5p/b04l8sml_640_360.jpg
++              # http://ichef.bbci.co.uk/images/ic/640x360/p01m1x5p.jpg
++              # Default to 150px width thumbnail;
++              my $thumbsize = $opt->{thumbsizecache} || 150;
++              my $image_pid = $1 if $entry =~ m{<image><pid>(.*?)</pid>}s;
++              my $suffix = Programme::bbciplayer->thumb_url_suffixes->{ 
$thumbsize };
++              my $thumbnail = 
"http://ichef.bbci.co.uk/programmeimages/${image_pid}/${pid}${suffix}";;
++              # build data structure
++              $prog->{$pid} = main::progclass($prog_type)->new(
++                      'pid'           => $pid,
++                      'name'          => $name,
++                      'versions'      => $version,
++                      'episode'       => $episode,
++                      'seriesnum'     => $seriesnum,
++                      'episodenum'    => $episodenum,
++                      'desc'          => $desc,
++                      'available'     => $start,
++                      'duration'      => $duration,
++                      'thumbnail'     => $thumbnail,
++                      'channel'       => $channel,
++                      'type'          => $prog_type,
++                      'web'           => "${bbc_prog_page_prefix}/${pid}",
++              );
++      }
++}
++
+ 
+ # Usage: download (<prog>, <ua>, <mode>, <version>, <version_pid>)
+ sub download {
+@@ -7886,6 +7940,72 @@
+ # Class vars
+ sub index_min { return 10001 }
+ sub index_max { return 19999 };
++sub channels_aod {
++      return {
++              # national stations
++              '1xtra'                 => 'BBC Radio 1Xtra',
++              'radio1'                => 'BBC Radio 1',
++              'radio2'                => 'BBC Radio 2',
++              'radio3'                => 'BBC Radio 3',
++              'radio4'                => 'BBC Radio 4',
++              'radio4extra'   => 'BBC Radio 4 Extra',
++              'fivelive'              => 'BBC Radio 5 live',
++              'sportsextra'   => 'BBC 5 live sports extra',
++              '6music'                => 'BBC 6 Music',
++              'asiannetwork'  => 'BBC Asian Network',
++              # nations
++              'radiofoyle'    => 'BBC Radio Foyle',
++              'radioscotland' => 'BBC Radio Scotland',
++              'alba'                  => 'BBC Radio Nan Gaidheal',
++              'radioulster'   => 'BBC Radio Ulster',
++              'radiowales'    => 'BBC Radio Wales',
++              'radiocymru'    => 'BBC Radio Cymru',
++              'worldservice'  => 'BBC World Service',
++              # local
++              'bbc_radio_cumbria'                     => 'BBC Radio Cumbria',
++              'bbc_radio_newcastle'                   => 'BBC Newcastle',
++              'bbc_tees'                              => 'BBC Tees',
++              'bbc_radio_lancashire'                  => 'BBC Radio 
Lancashire',
++              'bbc_radio_merseyside'                  => 'BBC Radio 
Merseyside',
++              'bbc_radio_manchester'                  => 'BBC Radio 
Manchester',
++              'bbc_radio_leeds'                       => 'BBC Radio Leeds',
++              'bbc_radio_sheffield'                   => 'BBC Radio 
Sheffield',
++              'bbc_radio_york'                        => 'BBC Radio York',
++              'bbc_radio_humberside'                  => 'BBC Radio 
Humberside',
++              'bbc_radio_lincolnshire'                => 'BBC Radio 
Lincolnshire',
++              'bbc_radio_nottingham'                  => 'BBC Radio 
Nottingham',
++              'bbc_radio_leicester'                   => 'BBC Radio 
Leicester',
++              'bbc_radio_derby'                       => 'BBC Radio Derby',
++              'bbc_radio_stoke'                       => 'BBC Radio Stoke',
++              'bbc_radio_shropshire'                  => 'BBC Radio 
Shropshire',
++              'bbc_wm'                                => 'BBC WM 95.6',
++              'bbc_radio_coventry_warwickshire'       => 'BBC Coventry & 
Warwickshire',
++              'bbc_radio_hereford_worcester'          => 'BBC Hereford & 
Worcester',
++              'bbc_radio_northampton'                 => 'BBC Radio 
Northampton',
++              'bbc_three_counties_radio'              => 'BBC Three Counties 
Radio',
++              'bbc_radio_cambridge'                   => 'BBC Radio 
Cambridgeshire',
++              'bbc_radio_norfolk'                     => 'BBC Radio Norfolk',
++              'bbc_radio_suffolk'                     => 'BBC Radio Suffolk',
++              'bbc_radio_essex'                       => 'BBC Essex',
++              'bbc_london'                            => 'BBC London 94.9',
++              'bbc_radio_kent'                        => 'BBC Radio Kent',
++              'bbc_radio_surrey'                      => 'BBC Surrey',
++              'bbc_radio_sussex'                      => 'BBC Sussex',
++              'bbc_radio_oxford'                      => 'BBC Radio Oxford',
++              'bbc_radio_berkshire'                   => 'BBC Radio 
Berkshire',
++              'bbc_radio_solent'                      => 'BBC Radio Solent',
++              'bbc_radio_gloucestershire'             => 'BBC Radio 
Gloucestershire',
++              'bbc_radio_wiltshire'                   => 'BBC Wiltshire',
++              'bbc_radio_bristol'                     => 'BBC Radio Bristol',
++              'bbc_radio_somerset_sound'              => 'BBC Somerset',
++              'bbc_radio_devon'                       => 'BBC Radio Devon',
++              'bbc_radio_cornwall'                    => 'BBC Radio Cornwall',
++              'bbc_radio_guernsey'                    => 'BBC Radio Guernsey',
++              'bbc_radio_jersey'                      => 'BBC Radio Jersey',
++              'bbc_radio_jersey'                      => 'BBC Radio Jersey',
++      };
++}
++
+ sub channels {
+       return {
+               'bbc_1xtra'                             => 'BBC Radio 1Xtra',
+@@ -7897,7 +8017,7 @@
+               'bbc_radio_five_live'                   => 'BBC Radio 5 live',
+               'bbc_radio_five_live_sports_extra'      => 'BBC 5 live sports 
extra',
+               'bbc_6music'                            => 'BBC 6 Music',
+-              'bbc_7'                                 => 'BBC 7',
++              #'bbc_7'                                        => 'BBC 7',
+               'bbc_asian_network'                     => 'BBC Asian Network',
+               'bbc_radio_foyle'                       => 'BBC Radio Foyle',
+               'bbc_radio_scotland'                    => 'BBC Radio Scotland',
+@@ -7946,8 +8066,6 @@
+               'bbc_radio_cornwall'                    => 'BBC Radio Cornwall',
+               'bbc_radio_guernsey'                    => 'BBC Radio Guernsey',
+               'bbc_radio_jersey'                      => 'BBC Radio Jersey',
+-              'popular/radio'                         => 'Popular',
+-              'highlights/radio'                      => 'Highlights',
+       };
+ }
+ 
+--- get-iplayer-2.87.orig/get_iplayer.1
++++ get-iplayer-2.87/get_iplayer.1
+@@ -508,12 +508,30 @@
+ \fB\-\-refresh\-exclude <string>
+ Exclude matched channel(s) when refreshing cache (regex or comma separated 
values)
+ .TP
++\fB\-\-refresh\-feeds <string>
++Alternate source for programme data.  Valid values: 'schedule'
++.TP
++\fB\-\-refresh\-feeds\-radio <string>
++Alternate source for radio programme data.  Valid values: 'schedule'
++.TP
++\fB\-\-refresh\-feeds\-tv <string>
++Alternate source for TV programme data.  Valid values: 'schedule'
++.TP
+ \fB\-\-refresh\-future
+ Obtain future programme schedule when refreshing cache (between 7\-14 days)
+ .TP
+ \fB\-\-refresh\-include <string>
+ Include matched channel(s) when refreshing cache (regex or comma separated 
values)
+ .TP
++\fB\-\-refresh\-limit <integer>
++Number of days of programmes to cache. Only applied with 
\-\-refresh\-feeds=schedule. Makes cache updates VERY slow. Default: 7 Min: 1 
Max: 30
++.TP
++\fB\-\-refresh\-limit\-radio <integer>
++Number of days of radio programmes to cache. Only applied with 
\-\-refresh\-feeds=schedule. Makes cache updates VERY slow. Default: 7 Min: 1 
Max: 30
++.TP
++\fB\-\-refresh\-limit\-tv <integer>
++Number of days of TV programmes to cache. Only applied with 
\-\-refresh\-feeds=schedule. Makes cache updates VERY slow. Default: 7 Min: 1 
Max: 30
++.TP
+ \fB\-\-skipdeleted
+ Skip the download of metadata/thumbs/subs if the media file no longer exists. 
Use with \-\-history & \-\-metadataonly/subsonly/thumbonly.
+ .TP
diff -Nru get-iplayer-2.87/debian/patches/data-feeds.patch 
get-iplayer-2.87/debian/patches/data-feeds.patch
--- get-iplayer-2.87/debian/patches/data-feeds.patch    1970-01-01 
01:00:00.000000000 +0100
+++ get-iplayer-2.87/debian/patches/data-feeds.patch    2014-12-04 
23:22:09.000000000 +0000
@@ -0,0 +1,221 @@
+From: dinkypumpkin <dinkypump...@gmail.com>
+Date: Fri, 31 Oct 2014 12:40:29 +0000
+Subject: Fixes for metadata problems due to BBC removing data feeds
+Origin: 
http://git.infradead.org/get_iplayer.git/commit/3b11bf9baffd66e17d18116eb546566f84ae7927
+Last-Update: 2014-12-03
+
+--- get-iplayer-2.87.orig/get_iplayer
++++ get-iplayer-2.87/get_iplayer
+@@ -5421,28 +5421,26 @@
+       # flatten
+       $xml =~ s/\n/ /g;
+ 
++      # set title here - broken in JSON playlists
++      $prog->{title} = decode_entities($1) if $xml =~ 
m{<title>\s*(.+?)\s*<\/title>};
++      $prog->{thumbnail} = $1 if $xml =~ m{<link rel="holding" href="(.*?)"};
++      $prog->{guidance} = $1 if $xml =~ m{<guidance.*?>(.*?)</guidance>};
++      $prog->{descshort} = $1 if $xml =~ m{<summary>(.*?)</summary>};
++      $prog->{type} = 'tv' if grep /kind="programme"/, $xml;
++      $prog->{type} = 'radio' if grep /kind="radioProgramme"/, $xml;
++
+       # Detect noItems or no programmes
+       if ( $xml =~ m{<noItems\s+reason="(\w+)"} || $xml !~ 
m{kind="(programme|radioProgramme)"} ) {
+-              # set title here - broken in JSON playlists
+-              $prog->{title} = decode_entities($1) if $xml =~ 
m{<title>\s*(.+?)\s*<\/title>};
+-              my $rc_verpids = $prog->get_verpids_json( $ua );
+-              if ( $rc_verpids ) {
+-                      $rc_verpids = $prog->get_verpids_html( $ua )
++              my $rc_json = $prog->get_verpids_json( $ua );
++              my $rc_html = 1;
++              if ( ! $prog->{type} || $prog->{type} eq 'tv' ) {
++                      $rc_html = $prog->get_verpids_html( $ua );
+               }
+-              return 0 if ! $rc_verpids;
+-              main::logger "\rWARNING: No programmes are available for this 
pid with version(s): ".($opt->{versionlist} ? $opt->{versionlist} : 
'default').($prog->{versions} ? " (available versions: $prog->{versions})\n" : 
"\n");
++              return 0 if ! $rc_json || ! $rc_html;
++              main::logger "\nWARNING: No programmes are available for this 
pid with version(s): ".($opt->{versionlist} ? $opt->{versionlist} : 
'default').($prog->{versions} ? " (available versions: $prog->{versions})\n" : 
"\n");
+               return 1;
+       }
+ 
+-      # Get title
+-      # <title>Amazon with Bruce Parry: Episode 1</title>
+-      my ( $title, $prog_type );
+-      $title = $1 if $xml =~ m{<title>\s*(.+?)\s*<\/title>};
+-
+-      # Get type
+-      $prog_type = 'tv' if grep /kind="programme"/, $xml;
+-      $prog_type = 'radio' if grep /kind="radioProgramme"/, $xml;
+-
+       # Split into <item kind="programme"> sections
+       my $prev_version = '';
+       for ( split /<item\s+kind="(radioProgramme|programme)"/, $xml ) {
+@@ -5530,7 +5528,6 @@
+ 
+       # Add to prog hash
+       $prog->{versions} = join ',', keys %{ $prog->{verpids} };
+-      $prog->{title} = decode_entities($title);
+       return 0;
+ }
+ 
+@@ -5556,30 +5553,37 @@
+               return 1;
+       }
+       my ( $default, $versions ) = split /"allAvailableVersions"/, $json;
+-      my $channel = $1 if $default =~ /"masterBrandName":"(.*?)"/;
+-      $prog->{channel} = $channel if $channel;
+-      my $descshort = $1 if $default =~ /"summary":"(.*?)"/;
+-      $prog->{descshort} = $descshort if $descshort;
+-      my $guidance = $2 if $default =~ /"guidance":(null|"(.*?)")/;
+-      $prog->{guidance} = "Yes" if $guidance;
+-      my $thumbnail = $1 if $default =~ /"holdingImageURL":"(.*?)"/;
+-      $thumbnail =~ s/\\\//\//g;
+-      my $thumbsize = $opt->{thumbsize} || $opt->{thumbsizecache} || 150;
+-      my $recipe = Programme::bbciplayer->thumb_url_recipes->{ $thumbsize };
+-      if ( ! $recipe ) {
+-              main::logger "WARNING: Invalid thumbnail size: $thumbsize - 
using default (JSON)\n";
+-              $recipe = Programme::bbciplayer->thumb_url_recipes->{ 150 };
+-      }
+-      $thumbnail =~ s/\$recipe/$recipe/;
+-      $prog->{thumbnail} = $thumbnail if $thumbnail;
+-      if ( ! $prog->{title} ) {
++      unless ( $prog->{channel} ) {
++              $prog->{channel} = $1 if $default =~ 
/"masterBrandName":"(.*?)"/;
++      }
++      unless ( $prog->{descshort} ) {
++              $prog->{descshort} = $1 if $default =~ /"summary":"(.*?)"/;
++      }
++      unless ( $prog->{guidance} ) {
++              my $guidance = $2 if $default =~ /"guidance":(null|"(.*?)")/;
++              $prog->{guidance} = "Yes" if $guidance;
++      }
++      unless ( $prog->{thumbnail} ) {
++              my $thumbnail = $1 if $default =~ /"holdingImageURL":"(.*?)"/;
++              $thumbnail =~ s/\\\//\//g;
++              my $thumbsize = $opt->{thumbsize} || $opt->{thumbsizecache} || 
150;
++              my $recipe = Programme::bbciplayer->thumb_url_recipes->{ 
$thumbsize };
++              if ( ! $recipe ) {
++                      main::logger "WARNING: Invalid thumbnail size: 
$thumbsize - using default (JSON)\n";
++                      $recipe = Programme::bbciplayer->thumb_url_recipes->{ 
150 };
++              }
++              $thumbnail =~ s/\$recipe/$recipe/;
++              $prog->{thumbnail} = $thumbnail if $thumbnail;
++      }
++      unless ( $prog->{title} ) {
+               my $title = $1 if $default =~ /"title":"(.*?)"/;
+               $title =~ s/\\\//\//g;
+               $prog->{title} = decode_entities($title) if $title;
+       }
+-      my $prog_type = 'tv' if $default =~ /"kind":"video"/;
+-      $prog_type = 'radio' if $default =~ /"kind":"audio"/;
+-      $prog->{type} = $prog_type if $prog_type;
++      unless ( $prog->{type} ) {
++              $prog->{type} = 'tv' if $default =~ /"kind":"video"/;
++              $prog->{type} = 'radio' if $default =~ /"kind":"audio"/;
++      }
+       my $version_json = {
+               'DubbedAudioDescribed' => 'audiodescribed', 
+               'Signed' => 'signed'
+@@ -5609,6 +5613,7 @@
+                       }
+                       unless ( $prog->{player} ) {
+                               $prog->{player} = $episode_url if $episode_url;
++                              last;
+                       }
+               }
+       }
+@@ -5636,8 +5641,10 @@
+               main::logger "INFO: pid changed from $prog->{pid} to $pid 
(HTML)\n" if $opt->{verbose};
+               $prog->{pid} = $pid;
+       }
++      my $version_list = $opt->{versionlist} || 'default';
+       my $version_map = { "default" => "", "audiodescribed" => "ad", "signed" 
=> "sign"};
+       for my $version ( "default", "audiodescribed", "signed" ) {
++              next if $version_list !~ /$version/ || 
$prog->{verpids}->{$version};
+               my $url = 
'http://www.bbc.co.uk/iplayer/episode/'.$pid."/$version_map->{$version}";
+               main::logger "INFO: iPlayer metadata URL (HTML) [$version] = 
$url\n" if $opt->{verbose};
+               my $html = main::request_url_retry( $ua, $url, 3 );
+@@ -5653,12 +5660,10 @@
+                       next;
+               }
+               unless ( $prog->{channel} ) {
+-                      my $channel = $1 if $config =~ 
/"masterBrandTitle":"(.*?)"/;
+-                      $prog->{channel} = $channel if $channel;
++                      $prog->{channel} = $1 if $config =~ 
/"masterBrandTitle":"(.*?)"/;
+               }
+               unless ( $prog->{descshort} ) {
+-                      my $descshort = $1 if $config =~ /"summary":"(.*?)"/;
+-                      $prog->{descshort} = $descshort if $descshort;
++                      $prog->{descshort} = $1 if $config =~ 
/"summary":"(.*?)"/;
+               }
+               unless ( $prog->{guidance} ) {
+                       my $guidance = $2 if $config =~ 
/"guidance":(null|"(.*?)")/;
+@@ -5691,7 +5696,6 @@
+               $prog->{durations}->{$version} = $1 if $config =~ 
/"duration":(\d+)/;
+       }
+       $prog->{versions} = join ',', keys %{ $prog->{verpids} };
+-      my $version_list = $opt->{versionlist} || 'default';
+       for ( split /,/, $version_list ) {
+               if ( $prog->{verpids}->{$_} ) {
+                       my $episode_url;
+@@ -5702,6 +5706,7 @@
+                       }
+                       unless ( $prog->{player} ) {
+                               $prog->{player} = $episode_url if $episode_url;
++                              last;
+                       }
+               }
+       }
+@@ -5806,7 +5811,8 @@
+       #</feed>
+ 
+       # Don't get metadata from this URL if the pid contains a full url 
(problem: this still tries for BBC iPlayer live channels)
+-      if ( $prog->{pid} !~ m{^http}i ) {
++      # skip this section since BBC killed feeds, but keep code for reference
++      if ( $prog->{pid} !~ m{^http}i && 0) {
+               $entry = main::request_url_retry($ua, 
$prog_feed_url.$prog->{pid}, 3, '', '');
+               decode_entities($entry);
+               main::logger "DEBUG: $prog_feed_url.$prog->{pid}:\n$entry\n\n" 
if $opt->{debug};
+@@ -5870,6 +5876,21 @@
+               }
+       }
+ 
++      # Get list of available modes for each version available
++      # populate version pid metadata if we don't have it already
++      if ( keys %{ $prog->{verpids} } == 0 ) {
++              if ( $prog->get_verpids( $ua ) ) {
++                      main::logger "ERROR: Could not get version pid 
metadata\n" if $opt->{verbose};
++                      # Only return at this stage unless we want 
metadata/tags only for various reasons
++                      return 1 if ! ( $opt->{info} || $opt->{metadataonly} || 
$opt->{thumbonly} || $opt->{tagonly} )
++              }
++      }
++      # Re-split title if changed in get_verpids()
++      #if ( $prog->{title} && $prog->{title} ne $title ) {
++      #       $title = $prog->{title};
++      #       ( $name, $episode ) = Programme::bbciplayer::split_title( 
$title );
++      #}
++      $versions = join ',', sort keys %{ $prog->{verpids} };
+ 
+       # Even more info...
+       #<?xml version="1.0" encoding="utf-8"?>                                 
 
+@@ -5957,21 +5978,6 @@
+       $categories ||= "get_iplayer";
+       $category ||= "get_iplayer";
+ 
+-      # Get list of available modes for each version available
+-      # populate version pid metadata if we don't have it already
+-      if ( keys %{ $prog->{verpids} } == 0 ) {
+-              if ( $prog->get_verpids( $ua ) ) {
+-                      main::logger "ERROR: Could not get version pid 
metadata\n" if $opt->{verbose};
+-                      # Only return at this stage unless we want 
metadata/tags only for various reasons
+-                      return 1 if ! ( $opt->{info} || $opt->{metadataonly} || 
$opt->{thumbonly} || $opt->{tagonly} )
+-              }
+-      }
+-      # Re-split title if changed in get_verpids()
+-      if ( $prog->{title} && $prog->{title} ne $title ) {
+-              $title = $prog->{title};
+-              ( $name, $episode ) = Programme::bbciplayer::split_title( 
$title );
+-      }
+-      $versions = join ',', sort keys %{ $prog->{verpids} };
+       my $modes;
+       my $mode_sizes;
+       my $first_broadcast;
diff -Nru get-iplayer-2.87/debian/patches/future-schedule.patch 
get-iplayer-2.87/debian/patches/future-schedule.patch
--- get-iplayer-2.87/debian/patches/future-schedule.patch       1970-01-01 
01:00:00.000000000 +0100
+++ get-iplayer-2.87/debian/patches/future-schedule.patch       2014-12-04 
23:22:09.000000000 +0000
@@ -0,0 +1,81 @@
+From: dinkypumpkin <dinkypump...@gmail.com>
+Date: Sun, 2 Nov 2014 21:24:12 +0000
+Subject: Restored future schedule refresh
+Origin: 
http://git.infradead.org/get_iplayer.git/commitdiff/cad12b98c56d796c3e73210eba5f348b08385d3a?hp=9ddffb88f05dd15c6e9192cd8df04a71d82a6a67
+Last-Update: 2014-12-03
+
+--- get-iplayer-2.87.orig/get_iplayer
++++ get-iplayer-2.87/get_iplayer
+@@ -7410,15 +7410,15 @@
+       my $prog = shift;
+       my $prog_type = shift;
+       my $feeds = lc($opt->{"refreshfeeds".${prog_type}} || 
$opt->{"refreshfeeds"});
+-      main::logger "INFO: Getting $prog_type Index Feeds (this may take a few 
minutes)\n";
++      main::logger "\nINFO: Getting $prog_type Index Feeds (this may take a 
few minutes)\n";
+       if ( $feeds eq 'schedule' ) {
+-              return $self->get_links_schedule($prog, $prog_type, 0);
++              $self->get_links_schedule($prog, $prog_type, 0);
+       } else {
+               if ( $prog_type eq 'radio' ) {
+-                      return $self->get_links_aod($prog, $prog_type);
++                      $self->get_links_aod($prog, $prog_type);
+               }
+               elsif ( $prog_type eq 'tv' ) {
+-                      return $self->get_links_ion($prog, $prog_type);
++                      $self->get_links_ion($prog, $prog_type);
+               }
+       }
+ 
+@@ -7561,20 +7561,12 @@
+       main::logger "\nINFO: Got ".($#entries + 1)." programmes for 
$channel\n" if $opt->{verbose};
+       my $now = time();
+       foreach my $entry (@entries) {
+-              my ( $title, $name, $episode, $episodetitle, $nametitle, 
$seriestitle, $episodenum, $seriesnum, $desc, $pid, $available, $duration, 
$thumbnail, $version, $guidance, $descshort, $start );
+-              $start = $1 if $entry =~ m{<start>\s*(.+?)\s*</start>};
+-              next if ! $start;
+-              my $xstart = Programme::get_time_string( $start );
+-              next if $future &&  $xstart < $now;
+-              next if ! $future && $xstart >= $now;
+-              next if ! $future && $limit && $xstart < $limit;
+-              $available = $1 if $entry =~ 
m{<available_until>\s*(.+?)\s*</available_until>};
+-              my $availability = $1 if $entry =~ 
m{<availability>.*?(Available).*?</availability>}i;
+-              next if ! ( $available || $availability );
+-              if ( $available ) {
+-                      my $xavailable = Programme::get_time_string( $available 
);
+-                      next if $xavailable < $now;
+-              }
++              my ( $title, $name, $episode, $episodetitle, $nametitle, 
$seriestitle, $episodenum, $seriesnum, $desc, $pid, $available, $duration, 
$thumbnail, $version, $guidance, $descshort );
++              # Don't create this prog instance if the availablity is in the 
past 
++              # this prevents programmes which never appear in iPlayer from 
being indexed
++              $available = $1 if $entry =~ m{<start>\s*(.+?)\s*</start>};
++              next if $future && Programme::get_time_string( $available ) < 
$now;
++              next if ! $future && $limit && Programme::get_time_string( 
$available ) < $limit;
+               $pid = $1 if $entry =~ 
m{<programme\s+type="episode">.*?<pid>\s*(.+?)\s*</pid>};
+               $episode = $1 if $entry =~ 
m{<programme\s+type="episode">.*?<title>\s*(.*?)\s*</title>};
+               $nametitle = $1 if $entry =~ 
m{<programme\s+type="brand">.*?<title>\s*(.*?)\s*</title>.*?</programme>};
+@@ -7639,6 +7631,7 @@
+               my $image_pid = $1 if $entry =~ m{<image><pid>(.*?)</pid>}s;
+               my $suffix = Programme::bbciplayer->thumb_url_suffixes->{ 
$thumbsize };
+               my $thumbnail = 
"http://ichef.bbci.co.uk/programmeimages/${image_pid}/${pid}${suffix}";;
++
+               # build data structure
+               $prog->{$pid} = main::progclass($prog_type)->new(
+                       'pid'           => $pid,
+@@ -7648,7 +7641,7 @@
+                       'seriesnum'     => $seriesnum,
+                       'episodenum'    => $episodenum,
+                       'desc'          => $desc,
+-                      'available'     => $start,
++                      'available'     => $available,
+                       'duration'      => $duration,
+                       'thumbnail'     => $thumbnail,
+                       'channel'       => $channel,
+@@ -7939,7 +7932,7 @@
+ 
+ # Class vars
+ sub index_min { return 10001 }
+-sub index_max { return 19999 };
++sub index_max { return 39999 };
+ sub channels_aod {
+       return {
+               # national stations
diff -Nru get-iplayer-2.87/debian/patches/live-tv.patch 
get-iplayer-2.87/debian/patches/live-tv.patch
--- get-iplayer-2.87/debian/patches/live-tv.patch       1970-01-01 
01:00:00.000000000 +0100
+++ get-iplayer-2.87/debian/patches/live-tv.patch       2014-12-04 
23:22:09.000000000 +0000
@@ -0,0 +1,18 @@
+From: dinkypumpkin <dinkypump...@gmail.com>
+Date: Sat, 25 Oct 2014 15:48:54 +0000 (+0100)
+Subject: Fixed live TV streaming
+Origin: 
http://git.infradead.org/get_iplayer.git/commitdiff_plain/754b2007a18c75d4aca96d153c4df65606676c09?hp=b7e729ccdc7261d6270b26c054e79876decd6ecb
+Last-Update: 2014-12-03
+
+--- get-iplayer-2.87.orig/get_iplayer
++++ get-iplayer-2.87/get_iplayer
+@@ -5454,7 +5454,8 @@
+               if ( m{\s+simulcast="true"} ) {
+                       $version = 'default';
+                       # <item kind="programme" live="true" liverewind="true" 
identifier="bbc_two_england" group="bbc_two_england" simulcast="true" 
availability_class="liverewind">
+-                      $verpid = 
"http://www.bbc.co.uk/emp/simulcast/".$2.".xml"; if 
m{\s+live="true"\s+(liverewind="true"\s+)?identifier="(.+?)"};
++                      # $verpid = 
"http://www.bbc.co.uk/emp/simulcast/".$2.".xml"; if 
m{\s+live="true"\s+(liverewind="true"\s+)?identifier="(.+?)"};
++                      $verpid = $2 if 
m{\s+live="true"\s+(liverewind="true"\s+)?identifier="(.+?)"};
+                       main::logger "INFO: Using Live TV: $verpid\n" if 
$opt->{verbose} && $verpid;
+ 
+               # Live/Non-live EMP tv/radio XML URL
diff -Nru get-iplayer-2.87/debian/patches/series 
get-iplayer-2.87/debian/patches/series
--- get-iplayer-2.87/debian/patches/series      2014-10-20 20:39:26.000000000 
+0100
+++ get-iplayer-2.87/debian/patches/series      2014-12-04 23:22:09.000000000 
+0000
@@ -0,0 +1,4 @@
+live-tv.patch
+data-feeds.patch
+cache-search.patch
+future-schedule.patch

Attachment: signature.asc
Description: Digital signature

Reply via email to