diff options
Diffstat (limited to 'abs/extra/mythvodka/mythvodka.diff')
-rw-r--r-- | abs/extra/mythvodka/mythvodka.diff | 3933 |
1 files changed, 0 insertions, 3933 deletions
diff --git a/abs/extra/mythvodka/mythvodka.diff b/abs/extra/mythvodka/mythvodka.diff deleted file mode 100644 index 5735a42..0000000 --- a/abs/extra/mythvodka/mythvodka.diff +++ /dev/null @@ -1,3933 +0,0 @@ -diff -ruaN mythvodka.orig/mythvodka/streamsui.cpp mythvodka/mythvodka/streamsui.cpp ---- mythvodka.orig/mythvodka/streamsui.cpp 2009-01-06 00:18:35.000000000 +0000 -+++ mythvodka/mythvodka/streamsui.cpp 2009-02-12 07:28:31.000000000 +0000 -@@ -646,7 +646,7 @@ - - MythProgressDialog *buffer_progress; - buffer_progress = new MythProgressDialog( -- QObject::tr("If she kicks you in the balls, you have the ability and the right..."), bufferSize, true, this, SLOT(cancelPressed())); -+ QObject::tr("Buffering... Just a moment please."), bufferSize, true, this, SLOT(cancelPressed())); - - QFile file(filename); - -@@ -729,7 +729,7 @@ - - MythProgressDialog *buffer_progress; - buffer_progress = new MythProgressDialog( -- QObject::tr("Nascar Sucks / Hillary For President / Man Love Rules Ok"), bufferSize, true, this, SLOT(cancelPressed())); -+ QObject::tr("Your video is being loaded..."), bufferSize, true, this, SLOT(cancelPressed())); - - QFile file(filename); - -@@ -822,7 +822,7 @@ - - MythProgressDialog *buffer_progress; - buffer_progress = new MythProgressDialog( -- QObject::tr("RING RING... I FUCKED YOUR GRANDDAUGHTER"), bufferSizemov, true, this, SLOT(cancelPressed())); -+ QObject::tr("We seem to have hit some sort of problem..."), bufferSizemov, true, this, SLOT(cancelPressed())); - - QFile filemov(filenamemov); - -diff -ruaN mythvodka.orig/scripts/get_iplayer mythvodka/scripts/get_iplayer ---- mythvodka.orig/scripts/get_iplayer 2009-01-06 19:11:24.000000000 +0000 -+++ mythvodka/scripts/get_iplayer 2009-02-12 07:27:44.000000000 +0000 -@@ -3,16 +3,17 @@ - # get_iplayer - # - # Lists and downloads BBC iPlayer audio and video streams --# -+# + Downloads ITVplayer Catch-Up video streams -+# - # Author: Phil Lewis - # Email: iplayer (at sign) linuxcentre.net - # Web: http://linuxcentre.net/iplayer - # License: GPLv3 (see LICENSE.txt) - # - # Other credits: --# RTMP additions: Andrej Stepanchuk -+# RTMP additions: Andrej Stepanchuk - # --my $version = 1.04; -+my $version = 1.17; - # - # Help: - # ./get_iplayer --help -@@ -29,13 +30,16 @@ - # * Index/Download live radio streams w/schedule feeds to assist timing - # * Podcasts for 'local' stations are missing (only a handful). They use a number of different station ids which will involve reading html to determine rss feed. - # * Remove all rtsp/mplayer/lame/tee dross when realaudio streams become obselete (not quite yet) --# * Cope with radio via rtmp - # * Stdout mode with rtmp --# -+# * Do subtitle downloading after programme download so that rtmp auth doesn't timeout -+ - # Known Issues: - # * In ActivePerl/windows downloaded iPhone video files do not get renamed (remain with .partial.mov) - # * vlc does not quit after downloading an rtsp N95 video stream (ctrl-c is required) - need a --play-and-quit option if such a thing exists --# * flv conversions from rtmp downloads aren't quite right yet. A/V sync issues? -+# * rtmpdump (v1.2) of flashaudio fails at end of stream => non-zero exit code -+# * if ffmpeg trys to convert flv to mp3 it succeeds but => non-zero exit code -+# * Some rtmpdump downloads always give a non-zero exit code regardless of success (using a min-filesize workaround for now) -+# * resuming a flashaudio download fails - - use Env qw[@PATH]; - use Fcntl; -@@ -62,10 +66,17 @@ - my %opt_cmdline = (); # a hash of which options came from the cmdline rather than the options files - my %opt_file = (); # a hash of which options came from the options files rather than the cmdline - --# Print to STDERR if not quiet unless verbose or debug -+# Print to STDERR/STDOUT if not quiet unless verbose or debug - sub logger(@) { - # Make sure quiet can be overridden by verbose and debug options -- print STDERR $_[0] if (! $opt{quiet}) || $opt{verbose} || $opt{debug}; -+ if ( $opt{verbose} || $opt{debug} || ! $opt{quiet} ) { -+ # Only send messages to STDERR if pvr or stdout options are being used. -+ if ( $opt{stdout} || $opt{pvr} || $opt{stderr} ) { -+ print STDERR $_[0]; -+ } else { -+ print STDOUT $_[0]; -+ } -+ } - } - - sub usage { -@@ -74,7 +85,7 @@ - Search Programmes: get_iplayer [<search options>] [<regex|index|pid|pidurl> ...] - Download files: get_iplayer --get [<search options>] <regex|index|pid|pidurl> ... - get_iplayer --pid <pid|pidurl> [<options>] --Stream Downloads: get_iplayer --stdout [<options>] <regex|index|pid|pidurl> | mplayer -cache 2048 - -+Stream Downloads: get_iplayer --stdout [<options>] <regex|index|pid|pidurl> | mplayer -cache 3072 - - Update get_iplayer: get_iplayer --update - - Search Options: -@@ -83,9 +94,10 @@ - --channel <regex> Narrow search to matched channel(s) - --category <regex> Narrow search to matched categories - --versions <regex> Narrow search to matched programme version(s) -+ --exclude <regex> Narrow search to exclude matched programme names - --exclude-channel <regex> Narrow search to exclude matched channel(s) - --exclude-category <regex> Narrow search to exclude matched catogories -- --type <radio|tv|podcast|all> Only search in these types of programmes (tv is default) -+ --type <type> Only search in these types of programmes: radio, tv, podcast, all, itv (tv is default) - --since <hours> Limit search to programmes added to the cache in the last N hours - - Display Options: -@@ -95,26 +107,25 @@ - -i, --info Show full programme metadata (only if number of matches < 50) - --list <categories|channel> Show a list of available categories/channels for the selected type and exit - --hide Hide previously downloaded programmes -+ --streaminfo Returns all of the media stream urls of the programme(s) - - Download Options: - -g, --get Download matching programmes - -x, --stdout Additionally stream to STDOUT (so you can pipe output to a player) - -p, --proxy <url> Web proxy URL spec - --partial-proxy Works around for some broken web proxies (try this extra option if your proxy fails) -- --pid <pid|url> Download an arbitrary pid that does not appear in the index -+ --pid <pid|url> Download an arbitrary pid that does not appear in the index (itv:<pid> for itv programmes) - --force-download Ignore download history (unsets --hide option also) -- --realaudio Use the RealAudio radio stream and not the MP3 stream -- --mp3audio Use the MP3 radio stream for radio and dont fallback to the RealAudio stream -+ --amode <mode>,<mode>,... Audio Download mode(s): iphone,flashaudio,realaudio (default: iphone,flashaudio,realaudio) -+ --vmode <mode>,<mode>,... Video Download mode(s): iphone,rtmp,flashhigh,flashnormal,flashwii,n95_wifi (default: iphone,flashhigh,flashnormal) - --wav In radio realaudio mode output as wav and don't transcode to mp3 -- --raw In radio/realaudio or iPhone/video mode don't transcode or change the downloaded stream in any way -- --n95 In TV mode download/stream low quality Nokia N95 H.264 stream (alpha) -- --rtmp In TV mode download/stream high quality flash stream (alpha) -+ --raw Don't transcode or change the downloaded stream in any way (i.e. radio/realaudio, rtmp/flv, iphone/mov) - --bandwidth In radio realaudio mode specify the link bandwidth in bps for rtsp streaming (default 512000) - --subtitles In TV mode, download subtitles into srt/SubRip format if available - --suboffset <offset> Offset the subtitle timestamps by the specified number of milliseconds - --version-list <versions> Override the version of programme to download (e.g. '--version-list signed,default') - -t, --test Test only - no download (will show programme type) -- -+ - PVR Options: - --pvr Runs the PVR download using all saved PVR searches (intended to be run every hour from cron etc) - --pvradd <search name> Add the current search terms to the named PVR search -@@ -139,18 +150,18 @@ - -f, --flush, --refresh Refresh cache - -e, --expiry <secs> Cache expiry in seconds (default 4hrs) - --symlink <file> Create symlink to <file> once we have the header of the download -- --fxd <file> Create Freevo FXD XML in specified file -- --mythtv <file> Create Mythtv streams XML in specified file -+ --fxd <file> Create Freevo FXD XML of matching programmes in specified file -+ --mythtv <file> Create Mythtv streams XML of matching programmes in specified file - --xml-channels Create freevo/Mythtv menu of channels -> programme names -> episodes - --xml-names Create freevo/Mythtv menu of programme names -> episodes - --xml-alpha Create freevo/Mythtv menu sorted alphabetically by programme name -- --html <file> Create basic HTML index of programmes in specified file -+ --html <file> Create basic HTML index of matching programmes in specified file - --mplayer <path> Location of mplayer binary -+ --ffmpeg <path> Location of ffmpeg binary - --lame <path> Location of lame binary - --id3v2 <path> Location of id3v2 binary - --rtmpdump <path> Location of rtmpdump binary -- --vlc <path> Location of vlc binary -- --streaminfo Returns all of the media stream urls of the programme(s) -+ --vlc <path> Location of vlc or cvlc binary - -v, --verbose Verbose - -u, --update Update get_iplayer if a newer one exists - -h, --help Help -@@ -192,14 +203,17 @@ - Getopt::Long::Configure ("bundling"); - # cmdline opts take precedence - GetOptions( -+ "amode=s" => \$opt_cmdline{amode}, - "bandwidth=n" => \$opt_cmdline{bandwidth}, - "category=s" => \$opt_cmdline{category}, - "channel=s" => \$opt_cmdline{channel}, - "c|command=s" => \$opt_cmdline{command}, - "debug" => \$opt_cmdline{debug}, -+ "exclude=s" => \$opt_cmdline{exclude}, - "exclude-category=s" => \$opt_cmdline{excludecategory}, - "exclude-channel=s" => \$opt_cmdline{excludechannel}, - "expiry|e=n" => \$opt_cmdline{expiry}, -+ "ffmpeg=s" => \$opt_cmdline{ffmpeg}, - "file-prefix|fileprefix=s" => \$opt_cmdline{fileprefix}, - "flush|refresh|f" => \$opt_cmdline{flush}, - "force-download" => \$opt_cmdline{forcedownload}, -@@ -238,7 +252,7 @@ - "rtmpdump=s" => \$opt_cmdline{rtmpdump}, - "save" => \$save, - "since=n" => \$opt_cmdline{since}, -- "stdout|stream|x" => \$opt_cmdline{stdout}, -+ "stdout|x" => \$opt_cmdline{stdout}, - "streaminfo" => \$opt_cmdline{streaminfo}, - "subdirs|subdir|s" => \$opt_cmdline{subdir}, - "suboffset=n" => \$opt_cmdline{suboffset}, -@@ -253,6 +267,7 @@ - "versions=s" => \$opt_cmdline{versions}, - "verbose|v" => \$opt_cmdline{verbose}, - "vlc=s" => \$opt_cmdline{vlc}, -+ "vmode=s" => \$opt_cmdline{vmode}, - "wav" => \$opt_cmdline{wav}, - "whitespace|ws|w" => \$opt_cmdline{whitespace}, - "xml-channels|fxd-channels" => \$opt_cmdline{xmlchannels}, -@@ -269,7 +284,7 @@ - save_options_file( $optfile ) if $save; - - --# Global vars -+### Global vars ### - - # Programme data structure - # $prog{$pid} = { -@@ -283,7 +298,7 @@ - # 'thumbnail' => <programme thumbnail url> - # 'channel => <channel> - # 'categories' => <Comma separated list of categories> --# 'type' => <Type: tv, radio or podcast> -+# 'type' => <Type: tv, radio, itv or podcast> - # 'timeadded' => <timestamp when programme was added to cache> - # 'longname' => <Long name (only parsed in stage 1 download)>, - # 'version' => <selected version e.g default, signed, etc - only set before d/load> -@@ -292,11 +307,35 @@ - # 'fileprefix' => <Filename Prefix of saved file - set only while downloading> - # 'ext' => <Filename Extension of saved file - set only while downloading> - #}; -+ -+# Define cache file format -+my @cache_format = qw/index type name pid available episode versions duration desc channel categories thumbnail timeadded guidance/; -+ -+# List of all types -+my @all_prog_types = qw/ tv radio podcast itv /; -+ -+# Ranges of numbers used in the indicies for each programme type -+my %index_range; -+$index_range{tv}{min} = 1; -+$index_range{tv}{max} = 9999; -+$index_range{radio}{min} = 10001; -+$index_range{radio}{max} = 19999; -+$index_range{podcast}{min} = 20001; -+$index_range{podcast}{max} = 29999; -+$index_range{itv}{min} = 100001; -+$index_range{itv}{max} = 199999; -+# Set maximun index number -+my $max_index; -+for (@all_prog_types) { -+ $max_index = $index_range{$_}{max} if $index_range{$_}{max} > $max_index; -+} - my %prog; -+my %type; - my %pids_history; - my %index_pid; # Hash to obtain pid given an index - my $now; - my $childpid; -+my $min_download_size = 1000000; - - # Static URLs - my $channel_feed_url = 'http://feeds.bbc.co.uk/iplayer'; # /$channel/list/limit/400 -@@ -396,6 +435,18 @@ - 'bbc_radio_jersey' => 'radio|BBC Jersey', - }; - -+$channels{itv} = { -+ 'crime' => 'itv|TV Classics Crime Drama', -+ 'perioddrama' => 'itv|TV Classics Period Drama', -+ 'familydrama' => 'itv|TV Classics Family Drama', -+ 'documentary' => 'itv|TV Classics Documentaries', -+ 'comedy' => 'itv|TV Classics Comedy', -+ 'kids' => 'itv|TV Classics Children\'s TV', -+ 'soaps' => 'itv|TV Classics Soaps', -+ '/' => 'itv|TV Classics', -+}; -+ -+ - # User Agents - my %user_agent = ( - coremedia => 'Apple iPhone v1.1.1 CoreMedia v1.0.0.3A110a', -@@ -410,6 +461,7 @@ - - # Other Non-option dependant vars - my %cachefile = ( -+ 'itv' => "${profile_dir}/itv.cache", - 'tv' => "${profile_dir}/tv.cache", - 'radio' => "${profile_dir}/radio.cache", - 'podcast' => "${profile_dir}/podcast.cache", -@@ -430,6 +482,7 @@ - my $mplayer; - #my $mencoder; - my $ffmpeg; -+my $ffmpeg_opts; - my $rtmpdump; - my $mplayer_opts; - my $lame; -@@ -480,7 +533,7 @@ - # Display default options - display_default_options(); - # For each PVR search -- for my $name ( sort {$a <=> $b} keys %pvrsearches ) { -+ for my $name ( sort {lc $a cmp lc $b} keys %pvrsearches ) { - # Ignore if this search is disabled - if ( $pvrsearches{$name}{disable} ) { - logger "\nSkipping disabled PVR Search '$name'\n" if $opt{verbose}; -@@ -519,27 +572,29 @@ - # Option dependant vars - %download_dir = ( - 'tv' => $opt{outputtv} || $opt{output} || $ENV{IPLAYER_OUTDIR} || '.', -+ 'itv' => $opt{outputtv} || $opt{output} || $ENV{IPLAYER_OUTDIR} || '.', - 'radio' => $opt{outputradio} || $opt{output} || $ENV{IPLAYER_OUTDIR} || '.', - 'podcast' => $opt{outputpodcast} || $opt{output} || $ENV{IPLAYER_OUTDIR} || '.', - ); -- # Default to type=tv -- $opt{type} = 'tv' if ! $opt{type}; -- # Expand 'all' to various prog types -- $opt{type} = 'tv,radio,podcast' if $opt{type} =~ /(all|any)/i; -+ - # Ensure lowercase -- $opt{type} = lc( $opt{type} ); -+ $opt{type} = lc( $opt{type} ); -+ # Expand 'all' to comma separated list all prog types -+ $opt{type} = join(',', @all_prog_types) if $opt{type} =~ /(all|any)/i; -+ # Hash to store specified prog types -+ %type = (); -+ $type{$_} = 1 for split /,/, $opt{type}; -+ # Default to type=tv if no type option is set -+ $type{tv} = 1 if keys %type == 0; - $cache_secs = $opt{expiry} || 14400; - $mplayer = $opt{mplayer} || 'mplayer'; - $mplayer_opts = '-nolirc'; - $mplayer_opts .= ' -really-quiet' if $opt{quiet}; -- # Assume mencoder/ffmpeg is in the same path as mplayer --# $mencoder = $mplayer; --# $mencoder =~ s|^(.*?)mplayer|$1mencoder|g; -- $ffmpeg = $mplayer; -- $ffmpeg =~ s|^(.*?)mplayer|$1ffmpeg|g; -+ $ffmpeg = $opt{ffmpeg} || 'ffmpeg'; -+ $ffmpeg_opts = ''; - $lame = $opt{lame} || 'lame'; -- $lame_opts = '-f '; -- $lame_opts .= '--quiet ' if $opt{quiet}; -+ $lame_opts = '-f'; -+ $lame_opts .= ' --quiet ' if $opt{quiet}; - $vlc = $opt{vlc} || 'cvlc'; - $vlc_opts = '-vv'; - $id3v2 = $opt{id3v2} || 'id3v2'; -@@ -562,11 +617,11 @@ - exit 1; - } - -- # Disable rtmp mode if rtmpdump does not exist -- if ( $opt{rtmp} && ! exists_in_path($rtmpdump)) { -- logger "\nERROR: Required program $rtmpdump does not exist (see http://linuxcentre.net/getiplayer/installation and http://linuxcentre.net/getiplayer/download), falling back to iphone mode\n"; -- delete $opt{rtmp}; -- } -+ # Backward compatability options - to be removed eventually -+ $opt{vmode} = 'rtmp' if $opt{rtmp}; -+ $opt{vmode} = 'n95_wifi' if $opt{n95}; -+ $opt{amode} = 'realaudio' if $opt{realaudio}; -+ $opt{amode} = 'iphone' if $opt{mp3audio}; - - # Web proxy - $proxy_url = $opt{proxy} || $ENV{HTTP_PROXY} || $ENV{http_proxy} || ''; -@@ -585,8 +640,21 @@ - } - } - -- # Get arbitrary pid -+ # Get prog by arbitrary pid (then exit) - if ( $opt{pid} ) { -+ -+ # Temporary hack to get 'ITV Catch-up' downloads specified as --pid itv:<pid> -+ $type{itv} = 1 if $opt{pid} =~ m{^itv:(.+?)$}; -+ if ( $type{itv} ) { -+ exit 1 if ( ! $opt{streaminfo} ) && check_download_history( $opt{pid} ); -+ # Remove leading itv: tag (backwards compat) -+ $opt{pid} =~ s/^itv:(.+?)$/$1/ig; -+ # Force prog type to itv -+ $prog{$opt{pid}}{type} = 'itv'; -+ download_programme( $opt{pid} ); -+ exit 0; -+ } -+ - # Remove any url parts from the pid - $opt{pid} =~ s/^.*(b0[a-z,0-9]{6}).*$/$1/g; - # Retry loop -@@ -594,21 +662,29 @@ - my $retries = 3; - my $retcode; - exit 1 if ( ! $opt{streaminfo} ) && check_download_history( $opt{pid} ); -- while ( $count < $retries && ($retcode = download_programme( $opt{pid} )) eq 'retry' ) { -- logger "WARNING: Retrying download for PID $opt{pid}\n"; -- $count++; -+ for ($count = 1; $count <= $retries; $count++) { -+ $retcode = download_programme( $opt{pid} ); -+ return 0 if $retcode eq 'skip'; -+ if ( $retcode eq 'retry' && $count < $retries ) { -+ logger "WARNING: Retrying download for PID $opt{pid}\n"; -+ } else { -+ $retcode = 1 if $retcode eq 'retry'; -+ last; -+ } - } - # Add to history, tag and Run post download command if download was successful - if ($retcode == 0) { - add_to_download_history( $opt{pid} ); - tag_file( $opt{pid} ); - run_user_command( $opt{pid}, $opt{command} ) if $opt{command}; -- } -+ } elsif (! $opt{test}) { -+ logger "ERROR: Failed to download PID $opt{pid}\n"; -+ } - exit 0; - } - - # Get stream links from BBC iplayer site or from cache (also populates all hashes) specified in --type option -- get_links( $_ ) for split /,/, $opt{type}; -+ get_links( $_ ) for keys %type; - - # List elements (i.e. 'channel' 'categories') if required and exit - if ( $opt{list} ) { -@@ -616,18 +692,13 @@ - exit 0; - } - -- # Write HTML and XML files if required -- create_html( sort {$a <=> $b} keys %index_pid ) if $opt{html}; -- create_xml( $opt{fxd}, sort {$a <=> $b} keys %index_pid ) if $opt{fxd}; -- create_xml( $opt{mythtv}, sort {$a <=> $b} keys %index_pid ) if $opt{mythtv}; -- - # Parse remaining args - my @match_list; - for ( @search_args ) { - chomp(); -- -- # If Numerical value < 30000 -- if ( /^[\d]+$/ && $_ < 30000) { -+ -+ # If Numerical value < $max_index -+ if ( /^[\d]+$/ && $_ <= $max_index) { - push @match_list, $_; - - # If PID then find matching programmes with this PID -@@ -649,24 +720,29 @@ - # Go get the cached data for other programme types if the index numbers require it - my %require; - for ( @match_list ) { -- $require{tv} = 1 if $_ >= 1 && $_ < 10000 && ( ! $require{tv} ) && $opt{type} !~ /tv/; -- $require{radio} = 1 if $_ >= 10000 && $_ < 20000 && ( ! $require{radio} ) && $opt{type} !~ /radio/; -- $require{podcast} = 1 if $_ >= 20000 && $_ < 30000 && ( ! $require{podcast} ) && $opt{type} !~ /podcast/; -+ for my $types ( @all_prog_types ) { -+ $require{$types} = 1 if $_ >= $index_range{$types}{min} && $_ <= $index_range{$types}{max} && ( ! $require{$types} ) && ( ! $type{$types} ); -+ } - } -+ - # Get extra required programme caches - logger "INFO: Additionally getting cached programme data for ".(join ', ', keys %require)."\n" if %require > 0; - # Get stream links from BBC iplayer site or from cache (also populates all hashes) - for (keys %require) { - # Get $_ stream links - get_links( $_ ); -- # Add new prog types to the type option -- $opt{type} .= ",$_"; -+ # Add new prog types to the type list -+ $type{$_} = 1; - } -- - # Display list for download - logger "Matches:\n" if @match_list; - @match_list = list_progs( @match_list ); - -+ # Write HTML and XML files if required (with search options applied) -+ create_html( @match_list ) if $opt{html}; -+ create_xml( $opt{fxd}, @match_list ) if $opt{fxd}; -+ create_xml( $opt{mythtv}, @match_list ) if $opt{mythtv}; -+ - # Do the downloads based on list of index numbers if required - if ( $opt{get} || $opt{stdout} ) { - for (@match_list) { -@@ -681,16 +757,27 @@ - logger "ERROR: No PID for index $_ (try using --type option ?)\n"; - next; - } -- while ( $count < $retries && $pid && ($retcode = download_programme( $pid )) eq 'retry' ) { -- logger "WARNING: Retrying download for '$prog{$pid}{name} - $prog{$pid}{episode}'\n"; -- $count++; -+ for ($count = 1; $count <= $retries; $count++) { -+ $retcode = download_programme( $pid ); -+ last if $retcode eq 'skip'; -+ if ( $retcode eq 'retry' && $count < $retries ) { -+ logger "WARNING: Retrying download for '$prog{$pid}{name} - $prog{$pid}{episode}'\n"; -+ } else { -+ $retcode = 1 if $retcode eq 'retry'; -+ last; -+ } - } - # Add to history, tag file, and run post download command if download was successful -- if ($retcode eq '0') { -+ if ($retcode == 0) { - add_to_download_history( $pid ); - tag_file( $pid ); - run_user_command( $pid, $opt{command} ) if $opt{command}; - pvr_report( $pid ) if $opt{pvr}; -+ # Next match if 'skip' was returned -+ } elsif ( $retcode eq 'skip' ) { -+ last; -+ } elsif (! $opt{test}) { -+ logger "ERROR: Failed to download '$prog{$pid}{name} - $prog{$pid}{episode}'\n"; - } - } - } -@@ -702,16 +789,11 @@ - - # Lists progs given an array of index numbers, also returns an array with non-existent entries removed - sub list_progs { -- my $ua; -+ my $ua = create_ua('desktop'); - my @checked; - my %names; - # Setup user agent for a persistent connection to get programme metadata - if ( $opt{info} ) { -- $ua = LWP::UserAgent->new; -- $ua->timeout([$lwp_request_timeout]); -- $ua->proxy( ['http'] => $proxy_url ); -- $ua->agent( $user_agent{desktop} ); -- $ua->conn_cache(LWP::ConnCache->new()); - # Truncate array if were lisiting info and > $info_limit entries are requested - be nice to the beeb! - if ( $#_ >= $info_limit ) { - $#_ = $info_limit - 1; -@@ -733,18 +815,7 @@ - push @checked, $_; - if ( $opt{info} ) { - my %metadata = get_pid_metadata( $ua, $pid ); -- logger "\nPid:\t\t$metadata{pid}\n"; -- logger "Index:\t\t$metadata{index}\n"; -- logger "Type:\t\t$metadata{type}\n"; -- logger "Duration:\t$metadata{duration}\n"; -- logger "Channel:\t$metadata{channel}\n"; -- logger "Available:\t$metadata{available}\n"; -- logger "Expires:\t$metadata{expiry}\n"; -- logger "Versions:\t$metadata{versions}\n"; -- logger "Guidance:\t$metadata{guidance}\n"; -- logger "Categories:\t$metadata{categories}\n"; -- logger "Description:\t$metadata{desc}\n"; -- logger "Player:\t\t$metadata{player}\n"; -+ display_metadata( \%metadata, qw/ pid index type duration channel available expiry versions guidance categories desc player / ); - } - } - logger "\n"; -@@ -758,8 +829,9 @@ - # Display a line containing programme info (using long, terse, and type options) - sub list_prog_entry { - my ( $pid, $prefix, $tree ) = ( @_ ); -- my $type = ''; -- $type = "$prog{$pid}{type}, " if $opt{type} !~ /^(tv|radio|podcast)$/i; -+ my $prog_type = ''; -+ # Show the type field if >1 type has been specified -+ $prog_type = "$prog{$pid}{type}, " if keys %type > 1; - my $name; - # If tree view - if ( $opt{tree} ) { -@@ -768,20 +840,21 @@ - } else { - $name = "$prog{$pid}{name} - "; - } -- # Remove some info depending on type -+ # Remove some info depending on prog_type - my $optional; - $optional = ", '$prog{$pid}{channel}', $prog{$pid}{categories}, $prog{$pid}{versions}" if $prog{$pid}{type} eq 'tv'; -+ $optional = ", '$prog{$pid}{channel}'" if $prog{$pid}{type} eq 'itv'; - $optional = ", '$prog{$pid}{channel}', $prog{$pid}{categories}" if $prog{$pid}{type} eq 'radio'; - $optional = ", '$prog{$pid}{available}', '$prog{$pid}{channel}', $prog{$pid}{categories}" if $prog{$pid}{type} eq 'podcast'; -- logger "\n${type}$prog{$pid}{name}\n" if $opt{tree} && ! $tree; -+ logger "\n${prog_type}$prog{$pid}{name}\n" if $opt{tree} && ! $tree; - # Display based on output options - if ( $opt{long} ) { - my @time = gmtime( time() - $prog{$pid}{timeadded} ); -- logger "${prefix}$prog{$pid}{index}:\t${type}${name}$prog{$pid}{episode}${optional}, $time[7] days $time[2] hours ago - $prog{$pid}{desc}\n"; -+ logger "${prefix}$prog{$pid}{index}:\t${prog_type}${name}$prog{$pid}{episode}${optional}, $time[7] days $time[2] hours ago - $prog{$pid}{desc}\n"; - } elsif ( $opt{terse} ) { -- logger "${prefix}$prog{$pid}{index}:\t${type}${name}$prog{$pid}{episode}\n"; -+ logger "${prefix}$prog{$pid}{index}:\t${prog_type}${name}$prog{$pid}{episode}\n"; - } else { -- logger "${prefix}$prog{$pid}{index}:\t${type}${name}$prog{$pid}{episode}${optional}\n"; -+ logger "${prefix}$prog{$pid}{index}:\t${prog_type}${name}$prog{$pid}{episode}${optional}\n"; - } - return 0; - } -@@ -795,6 +868,7 @@ - my $channel_regex = $opt{channel} || '.*'; - my $category_regex = $opt{category} || '.*'; - my $versions_regex = $opt{versions} || '.*'; -+ my $exclude_regex = $opt{exclude} || '^ROGUE$'; - my $channel_exclude_regex = $opt{excludechannel} || '^ROGUE$'; - my $category_exclude_regex = $opt{excludecategory} || '^ROGUE$'; - my $since = $opt{since} || 99999; -@@ -808,6 +882,7 @@ - && $prog{$pid}{categories} =~ /$category_regex/i - && $prog{$pid}{versions} =~ /$versions_regex/i - && $prog{$pid}{channel} !~ /$channel_exclude_regex/i -+ && $prog{$pid}{name} !~ /$exclude_regex/i - && $prog{$pid}{categories} !~ /$category_exclude_regex/i - && $prog{$pid}{timeadded} >= $now - ($since * 3600) - ) { -@@ -828,25 +903,22 @@ - ); - } - } -+ - return sort {$a <=> $b} keys %download_hash; - } - - --# get_links_atom (%channels) --sub get_links_atom { -- my $type = shift; -+# get_links_bbciplayer (%channels) -+sub get_links_bbciplayer { -+ my $prog_type = shift; - my %channels = %{$_[0]}; - - my $xml; - my $feed_data; - my $res; -- logger "INFO: Getting $type Index Feeds\n"; -+ logger "INFO: Getting $prog_type Index Feeds\n"; - # Setup User agent -- my $ua = LWP::UserAgent->new; -- $ua->timeout([$lwp_request_timeout]); -- $ua->proxy( ['http'] => $proxy_url ); -- $ua->agent( $user_agent{desktop} ); -- $ua->conn_cache(LWP::ConnCache->new()); -+ my $ua = create_ua('desktop'); - - # 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 -@@ -909,7 +981,7 @@ - # Discard first element == header - shift @entries; - -- my ( $name, $episode, $desc, $pid, $available, $channel, $duration, $thumbnail, $type, $versions ); -+ my ( $name, $episode, $desc, $pid, $available, $channel, $duration, $thumbnail, $prog_type, $versions ); - foreach my $entry (@entries) { - - my $entry_flat = $entry; -@@ -939,7 +1011,7 @@ - } - - # Extract channel and type -- ($type, $channel) = (split /\|/, $channels{$_})[0,1]; -+ ($prog_type, $channel) = (split /\|/, $channels{$_})[0,1]; - - logger "DEBUG: '$pid, $name - $episode, $channel'\n" if $opt{debug}; - -@@ -980,7 +1052,7 @@ - 'thumbnail' => "${thumbnail_prefix}/${pid}_150_84.jpg", - 'channel' => $channel, - 'categories' => join(',', @category), -- 'type' => $type, -+ 'type' => $prog_type, - }; - } - } -@@ -996,13 +1068,8 @@ - - # Add index field based on alphabetical sorting by prog name - my %index; -- $index{tv} = 1; -- -- # Start index counter at 10001 for radio progs -- $index{radio} = 10001; -- -- # Start index counter at 20001 for podcast progs -- $index{podcast} = 20001; -+ # Start index counter at 'min' for each prog type -+ $index{$_} = $index_range{$_}{min} for @all_prog_types; - - my @prog_pid; - -@@ -1013,10 +1080,10 @@ - for (sort @prog_pid) { - # Extract pid - my $pid = (split /\|/)[1]; -- my $type = $prog{$pid}{type}; -- $index_pid{ $index{$type} } = $pid; -- $prog{$pid}{index} = $index{$type}; -- $index{$type}++; -+ my $prog_type = $prog{$pid}{type}; -+ $index_pid{ $index{$prog_type} } = $pid; -+ $prog{$pid}{index} = $index{$prog_type}; -+ $index{$prog_type}++; - } - return 0; - } -@@ -1024,18 +1091,14 @@ - - - # Uses: $podcast_index_feed_url --# get_podcast_links () --sub get_podcast_links { -+# get_links_bbcpodcast () -+sub get_links_bbcpodcast { - - my $xml; - my $res; - logger "INFO: Getting podcast Index Feeds\n"; - # Setup User agent -- my $ua = LWP::UserAgent->new; -- $ua->timeout([$lwp_request_timeout]); -- $ua->proxy( ['http'] => $proxy_url ); -- $ua->agent( $user_agent{get_iplayer} ); -- $ua->conn_cache(LWP::ConnCache->new()); -+ my $ua = create_ua('get_iplayer'); - - # Method - # $podcast_index_feed_url (gets list of rss feeds for each podcast prog) => -@@ -1224,6 +1287,302 @@ - - - -+# Uses: -+# get_links_itv () -+sub get_links_itv { -+ my %channels = %{$_[0]}; -+ my $xml; -+ my $res; -+ my %series_pid; -+ my %episode_pid; -+ logger "INFO: Getting itv Index Feeds\n"; -+ # Setup User agent -+ my $ua = create_ua('desktop'); -+ -+ # Method -+ # http://www.itv.com/_data/xml/CatchUpData/CatchUp360/CatchUpMenu.xml (gets list of urls for each prog series) => -+ # => -+ -+ # Download index feed -+ my $itv_index_shows_url = 'http://www.itv.com/ClassicTVshows/'; # $channel/default.html -+ my $itv_index_feed_url = 'http://www.itv.com/_data/xml/CatchUpData/CatchUp360/CatchUpMenu.xml'; -+ -+ # Sort feeds so that pages are done last - this makes sure that the channels get defined correctly if there are dups -+ my @channel_list; -+ push @channel_list, grep !/\//, keys %channels; -+ push @channel_list, grep /\//, keys %channels; -+ # ITV ClassicShows parsing -+ for my $channel ( @channel_list ) { -+ # <li class="first-child"><a href="http://www.itv.com/ClassicTVshows/comedy/ABitofaDo/default.html">A Bit of a Do</a><br></li> -+ # <li><a href="http://www.itv.com/ClassicTVshows/familydrama/achristmascarol/default.html">A Christmas Carol</a><br></li> -+ # Get page, search for relevent lines which contain series links and loop through each matching line -+ for my $s_line ( grep /(<li><a\s+href=".+?"><img\s+src=".+?"\s+alt=".+?"><\/a><h4>|<li.*?><a href=".+?">.+?<\/a><br><\/li>)/, ( split /\n/, request_url_retry($ua, $itv_index_shows_url.${channel}.'/default.html', 3, '.', "WARNING: Failed to get itv ${channel} index from site\n") ) ) { -+ my ($url, $name); -+ # Extract series url + series description -+ ($url, $name) = ($1, $2) if $s_line =~ m{<li><a\s+href="\s*(.+?)\s*"><img\s+src=".+?"\s+alt="\s*(.+?)\s*"><\/a><h4>}; -+ ($url, $name) = ($1, $2) if $s_line =~ m{<li.*?><a href="\s*(.+?)\s*">\s*(.+?)\s*<\/a><br><\/li>}; -+ chomp($url); -+ chomp($name); -+ next if ! ($url && $name); -+ logger "DEBUG: Channel: '$channel' Series: '$name' URL: '$url'\n" if $opt{verbose}; -+ -+ # Get list of episodes for this series -+ # e.g. <li class="first-child"><a title="Play" href="?vodcrid=crid://itv.com/993&DF=0">Episode one</a><br>The Sun in a Bottle</li> -+ # <li><a title="Play" href="?vodcrid=crid://itv.com/994&DF=0">Episode two</a><br>Castle Saburac</li> -+ # <li class="first-child"><a class="nsat" title="This programme contains strong language and violence " href="?vodcrid=crid://itv.com/588&G=10&DF=0">Episode one</a><br>The Dead of Jericho</li> -+ # -+ # e.g. <li><a class="playVideo" title="Play" href="?vodcrid=crid://itv.com/1232&DF=0"><img src="img/60x45/Crossroads-Rosemary-shoots-David-efeef7cd-8d41-416c-9e30-26ce1b3d625c.jpg" alt="Crossroads: Rosemary shoots David"><span>Play</span></a><h4> -+ # vodcrid=crid://itv.com/971&DF=0"><img src="img/60x45/9d20fd47-5d4b-44f5-9188-856505de0d0f.jpg" alt="Emmerdale 2002 Louise kills Ray" -+ # -+ # e.g. <a class="playVideo" title="Play" href="?vodcrid=crid://itv.com/1854&DF=1"><img src="img/157x104/140c456c-d8bd-49d5-90f8-f7cc6d86f132.jpg" alt="Soldier Soldier "><span>Play</span></a><h2>Soldier Soldier</h2> -+ # -+ for my $e_line ( grep /vodcrid=crid/, ( split /\n/, request_url_retry($ua, $url, 3, '.', "WARNING: Failed to get ${name} index from site\n") ) ) { -+ my ($guidance, $pid, $episode, $thumbnail); -+ logger "DEBUG: Match Line: $e_line\n" if $opt{debug}; -+ # Extract episode data -+ ($guidance, $pid, $episode) = ($2, $3, $4) if $e_line =~ m{<li.*?><a\s+(class="nsat"\s+)?title="\s*(.+?)\s*"\s+href="\?vodcrid=crid://itv.com/(\d+?)&.+?>\s*(.+?)\s*<}; -+ ($pid, $thumbnail, $episode) = ($1, $2, $3) if $e_line =~ m{vodcrid=crid://itv.com/(\d+?)&.+?><img\s+src="(.+?)"\s+alt="\s*(.+?)\s*"}; -+ next if ! ($pid && $episode); -+ # Remove 'Play' -+ $guidance =~ s/^Play$//ig; -+ # Strip non-printables -+ $guidance =~ s/[\s\x00\xc2\xa0]+$//ig; -+ #$guidance =~ s|[^\w\s\-\!"£\$\\/%\^&\*\(\)\+=,\.\?':;@~\[\]]+||gi; -+ #$guidance =~ s/(\s\s)+//g; -+ logger "DEBUG: PID: '$pid' Episode: '$episode' Guidance: '$guidance'\n" if $opt{debug}; -+ -+ # Skip if this pid is a duplicate -+ if ( defined $prog{$pid} ) { -+ logger "WARNING: '$pid, $prog{$pid}{name} - $prog{$pid}{episode}, $prog{$pid}{channel}' already exists (this channel = $channel)\n" if $opt{verbose}; -+ # Merge data (hack) -+ #$prog{$pid}{episode} .= ','.$episode; -+ my $oldname = $prog{$pid}{name}; -+ $prog{$pid}{episode} = $episode if (! $prog{$pid}{episode}) || $prog{$pid}{episode} =~ /$oldname/i; -+ $prog{$pid}{thumbnail} = $thumbnail if ! $prog{$pid}{thumbnail}; -+ $prog{$pid}{guidance} = $guidance if ! $prog{$pid}{guidance}; -+ next; -+ } -+ -+ # build data structure -+ $prog{$pid} = { -+ 'name' => $name, -+ 'versions' => 'default', -+ 'episode' => $episode, -+ 'channel' => (split /\|/, $channels{$channel})[1], -+ 'guidance' => $guidance, -+ 'categories' => (split /\|/, $channels{$channel})[1], -+ 'type' => 'itv', -+ }; -+ } -+ } -+ } -+ -+ my $xmlindex = request_url_retry($ua, $itv_index_feed_url, 3, '.', "WARNING: Failed to get itv index from site\n"); -+ $xmlindex =~ s/[\n\r]//g; -+ -+ # This gives a list of programme series (sometimes episodes) -+ # <ITVCatchUpProgramme> -+ # <ProgrammeId>50</ProgrammeId> -+ # <ProgrammeTitle>A CHRISTMAS CAROL</ProgrammeTitle> -+ # <ProgrammeMediaId>615915</ProgrammeMediaId> -+ # <ProgrammeMediaUrl> -+ # http://www.itv.com//img/150x113/A-Christmas-Carol-2f16d25a-de1d-4a3a-90cb-d47489eee98e.jpg</ProgrammeMediaUrl> -+ # <LastUpdated>2009-01-06T12:24:22.7419643+00:00</LastUpdated> -+ # <Url> -+ # http://www.itv.com/CatchUp/Video/default.html?ViewType=5&Filter=32910</Url> -+ # <EpisodeCount>1</EpisodeCount> -+ # <VideoID>32910</VideoID> -+ # <DentonID>-1</DentonID> -+ # <DentonRating></DentonRating> -+ # <AdditionalContentUrl /> -+ # <AdditionalContentUrlText /> -+ # </ITVCatchUpProgramme> -+ -+ for my $feedxml ( split /<ITVCatchUpProgramme>/, $xmlindex ) { -+ # Extract feed data -+ my ($episodecount, $viewtype, $videoid, $url); -+ my @entries; -+ -+ logger "\n\nDEBUG: XML: $feedxml\n" if $opt{debug}; -+ -+ # <EpisodeCount>1</EpisodeCount> -+ $episodecount = $1 if $feedxml =~ m{<EpisodeCount>\s*(\d+)\s*<\/EpisodeCount>}; -+ -+ # <Url>http://www.itv.com/CatchUp/Video/default.html?ViewType=5&Filter=32910</Url> -+ ($viewtype, $videoid) = ($1, $2) if $feedxml =~ m{<Url>\s*.+?ViewType=(\d+).+?Filter=(\d+)\s*<\/Url>}i; -+ -+ ## <VideoID>32910</VideoID> -+ #$videoid = $1 if $feedxml =~ m{<VideoID>\s*(\d+)\s*<\/VideoID>}; -+ -+ # Skip if there is no feed data for channel -+ next if ($viewtype =~ /^0*$/ || $videoid =~ /^0*$/ ); -+ -+ logger "DEBUG: Got ViewType=$viewtype VideoId=$videoid EpisodeCount=$episodecount\n" if $opt{debug}; -+ -+ my $url = "http://www.itv.com/_app/Dynamic/CatchUpData.ashx?ViewType=${viewtype}&Filter=${videoid}"; -+ -+ # Add response from episode metadata url to list to be parsed if this is an episode link -+ if ( $viewtype == 5 ) { -+ next if $episode_pid{$videoid}; -+ $episode_pid{$videoid} = 1; -+ # Get metadata pages for episode -+ -+ my ( $name, $guidance, $channel, $episode, $desc, $pid, $available, $duration, $thumbnail ); -+ -+ $pid = $videoid; -+ $channel = 'ITV Catch-up'; -+ -+ # Skip if this pid is a duplicate -+ if ( defined $prog{$pid} ) { -+ logger "WARNING: '$pid, $prog{$pid}{name} - $prog{$pid}{episode}, $prog{$pid}{channel}' already exists (this channel = $channel)\n" if $opt{verbose}; -+ next; -+ } -+ -+ $name = $1 if $feedxml =~ m{<ProgrammeTitle>\s*(.+?)\s*<\/ProgrammeTitle>}; -+ $guidance = $1 if $feedxml =~ m{<DentonRating>\s*(.+?)\s*<\/DentonRating>}; -+ $thumbnail = $1 if $feedxml =~ m{<ProgrammeMediaUrl>\s*(.+?)\s*<\/ProgrammeMediaUrl>}; -+ $episode = $pid; -+ # Strip non-printable chars -+ $guidance =~ s/[\s\x00\xc2\xa0]+$//ig; -+ -+ # build data structure -+ $prog{$pid} = { -+ 'name' => $name, -+ 'versions' => 'default', -+ 'episode' => $episode, -+ 'guidance' => $guidance, -+ 'desc' => $desc, -+ 'available' => $available, -+ 'duration' => $duration, -+ 'thumbnail' => $thumbnail, -+ 'channel' => $channel, -+ 'categories' => 'TV', -+ 'type' => 'itv', -+ }; -+ -+ -+ -+ -+ -+ # Get next episode list and parse -+ # <div class="listItem highlight contain"> -+ # <div class="floatLeft"><a href="http://www.itv.com/CatchUp/Video/default.html?ViewType=5&Filter=33383"><img src="http://www.itv.com//img/157x88/P7-67e0b86f-b335-4f6b-8db -+ # <div class="content"> -+ # <h3><a href="http://www.itv.com/CatchUp/Video/default.html?ViewType=5&Filter=33383">Emmerdale</a></h3> -+ # <p class="date">Mon 05 Jan 2009</p> -+ # <p class="progDesc">Donna is stunned to learn Marlon has pointed the finger at Ross. Aaron defaces Tom King's grave.</p> -+ # <ul class="progDetails"> -+ # <li> -+ # Duration: 30 min -+ # </li> -+ # <li class="days"> -+ # Expires in -+ # <strong>29</strong> -+ # days -+ # </li> -+ # </ul> -+ # </div> -+ # </div> -+ # <div class="listItem contain"> -+ # <div class="floatLeft"><a href="http://www.itv.com/CatchUp/Video/default.html?ViewType=5&Filter=33245"><img src="http://www.itv.com//img/157x88/Marlon-Dingle-742c50b3-3b -+ # <div class="content"> -+ # <h3><a href="http://www.itv.com/CatchUp/Video/default.html?ViewType=5&Filter=33245">Emmerdale</a></h3> -+ # <p class="date">Fri 02 Jan 2009</p> -+ # <p class="progDesc">Marlon gets his revenge on Ross. The King brothers struggle to restart their business without Matthew. Scarlett is fed up with Victoria getting all Daz -+ # <ul class="progDetails"> -+ # <li> -+ # Duration: 30 min -+ # </li> -+ # <li class="days"> -+ # Expires in -+ # <strong>26</strong> -+ # days -+ # </li> -+ # </ul> -+ # </div> -+ # </div> -+ # -+ } elsif ( $viewtype == 1 ) { -+ # Make sure we don't duplicate parsing a series -+ next if $series_pid{$videoid}; -+ $series_pid{$videoid} = 1; -+ -+ # Get metadata pages for each series -+ logger "DEBUG: Getting series metadata $url\n" if $opt{debug}; -+ $xml = request_url_retry($ua, $url, 2, '.', "WARNING: Failed to get itv series data for ${videoid} from itv site\n") if $opt{verbose}; -+ $xml = request_url_retry($ua, $url, 2, '.', '') if ! $opt{verbose}; -+ -+ # skip if no data -+ next if ! $xml; -+ -+ decode_entities($xml); -+ # Flatten entry -+ $xml =~ s/[\n\r]//g; -+ -+ # Extract Filter (pids) from this list -+ # e.g. <h3><a href="http://www.itv.com/CatchUp/Video/default.html?ViewType=5&Filter=32042">Emmerdale</a></h3> -+ my @videoids = (split /<h3><a href=.+?Filter=/, $xml); -+ -+ # Get episode data for each videoid -+ $viewtype = 5; -+ -+ my @episode_data = split/<h3><a href=.+?Filter=/, $xml; -+ # Ignore first entry -+ shift @episode_data; -+ logger "INFO: Got ".($#episode_data+1)." programmes\n" if $opt{verbose}; -+ -+ for my $xml (@episode_data) { -+ $videoid = $1 if $xml =~ m{^(\d+?)".+$}i; -+ -+ # Make sure we don't duplicate parsing an episode -+ next if $episode_pid{$videoid}; -+ $episode_pid{$videoid} = 1; -+ -+ my ( $name, $guidance, $channel, $episode, $desc, $pid, $available, $duration, $thumbnail ); -+ -+ $pid = $videoid; -+ $channel = 'ITV Catch-up'; -+ -+ # Skip if this pid is a duplicate -+ if ( defined $prog{$pid} ) { -+ logger "WARNING: '$pid, $prog{$pid}{name} - $prog{$pid}{episode}, $prog{$pid}{channel}' already exists (this channel = $channel)\n" if $opt{verbose}; -+ next; -+ } -+ $name = $1 if $feedxml =~ m{<ProgrammeTitle>\s*(.+?)\s*<\/ProgrammeTitle>}; -+ $available = $1 if $xml =~ m{<p\s+class="date">(.+?)<\/p>}i; -+ $episode = $available; -+ $duration = $1 if $xml =~ m{<li>Duration:\s*(.+?)\s*<\/li>}i; -+ $desc = $1 if $xml =~ m{<p\s+class="progDesc">(.+?)\s*<\/p>}; -+ $guidance = $1 if $feedxml =~ m{<DentonRating>\s*(.+?)\s*<\/DentonRating>}; -+ $thumbnail = $1 if $feedxml =~ m{<ProgrammeMediaUrl>\s*(.+?)\s*<\/ProgrammeMediaUrl>}; -+ $guidance =~ s/[\s\x00\xc2\xa0]+$//ig; -+ -+ logger "DEBUG: name='$name' episode='$episode' pid=$pid available='$available' \n" if $opt{debug}; -+ -+ # build data structure -+ $prog{$pid} = { -+ 'name' => $name, -+ 'versions' => 'default', -+ 'episode' => $episode, -+ 'guidance' => $guidance, -+ 'desc' => $desc, -+ 'available' => $available, -+ 'duration' => $duration, -+ 'thumbnail' => $thumbnail, -+ 'channel' => $channel, -+ 'categories' => 'TV', -+ 'type' => 'itv', -+ }; -+ } -+ } -+ -+ } -+ logger "\n"; -+ return 0; -+} -+ -+ -+ - # Feed info: - # # Also see http://derivadow.com/2008/07/18/interesting-bbc-data-to-hack-with/ - # # All podcasts menu (iphone) -@@ -1261,14 +1620,14 @@ - # http://www.bbc.co.uk/cbbc/programmes/genres/childrens/player - # http://www.bbc.co.uk/programmes/genres/childrens/schedules/upcoming.ics - # --# get_links( <radio|tv|podcast> ) -+# get_links( <prog_type> ) - sub get_links { - my @cache; - my $now = time(); -- my $type = shift; -+ my $prog_type = shift; - - # Open cache file (need to verify we can even read this) -- if ( open(CACHE, "< $cachefile{$type}") ) { -+ if ( open(CACHE, "< $cachefile{$prog_type}") ) { - # Get file contents less any comments - @cache = grep !/^[\#\s]/, <CACHE>; - close (CACHE); -@@ -1281,47 +1640,40 @@ - for (@cache) { - # Populate %prog from cache - chomp(); -- my ($index, $type, $name, $pid, $available, $episode, $versions, $duration, $desc, $channel, $categories, $thumbnail, $timeadded) = split /\|/; -- # Create data structure with prog data -- $prog_old{$pid} = { -- 'index' => $index, -- 'name' => $name, -- 'episode' => $episode, -- 'desc' => $desc, -- 'available' => $available, -- 'duration' => $duration, -- 'versions' => $versions, -- 'channel' => $channel, -- 'categories' => $categories, -- 'thumbnail' => $thumbnail, -- 'type' => $type, -- 'timeadded' => $timeadded, -- }; -- $index_pid_old{$index} = $pid; -+ # Get cache line -+ my @record = split /\|/; -+ my %record_entries; -+ # Update fields in %prog hash for $pid -+ $record_entries{$_} = shift @record for @cache_format; -+ $prog_old{ $record_entries{pid} } = \%record_entries; -+ $index_pid_old{ $record_entries{index} } = $record_entries{pid}; - } - } - - # if a cache file doesn't exist/corrupted, flush option is specified or original file is older than $cache_sec then download new data -- if ( (! @cache) || (! -f $cachefile{$type}) || $opt{flush} || ($now >= ( stat($cachefile{$type})->mtime + $cache_secs )) ) { -+ if ( (! @cache) || (! -f $cachefile{$prog_type}) || $opt{flush} || ($now >= ( stat($cachefile{$prog_type})->mtime + $cache_secs )) ) { - -- # Podcast only -- get_podcast_links() if $type eq 'podcast'; -+ # BBC Podcast only -+ get_links_bbcpodcast() if $prog_type eq 'podcast'; - -- # Radio and TV -- get_links_atom( $type, \%{$channels{$type}} ) if $type =~ /(tv|radio)/; -+ # ITV only -+ get_links_itv( \%{$channels{$prog_type}} ) if $prog_type eq 'itv'; -+ -+ # BBC Radio and TV -+ get_links_bbciplayer( $prog_type, \%{$channels{$prog_type}} ) if $prog_type =~ /^(tv|radio)$/; - - # Sort indexes - sort_indexes(); - - # Open cache file for writing -- unlink $cachefile{$type}; -+ unlink $cachefile{$prog_type}; - my $now = time(); -- if ( open(CACHE, "> $cachefile{$type}") ) { -- print CACHE "#Index|Type|Name|Pid|Available|Episode|Versions|Duration|Desc|Channel|Categories|Thumbnail|TimeAdded\n"; -+ if ( open(CACHE, "> $cachefile{$prog_type}") ) { -+ print CACHE "#".(join '|', @cache_format)."\n"; - for (sort {$a <=> $b} keys %index_pid) { - my $pid = $index_pid{$_}; - # Only write entries for correct prog type -- if ($prog{$pid}{type} eq $type) { -+ if ($prog{$pid}{type} eq $prog_type) { - # Merge old and new data to retain timestamps - # if the entry was in old cache then retain timestamp from old entry - if ( $prog_old{$pid}{timeadded} ) { -@@ -1332,12 +1684,17 @@ - list_prog_entry( $pid, 'Added: ' ); - } - # write to cache file -- print CACHE "$_|$prog{$pid}{type}|$prog{$pid}{name}|$pid|$prog{$pid}{available}|$prog{$pid}{episode}|$prog{$pid}{versions}|$prog{$pid}{duration}|$prog{$pid}{desc}|$prog{$pid}{channel}|$prog{$pid}{categories}|$prog{$pid}{thumbnail}|$prog{$pid}{timeadded}\n"; -+ $prog{$pid}{pid} = $pid; -+ # Write each field into cache line -+ for my $field (@cache_format) { -+ print CACHE $prog{$pid}{$field}.'|'; -+ } -+ print CACHE "\n"; - } - } - close (CACHE); - } else { -- logger "WARNING: Couldn't open cache file '$cachefile{$type}' for writing\n"; -+ logger "WARNING: Couldn't open cache file '$cachefile{$prog_type}' for writing\n"; - } - - -@@ -1354,172 +1711,434 @@ - # Usage: download_programme (<pid>) - sub download_programme { - my $pid = shift; -+ my %streamdata; -+ my %version_pids; -+ my $return; - - # Setup user-agent -- # Switch off automatic redirects -- my $ua = LWP::UserAgent->new( requests_redirectable => [] ); -- # Setup user agent -- $ua->timeout([$lwp_request_timeout]); -- $ua->proxy( ['http'] => $proxy_url ); -- $ua->cookie_jar( HTTP::Cookies->new( file => $cookiejar, autosave => 1, ignore_discard => 1 ) ); -+ my $ua = create_ua('desktop'); - -- my $dir = $download_dir{ $prog{$pid}{type} }; -- $prog{$pid}{ext} = 'mov'; -+ # download depending on the prog type -+ logger "INFO: Attempting to Download $prog{$pid}{type}: $prog{$pid}{name} - $prog{$pid}{episode}\n"; - -- # If were a podcast... -+ # ITV TV -+ if ( $prog{$pid}{type} eq 'itv' ) { -+ # stream data -+ # Display media stream data if required -+ if ( $opt{streaminfo} ) { -+ display_stream_info( $pid, undef, 'all' ); -+ $opt{quiet} = 1; -+ return 'skip'; -+ } -+ return download_programme_itv( $ua, $pid ); -+ } -+ -+ # BBC Podcasts - if ( $prog{$pid}{type} eq 'podcast' ) { -- # Determine the correct filename and extension for this download -- my $filename_orig = $pid; -- $prog{$pid}{ext} = $pid; -- $filename_orig =~ s|^.+/(.+?)\.\w+$|$1|g; -- $prog{$pid}{ext} =~ s|^.*\.(\w+)$|$1|g; -- $prog{$pid}{fileprefix} = generate_download_filename_prefix($pid, $dir, $opt{fileprefix} || "<longname> - <episode> $filename_orig"); -- $prog{$pid}{dir} = $dir; -- logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -- my $file_done = "${dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -- my $file = "${dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -- $prog{$pid}{filename} = $file_done; -- if ( -f $file_done ) { -- logger "WARNING: File $file_done already exists\n\n"; -- return 1; -+ # stream data not available -+ return 'skip' if $opt{streaminfo}; -+ return download_programme_podcast( $ua, $pid ); -+ } -+ -+ # For BBC Radio/TV we might have a pid with no prog_type - determine here first -+ ( $prog{$pid}{type}, $prog{$pid}{longname}, %version_pids ) = get_version_pids( $ua, $pid ); -+ -+ # BBC TV -+ if ( $prog{$pid}{type} eq 'tv' ) { -+ -+ # Deal with BBC TV fallback modes -+ # Valid modes are iphone,rtmp,flashhigh,flashnormal,flashwii,n95_wifi -+ $opt{vmode} = 'flashhigh,flashnormal' if $opt{vmode} eq 'rtmp' || $opt{vmode} eq 'flash'; -+ # Defaults -+ if ( $opt{vmode} eq 'auto' || ! $opt{vmode} ) { -+ if ( ! exists_in_path($rtmpdump) ) { -+ $opt{vmode} = 'iphone'; -+ } else { -+ $opt{vmode} = 'iphone,flashhigh,flashnormal'; -+ } - } -+ # Expand the modes into a loop -+ logger "INFO: $opt{vmode} modes will be tried\n"; -+ for my $mode ( split /,/, $opt{vmode} ) { -+ chomp( $mode ); -+ logger "INFO: Attempting to download using $mode mode\n"; -+ $return = download_programme_tv( $ua, $pid, $mode, \%version_pids ); -+ logger "DEBUG: Download using $mode mode return code: '$return'\n" if $opt{debug}; - -- # Skip from here if we are only testing downloads -- return 1 if $opt{test}; -+ # Give up trying alternative download methods -+ return 2 if $return eq 'abort'; - -- # Create symlink filename if required -- my $file_symlink; -- if ( $opt{symlink} ) { -- # Substitute the fields for the pid -- $file_symlink = substitute_fields( $pid, $opt{symlink} ); -+ # Return to retry loop if successful or retry requested -+ return 'retry' if $return eq 'retry'; -+ -+ # Return to retry loop and do nothing -+ return 'skip' if $return eq 'skip'; -+ -+ # Return 0 if successful -+ return 0 if ! $return; -+ -+ # Return failed if there is no 'next' -+ return 1 if $return ne 'next'; - } -- -- return download_podcast_stream( $ua, $pid, $file, $file_done, $file_symlink ); - } - -- logger "INFO: Attempting to Download: $prog{$pid}{name} - $prog{$pid}{episode}\n"; -+ # BBC Radio -+ if ( $prog{$pid}{type} eq 'radio' ) { -+ # This will always be the pid version for radio -+ $prog{$pid}{version} = 'default'; -+ # Display media stream data if required -+ if ( $opt{streaminfo} ) { -+ display_stream_info( $pid, $version_pids{default}, 'all' ); -+ $opt{quiet} = 1; -+ return 'skip'; -+ } -+ -+ # Deal with radio fallback modes -+ # Valid modes are mp3|iphone,flash|rtmp,real|ra -+ # Defaults -+ $opt{amode} = 'iphone,flash,real' if $opt{amode} eq 'auto' || ! $opt{amode}; -+ -+ # Expand the modes into a loop -+ logger "INFO: $opt{amode} modes will be tried\n"; -+ for my $mode ( split /,/, $opt{amode} ) { -+ chomp( $mode ); -+ logger "INFO: Attempting to download using $mode mode\n"; -+ # RealAudio -+ if ( $mode =~ /^(real|ra)/ ) { -+ $return = download_programme_radio_realaudio( $ua, $pid, \%version_pids ); -+ -+ # FlashAudio -+ } elsif ( $mode =~ /^(flash|rtmp)/ ) { -+ $return = download_programme_radio_flashaudio( $ua, $pid, $mode, \%version_pids ); -+ -+ # iPhone -+ } elsif ( $mode =~ /^(iphone|mp3)/ ) { -+ $return = download_programme_radio_iphone( $ua, $pid, \%version_pids ); -+ } -+ logger "DEBUG: Download using $mode mode return code: '$return'\n" if $opt{debug}; - -- # Get version => pid hash -- my ( $type, $title, %version_pids ) = get_version_pids( $ua, $pid ); -+ # Give up trying alternative download methods -+ return 2 if $return eq 'abort'; - -- # Extract Long Name, e.g.: iplayer.episode.setTitle("DIY SOS: Series 16: Swansea"); -- $prog{$pid}{longname} = $title; -+ # Not going to allow retries here until rtmpdump/flashaudio exits correctly - so just just skip to next mode for now -+ $return = 'next' if $return eq 'retry'; -+ ## Return to retry loop if successful or retry requested -+ #return 'retry' if $return eq 'retry'; - -- # Strip off the episode name -- $prog{$pid}{longname} =~ s/^(.+):.*?$/$1/g; -+ # Return to retry loop and do nothing -+ return 'skip' if $return eq 'skip'; - -- # Detect if this content is for radio -- my $usemp3 = 0; -- if ( $type eq 'radio' ) { -+ # Return 0 if successful -+ return 0 if ! $return; - -- # Display media stream data if required -- if ( $opt{streaminfo} ) { -- get_media_stream_data( $pid, $version_pids{'default'}, 'all' ); -- return 1; -+ # Return failed if there is no 'next' -+ return 1 if $return ne 'next'; - } -+ } - -- # Type is definitely radio -- $prog{$pid}{type} = 'radio'; -- $dir = $download_dir{ $prog{$pid}{type} }; -- -- # Check for mp3 stream - unless realaudio option is specified -- if ( ! $opt{realaudio} ) { -- # Check for iphone mp3 radio stream -- if ( get_media_stream_data( $pid, $version_pids{default}, 'iphone' ) ) { -- $usemp3 = 1; -- $prog{$pid}{ext} = 'mp3'; -- logger "INFO: MP3 stream media is available\n" if $opt{verbose}; -- -- # if mp3audio option is specified do not fallback to realaudio -- } elsif ( $opt{mp3audio} ) { -- logger "ERROR: No MP3 stream media is available - not falling back to RealAudio\n"; -- return 1; -+ # If we get here then we have failed -+ return 1; -+} - -- # if not then force realaudio option as fallback -- } else { -- $opt{realaudio} = 1; -- logger "INFO: No MP3 stream media is available - falling back to RealAudio\n" if $opt{verbose}; -- } -- } - -- # Use realplayer stream -- if ( $opt{realaudio} ) { - -- # Check dependancies for radio programme transcoding / streaming -- # Check if we need 'tee' -- if ( (! exists_in_path($tee)) && $opt{stdout} && (! $opt{nowrite}) ) { -- logger "\nERROR: $tee does not exist in path, skipping\n"; -- return 20; -- } -- # Check if we have mplayer and lame -- if ( (! $opt{wav}) && (! $opt{raw}) && (! exists_in_path($lame)) ) { -- logger "\nWARNING: Required $lame does not exist, falling back to wav mode\n"; -- $opt{wav} = 1; -- } -- if (! exists_in_path($mplayer)) { -- logger "\nERROR: Required $mplayer does not exist, skipping\n"; -- return 20; -- } -+sub download_programme_itv { -+ my ( $ua, $pid ) = ( @_ ); -+ my %streamdata; -+ -+ # Check for mplayer (required) -+ if (! exists_in_path($mplayer)) { -+ logger "\nERROR: Required $mplayer does not exist, skipping\n"; -+ return 21; -+ } - -- my $url_2 = get_media_stream_data( $pid, $version_pids{default}, 'realaudio' ); -+ $prog{$pid}{dir} = $download_dir{ $prog{$pid}{type} }; -+ $prog{$pid}{pid} = $pid; -+ $prog{$pid}{ext} = 'mp4'; -+ $prog{$pid}{ext} = 'asf' if $opt{raw}; - -- logger "INFO: Version = $prog{$pid}{version}\n" if $opt{verbose}; -- logger "INFO: Stage 2 URL = $url_2\n" if $opt{verbose}; -+ my @url_list = %{get_media_stream_data( $pid, undef, 'itv')}->{streamurl}; - -- # Report error if no versions are available -- if ( ! $url_2 ) { -- logger "ERROR: No Stage 2 URL\n" if $opt{verbose}; -- return 15; -- } -+ # Get and set more meta data - Set the %prog values from metadata if they aren't already set -+ my %metadata = get_pid_metadata($ua, $pid); -+ for ( qw/ name episode available duration thumbnail desc guidance / ) { -+ $prog{$pid}{$_} = $metadata{$_} if ! $prog{$pid}{$_}; -+ } - -- # Determine the correct filenames for this download -- $prog{$pid}{ext} = 'mp3'; -- $prog{$pid}{ext} = 'ra' if $opt{raw}; -- $prog{$pid}{ext} = 'wav' if $opt{wav}; -- $prog{$pid}{fileprefix} = generate_download_filename_prefix( $pid, ${dir}, $opt{fileprefix} || "<longname> - <episode> <pid>" ); -- logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -- $prog{$pid}{dir} = $dir; -- my $file_done = "${dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -- my $file = "${dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -- $prog{$pid}{filename} = $file_done; -- if ( -f $file_done ) { -- logger "WARNING: File $file_done already exists\n\n"; -- return 1; -- } -+ $prog{$pid}{fileprefix} = generate_download_filename_prefix( $pid, $prog{$pid}{dir}, $opt{fileprefix} || "<name> <pid>" ); -+ logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -+ # Create a subdir if there are multiple parts -+ if ($#url_list > 0) { -+ $prog{$pid}{dir} .= "/$prog{$pid}{fileprefix}"; -+ logger "INFO: Creating subdirectory $prog{$pid}{dir} for programme\n" if $opt{verbose}; -+ mkpath $prog{$pid}{dir} if ! -d $prog{$pid}{dir}; -+ } -+ my $file_done = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -+ my $file = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -+ $prog{$pid}{filename} = $file_done; - -- # Skip from here if we are only testing downloads -- return 1 if $opt{test}; -+ # Display metadata -+ display_metadata( $prog{$pid}, qw/ pid index name duration available expiry desc / ); - -- # Create symlink filename if required -- my $file_symlink; -- if ( $opt{symlink} ) { -- # Substitute the fields for the pid -- $file_symlink = substitute_fields( $pid, $opt{symlink} ); -- } -+ # Skip from here if we are only testing downloads -+ return 1 if $opt{test}; - -- # Do the audio download -- return download_rtsp_stream( $ua, $url_2, $file, $file_done, $file_symlink, $pid ); -- } -+ return download_stream_mms_video( $ua, (join '|', @url_list), $file, $file_done, $pid ); -+} -+ -+ -+ -+sub download_programme_podcast { -+ my ( $ua, $pid ) = ( @_ ); -+ my %streamdata; -+ -+ $prog{$pid}{dir} = $download_dir{ $prog{$pid}{type} }; -+ -+ # Determine the correct filename and extension for this download -+ my $filename_orig = $pid; -+ $prog{$pid}{ext} = $pid; -+ $filename_orig =~ s|^.+/(.+?)\.\w+$|$1|g; -+ $prog{$pid}{ext} =~ s|^.*\.(\w+)$|$1|g; -+ $prog{$pid}{fileprefix} = generate_download_filename_prefix($pid, $prog{$pid}{dir}, $opt{fileprefix} || "<longname> - <episode> $filename_orig"); -+ logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -+ my $file_done = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -+ my $file = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -+ $prog{$pid}{filename} = $file_done; -+ if ( -f $file_done && stat($file_done)->size > $min_download_size ) { -+ logger "WARNING: File $file_done already exists\n\n"; -+ return 1; -+ } -+ -+ # Skip from here if we are only testing downloads -+ return 1 if $opt{test}; -+ -+ # Create symlink filename if required -+ my $file_symlink; -+ if ( $opt{symlink} ) { -+ # Substitute the fields for the pid -+ $file_symlink = substitute_fields( $pid, $opt{symlink} ); -+ } -+ -+ return download_stream_podcast( $ua, $pid, $file, $file_done, $file_symlink ); -+} -+ -+ -+ -+sub download_programme_radio_realaudio { -+ my $ua = shift; -+ my $pid = shift; -+ my %version_pids = %{@_[0]}; -+ my %streamdata; -+ -+ # Check dependancies for radio programme transcoding / streaming -+ # Check if we need 'tee' -+ if ( (! exists_in_path($tee)) && $opt{stdout} && (! $opt{nowrite}) ) { -+ logger "\nERROR: $tee does not exist in path, skipping\n"; -+ return 'abort'; -+ } -+ if (! exists_in_path($mplayer)) { -+ logger "\nWARNING: Required $mplayer does not exist\n"; -+ return 'next'; -+ } -+ # Check if we have mplayer and lame -+ if ( (! $opt{wav}) && (! $opt{raw}) && (! exists_in_path($lame)) ) { -+ logger "\nWARNING: Required $lame does not exist, will save file in wav format\n"; -+ $opt{wav} = 1; -+ } -+ -+ $prog{$pid}{dir} = $download_dir{ $prog{$pid}{type} }; -+ $prog{$pid}{ext} = 'mp3'; -+ $prog{$pid}{ext} = 'ra' if $opt{raw}; -+ $prog{$pid}{ext} = 'wav' if $opt{wav}; - -+ my $url_2 = %{get_media_stream_data( $pid, $version_pids{default}, 'realaudio')}->{streamurl}; -+ -+ # Report error if no versions are available -+ if ( ! $url_2 ) { -+ logger "WARNING: RealAudio version not available\n"; -+ return 'next'; - } else { -- # Type is definitely tv -- $prog{$pid}{type} = 'tv'; -- $dir = $download_dir{ $prog{$pid}{type} }; -+ logger "INFO: Stage 2 URL = $url_2\n" if $opt{verbose}; -+ } -+ -+ # Determine the correct filenames for this download -+ $prog{$pid}{fileprefix} = generate_download_filename_prefix( $pid, $prog{$pid}{dir}, $opt{fileprefix} || "<longname> - <episode> <pid>" ); -+ logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -+ my $file_done = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -+ my $file = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -+ $prog{$pid}{filename} = $file_done; -+ if ( -f $file_done ) { -+ logger "WARNING: File $file_done already exists\n\n"; -+ return 'abort'; - } - -+ # Skip from here if we are only testing downloads -+ return 'abort' if $opt{test}; - -- # iPhone mp3/h.264 stream downloading... -+ # Create symlink filename if required -+ my $file_symlink; -+ if ( $opt{symlink} ) { -+ # Substitute the fields for the pid -+ $file_symlink = substitute_fields( $pid, $opt{symlink} ); -+ } - -- # Check if we have vlc - if not use iPhone mode -- if ( $opt{n95} && (! exists_in_path($vlc)) ) { -- logger "\nWARNING: Required $vlc does not exist, falling back to iPhone mode\n"; -- $opt{n95} = 0; -- } -+ # Do the audio download -+ return download_stream_rtsp( $ua, $url_2, $file, $file_done, $file_symlink, $pid ); -+} - - -+ -+sub download_programme_radio_iphone { -+ my $ua = shift; -+ my $pid = shift; -+ my %version_pids = %{@_[0]}; -+ my %streamdata; -+ my $url_2; -+ -+ $prog{$pid}{dir} = $download_dir{ $prog{$pid}{type} }; -+ $prog{$pid}{ext} = 'mp3'; -+ -+ my $url_2 = %{get_media_stream_data( $pid, $version_pids{ $prog{$pid}{version} }, 'iphone')}->{streamurl}; -+ -+ # Report error if no versions are available -+ if ( ! $url_2 ) { -+ logger "WARNING: iPhone stream media not available\n"; -+ return 'next'; -+ } else { -+ logger "INFO: Stage 2 URL = $url_2\n" if $opt{verbose}; -+ } -+ -+ # Determine the correct filenames for this download -+ $prog{$pid}{fileprefix} = generate_download_filename_prefix( $pid, $prog{$pid}{dir}, $opt{fileprefix} || "<longname> - <episode> <pid> <version>" ); -+ logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -+ my $file_done = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -+ my $file = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -+ $prog{$pid}{filename} = $file_done; -+ if ( -f $file_done ) { -+ logger "WARNING: File $file_done already exists\n\n"; -+ return 'abort'; -+ } -+ -+ # Skip from here if we are only testing downloads -+ return 'abort' if $opt{test}; -+ -+ # Create symlink filename if required -+ my $file_symlink; -+ if ( $opt{symlink} ) { -+ # Substitute the fields for the pid -+ $file_symlink = substitute_fields( $pid, $opt{symlink} ); -+ } -+ -+ my $return; -+ # Disable proxy here if required -+ $ua->proxy( ['http'] => undef ) if $opt{partialproxy}; -+ $return = download_stream_iphone( $ua, $url_2, $pid, $file, $file_done, $file_symlink, 0 ); -+ # Re-enable proxy here if required -+ $ua->proxy( ['http'] => $proxy_url ) if $opt{partialproxy}; -+ -+ return $return; -+} -+ -+ -+ -+sub download_programme_radio_flashaudio { -+ my $ua = shift; -+ my $pid = shift; -+ my $mode = shift; -+ my %version_pids = %{@_[0]}; -+ my %streamdata; -+ my $url_2; -+ -+ # Force raw mode if ffmpeg is not installed -+ if ( ! exists_in_path($ffmpeg) ) { -+ logger "\nWARNING: $ffmpeg does not exist - not converting flv file\n"; -+ $opt{raw} = 1; -+ } -+ # Disable rtmp modes if rtmpdump does not exist -+ if ( ! exists_in_path($rtmpdump) ) { -+ logger "\nERROR: Required program $rtmpdump does not exist (see http://linuxcentre.net/getiplayer/installation and http://linuxcentre.net/getiplayer/download)\n"; -+ return 'next'; -+ } -+ -+ $prog{$pid}{dir} = $download_dir{ $prog{$pid}{type} }; -+ $prog{$pid}{ext} = 'mp3'; -+ $prog{$pid}{ext} = 'flv' if $opt{raw}; -+ -+ logger "INFO: Trying to get media stream metadata for flashaudio RTMP mode\n" if $opt{verbose}; -+ %streamdata = %{ get_media_stream_data( $pid, $version_pids{ $prog{$pid}{version} }, 'flashaudio') }; -+ $url_2 = $streamdata{streamurl}; -+ if ( ! $url_2 ) { -+ logger "WARNING: No flashaudio version available\n"; -+ return 'next'; -+ } -+ -+ # Determine the correct filenames for this download -+ $prog{$pid}{fileprefix} = generate_download_filename_prefix( $pid, $prog{$pid}{dir}, $opt{fileprefix} || "<longname> - <episode> <pid> <version>" ); -+ logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -+ my $file_done = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -+ my $file = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -+ $prog{$pid}{filename} = $file_done; -+ if ( -f $file_done ) { -+ logger "WARNING: File $file_done already exists\n\n"; -+ return 'abort'; -+ } -+ -+ # Skip from here if we are only testing downloads -+ return 'abort' if $opt{test}; -+ -+ # Create symlink filename if required -+ my $file_symlink; -+ if ( $opt{symlink} ) { -+ # Substitute the fields for the pid -+ $file_symlink = substitute_fields( $pid, $opt{symlink} ); -+ } -+ -+ # Do the RTMP flashaudio download -+ return download_stream_rtmp( $ua, $streamdata{streamurl}, $pid, $mode, $streamdata{application}, $streamdata{tcurl}, $streamdata{authstring}, $streamdata{swfurl}, $file, $file_done, $file_symlink ); -+} -+ -+ -+ -+# Usage: download_programme_tv (<pid>) -+sub download_programme_tv { -+ my $ua = shift; -+ my $pid = shift; -+ my $mode = shift; -+ my %version_pids = %{@_[0]}; -+ my %streamdata; - my $url_2; - my $got_url; - -+ # Check if we have vlc - if not use iPhone mode -+ if ( $opt{vmode} eq 'n95' && (! exists_in_path($vlc)) ) { -+ logger "\nWARNING: Required $vlc does not exist\n"; -+ return 'next'; -+ } -+ # if rtmpdump does not exist -+ if ( $mode =~ /^(rtmp|flash)/ && ! exists_in_path($rtmpdump)) { -+ logger "WARNING: Required program $rtmpdump does not exist (see http://linuxcentre.net/getiplayer/installation and http://linuxcentre.net/getiplayer/download)\n"; -+ return 'next'; -+ } -+ # Force raw mode if ffmpeg is not installed -+ if ( $mode =~ /^(flash|rtmp)/ && ! exists_in_path($ffmpeg)) { -+ logger "\nWARNING: $ffmpeg does not exist - not converting flv file\n"; -+ $opt{raw} = 1; -+ } -+ -+ $prog{$pid}{dir} = $download_dir{ $prog{$pid}{type} }; -+ $prog{$pid}{ext} = 'mov'; -+ # Lookup table to determine which ext to use for different download methods -+ my %stream_ext = ( -+ iphone => 'mov', -+ flashhigh => 'mp4', -+ flashnormal => 'avi', -+ flashwii => 'avi', -+ n95_wifi => '3gp', -+ n95_3g => '3gp', -+ ); -+ $prog{$pid}{ext} = $stream_ext{$mode} if not $opt{raw}; -+ $prog{$pid}{ext} = 'flv' if $mode =~ /^(flash|rtmp)/ && $opt{raw}; -+ - # Do this for each version tried in this order (if they appeared in the content) - for my $version ( @version_search_list ) { - -@@ -1528,52 +2147,41 @@ - logger "INFO: Checking existence of $version version\n"; - $prog{$pid}{version} = $version; - logger "INFO: Version = $prog{$pid}{version}\n" if $opt{verbose}; -- if( ! $opt{rtmp} ) { -- $url_2 = get_iphone_stream_download_url( $ua, $version_pids{$version} ); -- } else { -- $url_2 = get_media_stream_data( $pid, $version_pids{ $prog{$pid}{version} }, 'flashhigh' ); -- } -- $got_url = 1; -+ # Try to get stream data -+ %streamdata = %{ get_media_stream_data( $pid, $version_pids{ $prog{$pid}{version} }, $mode) }; -+ $url_2 = $streamdata{streamurl}; - } - # Break out of loop if we have an actual URL -- last if $got_url && $url_2; -- } -- -- # Report error if no versions are available -- if ( ! $got_url ) { -- logger "ERROR: No versions exist for download\n"; -- return 14; -+ last if $url_2; - } - - # Display media stream data if required - if ( $opt{streaminfo} ) { -- get_media_stream_data( $pid, $version_pids{'default'}, 'all' ); -- return 1; -+ display_stream_info( $pid, $version_pids{ $prog{$pid}{version} }, 'all' ); -+ $opt{quiet} = 1; -+ return 'skip'; - } - -- # Report error if failed to get URL for version -- if ( $got_url && ! $url_2 ) { -- logger "ERROR: No Stage 2 URL\n" if $opt{verbose}; -- # If mp3 audio stream does not exist force realaudio mode and retry -- if ( $usemp3 && ! $opt{mp3audio}) { -- $opt{realaudio} = 1; -- return 'retry'; -- } -- return 15; -+ # Report error if no versions are available -+ if ( ! $url_2 ) { -+ logger "WARNING: No $mode versions available\n"; -+ return 'next'; - } -- -+ - # Determine the correct filenames for this download -- $prog{$pid}{fileprefix} = generate_download_filename_prefix( $pid, $dir, $opt{fileprefix} || "<longname> - <episode> <pid> <version>" ); -+ $prog{$pid}{fileprefix} = generate_download_filename_prefix( $pid, $prog{$pid}{dir}, $opt{fileprefix} || "<longname> - <episode> <pid> <version>" ); - logger "\rINFO: File name prefix = $prog{$pid}{fileprefix} \n"; -- $prog{$pid}{dir} = $dir; -- my $file_done = "${dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -- my $file = "${dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; -+ my $file_done = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.$prog{$pid}{ext}"; -+ my $file = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.partial.$prog{$pid}{ext}"; - $prog{$pid}{filename} = $file_done; - if ( -f $file_done ) { -- logger "WARNING: File $file_done already exists\n\n"; -- return 1; -+ logger "ERROR: File $file_done already exists\n\n"; -+ return 'abort'; - } - -+ # Skip from here if we are only testing downloads -+ return 'abort' if $opt{test}; -+ - # Create symlink filename if required - my $file_symlink; - if ( $opt{symlink} ) { -@@ -1581,53 +2189,34 @@ - $file_symlink = substitute_fields( $pid, $opt{symlink} ); - } - -- # Skip from here if we are only testing downloads -- return 1 if $opt{test}; -- -- # Get subtitles if they exist and are required -+ # Get subtitles if they exist and are required -+ # best to do this before d/l of file so that the subtitles can be enjoyed while download progresses - my $subfile_done; - my $subfile; - if ( $opt{subtitles} ) { -- $subfile_done = "${dir}/$prog{$pid}{fileprefix}.srt"; -- $subfile = "${dir}/$prog{$pid}{fileprefix}.partial.srt"; -- download_subtitles( $ua, $subfile, $version_pids{ $prog{$pid}{version} } ); -+ $subfile_done = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.srt"; -+ $subfile = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}.partial.srt"; -+ $ua->proxy( ['http'] => undef ) if $opt{partialproxy}; -+ download_stream_subtitles( $ua, $subfile, $version_pids{ $prog{$pid}{version} } ); -+ $ua->proxy( ['http'] => $proxy_url ) if $opt{partialproxy}; - } - - my $return; - # Do rtmp download -- if ( $opt{rtmp} ) { -- # Get player url -- my %metadata = get_pid_metadata( $ua, $pid ); -- # get this redirected page and find out where it is redirected to -- my ($ub, $request, $response, $prog_url); -- $ub = new LWP::UserAgent; -- $request = new HTTP::Request HEAD => $metadata{player}; -- $response = $ub->request($request); -- $prog_url = $response->request->url; -- $return = download_h264_rtmp_stream( $ua, $url_2, $prog_url, $file, $file_done, $file_symlink ); -+ if ( $mode =~ /^(rtmp|flash)/ ) { -+ $return = download_stream_rtmp( $ua, $streamdata{streamurl}, $pid, $mode, $streamdata{application}, $streamdata{tcurl}, $streamdata{authstring}, $streamdata{swfurl}, $file, $file_done, $file_symlink ); - - # Do the N95 h.264 download -- } elsif ( $opt{n95} ) { -- my $url = get_media_stream_data( $pid, $version_pids{ $prog{$pid}{version} }, 'n95_wifi' ); -- $return = download_h264_low_stream( $ua, $url, $file, $file_done ); -+ } elsif ( $mode =~ /^n95/ ) { -+ $return = download_stream_h264_low( $ua, $url_2, $file, $file_done, $pid, $mode ); - - # Do the iPhone h.264 download -- } elsif ( $prog{$pid}{type} eq 'tv' ) { -- # Disable proxy here if required -- $ua->proxy( ['http'] => undef ) if $opt{partialproxy}; -- $return = download_iphone_stream( $ua, $url_2, $file, $file_done, $file_symlink, 1 ); -- # Re-enable proxy here if required -- $ua->proxy( ['http'] => $proxy_url ) if $opt{partialproxy}; -- -- # Do the iPhone mp3 download -- } elsif ( $prog{$pid}{type} eq 'radio' ) { -+ } else { - # Disable proxy here if required - $ua->proxy( ['http'] => undef ) if $opt{partialproxy}; -- $return = download_iphone_stream( $ua, $url_2, $file, $file_done, $file_symlink, 0 ); -+ $return = download_stream_iphone( $ua, $url_2, $pid, $file, $file_done, $file_symlink, 1 ); - # Re-enable proxy here if required - $ua->proxy( ['http'] => $proxy_url ) if $opt{partialproxy}; -- # If the iphone mp3 download fails then it's probably not ready yet so retry using realaudio -- $opt{realaudio} = 1 if $return eq 'retry'; - } - - # Rename the subtitle file accordingly -@@ -1647,13 +2236,12 @@ - - - # Download Subtitles, convert to srt(SubRip) format and apply time offset --sub download_subtitles { -+sub download_stream_subtitles { - my ( $ua, $file, $verpid ) = @_; - my $suburl; - my $subs; - logger "INFO: Getting Subtitle metadata for $verpid\n" if $opt{verbose}; -- $suburl = get_media_stream_data( undef, $verpid, 'subtitles' ); -- -+ $suburl = %{get_media_stream_data( undef, $verpid, 'subtitles')}->{streamurl}; - # Return if we have no url - if (! $suburl) { - logger "INFO: Subtitles not available\n"; -@@ -1793,12 +2381,12 @@ - - # Get title - # <title>Amazon with Bruce Parry: Episode 1</title> -- my ( $title, $type ); -+ my ( $title, $prog_type ); - $title = $1 if $xml =~ m{<title>\s*(.+?)\s*<\/title>}; - - # Get type -- $type = 'tv' if grep /kind="programme"/, $xml; -- $type = 'radio' if grep /kind="radioProgramme"/, $xml; -+ $prog_type = 'tv' if grep /kind="programme"/, $xml; -+ $prog_type = 'radio' if grep /kind="radioProgramme"/, $xml; - - # Split into <item kind="programme"> sections - for ( split /<item\s+kind="(radioProgramme|programme)"/, $xml ) { -@@ -1812,310 +2400,389 @@ - $version_pids{$version} = $verpid; - logger "INFO: Version: $version, VersionPid: $verpid\n" if $opt{verbose}; - } -+ -+ # Extract Long Name, e.g.: iplayer.episode.setTitle("DIY SOS: Series 16: Swansea"), Strip off the episode name -+ $title =~ s/^(.+):.*?$/$1/g; -+ - # Add to prog hash - $prog{$pid}{versions} = join ',', keys %version_pids; -- return ( $type, $title, %version_pids ); -+ return ( $prog_type, $title, %version_pids ); - } - - - - # Gets media streams data for this version pid --# $media = all|flashhigh|flashnormal|iphone|flashwii|n95_wifi|n95_3g|mobile|flashaudio|realaudio|wma|subtitles -+# $media = all|itv|flashhigh|flashnormal|iphone|flashwii|n95_wifi|n95_3g|mobile|flashaudio|realaudio|wma|subtitles - sub get_media_stream_data { - my ( $pid, $verpid, $media ) = @_; -- my %streams; -- my $ua = LWP::UserAgent->new(); -+ my %data; -+ - # Setup user agent with redirection enabled -- $ua->timeout([$lwp_request_timeout]); -- $ua->proxy( ['http'] => $proxy_url ); -- $ua->cookie_jar( HTTP::Cookies->new( file => $cookiejar, autosave => 1, ignore_discard => 1 ) ); -+ my $ua = create_ua('desktop'); - $opt{quiet} = 0 if $opt{streaminfo}; -- logger "INFO: Getting media stream metadata for $prog{$pid}{name} - $prog{$pid}{episode}, $verpid\n" if $pid; -- my $xml1 = request_url_retry($ua, $media_stream_data_prefix.$verpid, 3, '', ''); -- logger "\n$xml1\n" if $opt{debug}; -- # flatten -- $xml1 =~ s/\n/ /g; - -- for my $xml ( split /<media/, $xml1 ) { -- $xml = "<media".$xml; -+ # ITV streams -+ if ( $prog{$pid}{type} eq 'itv' ) { -+ my $prog_type = 'itv'; -+ $data{$prog_type}{type} = 'ITV ASF Video stream'; -+ $opt{quiet} = 1 if $opt{streaminfo}; -+ $data{$prog_type}{streamurl} = join('|', get_stream_url_itv($ua, $pid) ); -+ $opt{quiet} = 0 if $opt{streaminfo}; -+ -+ # BBC streams -+ } else { -+ my $xml1 = request_url_retry($ua, $media_stream_data_prefix.$verpid, 3, '', ''); -+ logger "\n$xml1\n" if $opt{debug}; -+ # flatten -+ $xml1 =~ s/\n/ /g; -+ -+ for my $xml ( split /<media/, $xml1 ) { -+ $xml = "<media".$xml; -+ my $prog_type; -+ -+ # h.264 high quality stream -+ # <media kind="video" -+ # width="640" -+ # height="360" -+ # type="video/mp4" -+ # encoding="h264" > -+ # <connection -+ # priority="10" -+ # application="bbciplayertok" -+ # kind="level3" -+ # server="bbciplayertokfs.fplive.net" -+ # identifier="mp4:b000zxf4-H26490898078" -+ # authString="d52f77fede048f1ffd6587fd47446dee" -+ # /> -+ # application: bbciplayertok -+ # tcURL: rtmp://bbciplayertokfs.fplive.net:80/bbciplayertok -+ if ( $media =~ /^(flashhigh|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/mp4".+?encoding="h264".+?application="(.+?)".+?kind="level3"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -+ $prog_type = 'flashhigh'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{application}, $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{authstring} ) = ( $1, $2, $3, $4 ); -+ $data{$prog_type}{type} = 'Flash RTMP H.264 high quality stream'; -+ $data{$prog_type}{tcurl} = "rtmp://$data{$prog_type}{server}:80/$data{$prog_type}{application}"; -+ $data{$prog_type}{swfurl} = "http://www.bbc.co.uk/emp/9player.swf?revision=7276"; -+ $data{$prog_type}{streamurl} = "rtmp://$data{$prog_type}{server}:1935/ondemand?_fcs_vhost=$data{$prog_type}{server}&auth=$data{$prog_type}{authstring}&aifp=v001&slist=$data{$prog_type}{identifier}"; -+ } -+ -+ # h.264 normal quality stream -+ # <media kind="video" -+ # width="640" -+ # height="360" -+ # type="video/x-flv" -+ # encoding="vp6" > -+ # <connection -+ # priority="10" -+ # kind="akamai" -+ # server="cp41752.edgefcs.net" -+ # identifier="secure/b000zxf4-streaming90898078" -+ # authString="daEdSdgbcaibFa7biaobCaYdadyaTamazbq-biXsum-cCp-FqrECnEoGBwFvwG" -+ # /> -+ # </media> -+ # -+ # application (e.g.): ondemand?_fcs_vhost=cp41752.edgefcs.net&auth=daEcia8aQaRardxdwb_dCbvc0cPbLavc2cL-bjw5rj-cCp-JnlDCnzn.MEqHpxF&aifp=v001&slist=secure/b000gy717streaming103693754 -+ # tcURL: rtmp://88.221.26.165:80/ondemand?_fcs_vhost=cp41752.edgefcs.net&auth=daEcia8aQaRardxdwb_dCbvc0cPbLavc.2cL-bjw5rj-cCp-JnlDCnznMEqHpxF&aifp=v001&slist=secure/b000gy717streaming103693754 -+ if ( $media =~ /^(flashnormal|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/x-flv".+?encoding="vp6".+?kind="akamai"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -+ $prog_type = 'flashnormal'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{authstring} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{application} = "ondemand?_fcs_vhost=$data{$prog_type}{server}&auth=$data{$prog_type}{authstring}&aifp=v001&slist=$data{$prog_type}{identifier}"; -+ $data{$prog_type}{type} = 'Flash RTMP H.264 normal quality stream'; -+ $data{$prog_type}{tcurl} = "rtmp://$data{$prog_type}{server}:80/$data{$prog_type}{application}"; -+ $data{$prog_type}{swfurl} = "http://www.bbc.co.uk/emp/9player.swf?revision=7276"; -+ $data{$prog_type}{streamurl} = "rtmp://$data{$prog_type}{server}:1935/ondemand?_fcs_vhost=$data{$prog_type}{server}&auth=$data{$prog_type}{authstring}&aifp=v001&slist=$data{$prog_type}{identifier}"; -+ } -+ -+ # Wii h.264 standard quality stream -+ #<media kind="video" -+ # width="512" -+ # height="288" -+ # type="video/x-flv" -+ # encoding="spark" > -+ # <connection -+ # priority="10" -+ # kind="akamai" -+ # server="cp41752.edgefcs.net" -+ # identifier="secure/5242138581547639062" -+ # authString="daEd8dLbGaPaZdzdNcwd.auaydJcxcHandp-biX5YL-cCp-BqsECnxnGEsHwyE" -+ # /> -+ #</media> -+ # application (e.g.): ondemand?_fcs_vhost=cp41752.edgefcs.net&auth=daEcpc6cYbhdIakdWduc6bJdPbydbazdmdp-bjxPBF-cCp-GptFAoDqJBnHvzC&aifp=v001&slist=secure/b000g884xstreaming101052333 -+ # tcURL: rtmp: //88.221.26.173:1935/ondemand?_fcs_vhost=cp41752.edgefcs.net&auth=daEcpc6cYbhdIakdWduc6bJdPbydbazdmdp-bjxPBF-cCp-GptFAoDqJBnHvzC&aifp=v001&slist=secure/b000g884xstreaming101052333 -+ # swfUrl: http://www.bbc.co.uk/emp/iplayer/7player.swf?revision=3897 -+ if ( $media =~ /^(flashwii|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/x-flv".+?encoding="spark".+?kind="akamai"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -+ $prog_type = 'flashwii'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{authstring} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{application} = "ondemand?_fcs_vhost=$data{$prog_type}{server}&auth=$data{$prog_type}{authstring}&aifp=v001&slist=$data{$prog_type}{identifier}"; -+ $data{$prog_type}{type} = 'Flash RTMP H.264 Wii stream'; -+ $data{$prog_type}{tcurl} = "rtmp://$data{$prog_type}{server}:1935/$data{$prog_type}{application}"; -+ $data{$prog_type}{swfurl} = "http://www.bbc.co.uk/emp/iplayer/7player.swf?revision=3897"; -+ $data{$prog_type}{streamurl} = "rtmp://$data{$prog_type}{server}:1935/ondemand?_fcs_vhost=$data{$prog_type}{server}&auth=$data{$prog_type}{authstring}&aifp=v001&slist=$data{$prog_type}{identifier}"; -+ } -+ -+ # iPhone h.264/mp3 stream -+ #<media kind="video" -+ # width="480" -+ # height="272" -+ # type="video/mp4" -+ # encoding="h264" > -+ # <connection -+ # priority="10" -+ # kind="sis" -+ # server="http://www.bbc.co.uk/mediaselector/3/auth/stream/" -+ # identifier="5242138581547639062" -+ # href="http://www.bbc.co.uk/mediaselector/3/auth/stream/5242138581547639062.mp4" -+ # /> -+ #</media> -+ if ( $media =~ /^(iphone|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/mp4".+?encoding="h264".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -+ $prog_type = 'iphone'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{streamurl} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{type} = 'iPhone stream'; -+ } -+ -+ # Nokia N95 h.264 low quality stream (WiFi) -+ #<media kind="video" -+ # type="video/mpeg" -+ # encoding="h264" > -+ # <connection -+ # priority="10" -+ # kind="sis" -+ # server="http://www.bbc.co.uk/mediaselector/4/sdp/" -+ # identifier="b00108ld/iplayer_streaming_n95_wifi" -+ # href="http://www.bbc.co.uk/mediaselector/4/sdp/b00108ld/iplayer_streaming_n95_wifi" -+ # /> -+ #</media> -+ if ( $media =~ /^(n95_wifi|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/mpeg".+?encoding="h264".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -+ $prog_type = 'n95_wifi'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{href} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{type} = 'Nokia N95 h.264 low quality WiFi stream'; -+ $opt{quiet} = 1 if $opt{streaminfo}; -+ chomp( $data{$prog_type}{streamurl} = request_url_retry($ua, $data{$prog_type}{href}, 2, '', '') ); -+ $opt{quiet} = 0 if $opt{streaminfo}; -+ } -+ -+ # Nokia N95 h.264 low quality stream (3G) -+ #<media kind="" -+ # expires="2008-10-30T12:29:00+00:00" -+ # type="video/mpeg" -+ # encoding="h264" > -+ # <connection -+ # priority="10" -+ # kind="sis" -+ # server="http://www.bbc.co.uk/mediaselector/4/sdp/" -+ # identifier="b009tzxx/iplayer_streaming_n95_3g" -+ # href="http://www.bbc.co.uk/mediaselector/4/sdp/b009tzxx/iplayer_streaming_n95_3g" -+ # /> -+ #</media> -+ if ( $media =~ /^(n95_3g|all)$/ && $xml =~ m{<media\s+kind="".+?type="video/mpeg".+?encoding="h264".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -+ $prog_type = 'n95_3g'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{href} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{type} = 'Nokia N95 h.264 low quality 3G stream'; -+ $opt{quiet} = 1 if $opt{streaminfo}; -+ chomp( $data{$prog_type}{streamurl} = request_url_retry($ua, $data{$prog_type}{href}, 2, '', '') ); -+ $opt{quiet} = 0 if $opt{streaminfo}; -+ } -+ -+ # Mobile WMV DRM -+ #<media kind="video" -+ # expires="2008-10-20T21:59:00+01:00" -+ # type="video/wmv" > -+ # <connection -+ # priority="10" -+ # kind="licence" -+ # server="http://iplayldsvip.iplayer.bbc.co.uk/WMLicenceIssuer/LicenceDelivery.asmx" -+ # identifier="0A1CA43B-98A8-43EA-B684-DA06672C0575" -+ # href="http://iplayldsvip.iplayer.bbc.co.uk/WMLicenceIssuer/LicenceDelivery.asmx/0A1CA43B-98A8-43EA-B684-DA06672C0575" -+ # /> -+ #<connection -+ # priority="10" -+ # kind="sis" -+ # server="http://directdl.iplayer.bbc.co.uk/windowsmedia/" -+ # identifier="AmazonwithBruceParry_Episode5_200810132100_mobile" -+ # href="http://directdl.iplayer.bbc.co.uk/windowsmedia/AmazonwithBruceParry_Episode5_200810132100_mobile.wmv" -+ # /> -+ #</media> -+ if ( $media =~ /^(mobile|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/wmv".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -+ $prog_type = 'mobile'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{streamurl} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{type} = 'Mobile WMV DRM stream'; -+ } -+ -+ # Audio rtmp mp3 -+ #<media kind="audio" -+ # type="audio/mpeg" -+ # encoding="mp3" > -+ # <connection -+ # priority="10" -+ # kind="akamai" -+ # server="cp48181.edgefcs.net" -+ # identifier="mp3:secure/radio1/RBN2_mashup_b00d67h9_2008_09_05_22_14_25" -+ # authString="daEbQa1c6cda6aHdudxagcCcUcVbvbncmdK-biXtzq-cCp-DnoFIpznNBqHnzF" -+ # /> -+ #</media> -+ #app: ondemand?_fcs_vhost=cp48181.edgefcs.net&auth=daEasducLbidOancObacmc0amd6d7ana8c6-bjx.9v-cCp-JqlFHoEq.FBqGnxC&aifp=v001&slist=secure/radio1/RBN2_radio_1_-_wednesday_1000_b00g3xcj_2008_12_31_13_21_49 -+ #swfUrl: http://www.bbc.co.uk/emp/9player.swf?revision=7276 -+ #tcUrl: rtmp://92.122.210.173:1935/ondemand?_fcs_vhost=cp48181.edgefcs.net&auth=daEasducLbidOancObacmc0amd6d7ana8c6-bjx.9v-cCp-JqlFHoEqFBqGnxC&aifp=v001&slist=secure/radio1/RBN2_radio_1_-_wednesday_1.000_b00g3xcj_2008_12_31_13_21_49 -+ #pageUrl: http://www.bbc.co.uk/iplayer/episode/b00g3xp7/Annie_Mac_31_12_2008/ -+ if ( $media =~ /^(flashaudio|all)$/ && $xml =~ m{<media\s+kind="audio".+?type="audio/mpeg".+?encoding="mp3".+?kind="akamai"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -+ $prog_type = 'flashaudio'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{authstring} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{streamurl} = "rtmp://$data{$prog_type}{server}:1935/ondemand?_fcs_vhost=$data{$prog_type}{server}&auth=$data{$prog_type}{authstring}&aifp=v001&slist=$data{$prog_type}{identifier}"; -+ # Remove offending mp3: at the start of the identifier (don't remove in stream url) -+ $data{$prog_type}{identifier} =~ s/^mp3://; -+ $data{$prog_type}{application} = "ondemand?_fcs_vhost=$data{$prog_type}{server}&auth=$data{$prog_type}{authstring}&aifp=v001&slist=$data{$prog_type}{identifier}"; -+ $data{$prog_type}{type} = 'RTMP MP3 stream'; -+ $data{$prog_type}{tcurl} = "rtmp://$data{$prog_type}{server}:1935/$data{$prog_type}{application}"; -+ $data{$prog_type}{swfurl} = "http://www.bbc.co.uk/emp/9player.swf?revision=7276"; -+ } -+ -+ # RealAudio stream -+ #<media kind="audio" -+ # type="audio/real" -+ # encoding="real" > -+ # <connection -+ # priority="10" -+ # kind="sis" -+ # server="http://www.bbc.co.uk" -+ # identifier="/radio/aod/playlists/9h/76/d0/0b/2000_bbc_radio_one" -+ # href="http://www.bbc.co.uk/radio/aod/playlists/9h/76/d0/0b/2000_bbc_radio_one.ram" -+ # /> -+ #</media> -+ # Realaudio for worldservice -+ #<media kind="" -+ #type="audio/real" -+ #encoding="real" > -+ #<connection -+ # priority="10" -+ # kind="edgesuite" -+ # server="http://http-ws.bbc.co.uk.edgesuite.net" -+ # identifier="/generatecssram.esi?file=/worldservice/css/nb/410060838.ra" -+ # href="http://http-ws.bbc.co.uk.edgesuite.net/generatecssram.esi?file=/worldservice/css/nb/410060838.ra" -+ #/> -+ #</media> -+ #</mediaSelection> -+ if ( $media =~ /^(realaudio|all)$/ && $xml =~ m{<media\s+kind="(audio|)".+?type="audio/real".+?encoding="real".+?kind="(sis|edgesuite)"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -+ $prog_type = 'realaudio'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{href} ) = ( $3, $4, $5 ); -+ $data{$prog_type}{type} = 'RealAudio RTSP stream'; -+ $opt{quiet} = 1 if $opt{streaminfo}; -+ chomp( $data{$prog_type}{streamurl} = request_url_retry($ua, $data{$prog_type}{href}, 2, '', '') ); -+ $data{$prog_type}{streamurl} =~ s/[\s\n]//g; -+ $opt{quiet} = 0 if $opt{streaminfo}; -+ } -+ -+ # Radio WMA (low quality) -+ #<mediaSelection xmlns="http://bbc.co.uk/2008/mp/mediaselection"> -+ #<media kind="" -+ # type="audio/wma" -+ # encoding="wma" > -+ # <connection -+ # priority="10" -+ # kind="edgesuite" -+ # server="http://http-ws.bbc.co.uk.edgesuite.net" -+ # identifier="/generatecssasx.esi?file=/worldservice/css/nb/410060838" -+ # href="http://http-ws.bbc.co.uk.edgesuite.net/generatecssasx.esi?file=/worldservice/css/nb/410060838.wma" -+ # /> -+ #</media> -+ if ( $media =~ /^(wma|all)$/ && $xml =~ m{<media\s+kind="(audio|)".+?type="audio/wma".+?encoding="wma".+?kind="(sis|edgesuite)"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -+ $prog_type = 'wma'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{href} ) = ( $3, $4, $5 ); -+ $data{$prog_type}{type} = 'WMA MMS stream'; -+ $opt{quiet} = 1 if $opt{streaminfo}; -+ chomp( $data{$prog_type}{streamurl} = request_url_retry($ua, $data{$prog_type}{href}, 2, '', '') ); -+ $data{$prog_type}{streamurl} =~ s/[\s\n]//g; -+ # HREF="mms://a1899.v394403.c39440.g.vm.akamaistream.net/7/1899/39440/1/bbcworldservice.download.akamai.com/39440//worldservice/css/nb/410060838.wma" -+ $data{$prog_type}{streamurl} =~ s/^.*href=\"(.+?)\".*$/$1/gi; -+ $opt{quiet} = 0 if $opt{streaminfo}; -+ } -+ -+ # Subtitles stream -+ #<media kind="captions" -+ # type="application/ttaf+xml" > -+ # <connection -+ # priority="10" -+ # kind="http" -+ # server="http://www.bbc.co.uk/iplayer/subtitles/" -+ # identifier="b0008dc8rstreaming89808204.xml" -+ # href="http://www.bbc.co.uk/iplayer/subtitles/b0008dc8rstreaming89808204.xml" -+ # /> -+ #</media> -+ if ( $media =~ /^(subtitles|all)$/ && $xml =~ m{<media\s+kind="captions".+?type="application/ttaf\+xml".+?kind="http"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -+ $prog_type = 'subtitles'; -+ logger "DEBUG: Processing $prog_type stream\n" if $opt{verbose}; -+ ( $data{$prog_type}{server}, $data{$prog_type}{identifier}, $data{$prog_type}{streamurl} ) = ( $1, $2, $3 ); -+ $data{$prog_type}{type} = 'Subtitles stream'; -+ } -+ } -+ # Do iphone redirect check regardless of an xml entry for iphone - sometimes the iphone streams exist regardless -+ if ( my $streamurl = get_stream_url_iphone($ua, $verpid) ) { -+ my $prog_type = 'iphone'; -+ $data{$prog_type}{type} = 'iPhone stream'; -+ # Get iphone redirect -+ $data{$prog_type}{streamurl} = $streamurl; -+ } else { -+ logger "DEBUG: No iphone redirect stream\n" if $opt{verbose}; -+ } -+ -+ } -+ # Return a hash with media => url if 'all' is specified - otherwise just the specified url -+ if ( $media eq 'all' ) { -+ return %data; -+ } else { -+ # Make sure this hash exists before we pass it back... -+ $data{$media}{exists} = 0 if not defined $data{$media}; -+ return $data{$media}; -+ } -+} -+ - -- my ($server, $authstring, $identifier, $href); - -- # h.264 high quality stream -- # <media kind="video" -- # width="640" -- # height="360" -- # type="video/mp4" -- # encoding="h264" > -- # <connection -- # priority="10" -- # application="bbciplayertok" -- # kind="level3" -- # server="bbciplayertokfs.fplive.net" -- # identifier="mp4:b000zxf4-H26490898078" -- # authString="d52f77fede048f1ffd6587fd47446dee" -- # /> -- if ( $media =~ /^(flashhigh|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/mp4".+?encoding="h264".+?kind="level3"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -- ( $server, $identifier, $authstring ) = ( $1, $2, $3 ); -- logger "INFO: RTMP h.264 high quality stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: authstring=$authstring\n" if $opt{verbose}; -- $streams{'flashhigh'} = "rtmp://${server}:1935/ondemand?_fcs_vhost=${server}&auth=${authstring}&aifp=v001&slist=${identifier}"; -- logger "INFO: RTMP high quality stream URL: $streams{'flashhigh'}\n"; -- } -- -- # h.264 normal quality stream -- # <media kind="video" -- # width="512" -- # height="288" -- # type="video/x-flv" -- # encoding="vp6" > -- # <connection -- # priority="10" -- # kind="akamai" -- # server="cp41752.edgefcs.net" -- # identifier="secure/b000zxf4-streaming90898078" -- # authString="daEdSdgbcaibFa7biaobCaYdadyaTamazbq-biXsum-cCp-FqrECnEoGBwFvwG" -- # /> -- # </media> -- # -- if ( $media =~ /^(flashnormal|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/x-flv".+?encoding="vp6".+?kind="akamai"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -- ( $server, $identifier, $authstring ) = ( $1, $2, $3 ); -- logger "INFO: RTMP h.264 normal quality stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: authstring=$authstring\n" if $opt{verbose}; -- $streams{'flashnormal'} = "rtmp://${server}:1935/ondemand?_fcs_vhost=${server}&auth=${authstring}&aifp=v001&slist=${identifier}"; -- logger "INFO: RTMP normal quality stream URL: $streams{'flashnormal'}\n"; -- } -- -- # Wii h.264 standard quality stream -- #<media kind="video" -- # width="512" -- # height="288" -- # type="video/x-flv" -- # encoding="spark" > -- # <connection -- # priority="10" -- # kind="akamai" -- # server="cp41752.edgefcs.net" -- # identifier="secure/5242138581547639062" -- # authString="daEd8dLbGaPaZdzdNcwd.auaydJcxcHandp-biX5YL-cCp-BqsECnxnGEsHwyE" -- # /> -- #</media> -- if ( $media =~ /^(flashwii|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/x-flv".+?encoding="spark".+?kind="akamai"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -- ( $server, $identifier, $authstring ) = ( $1, $2, $3 ); -- logger "INFO: RTMP Wii normal quality stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: authstring=$authstring\n" if $opt{verbose}; -- $streams{'flashwii'} = "rtmp://${server}:1935/ondemand?_fcs_vhost=${server}&auth=${authstring}&aifp=v001&slist=${identifier}"; -- logger "INFO: RTMP Wii normal quality stream URL: $streams{'flashwii'}\n"; -- } -- -- # iPhone h.264/mp3 stream -- #<media kind="video" -- # width="480" -- # height="272" -- # type="video/mp4" -- # encoding="h264" > -- # <connection -- # priority="10" -- # kind="sis" -- # server="http://www.bbc.co.uk/mediaselector/3/auth/stream/" -- # identifier="5242138581547639062" -- # href="http://www.bbc.co.uk/mediaselector/3/auth/stream/5242138581547639062.mp4" -- # /> -- #</media> -- if ( $media =~ /^(iphone|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/mp4".+?encoding="h264".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -- ( $server, $identifier, $href ) = ( $1, $2, $3 ); -- logger "INFO: iPhone stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: href=$href\n" if $opt{verbose}; -- $streams{'iphone'} = "$href"; -- logger "INFO: iPhone stream URL: $streams{'iphone'}\n"; -- } -- -- # Nokia N95 h.264 low quality stream (WiFi) -- #<media kind="video" -- # type="video/mpeg" -- # encoding="h264" > -- # <connection -- # priority="10" -- # kind="sis" -- # server="http://www.bbc.co.uk/mediaselector/4/sdp/" -- # identifier="b00108ld/iplayer_streaming_n95_wifi" -- # href="http://www.bbc.co.uk/mediaselector/4/sdp/b00108ld/iplayer_streaming_n95_wifi" -- # /> -- #</media> -- if ( $media =~ /^(n95_wifi|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/mpeg".+?encoding="h264".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -- ( $server, $identifier, $href ) = ( $1, $2, $3 ); -- $opt{quiet} = 1 if $opt{streaminfo}; -- chomp( my $rtsp = request_url_retry($ua, $href, 2, '', '') ); -- $opt{quiet} = 0 if $opt{streaminfo}; -- logger "INFO: Nokia N95 h.264 low quality WiFi stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: href=$href\n" if $opt{verbose}; -- $streams{'n95_wifi'} = "$rtsp"; -- logger "INFO: Nokia N95 h.264 low quality WiFi stream URL: $streams{'n95_wifi'}\n"; -- } -- -- # Nokia N95 h.264 low quality stream (3G) -- #<media kind="" -- # expires="2008-10-30T12:29:00+00:00" -- # type="video/mpeg" -- # encoding="h264" > -- # <connection -- # priority="10" -- # kind="sis" -- # server="http://www.bbc.co.uk/mediaselector/4/sdp/" -- # identifier="b009tzxx/iplayer_streaming_n95_3g" -- # href="http://www.bbc.co.uk/mediaselector/4/sdp/b009tzxx/iplayer_streaming_n95_3g" -- # /> -- #</media> -- if ( $media =~ /^(n95_3g|all)$/ && $xml =~ m{<media\s+kind="".+?type="video/mpeg".+?encoding="h264".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -- ( $server, $identifier, $href ) = ( $1, $2, $3 ); -- $opt{quiet} = 1 if $opt{streaminfo}; -- chomp( my $rtsp = request_url_retry($ua, $href, 2, '', '') ); -- $opt{quiet} = 0 if $opt{streaminfo}; -- logger "INFO: Nokia N95 h.264 low quality 3G stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: href=$href\n" if $opt{verbose}; -- $streams{'n95_3g'} = "$rtsp"; -- logger "INFO: Nokia N95 h.264 low quality 3G stream URL: $streams{'n95_3g'}\n"; -- } -- -- -- -- # Mobile WMV DRM -- #<media kind="video" -- # expires="2008-10-20T21:59:00+01:00" -- # type="video/wmv" > -- # <connection -- # priority="10" -- # kind="licence" -- # server="http://iplayldsvip.iplayer.bbc.co.uk/WMLicenceIssuer/LicenceDelivery.asmx" -- # identifier="0A1CA43B-98A8-43EA-B684-DA06672C0575" -- # href="http://iplayldsvip.iplayer.bbc.co.uk/WMLicenceIssuer/LicenceDelivery.asmx/0A1CA43B-98A8-43EA-B684-DA06672C0575" -- # /> -- #<connection -- # priority="10" -- # kind="sis" -- # server="http://directdl.iplayer.bbc.co.uk/windowsmedia/" -- # identifier="AmazonwithBruceParry_Episode5_200810132100_mobile" -- # href="http://directdl.iplayer.bbc.co.uk/windowsmedia/AmazonwithBruceParry_Episode5_200810132100_mobile.wmv" -- # /> -- #</media> -- if ( $media =~ /^(mobile|all)$/ && $xml =~ m{<media\s+kind="video".+?type="video/wmv".+?kind="sis"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -- ( $server, $identifier, $href ) = ( $1, $2, $3 ); -- logger "INFO: Mobile WMV DRM stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: href=$href\n" if $opt{verbose}; -- $streams{'mobile'} = "$href"; -- logger "INFO: Mobile WMV DRM stream URL: $streams{'mobile'}\n"; -- } -- -- # Audio rtmp mp3 -- #<media kind="audio" -- # type="audio/mpeg" -- # encoding="mp3" > -- # <connection -- # priority="10" -- # kind="akamai" -- # server="cp48181.edgefcs.net" -- # identifier="mp3:secure/radio1/RBN2_mashup_b00d67h9_2008_09_05_22_14_25" -- # authString="daEbQa1c6cda6aHdudxagcCcUcVbvbncmdK-biXtzq-cCp-DnoFIpznNBqHnzF" -- # /> -- #</media> -- if ( $media =~ /^(flashaudio|all)$/ && $xml =~ m{<media\s+kind="audio".+?type="audio/mpeg".+?encoding="mp3".+?kind="akamai"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?authString="(.+?)"} ) { -- ( $server, $identifier, $authstring ) = ( $1, $2, $3 ); -- # Remove offending mp3: at the start of the identifier -- $identifier =~ s/^mp3://; -- logger "INFO: RTMP MP3 stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: authstring=$authstring\n" if $opt{verbose}; -- $streams{'flashaudio'} = "rtmp://${server}:1935/ondemand?_fcs_vhost=${server}&auth=${authstring}&aifp=v001&slist=${identifier}"; -- logger "INFO: RTMP stream URL: $streams{'flashaudio'}\n"; -- } -- -- # RealAudio stream -- #<media kind="audio" -- # type="audio/real" -- # encoding="real" > -- # <connection -- # priority="10" -- # kind="sis" -- # server="http://www.bbc.co.uk" -- # identifier="/radio/aod/playlists/9h/76/d0/0b/2000_bbc_radio_one" -- # href="http://www.bbc.co.uk/radio/aod/playlists/9h/76/d0/0b/2000_bbc_radio_one.ram" -- # /> -- #</media> -- # Realaudio for worldservice -- #<media kind="" -- #type="audio/real" -- #encoding="real" > -- #<connection -- # priority="10" -- # kind="edgesuite" -- # server="http://http-ws.bbc.co.uk.edgesuite.net" -- # identifier="/generatecssram.esi?file=/worldservice/css/nb/410060838.ra" -- # href="http://http-ws.bbc.co.uk.edgesuite.net/generatecssram.esi?file=/worldservice/css/nb/410060838.ra" -- #/> -- #</media> -- #</mediaSelection> -- if ( $media =~ /^(realaudio|all)$/ && $xml =~ m{<media\s+kind="(audio|)".+?type="audio/real".+?encoding="real".+?kind="(sis|edgesuite)"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -- ( $server, $identifier, $href ) = ( $3, $4, $5 ); -- $opt{quiet} = 1 if $opt{streaminfo}; -- chomp( my $rtsp = request_url_retry($ua, $href, 2, '', '') ); -- $rtsp =~ s/[\s\n]//g; -- $opt{quiet} = 0 if $opt{streaminfo}; -- logger "INFO: RealAudio RTSP stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: href=$href\n" if $opt{verbose}; -- $streams{'realaudio'} = "$rtsp"; -- logger "INFO: RealAudio RTSP stream URL: $streams{'realaudio'}\n"; -- } -- -- -- # Radio WMA (low quality) -- #<mediaSelection xmlns="http://bbc.co.uk/2008/mp/mediaselection"> -- #<media kind="" -- # type="audio/wma" -- # encoding="wma" > -- # <connection -- # priority="10" -- # kind="edgesuite" -- # server="http://http-ws.bbc.co.uk.edgesuite.net" -- # identifier="/generatecssasx.esi?file=/worldservice/css/nb/410060838" -- # href="http://http-ws.bbc.co.uk.edgesuite.net/generatecssasx.esi?file=/worldservice/css/nb/410060838.wma" -- # /> -- #</media> -- if ( $media =~ /^(wma|all)$/ && $xml =~ m{<media\s+kind="(audio|)".+?type="audio/wma".+?encoding="wma".+?kind="(sis|edgesuite)"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -- ( $server, $identifier, $href ) = ( $3, $4, $5 ); -- $opt{quiet} = 1 if $opt{streaminfo}; -- chomp( my $mms = request_url_retry($ua, $href, 2, '', '') ); -- $mms =~ s/[\n]//g; -- # HREF="mms://a1899.v394403.c39440.g.vm.akamaistream.net/7/1899/39440/1/bbcworldservice.download.akamai.com/39440//worldservice/css/nb/410060838.wma" -- $mms =~ s/^.*href=\"(.+?)\".*$/$1/gi; -- $opt{quiet} = 0 if $opt{streaminfo}; -- logger "INFO: WMA MMS stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: href=$href\n" if $opt{verbose}; -- $streams{'wma'} = "$mms"; -- logger "INFO: WMA MMS stream URL: $streams{'wma'}\n"; -- } -- -- -- # Subtitles stream -- #<media kind="captions" -- # type="application/ttaf+xml" > -- # <connection -- # priority="10" -- # kind="http" -- # server="http://www.bbc.co.uk/iplayer/subtitles/" -- # identifier="b0008dc8rstreaming89808204.xml" -- # href="http://www.bbc.co.uk/iplayer/subtitles/b0008dc8rstreaming89808204.xml" -- # /> -- #</media> -- if ( $media =~ /^(subtitles|all)$/ && $xml =~ m{<media\s+kind="captions".+?type="application/ttaf\+xml".+?kind="http"\s+server="(.+?)"\s+?identifier="(.+?)"\s+?href="(.+?)"} ) { -- ( $server, $identifier, $href ) = ( $1, $2, $3 ); -- logger "INFO: Subtitles stream:\nINFO: server=$server\nINFO: identifier=$identifier\nINFO: href=$href\n" if $opt{verbose}; -- $streams{'subtitles'} = "$href"; -- logger "INFO: Subtitles stream URL: $streams{'subtitles'}\n"; -+sub display_stream_info { -+ my ($pid, $verpid, $media) = (@_); -+ logger "INFO: Getting media stream metadata for $prog{$pid}{name} - $prog{$pid}{episode}, $verpid\n" if $pid; -+ my %data = get_media_stream_data( $pid, $verpid, $media); -+ # Print out stream data -+ for my $prog_type (sort keys %data) { -+ logger "stream: $prog_type\n"; -+ for my $entry ( sort keys %{ $data{$prog_type} } ) { -+ logger sprintf("%-11s %s\n", $entry.':', $data{$prog_type}{$entry} ); - } -+ logger "\n"; - } -- logger "\n" if $opt{streaminfo}; -- $opt{quiet} = 1 if $opt{streaminfo}; -+ return 0; -+} - -- # Return a hash with media => url if 'all' is specified - otherwise just the specified url -- return %streams if $media eq 'all'; -- return $streams{$media}; -+ -+ -+# Displays specified metadata from supplied hash -+# Usage: display_metadata( <hashref>, <array of elements to display> ) -+sub display_metadata { -+ my %data = %{$_[0]}; -+ shift; -+ my @keys = @_; -+ @keys = keys %data if $#_ < 0; -+ logger "\n"; -+ for (@keys) { -+ logger sprintf "%-15s %s\n", ucfirst($_).':', $data{$_} if $data{$_}; -+ } -+ return 0; - } - - - - # Actually do the h.264/mp3 downloading - # ( $ua, $pid, $url_2, $file, $file_done, '0|1 == rearrange moov' ) --sub download_iphone_stream { -- my ( $ua, $url_2, $file, $file_done, $file_symlink, $rearrange ) = @_; -+sub download_stream_iphone { -+ my ( $ua, $url_2, $pid, $file, $file_done, $file_symlink, $rearrange ) = @_; - - # Stage 3a: Download 1st byte to get exact file length - logger "INFO: Stage 3 URL = $url_2\n" if $opt{verbose}; -@@ -2132,53 +2799,44 @@ - my $req = HTTP::Request->new ('GET', $url_2, $h); - my $res = $ua->request($req); - # e.g. Content-Range: bytes 0-1/181338136 (return if no content length returned) -- my $file_len = $res->header("Content-Range"); -- if ( ! $file_len ) { -- logger "ERROR: No Content-Range was obtained\n" if $opt{verbose}; -+ my $download_len = $res->header("Content-Range"); -+ if ( ! $download_len ) { -+ #logger "ERROR: No Content-Range was obtained\n" if $opt{verbose}; -+ logger "WARNING: iphone version not available\n"; - return 'retry' - } -- $file_len =~ s|^bytes 0-1/(\d+).*$|$1|; -- logger "INFO: Download File Length $file_len\n" if $opt{verbose}; -+ $download_len =~ s|^bytes 0-1/(\d+).*$|$1|; -+ logger "INFO: Download File Length $download_len\n" if $opt{verbose}; - - # Only do this if we're rearranging QT streams - my $mdat_start = 0; -- my $moov_start = $file_len + 1; -+ # default to this if we are not rearranging (tells the download chunk loop where to stop - i.e. EOF instead of end of mdat atom) -+ my $moov_start = $download_len + 1; - my $header; - if ($rearrange) { - # Get ftyp+wide header etc - $mdat_start = 0x1c; - my $buffer = download_block(undef, $url_2, $ua, 0, $mdat_start + 4); - # Get bytes upto (but not including) mdat atom start -> $header -- $header = download_block(undef, $url_2, $ua, 0, $mdat_start - 1, $file_len); -- -+ $header = substr($buffer, 0, $mdat_start); -+ - # Detemine moov start -- # Get mdat_end_offset_chars from downloaded block -- my $mdat_end_offset_chars = substr($buffer, $mdat_start, 4); -- my $mdat_end_offset = bytestring_to_int($mdat_end_offset_chars); -- logger "DEBUG: mdat_end_offset = ".get_hex($mdat_end_offset_chars)." = $mdat_end_offset\n" if $opt{debug}; -- logger "DEBUG: mdat_end_offset (decimal) = $mdat_end_offset\n" if $opt{debug}; -+ # Get mdat_length_chars from downloaded block -+ my $mdat_length_chars = substr($buffer, $mdat_start, 4); -+ my $mdat_length = bytestring_to_int($mdat_length_chars); -+ logger "DEBUG: mdat_length = ".get_hex($mdat_length_chars)." = $mdat_length\n" if $opt{debug}; -+ logger "DEBUG: mdat_length (decimal) = $mdat_length\n" if $opt{debug}; - # The MOOV box starts one byte after MDAT box ends -- $moov_start = $mdat_start + $mdat_end_offset; -- -- -- ## scan 2nd level atoms in moov atom until we get stco atom(s) -- # We can skip first 8 bytes (moov atom header) -- #my $i = 8; -- #while( $i < $moov_length - 4 ) { -- # my $atom_len = bytestring_to_int( substr($moovdata, $i, 4) ); -- # my $atom_name = substr($moovdata, $i+4, 4); -- # logger "Parsing atom: $atom_name, length: $atom_len\n"; -- # # Increment $i by atom_len to get next atom -- # $i += $atom_len; -- #} -+ $moov_start = $mdat_start + $mdat_length; - } - - # If we have partial content and wish to stream, resume the download & spawn off STDOUT from existing file start - # Sanity check - we cannot support downloading of partial content if we're streaming also. - if ( $opt{stdout} && (! $opt{nowrite}) && -f $file ) { - logger "WARNING: Partially downloaded file exists, streaming will start from the beginning of the programme\n"; -- # Don't do usual streaming code -- $opt{stdout} = 0; -+ # Don't do usual streaming code - also force all messages to go to stderr -+ delete $opt{stdout}; -+ $opt{stderr} = 1; - $childpid = fork(); - if (! $childpid) { - # Child starts here -@@ -2205,15 +2863,15 @@ - my $fh = open_file_append($file); - - # If the partial file already exists, then resume from the correct mdat/download offset -- my $restart_offset = $mdat_start; -+ my $restart_offset = 0; - my $moovdata; - my $moov_length = 0; - - if ($rearrange) { - # if cookie fails then trigger a retry after deleting cookiejar -- # Determine moov atom length so we can work out if the partially downloaded file has the moov atom in it already -+ # Determine orginal moov atom length so we can work out if the partially downloaded file has the moov atom in it already - $moov_length = bytestring_to_int( download_block( undef, $url_2, $ua, $moov_start, $moov_start+3 ) ); -- logger "INFO: moov atom length = $moov_length \n" if $opt{verbose}; -+ logger "INFO: original moov atom length = $moov_length \n" if $opt{verbose}; - # Sanity check this moov length - chances are that were being served up a duff file if this is > 10% of the file size or < 64k - if ( $moov_length > (${moov_start}/9.0) || $moov_length < 65536 ) { - logger "WARNING: Bad file download, deleting cookie \n"; -@@ -2222,33 +2880,73 @@ - unlink $file; - return 'retry'; - } -- } -- -- # If we have a too-small-sized file and not stdout and not no-write then this is a partial download -- if (-f $file && (! $opt{stdout}) && (! $opt{nowrite}) && stat($file)->size > ($moov_length+$mdat_start) ) { -- # Calculate new start offset (considering that we've put moov first in file) -- $restart_offset = stat($file)->size - $moov_length; -- logger "INFO: Resuming download from $restart_offset \n"; -- } - -- if ($rearrange) { -+ # we still need an accurate moovlength for the already downloaded moov atom for resume restart_offset..... - # If we have no existing file, a file which doesn't yet even have the moov atom, or using stdout (or no-write option) -- if ( $opt{stdout} || $opt{nowrite} || stat($file)->size < ($moov_length+$mdat_start) ) { -+ # (allow extra 1k on moov_length for metadata when testing) -+ if ( $opt{stdout} || $opt{nowrite} || stat($file)->size < ($moov_length+$mdat_start+1024) ) { - # get moov chunk into memory -- $moovdata = download_block( undef, $url_2, $ua, $moov_start, (${file_len}-1) ); -+ $moovdata = download_block( undef, $url_2, $ua, $moov_start, (${download_len}-1) ); -+ -+ # Create new udta atom with child atoms for metadata -+ my $udta_new = create_qt_atom('udta', -+ create_qt_atom( chr(0xa9).'nam', $prog{$pid}{name}.' - '.$prog{$pid}{episode}, 'string' ). -+ create_qt_atom( chr(0xa9).'alb', $prog{$pid}{name}, 'string' ). -+ create_qt_atom( chr(0xa9).'trk', $prog{$pid}{episode}, 'string' ). -+ create_qt_atom( chr(0xa9).'aut', $prog{$pid}{channel}, 'string' ). -+ create_qt_atom( chr(0xa9).'ART', $prog{$pid}{channel}, 'string' ). -+ create_qt_atom( chr(0xa9).'des', $prog{$pid}{desc}, 'string' ). -+ create_qt_atom( chr(0xa9).'cmt', 'Downloaded with get_iplayer', 'string' ). -+ create_qt_atom( chr(0xa9).'req', 'QuickTime 6.0 or greater', 'string' ). -+ create_qt_atom( chr(0xa9).'day', (localtime())[5] + 1900, 'string' ) -+ ); -+ # Insert new udta atom over the old one and get the new $moov_length (and update moov atom size field) -+ replace_moov_udta_atom ( $udta_new, $moovdata ); -+ - # Process the moov data so that we can relocate it (change the chunk offsets that are absolute) -+ # Also update moov+_length to be accurate after metadata is added etc - $moov_length = relocate_moov_chunk_offsets( $moovdata ); -- # write moov atom to file next (yes - were rearranging the file - moov+header+mdat - not header+mdat+moov) -- logger "INFO: Appending moov+ftype+wide atoms to $file\n" if $opt{verbose}; -- # Write moov atom -- print $fh $moovdata if ! $opt{nowrite}; -- print STDOUT $moovdata if $opt{stdout}; -+ logger "INFO: New moov atom length = $moov_length \n" if $opt{verbose}; -+ # write moov atom to file next (yes - were rearranging the file - header+moov+mdat - not header+mdat+moov) -+ logger "INFO: Appending ftype+wide+moov atoms to $file\n" if $opt{verbose}; - # Write header atoms (ftyp, wide) - print $fh $header if ! $opt{nowrite}; - print STDOUT $header if $opt{stdout}; -+ # Write moov atom -+ print $fh $moovdata if ! $opt{nowrite}; -+ print STDOUT $moovdata if $opt{stdout}; -+ # If were not resuming we want to only start the download chunk loop from mdat_start -+ $restart_offset = $mdat_start; -+ } -+ -+ # Get accurate moov_length from file (unless stdout or nowrite options are specified) -+ # Assume header+moov+mdat atom layout -+ if ( (! $opt{stdout}) && (! $opt{nowrite}) && stat($file)->size > ($moov_length+$mdat_start) ) { -+ logger "INFO: Getting moov atom length from partially downloaded file $file\n" if $opt{verbose}; -+ if ( ! open( MOOVDATA, "< $file" ) ) { -+ logger "ERROR: Cannot Read partially downloaded file\n"; -+ return 4; -+ } -+ my $data; -+ seek(MOOVDATA, $mdat_start, 0); -+ if ( read(MOOVDATA, $data, 4, 0) != 4 ) { -+ logger "ERROR: Cannot Read moov atom length from partially downloaded file\n"; -+ return 4; -+ } -+ close MOOVDATA; -+ # Get moov atom size from file -+ $moov_length = bytestring_to_int( substr($data, 0, 4) ); -+ logger "INFO: moov atom length (from partially downloaded file) = $moov_length \n" if $opt{verbose}; - } - } - -+ # If we have a too-small-sized file (greater than moov_length+mdat_start) and not stdout and not no-write then this is a partial download -+ if (-f $file && (! $opt{stdout}) && (! $opt{nowrite}) && stat($file)->size > ($moov_length+$mdat_start) ) { -+ # Calculate new start offset (considering that we've put moov first in file) -+ $restart_offset = stat($file)->size - $moov_length; -+ logger "INFO: Resuming download from $restart_offset \n"; -+ } -+ - # Create symlink if required - if ( $opt{symlink} ) { - # remove old symlink -@@ -2273,16 +2971,15 @@ - $e = $s + $chunk_size - 1; - } - # Get block from URL and append to $file -- if ( download_block($file, $url_2, $ua, $s, $e, $file_len, $fh ) ) { -+ if ( download_block($file, $url_2, $ua, $s, $e, $download_len, $fh ) ) { - logger "ERROR: Could not download block $s - $e from $file\n\n"; -- return 9; -+ return 'retry'; - } - } - - # end marker - my $end_time = time(); - -- # Should now be able to concatenate header.block + mdat.block + moov.block to get movie! - # Calculate average speed, duration and total bytes downloaded - logger sprintf("INFO: Downloaded %.2fMB in %s at %5.0fkbps to %s\n", - ($moov_start - 1 - $restart_offset) / (1024.0 * 1024.0), -@@ -2292,57 +2989,217 @@ - - # Moving file into place as complete (if not stdout) - move($file, $file_done) if ! $opt{stdout}; -+ -+ # Re-symlink file -+ if ( $opt{symlink} ) { -+ # remove old symlink -+ unlink $file_symlink if -l $file_symlink; -+ symlink $file_done, $file_symlink; -+ logger "INFO: Created symlink from '$file_symlink' -> '$file_done'\n" if $opt{verbose}; -+ } -+ $prog{$pid}{mode} = 'iphone'; - return 0; - } - - - --sub download_h264_rtmp_stream { -- my ( $ua, $url_2, $prog_url, $file, $file_done, $file_symlink ) = @_; -- my $file_flv = $file; # .'.flv'; -+# Actually do the RTMP stream downloading -+sub download_stream_rtmp { -+ my ( $ua, $url_2, $pid, $mode, $application, $tcurl, $authstring, $swfurl, $file, $file_done, $file_symlink ) = @_; -+ my $file_tmp; -+ my $cmd; -+ -+ if ( $opt{raw} ) { -+ $file_tmp = $file; -+ } else { -+ $file_tmp = $file.'.flv' -+ } - -- logger "INFO: url: $url_2, prog_url: $prog_url, file: $file, file_done: $file_done\n" if $opt{verbose}; -+ # Remove failed file download (below a certain size) - hack to get around rtmpdump not returning correct exit code -+ if ( -f $file_tmp && stat($file_tmp)->size < $min_download_size ) { -+ unlink( $file_tmp ); -+ } -+ -+ logger "INFO: RTMP_URL: $url_2, tcUrl: $tcurl, application: $application, authString: $authstring, swfUrl: $swfurl, file: $file, file_done: $file_done\n" if $opt{verbose}; - - # Create symlink if required - if ( $opt{symlink} ) { - # remove old symlink - unlink $file_symlink if -l $file_symlink; -- symlink $file_flv, $file_symlink; -- logger "INFO: Created symlink from '$file_symlink' -> '$file_flv'\n" if $opt{verbose}; -+ symlink $file_tmp, $file_symlink; -+ logger "INFO: Created symlink from '$file_symlink' -> '$file_tmp'\n" if $opt{verbose}; - } -+ $cmd = "$rtmpdump --resume --rtmp \"$url_2\" --auth \"$authstring\" --swfUrl \"$swfurl\" --tcUrl \"$tcurl\" --app \"$application\" -o \"$file_tmp\" >&2"; -+ logger "\n\nINFO: Command: $cmd\n" if $opt{verbose}; -+ my $return = system($cmd); -+ # Hack to get around rtmpdump prentending to fail on successful flash downloads -+ if ( (! -f $file_tmp) || ($return && -f $file_tmp && stat($file_tmp)->size < $min_download_size) ) { -+ logger "\n\nINFO: Command: $cmd\n" if $opt{verbose}; -+ logger "\nWARNING: Failed to download file $file_tmp via RTMP\n"; -+ unlink $file_tmp; -+ return 'next'; -+ } -+ -+ # Retain raw flv format if required -+ if ( $opt{raw} ) { -+ move($file_tmp, $file_done) if ! $opt{stdout}; -+ return 0; - -- my $cmd = "$rtmpdump --rtmp \"$url_2\" --pageUrl \"$prog_url\" --swfUrl \"http://www.bbc.co.uk/emp/9player.swf?revision=6928_7030\" --tcUrl \"rtmp://bbciplayertokfs.fplive.net:80/bbciplayertok\" --app \"bbciplayertok\" -o \"$file_flv\" >&2"; -- #my $cmd2 = "$ffmpeg -i \"$file_flv\" -vcodec copy -acodec copy -f mp4 -y \"$file\" >&2"; -- #my $cmd2 = "$mencoder -oac copy -ovc copy -o \"$file\" \"$file_flv\" >&2"; -+ # Convert flv to mp3 for flash audio -+ } elsif ( $mode eq 'flashaudio' ) { -+ # We could do id3 tagging here but id3v2 does this later anyway -+ $cmd = "$ffmpeg -i \"$file_tmp\" -vn -acodec copy -y \"$file\" >&2"; - -- # logger "\n\nINFO: Command1: $cmd\nINFO: Command2: $cmd2\n\n" if $opt{verbose}; -- if ( system($cmd) ) { -- logger "\nWARNING: Failed to download file $file via RTMP\n"; -- return 1; -+ # Convert video flv to mp4/avi if required -+ } else { -+ $cmd = "$ffmpeg $ffmpeg_opts -i \"$file_tmp\" -vcodec copy -acodec copy -f $prog{$pid}{ext} -y \"$file\" >&2"; -+ } -+ -+ logger "\n\nINFO: Command: $cmd\n\n" if $opt{verbose}; -+ # Run flv conversion and delete source file on success -+ if ( (! system($cmd)) && -f $file && stat($file)->size > $min_download_size ) { -+ unlink( $file_tmp ); -+ -+ # If the ffmpeg conversion failed, remove the failed-converted file attempt - move the file as done anyway -+ } else { -+ logger "WARNING: flv conversion failed - retaining flv file\n"; -+ unlink $file; -+ $file = $file_tmp; -+ $file_done = $file_tmp; -+ } -+ # Moving file into place as complete (if not stdout) -+ move($file, $file_done) if ! $opt{stdout}; -+ -+ # Re-symlink file -+ if ( $opt{symlink} ) { -+ # remove old symlink -+ unlink $file_symlink if -l $file_symlink; -+ symlink $file_done, $file_symlink; -+ logger "INFO: Created symlink from '$file_symlink' -> '$file_done'\n" if $opt{verbose}; -+ } -+ -+ logger "INFO: Downloaded $file_done\n"; -+ $prog{$pid}{mode} = $mode; -+ return 0; -+} -+ -+ -+ -+# Actually do the MMS video stream downloading -+sub download_stream_mms_video { -+ my ( $ua, $urls, $file, $file_done, $pid ) = @_; -+ my $file_tmp; -+ my $cmd; -+ my $null; -+ my @url_list = split /\|/, $urls; -+ my @file_tmp_list; -+ my %threadpid; -+ -+ logger "INFO: MMS_URLs: ".(join ', ', @file_tmp_list).", file: $file, file_done: $file_done\n" if $opt{verbose}; -+ -+ # Start marker -+ my $start_time = time(); -+ # Download each mms url (multi-threaded to download in parallel) -+ my $file_part_prefix = "$prog{$pid}{dir}/$prog{$pid}{fileprefix}_part"; -+ for ( my $count = 0; $count <= $#url_list; $count++ ) { -+ -+ # Create temp download filename -+ $file_tmp = $file_part_prefix.($count+1).".asf"; -+ $file_tmp_list[$count] = $file_tmp; -+ $null = " 2>/dev/null " if (! $opt{verbose}) && (! $opt{debug}); -+ $cmd = "$mplayer -dumpstream \"$url_list[$count]\" -dumpfile \"$file_tmp\" $null >&2 </dev/null"; -+ logger "\n\nINFO: Command: $cmd\n" if $opt{verbose}; -+ -+ my $childpid = fork(); -+ if (! $childpid) { -+ # Child starts here -+ logger "INFO: Downloading file $file_tmp\n"; -+ if ( system($cmd) ) { -+ logger "\nWARNING: Failed to download file $file_tmp via MMS\n"; -+ exit 1; -+ } -+ logger "INFO: Download thread has completed for file $file_tmp\n"; -+ exit 0; -+ } -+ # Create a hash of process_id => 'count' -+ $threadpid{$childpid} = $count; -+ } -+ # Wait for all threads to complete -+ $| = 1; -+ # Autoreap zombies -+ $SIG{CHLD}='IGNORE'; -+ my $done = 0; -+ while (keys %threadpid) { -+ my @sizes; -+ my $total_size = 0; -+ my $total_size_new = 0; -+ my $format = "Threads: "; -+ sleep 1; -+ #logger "DEBUG: ProcessIDs: ".(join ',', keys %threadpid)."\n"; -+ for my $procid (sort keys %threadpid) { -+ my $size = 0; -+ # Is this child still alive? -+ if ( kill 0 => $procid ) { -+ logger "DEBUG Thread $threadpid{$procid} still alive ($file_tmp_list[$threadpid{$procid}])\n" if $opt{debug}; -+ # Build the status string -+ $format .= "%d) %.3fMB "; -+ $size = stat($file_tmp_list[$threadpid{$procid}])->size if -f $file_tmp_list[$threadpid{$procid}]; -+ push @sizes, $threadpid{$procid}+1, $size/(1024.0*1024.0); -+ $total_size_new += $size; -+ } else { -+ $size = stat($file_tmp_list[$threadpid{$procid}])->size if -f $file_tmp_list[$threadpid{$procid}]; -+ # end marker -+ my $end_time = time(); -+ # Calculate average speed, duration and total bytes downloaded -+ logger sprintf("INFO: Thread #%d Downloaded %.2fMB in %s at %5.0fkbps to %s\n", -+ ($threadpid{$procid}+1), -+ $size / (1024.0 * 1024.0), -+ sprintf("%02d:%02d:%02d", ( gmtime($end_time - $start_time))[2,1,0] ), -+ $size / ($end_time - $start_time) / 1024.0 * 8.0, -+ $file_tmp_list[$threadpid{$procid}] ); -+ # Remove from thread test list -+ delete $threadpid{$procid}; -+ } -+ } -+ $format .= " downloaded (%.0fkbps) \r"; -+ logger sprintf $format, @sizes, ($total_size_new - $total_size) / (time() - $start_time) / 1024.0 * 8.0; - } -- # Only convert to mp4 if we have mencoder in path -- # Disable rtmp mode if rtmpdump does not exist -- if ( ! exists_in_path($ffmpeg)) { -- logger "\nWARNING: $ffmpeg does not exist - not converting flv file to mp4\n"; -- #} else { -- # Run flv conversion and delete source file on success -- #if ( ! system($cmd2) ) { -- # unlink( $file_flv ); -- #} else { -- # logger "ERROR: flv to mp4 conversion failed\n"; -- # return 2; -- #} -- # Moving file into place as complete (if not stdout) -- #move($file, $file_done) if ! $opt{stdout}; -+ logger "INFO: All download threads completed\n"; -+ # Unset autoreap -+ delete $SIG{CHLD}; -+ # Retain raw format if required -+ if ( $opt{raw} ) { -+ return 0; - } -+ -+# # Convert video asf to mp4 if required - need to find a suitable converter... -+# } else { -+# # Create part of cmd that specifies each partial file -+# my $filestring; -+# $filestring .= " -i \"$_\" " for (@file_tmp_list); -+# $cmd = "$ffmpeg $ffmpeg_opts $filestring -vcodec copy -acodec copy -f $prog{$pid}{ext} -y \"$file\" >&2"; -+# } -+# -+# logger "\n\nINFO: Command: $cmd\n\n" if $opt{verbose}; -+# # Run asf conversion and delete source file on success -+# if ( ! system($cmd) ) { -+# unlink( @file_tmp_list ); -+# } else { -+# logger "ERROR: asf conversion failed - retaining files ".(join ', ', @file_tmp_list)."\n"; -+# return 2; -+# } -+# # Moving file into place as complete (if not stdout) -+# move($file, $file_done) if ! $opt{stdout}; -+ -+ $prog{$pid}{mode} = 'itv'; - return 0; - } - - - - # Actually do the N95 h.264 downloading --sub download_h264_low_stream { -- my ( $ua, $url_2, $file, $file_done ) = @_; -+sub download_stream_h264_low { -+ my ( $ua, $url_2, $file, $file_done, $pid, $mode ) = @_; - - # Change filename extension - $file =~ s/mov$/mpg/gi; -@@ -2353,7 +3210,7 @@ - logger "INFO: Downloading Low Quality H.264 stream\n"; - my $cmd = "$vlc $vlc_opts --sout file/ts:${file} $url_2 1>&2"; - if ( system($cmd) ) { -- return 2; -+ return 'next'; - } - - # to STDOUT -@@ -2361,19 +3218,21 @@ - logger "INFO: Streaming Low Quality H.264 stream to stdout\n"; - my $cmd = "$vlc $vlc_opts --sout file/ts:- $url_2 1>&2"; - if ( system($cmd) ) { -- return 2; -+ return 'next'; - } - } - logger "INFO: Downloaded $file_done\n"; - # Moving file into place as complete (if not stdout) - move($file, $file_done) if ! $opt{stdout}; -+ -+ $prog{$pid}{mode} = $mode; - return 0; - } - - - - # Actually do the rtsp downloading --sub download_rtsp_stream { -+sub download_stream_rtsp { - my ( $ua, $url, $file, $file_done, $file_symlink, $pid ) = @_; - my $childpid; - -@@ -2389,7 +3248,7 @@ - # Create ID3 tagging options for lame (escape " for shell) - my ( $id3_name, $id3_episode, $id3_desc, $id3_channel ) = ( $prog{$pid}{name}, $prog{$pid}{episode}, $prog{$pid}{desc}, $prog{$pid}{channel} ); - $id3_name =~ s|"|\"|g for ($id3_name, $id3_episode, $id3_desc, $id3_channel); -- $lame_opts .= "--ignore-tag-errors --ty ".( (localtime())[5] + 1900 )." --tl \"$id3_name\" --tt \"$id3_episode\" --ta \"$id3_channel\" --tc \"$id3_desc\" "; -+ $lame_opts .= " --ignore-tag-errors --ty ".( (localtime())[5] + 1900 )." --tl \"$id3_name\" --tt \"$id3_episode\" --ta \"$id3_channel\" --tc \"$id3_desc\" "; - - # Use post-download transcoding using lame if namedpipes are not supported (i.e. ActivePerl/Windows) - # (Fallback if no namedpipe support and raw/wav not specified) -@@ -2402,14 +3261,14 @@ - logger "INFO: Downloading wav format (followed by transcoding)\n"; - $cmd = "$mplayer $mplayer_opts -cache 128 -bandwidth $bandwidth -vc null -vo null -ao pcm:waveheader:fast:file=\"${file}.wav\" \"$url\" 1>&2"; - if ( system($cmd) ) { -- return 2; -+ return 'next'; - } - # Transcode - logger "INFO: Transcoding ${file}.wav\n"; - $cmd = "$lame $lame_opts \"${file}.wav\" \"${file}.mp3\" 1>&2"; - logger "DEGUG: Running $cmd\n" if $opt{debug}; -- if ( system($cmd) ) { -- return 2; -+ if ( system($cmd) || (-f "${file}.wav" && stat("${file}.wav")->size < $min_download_size) ) { -+ return 'next'; - } - unlink "${file}.wav"; - move "${file}.mp3", $file_done; -@@ -2425,7 +3284,7 @@ - my $cmd = "$mplayer $mplayer_opts -cache 128 -bandwidth $bandwidth -vc null -vo null -ao pcm:waveheader:fast:file=\"$file\" \"$url\" 1>&2"; - logger "DEGUG: Running $cmd\n" if $opt{debug}; - if ( system($cmd) ) { -- return 2; -+ return 'next'; - } - # Move file to done state - move $file, $file_done if ! $opt{nowrite}; -@@ -2438,7 +3297,7 @@ - my $cmd = "$mplayer $mplayer_opts -cache 128 -bandwidth $bandwidth -dumpstream -dumpfile \"$file\" \"$url\" 1>&2"; - logger "DEGUG: Running $cmd\n" if $opt{debug}; - if ( system($cmd) ) { -- return 2; -+ return 'next'; - } - # Move file to done state - move $file, $file_done if ! $opt{nowrite}; -@@ -2498,7 +3357,7 @@ - if ( system($cmd) ) { - # If we fail then kill off child processes - kill 9, $childpid; -- return 2; -+ return 'next'; - } - # WAV / mp3 mode - } else { -@@ -2506,7 +3365,7 @@ - if ( system($cmd) ) { - # If we fail then kill off child processes - kill 9, $childpid; -- return 2; -+ return 'next'; - } - } - # Wait for child processes to prevent zombies -@@ -2522,13 +3381,14 @@ - logger "INFO: Created symlink from '$file_symlink' -> '$file_done'\n" if $opt{verbose}; - } - -+ $prog{$pid}{mode} = 'realaudio'; - return 0; - } - - - - # Actually do the podcast downloading --sub download_podcast_stream { -+sub download_stream_podcast { - my ( $ua, $url_2, $file, $file_done, $file_symlink ) = @_; - my $start_time = time(); - -@@ -2548,7 +3408,7 @@ - - if ( download_block($file, $url_2, $ua, $start, undef, undef, $fh) != 0 ) { - logger "ERROR: Download failed\n"; -- return 22; -+ return 'next'; - } else { - # end marker - my $end_time = time(); -@@ -2569,111 +3429,98 @@ - logger "INFO: Created symlink from '$file_symlink' -> '$file_done'\n" if $opt{verbose}; - } - } -+ -+ $prog{$url_2}{mode} = 'podcast'; - return 0; - } - - - - # Get streaming iphone URL --sub get_iphone_stream_download_url { -- my $ua = shift; -- my $pid = shift; -- -- # Create url with appended 6 digit random number -- my $url_1 = ${iphone_download_prefix}.'/'.${pid}.'?'.(sprintf "%06.0f", 1000000*rand(0)).'%20'; -- logger "INFO: media stream download URL = $url_1\n" if $opt{verbose}; -- -- # Stage 2: e.g. "Location: http://download.iplayer.bbc.co.uk/iplayer_streaming_http_mp4/121285241910131406.mp4?token=iVXexp1yQt4jalB2Hkl%2BMqI25nz2WKiSsqD7LzRmowrwXGe%2Bq94k8KPsm7pI8kDkLslodvHySUyU%0ApM76%2BxEGtoQTF20ZdFjuqo1%2B3b7Qmb2StOGniozptrHEVQl%2FYebFKVNINg%3D%3D%0A" -- logger "\rGetting iplayer download URL " if ! $opt{verbose}; -- my $h = new HTTP::Headers( -- 'User-Agent' => $user_agent{coremedia}, -- 'Accept' => '*/*', -- 'Range' => 'bytes=0-1', -- ); -- my $req = HTTP::Request->new ('GET', $url_1, $h); -- # send request -- my $res = $ua->request($req); -- # Get resulting Location header (i.e. redirect URL) -- my $url_2 = $res->header("location"); -- if ( ! $res->is_redirect ) { -- logger "ERROR: Failed to get redirect from iplayer site\n\n"; -- return ''; -- } -- # Extract redirection Location URL -- $url_2 =~ s/^Location: (.*)$/$1/g; -- # If we get a Redirection containing statuscode=404 then this prog is not yet ready -- if ( $url_2 =~ /statuscode=404/ ) { -- logger "\rERROR: Programme is not yet ready for download\n"; -- return ''; -- } -- -- return $url_2; --} -- -- -+sub get_stream_url_iphone { -+ my $ua = shift; -+ my $pid = shift; - --# Get streaming audio URL (Real => rtsp) --#<media kind="audio" --# type="audio/real" --# encoding="real" > --# <connection --# priority="10" --# kind="sis" --# server="http://www.bbc.co.uk" --# identifier="/radio/aod/playlists/gs/5d/c0/0b/0900_bbc_radio_two" --# href="http://www.bbc.co.uk/radio/aod/playlists/gs/5d/c0/0b/0900_bbc_radio_two.ram" --# /> --#</media> --# OR --#<media kind="" --# type="audio/real" --# encoding="real" > --# <connection --# priority="10" --# kind="edgesuite" --# server="http://http-ws.bbc.co.uk.edgesuite.net" --# identifier="/generatecssram.esi?file=/worldservice/css/nb/410591221152760.ra" --# href="http://http-ws.bbc.co.uk.edgesuite.net/generatecssram.esi?file=/worldservice/css/nb/410591221152760.ra" --# /> --#</media> --# --sub get_audio_stream_download_url { -- my $ua = shift; -- my $url_1 = shift; -- my $url_2; -+ # Create url with appended 6 digit random number -+ my $url_1 = ${iphone_download_prefix}.'/'.${pid}.'?'.(sprintf "%06.0f", 1000000*rand(0)).'%20'; -+ logger "INFO: media stream download URL = $url_1\n" if $opt{verbose}; - -- logger "\rGetting iplayer download URL " if ! $opt{verbose}; -- my $h = new HTTP::Headers( -- 'User-Agent' => $user_agent{coremedia}, -- 'Accept' => '*/*', -- 'Range' => 'bytes=0-', -- ); -- my $req = HTTP::Request->new ('GET', $url_1, $h); -- # send request -- my $res = $ua->request($req); -- # Get resulting content -- my $content = $res->content; -- # Flatten -- $content =~ s/\n/ /g; -- if ( ! $res->is_success ) { -- logger "ERROR: Failed to get audio url from iplayer site\n\n"; -- return ''; -- } -- # If we get a Redirection containing statuscode=404 then this prog is not yet ready -- if ( $content =~ /statuscode=404/ ) { -- logger "\rERROR: Programme is not yet ready for download\n"; -- return ''; -- } -- # extract ram URL -- $url_2 = $2 if $content =~ m{<media kind="(|audio)"\s*type="audio/real".*href="(.+?)"\s*}; -- -- # If we cannot see 'encoding="real"...' then we don't have real audio transcoded format then skip -- if ( ! $url_2 ) { -- logger "\rERROR: Programme is not yet ready for download in RealAudio format\n"; -- return ''; -- } -- -- return $url_2; -+ # Stage 2: e.g. "Location: http://download.iplayer.bbc.co.uk/iplayer_streaming_http_mp4/121285241910131406.mp4?token=iVXexp1yQt4jalB2Hkl%2BMqI25nz2WKiSsqD7LzRmowrwXGe%2Bq94k8KPsm7pI8kDkLslodvHySUyU%0ApM76%2BxEGtoQTF20ZdFjuqo1%2B3b7Qmb2StOGniozptrHEVQl%2FYebFKVNINg%3D%3D%0A" -+ logger "\rGetting iplayer download URL " if (! $opt{verbose}) && ! $opt{streaminfo}; -+ my $h = new HTTP::Headers( -+ 'User-Agent' => $user_agent{coremedia}, -+ 'Accept' => '*/*', -+ 'Range' => 'bytes=0-1', -+ ); -+ my $req = HTTP::Request->new ('GET', $url_1, $h); -+ # send request (use simple_request here because that will not allow redirects) -+ my $res = $ua->simple_request($req); -+ # Get resulting Location header (i.e. redirect URL) -+ my $url_2 = $res->header("location"); -+ if ( ! $res->is_redirect ) { -+ logger "ERROR: Failed to get redirect from iplayer site\n\n"; -+ return ''; -+ } -+ # Extract redirection Location URL -+ $url_2 =~ s/^Location: (.*)$/$1/g; -+ # If we get a Redirection containing statuscode=404 then this prog is not yet ready -+ if ( $url_2 =~ /statuscode=404/ ) { -+ logger "\rERROR: Programme is not yet ready for download\n" if $opt{verbose}; -+ return ''; -+ } -+ -+ return $url_2; -+} -+ -+ -+ -+sub get_stream_url_itv { -+ my ( $ua, $pid ) = ( @_ ); -+ -+ my ( $response, $url_1, $url_2, $url_3, $url_4 ); -+ my $part; -+ my $duration; -+ my $filename; -+ my @url_list; -+ -+ # construct stage 1 request url -+ $url_1 = 'http://www.itv.com/_app/video/GetMediaItem.ashx?vodcrid=crid://itv.com/'.$pid.'&bitrate=384&adparams=SITE=ITV/AREA=CATCHUP.VIDEO/SEG=CATCHUP.VIDEO%20HTTP/1.1'; -+ -+ # Extract '<LicencePlaylist>(.+?) HTTP/1.1</LicencePlaylist>' -+ logger "INFO: ITV Video Stage 1 URL: $url_1\n" if $opt{verbose}; -+ $response = request_url_retry($ua, $url_1, 2, '', ''); -+ logger "DEBUG: Response data: $response\n" if $opt{debug}; -+ $url_2 = $1 if $response =~ m{<LicencePlaylist>(.+?) HTTP/1.1</LicencePlaylist>}; -+ # replace '&' with '&' and append '%20HTTP/1.1' -+ $url_2 =~ s/&/&/g; -+ $url_2 .= '%20HTTP/1.1'; -+ logger "INFO: ITV Video Stage 2 URL: $url_2\n" if $opt{verbose}; -+ $response = request_url_retry($ua, $url_2, 2, '', ''); -+ logger "DEBUG: Response data: $response\n" if $opt{debug}; -+ -+ # Extract hrefs and names. There are multiple entries for parts of prog (due to ads): -+ # e.g. <asx><Title>Doctor Zhivago</Title><EntryRef href="HTTP://SAM.ITV.COM/XTSERVER/ACC_RANDOM=1231194223/SITE=ITV/AREA=CATCHUP.VIDEO/SEG=CATCHUP.VIDEO HTTP/1.1/SOURCE=CATCH.UP/GENRE=DRAMA/PROGNAME=DOCTOR.ZHIVAGO/PROGID=33105/SERIES=DOCTOR.ZHIVAGO/EPNUM=/EPTITLE=/BREAKNUM=0/ADPOS=1/PAGEID=01231194223/DENTON=0/CUSTOMRATING=/TOTDUR=90/PREDUR=0/POSDUR=905/GENERIC=6e0536bf-7883-4aaa-9230-94ecc4aea403/AAMSZ=VIDEO" /><EntryRef href="HTTP://SAM.ITV.COM/XTSERVER/ACC_RANDOM=1231194223/SITE=ITV/AREA=CATCHUP.VIDEO/SEG=CATCHUP.VIDEOHTTP/1.1/SOURCE=CATCH.UP/GENRE=DRAMA/PROGNAME=DOCTOR.ZHIVAGO/PROGID=33105/SERIES=DOCTOR.ZHIVAGO/EPNUM=/EPTITLE=/BREAKNUM=0/ADPOS=LAST/PAGEID=01231194223/DENTON=0/CUSTOMRATING=/TOTDUR=90/PREDUR=0/POSDUR=905/GENERIC=6e0536bf-7883-4aaa-9230-94ecc4aea403/AAMSZ=VIDEO" /> -+ $prog{$pid}{name} = $1 if $response =~ m{<Title>(.+?)<\/Title>}; -+ for my $entry (split /<Entry><ref\s+href=/, $response) { -+ logger "DEBUG: Entry data: $entry\n" if $opt{debug}; -+ $entry .= '<Entry><ref href='.$entry; -+ -+ ( $url_3, $part, $filename, $duration ) = ( $1, $2, $3, $4 ) if $entry =~ m{<Entry><ref\s+href="(.+?)"\s+\/><param\s+value="true"\s+name="Prebuffer"\s+\/>\s*<PARAM\s+NAME="PrgPartNumber"\s+VALUE="(.+?)"\s*\/><PARAM\s+NAME="FileName"\s+VALUE="(.+?)"\s*\/><PARAM\s+NAME="PrgLength"\s+VALUE="(.+?)"\s*\/>}; -+ next if not $url_3; -+ # Replace '&' with '&' in url -+ $url_3 =~ s/&/&/g; -+ logger "INFO: ITV Video Name: $part\n"; -+ -+ logger "INFO: ITV Video Stage 3 URL: $url_3\n" if $opt{verbose}; -+ $entry = request_url_retry($ua, $url_3, 2, '', ''); -+ logger "DEBUG: Response data: $entry\n" if $opt{debug}; -+ -+ # Extract mms (replace 'http' with 'mms') url: e.g.: Ref1=http://itvbrdbnd.wmod.llnwd.net/a1379/o21/ucontent/2007/6/22/1549_384_1_2.wmv?MSWMExt=.asf -+ chomp( $url_4 = 'mms'.$1 ) if $entry =~ m{Ref1=http(.+?)[\r\n]+}; -+ logger "INFO: ITV Video URL: $url_4\n" if $opt{verbose}; -+ push @url_list, $url_4; -+ } -+ return @url_list; - } - - -@@ -2711,7 +3558,7 @@ - # Change all the chunk offsets in moov->stco atoms and add moov_length to them all - # get moov atom length - my $moov_length = bytestring_to_int( substr($moovdata, 0, 4) ); -- # Use index() to seatch for a string within a string -+ # Use index() to search for a string within a string - my $i = -1; - while (($i = index($moovdata, 'stco', $i)) > -1) { - -@@ -2725,10 +3572,11 @@ - #logger "chunk_offset @ $i, $j = '".get_hex( substr($moovdata, $j, 4) )."', $chunk_offset + $moov_length = "; - $chunk_offset += $moov_length; - # write back bytes into $moovdata -- substr($moovdata, $j+0, 1) = chr( ($chunk_offset >> 24) & 0xFF ); -- substr($moovdata, $j+1, 1) = chr( ($chunk_offset >> 16) & 0xFF ); -- substr($moovdata, $j+2, 1) = chr( ($chunk_offset >> 8) & 0xFF ); -- substr($moovdata, $j+3, 1) = chr( ($chunk_offset >> 0) & 0xFF ); -+ #substr($moovdata, $j+0, 1) = chr( ($chunk_offset >> 24) & 0xFF ); -+ #substr($moovdata, $j+1, 1) = chr( ($chunk_offset >> 16) & 0xFF ); -+ #substr($moovdata, $j+2, 1) = chr( ($chunk_offset >> 8) & 0xFF ); -+ #substr($moovdata, $j+3, 1) = chr( ($chunk_offset >> 0) & 0xFF ); -+ write_msb_value_at_offset( $moovdata, $j, $chunk_offset ); - #$chunk_offset = bytestring_to_int( substr($moovdata, $j, 4) ); - #logger "$chunk_offset\n"; - } -@@ -2743,6 +3591,86 @@ - - - -+# Replace the moov->udta atom with a new user-supplied one and update the moov atom size -+# Usage: replace_moov_udta_atom ( $udta_new, $moovdata ) -+sub replace_moov_udta_atom { -+ my $udta_new = $_[0]; -+ my $moovdata = $_[1]; -+ -+ # get moov atom length -+ my $moov_length = bytestring_to_int( substr($moovdata, 0, 4) ); -+ -+ # Find the original udta atom start -+ # Use index() to search for a string within a string ($i will point at the beginning of the atom) -+ my $i = index($moovdata, 'udta', -1) - 4; -+ -+ # determine length of atom (4 bytes preceding the name) -+ my $udta_len = bytestring_to_int( substr($moovdata, $i, 4) ); -+ logger "INFO: Found udta atom at moov atom offset: $i length $udta_len\n" if $opt{verbose}; -+ -+ # Save the data before the udta atom -+ my $moovdata_before_udta = substr($moovdata, 0, $i); -+ -+ # Save the remainder portion of data after the udta atom for later -+ my $moovdata_after_udta = substr($moovdata, $i, $moovdata - $i + $udta_len); -+ -+ # Old udta atom should we need it -+ ### my $udta_old = substr($moovdata, $i, $udta_len); -+ -+ # Create new moov atom -+ $moovdata = $moovdata_before_udta.$udta_new.$moovdata_after_udta; -+ -+ # Recalculate the moov size and insert into moovdata -+ write_msb_value_at_offset( $moovdata, 0, length($moovdata) ); -+ -+ # Write $moovdata back to calling string -+ $_[1] = $moovdata; -+ -+ return 0; -+} -+ -+ -+ -+# Write the msb 4 byte $value starting at $offset into the passed string -+# Usage: write_msb_value($string, $offset, $value) -+sub write_msb_value_at_offset { -+ my $offset = $_[1]; -+ my $value = $_[2]; -+ substr($_[0], $offset+0, 1) = chr( ($value >> 24) & 0xFF ); -+ substr($_[0], $offset+1, 1) = chr( ($value >> 16) & 0xFF ); -+ substr($_[0], $offset+2, 1) = chr( ($value >> 8) & 0xFF ); -+ substr($_[0], $offset+3, 1) = chr( ($value >> 0) & 0xFF ); -+ return 0; -+} -+ -+ -+ -+# Returns a string containing an QT atom -+# Usage: create_qt_atom(<atome name>, <atom data>, ['string']) -+sub create_qt_atom { -+ my ($name, $data, $prog_type) = (@_); -+ if (length($name) != 4) { -+ logger "ERROR: Inavlid QT atom name length '$name'\n"; -+ exit 1; -+ } -+ # prepend string length if this is a string type -+ if ( $prog_type eq 'string' ) { -+ my $value = length($data); -+ $data = '1111'.$data; -+ # overwrite '1111' with total atom length in 2-byte MSB + 0x0 0x0 -+ substr($data, 0, 1) = chr( ($value >> 8) & 0xFF ); -+ substr($data, 1, 1) = chr( ($value >> 0) & 0xFF ); -+ substr($data, 2, 1) = chr(0); -+ substr($data, 3, 1) = chr(0); -+ } -+ my $atom = '0000'.$name.$data; -+ # overwrite '0000' with total atom length in MSB -+ write_msb_value_at_offset( $atom, 0, length($name.$data) + 4 ); -+ return $atom; -+} -+ -+ -+ - # Usage download_block($file, $url_2, $ua, $start, $end, $file_len, $fh); - # ensure filehandle $fh is open in append mode - # or, $content = download_block(undef, $url_2, $ua, $start, $end, $file_len); -@@ -2811,7 +3739,7 @@ - $rate = sprintf("%5.0fkbps", (8.0 / 1024.0) * $rate_bps); - $time = sprintf("%02d:%02d:%02d", ( gmtime( ($file_len - $size) / $rate_bps ) )[2,1,0] ); - } -- printf STDERR "%8.2fMB / %.2fMB %s %5.1f%%, %s remaining \r", -+ logger sprintf "%8.2fMB / %.2fMB %s %5.1f%%, %s remaining \r", - $size / 1024.0 / 1024.0, - $file_len / 1024.0 / 1024.0, - $rate, -@@ -2851,7 +3779,7 @@ - $time = sprintf("%02d:%02d:%02d", ( gmtime( ($file_len - $size) / $rate_bps ) )[2,1,0] ); - } - # time remaining -- printf STDERR "%8.2fMB / %.2fMB %s %5.1f%%, %s remaining \r", -+ logger sprintf "%8.2fMB / %.2fMB %s %5.1f%%, %s remaining \r", - $size / 1024.0 / 1024.0, - $file_len / 1024.0 / 1024.0, - $rate, -@@ -2865,7 +3793,7 @@ - } else { - $rate = sprintf("%5.0fkbps", (8.0 / 1024.0) * $size / ($timecalled - $now) ); - } -- printf STDERR "%8.2fMB %s \r", $size / 1024.0 / 1024.0, $rate; -+ logger sprintf "%8.2fMB %s \r", $size / 1024.0 / 1024.0, $rate; - } - }; - -@@ -2901,6 +3829,19 @@ - - - -+sub create_ua { -+ my $agent = shift; -+ my $ua = LWP::UserAgent->new; -+ $ua->timeout([$lwp_request_timeout]); -+ $ua->proxy( ['http'] => $proxy_url ); -+ $ua->agent( $user_agent{$agent} ); -+ $ua->conn_cache(LWP::ConnCache->new()); -+ $ua->cookie_jar( HTTP::Cookies->new( file => $cookiejar, autosave => 1, ignore_discard => 1 ) ); -+ return $ua; -+}; -+ -+ -+ - # Converts a string of chars to it's HEX representation - sub get_hex { - my $buf = shift || ''; -@@ -2985,11 +3926,7 @@ - sub update_script { - # Get version URL - my $script_file = $0; -- my $ua = LWP::UserAgent->new; -- $ua->timeout([$lwp_request_timeout]); -- $ua->proxy( ['http'] => $proxy_url ); -- $ua->agent( $user_agent{update} ); -- $ua->conn_cache(LWP::ConnCache->new()); -+ my $ua = create_ua('update'); - logger "INFO: Current version is $version\n"; - logger "INFO: Checking for latest version from linuxcentre.net\n"; - my $res = $ua->request( HTTP::Request->new( GET => $version_url ) ); -@@ -3070,10 +4007,8 @@ - <video><url id=\"p1\">${pid}.mov<playlist/></url></video> - <info><description>${desc}</description></info> - </movie>\n" if $opt{fxd}; -- my $newtitle = ${title} ; -- $newtitle =~ s/\&//g ; - print XML "<Stream> -- <Name>\"$newtitle\"</Name> -+ <Name>\"${title}\"</Name> - <url>${pid}.mov</url> - <Subtitle></Subtitle> - <Synopsis>${desc}</Synopsis> -@@ -3294,6 +4229,7 @@ - my $pid = shift; - my $metadata; - my $entry3; -+ my ($name, $episode, $duration, $available, $channel, $expiry, $longdesc, $versions, $guidance, $prog_type, $categories, $player, $thumbnail); - - # This URL works for all prog types: - # http://www.bbc.co.uk/iplayer/playlist/${pid} -@@ -3307,97 +4243,162 @@ - # This URL works for tv/radio prog types: - # $prog_feed_url = http://feeds.bbc.co.uk/iplayer/episode/$pid - -- if ( $prog{$pid}{type} =~ /(tv|radio)/i ) { -+ if ( $prog{$pid}{type} =~ /^(tv|radio)$/i ) { - $entry3 = request_url_retry($ua, $prog_feed_url.$pid, 3, '', ''); - decode_entities($entry3); - logger "DEBUG: $prog_feed_url.$pid:\n$entry3\n\n" if $opt{debug}; - # Flatten - $entry3 =~ s|\n| |g; -- } - -- # Entry3 format -- #<?xml version="1.0" encoding="utf-8"?> -- #<?xml-stylesheet href="http://www.bbc.co.uk/iplayer/style/rss.css" type="text/css"?> -- #<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="en-GB"> -- # <title>BBC iPlayer - Episode Detail: Edith Bowman: 22/09/2008</title> -- # <subtitle>Sara Cox sits in for Edith with another Cryptic Randomizer.</subtitle> -- # <updated>2008-09-29T10:59:45Z</updated> -- # <id>tag:feeds.bbc.co.uk,2008:/iplayer/feed/episode/b00djtfh</id> -- # <link rel="related" href="http://www.bbc.co.uk/iplayer" type="text/html" /> -- # <link rel="self" href="http://feeds.bbc.co.uk/iplayer/episode/b00djtfh" type="application/atom+xml" /> -- # <author> -- # <name>BBC</name> -- # <uri>http://www.bbc.co.uk</uri> -- # </author> -- # <entry> -- # <title type="text">Edith Bowman: 22/09/2008</title> -- # <id>tag:feeds.bbc.co.uk,2008:PIPS:b00djtfh</id> -- # <updated>2008-09-15T01:28:36Z</updated> -- # <summary>Sara Cox sits in for Edith with another Cryptic Randomizer.</summary> -- # <content type="html"> -- # <p> -- # <a href="http://www.bbc.co.uk/iplayer/episode/b00djtfh?src=a_syn30"> -- # <img src="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_150_84.jpg" alt="Edith Bowman: 22/09/2008" /> -- # </a> -- # </p> -- # <p> -- # Sara Cox sits in for Edith with movie reviews and great new music, plus another Cryptic Randomizer. -- # </p> -- # </content> -- # <link rel="alternate" href="http://www.bbc.co.uk/iplayer/episode/b00djtfh?src=a_syn31" type="text/html" title="Edith Bowman: 22/09/2008"> -- # <media:content medium="audio" duration="10800"> -- # <media:title>Edith Bowman: 22/09/2008</media:title> -- # <media:description>Sara Cox sits in for Edith with movie reviews and great new music, plus another Cryptic Randomizer.</media:description> -- # <media:player url="http://www.bbc.co.uk/iplayer/episode/b00djtfh?src=a_syn31" /> -- # <media:category scheme="urn:bbc:metadata:cs:iPlayerUXCategoriesCS" label="Entertainment">9100099</media:category> -- # <media:category scheme="urn:bbc:metadata:cs:iPlayerUXCategoriesCS" label="Music">9100006</media:category> -- # <media:category scheme="urn:bbc:metadata:cs:iPlayerUXCategoriesCS" label="Pop & Chart">9200069</media:category> -- # <media:credit role="Production Department" scheme="urn:ebu">BBC Radio 1</media:credit> -- # <media:credit role="Publishing Company" scheme="urn:ebu">BBC Radio 1</media:credit> -- # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_86_48.jpg" width="86" height="48" /> -- # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_150_84.jpg" width="150" height="84" /> -- # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_178_100.jpg" width="178" height="100" /> -- # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_512_288.jpg" width="512" height="288" /> -- # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_528_297.jpg" width="528" height="297" /> -- # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_640_360.jpg" width="640" height="360" /> -- # <dcterms:valid> -- # start=2008-09-22T15:44:20Z; -- # end=2008-09-29T15:02:00Z; -- # scheme=W3C-DTF -- # </dcterms:valid> -- # </media:content> -- # </link> -- # <link rel="self" href="http://feeds.bbc.co.uk/iplayer/episode/b00djtfh?format=atom" type="application/atom+xml" title="22/09/2008" /> -- # <link rel="related" href="http://www.bbc.co.uk/programmes/b006wks4/microsite" type="text/html" title="Edith Bowman" /> -- # <link rel="parent" href="http://feeds.bbc.co.uk/iplayer/programme_set/b006wks4" type="application/atom+xml" title="Edith Bowman" /> -- # </entry> -- #</feed> -- -- my ($duration, $available, $channel, $expiry, $longdesc, $versions, $guidance, $type, $categories, $player, $thumbnail); -- -- $expiry = $1 if $entry3 =~ m{<dcterms:valid>\s*start=.+?;\s*end=(.*?);}; -- $available = $1 if $entry3 =~ m{<dcterms:valid>\s*start=(.+?);\s*end=.*?;}; -- $duration = $1 if $entry3 =~ m{duration=\"(\d+?)\"}; -- $type = $1 if $entry3 =~ m{medium=\"(\w+?)\"}; -- $longdesc = $1 if $entry3 =~ m{<media:description>\s*(.*?)\s*<\/media:description>}; -- $guidance = $1 if $entry3 =~ m{<media:rating scheme="urn:simple">(.+?)<\/media:rating>}; -- $player = $1 if $entry3 =~ m{<media:player\s*url=\"(.*?)\"\s*\/>}; -- $thumbnail = $1 if $entry3 =~ m{<media:thumbnail url="([^"]+?)"\s+width="150"\s+height="84"\s*/>}; -- -- my @cats; -- for (split /<media:category scheme=\".+?\"/, $entry3) { -- push @cats, $1 if m{\s*label="(.+?)">\d+<\/media:category>}; -- } -- $categories = join ',', @cats; -+ # Entry3 format -+ #<?xml version="1.0" encoding="utf-8"?> -+ #<?xml-stylesheet href="http://www.bbc.co.uk/iplayer/style/rss.css" type="text/css"?> -+ #<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="en-GB"> -+ # <title>BBC iPlayer - Episode Detail: Edith Bowman: 22/09/2008</title> -+ # <subtitle>Sara Cox sits in for Edith with another Cryptic Randomizer.</subtitle> -+ # <updated>2008-09-29T10:59:45Z</updated> -+ # <id>tag:feeds.bbc.co.uk,2008:/iplayer/feed/episode/b00djtfh</id> -+ # <link rel="related" href="http://www.bbc.co.uk/iplayer" type="text/html" /> -+ # <link rel="self" href="http://feeds.bbc.co.uk/iplayer/episode/b00djtfh" type="application/atom+xml" /> -+ # <author> -+ # <name>BBC</name> -+ # <uri>http://www.bbc.co.uk</uri> -+ # </author> -+ # <entry> -+ # <title type="text">Edith Bowman: 22/09/2008</title> -+ # <id>tag:feeds.bbc.co.uk,2008:PIPS:b00djtfh</id> -+ # <updated>2008-09-15T01:28:36Z</updated> -+ # <summary>Sara Cox sits in for Edith with another Cryptic Randomizer.</summary> -+ # <content type="html"> -+ # <p> -+ # <a href="http://www.bbc.co.uk/iplayer/episode/b00djtfh?src=a_syn30"> -+ # <img src="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_150_84.jpg" alt="Edith Bowman: 22/09/2008" /> -+ # </a> -+ # </p> -+ # <p> -+ # Sara Cox sits in for Edith with movie reviews and great new music, plus another Cryptic Randomizer. -+ # </p> -+ # </content> -+ # <link rel="alternate" href="http://www.bbc.co.uk/iplayer/episode/b00djtfh?src=a_syn31" type="text/html" title="Edith Bowman: 22/09/2008"> -+ # <media:content medium="audio" duration="10800"> -+ # <media:title>Edith Bowman: 22/09/2008</media:title> -+ # <media:description>Sara Cox sits in for Edith with movie reviews and great new music, plus another Cryptic Randomizer.</media:description> -+ # <media:player url="http://www.bbc.co.uk/iplayer/episode/b00djtfh?src=a_syn31" /> -+ # <media:category scheme="urn:bbc:metadata:cs:iPlayerUXCategoriesCS" label="Entertainment">9100099</media:category> -+ # <media:category scheme="urn:bbc:metadata:cs:iPlayerUXCategoriesCS" label="Music">9100006</media:category> -+ # <media:category scheme="urn:bbc:metadata:cs:iPlayerUXCategoriesCS" label="Pop & Chart">9200069</media:category> -+ # <media:credit role="Production Department" scheme="urn:ebu">BBC Radio 1</media:credit> -+ # <media:credit role="Publishing Company" scheme="urn:ebu">BBC Radio 1</media:credit> -+ # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_86_48.jpg" width="86" height="48" /> -+ # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_150_84.jpg" width="150" height="84" /> -+ # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_178_100.jpg" width="178" height="100" /> -+ # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_512_288.jpg" width="512" height="288" /> -+ # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_528_297.jpg" width="528" height="297" /> -+ # <media:thumbnail url="http://www.bbc.co.uk/iplayer/images/episode/b00djtfh_640_360.jpg" width="640" height="360" /> -+ # <dcterms:valid> -+ # start=2008-09-22T15:44:20Z; -+ # end=2008-09-29T15:02:00Z; -+ # scheme=W3C-DTF -+ # </dcterms:valid> -+ # </media:content> -+ # </link> -+ # <link rel="self" href="http://feeds.bbc.co.uk/iplayer/episode/b00djtfh?format=atom" type="application/atom+xml" title="22/09/2008" /> -+ # <link rel="related" href="http://www.bbc.co.uk/programmes/b006wks4/microsite" type="text/html" title="Edith Bowman" /> -+ # <link rel="parent" href="http://feeds.bbc.co.uk/iplayer/programme_set/b006wks4" type="application/atom+xml" title="Edith Bowman" /> -+ # </entry> -+ #</feed> -+ -+ $expiry = $1 if $entry3 =~ m{<dcterms:valid>\s*start=.+?;\s*end=(.*?);}; -+ $available = $1 if $entry3 =~ m{<dcterms:valid>\s*start=(.+?);\s*end=.*?;}; -+ $duration = $1 if $entry3 =~ m{duration=\"(\d+?)\"}; -+ $prog_type = $1 if $entry3 =~ m{medium=\"(\w+?)\"}; -+ $longdesc = $1 if $entry3 =~ m{<media:description>\s*(.*?)\s*<\/media:description>}; -+ $guidance = $1 if $entry3 =~ m{<media:rating scheme="urn:simple">(.+?)<\/media:rating>}; -+ $player = $1 if $entry3 =~ m{<media:player\s*url=\"(.*?)\"\s*\/>}; -+ $thumbnail = $1 if $entry3 =~ m{<media:thumbnail url="([^"]+?)"\s+width="150"\s+height="84"\s*/>}; -+ -+ my @cats; -+ for (split /<media:category scheme=\".+?\"/, $entry3) { -+ push @cats, $1 if m{\s*label="(.+?)">\d+<\/media:category>}; -+ } -+ $categories = join ',', @cats; -+ -+ # populate version pid metadata -+ get_version_pids($ua, $pid); -+ -+ # ITV Catch-Up metadata -+ } elsif ( $prog{$pid}{type} eq 'itv' ) { -+ my $prog_metadata_url_itv = 'http://www.itv.com/_app/Dynamic/CatchUpData.ashx?ViewType=5&Filter='; # +<pid> -+ $entry3 = request_url_retry($ua, "${prog_metadata_url_itv}${pid}", 3, '', ''); -+ decode_entities($entry3); -+ logger "DEBUG: ${prog_metadata_url_itv}${pid}:\n$entry3\n\n" if $opt{debug}; -+ # Flatten -+ $entry3 =~ s|[\r\n]||g; -+ -+ #div class="itvCatchUpPlayerPanel" xmlns:ms="urn:schemas-microsoft-com:xslt"> -+ # <div class="cu-sponsor"><a href="http://sam.itv.com/accipiter/adclick/CID=000040d70000000000000000/acc_random=1/SITE=CLICKTRACK/AREAITVCATCHUP.VIDEO=CLICKTRACK..FREEVIEW.SPONSORBUTTON.OCT08/AAMSZ=120X60/pageid=1" title="ITV Player in assocation with Freeview"><img src="/_app/img/catchup/catchup_video_freeview2.jpg" alt="ITV Player is sponsored by Freeview"></a></div> -+ # <h2>Doctor Zhivago</h2> -+ # <p>Part 1 of 3. Dramatisation of the epic novel by Boris Pasternak. Growing up in Moscow with his uncle, aunt and cousin Tonya, Yury is captivated by a stunning young girl called ...</p> -+ # <p class="timings"><span class="date">Mon 29 Dec 2008</span><br /><br /><span> -+ # -+ # Duration: 1hr 30 min | -+ # Expires in -+ # <strong>22</strong> -+ # days -+ # </span></p> -+ # <p><a href="http://www.itv.com/CatchUp/Programmes/default.html?ViewType=1&Filter=2352">3 Episodes Available -+ # </a><br></br></p> -+ # <p class="channelLogo"><img src="/_app/img/logos/itv3-black.gif" alt="ITV 4"></p> -+ # <div id="cu-2-0-VideoID">33105</div> -+ # <div id="cu-2-0-DentonId">17</div> -+ # <div id="cu-2-0-ItemMediaUrl">http://www.itv.com//img/480x272/Doctor-Zhivago-c47828f8-a1af-4cd2-b5a2-40c18eb7e63c.jpg</div> -+ #</div><script language="javascript" type="text/javascript" xmlns:ms="urn:schemas-microsoft-com:xslt"> -+ # SetCatchUpModuleID(0); -+ # </script> -+ # - -- # populate version pid metadata -- get_version_pids($ua, $pid); -+ #<div class="itvCatchUpPlayerPanel" xmlns:ms="urn:schemas-microsoft-com:xslt"> -+ # <div class="cu-sponsor"><a href="http://sam.itv.com/accipiter/adclick/CID=000040d70000000000000000/acc_random=1/SITE=CLICKTRACK/AREAITVCATCHUP.VIDEO=CLICKTRACK..FREEVIEW.SPONSORBUTTON.OCT08/AAMSZ=120X60/pageid=1" title="ITV Player in assocation with Freeview"><img src="/_app/img/catchup/catchup_video_freeview2.jpg" alt="ITV Player is sponsored by Freeview"></a></div> -+ # <h2>Affinity</h2> -+ # <p>Victorian period drama with a murderous, pyschological twist.</p> -+ # <p class="timings"><span class="date">Sun 28 Dec 2008</span><br /><br /><span> -+ # -+ # Duration: 2hr 00 min | -+ # Expires in -+ # <strong>21</strong> -+ # days -+ # </span></p> -+ # <p class="channelLogo"><img src="/_app/img/logos/itv1-black.gif" alt="ITV 2"></p> -+ # <div class="guidance"> -+ # <div><strong>ITV Video Guidance</strong><p>This programme contains strong language and scenes of a sexual nature </p> -+ # </div> -+ # </div> -+ # <div id="cu-2-0-VideoID">33076</div> -+ # <div id="cu-2-0-DentonId">11</div> -+ # <div id="cu-2-0-ItemMediaUrl">http://www.itv.com//img/480x272/Affinity-9624033b-6e05-4784-85f7-114be0559b24.jpg</div> -+ #</div><script language="javascript" type="text/javascript" xmlns:ms="urn:schemas-microsoft-com:xslt"> -+ # SetCatchUpModuleID(0); -+ # </script> -+ # -+ -+ #$expiry = $1 if $entry3 =~ m{<dcterms:valid>\s*start=.+?;\s*end=(.*?);}; -+ $available = $1 if $entry3 =~ m{<p\s+class="timings">\s*<span\s+class="date">(.+?)<\/span>}; -+ $duration = $1 if $entry3 =~ m{Duration:\s*(.+?)\s+\|}; -+ #$prog_type = $1 if $entry3 =~ m{medium=\"(\w+?)\"}; -+ $longdesc = $1 if $entry3 =~ m{<p>(.+?)<\/p>}i; -+ $guidance = $1 if $entry3 =~ m{ITV Video Guidance<\/strong><p>\s*(.+?)[\W\s]*<\/p>}; -+ #$player = $1 if $entry3 =~ m{<media:player\s*url=\"(.*?)\"\s*\/>}; -+ $thumbnail = $1 if $entry3 =~ m{<div id="cu-2-0-ItemMediaUrl">(.+?)</div>}; -+ $name = $1 if $entry3 =~ m{<h2>(.+?)</h2>}; -+ } - - # Fill in from cache if not got from metadata - my %metadata; - $metadata{pid} = $pid; - $metadata{index} = $prog{$pid}{index}; -- $metadata{type} = $type || $prog{$pid}{type}; -+ $metadata{name} = $name || $prog{$pid}{name}; -+ $metadata{episode} = $episode || $prog{$pid}{episode}; -+ $metadata{type} = $prog_type || $prog{$pid}{type}; - $metadata{duration} = $duration || $prog{$pid}{duration}; - $metadata{channel} = $channel || $prog{$pid}{channel}; - $metadata{available} = $available || $prog{$pid}{available}; -@@ -3492,7 +4493,7 @@ - logger "WARNING: Cannot write or append to $historyfile\n\n"; - return 1; - } -- print HIST "$pid|$prog{$pid}{name}|$prog{$pid}{episode}|$prog{$pid}{type}|".time()."\n"; -+ print HIST "$pid|$prog{$pid}{name}|$prog{$pid}{episode}|$prog{$pid}{type}|".time()."|$prog{$pid}{mode}\n"; - close HIST; - return 0; - } -@@ -3555,7 +4556,10 @@ - # Add id3 tag to MP3 files if required - sub tag_file { - my $pid = shift; -+ - if ( $prog{$pid}{ext} eq 'mp3' ) { -+ # Return if file does not exist -+ return if ! -f $prog{$pid}{filename}; - # Create ID3 tagging options for external tagger program (escape " for shell) - my ( $id3_name, $id3_episode, $id3_desc, $id3_channel ) = ( $prog{$pid}{name}, $prog{$pid}{episode}, $prog{$pid}{desc}, $prog{$pid}{channel} ); - $id3_name =~ s|"|\"|g for ($id3_name, $id3_episode, $id3_desc, $id3_channel); -@@ -3580,9 +4584,16 @@ - sub list_unique_element_counts { - my $element_name = shift; - my %elements; -- logger "INFO: $opt{type} $element_name List:\n" if $opt{verbose}; -+ logger "INFO: ".(join ',', keys %type)." $element_name List:\n" if $opt{verbose}; - for my $pid (keys %prog) { -- for my $element ( split /,/, $prog{$pid}{$element_name} ) { -+ my @element; -+ # Need to separate the categories -+ if ($element_name eq 'categories') { -+ @element = split /,/, $prog{$pid}{$element_name}; -+ } else { -+ @element[0] = $prog{$pid}{$element_name}; -+ } -+ for my $element (@element) { - $elements{ $element }++; - } - } -@@ -3659,6 +4670,7 @@ - - - -+ - # Save the options on the cmdline as a PVR search with the specified name - sub pvr_add { - my $name = shift; -@@ -3670,7 +4682,7 @@ - return 1; - } - # Parse valid options and create array (ignore options from the options files that have not been overriden on the cmdline) -- for (grep /^(long|output.*|proxy|subdir|whitespace|versions|type|(exclude)?category|(exclude)?channel|command|realaudio|mp3audio|wav|raw|bandwidth|subtitles|suboffset|since|versionlist|verbose)$/, sort {$a <=> $b} keys %opt_cmdline) { -+ for (grep /^(amode|vmode|long|output.*|proxy|subdir|whitespace|versions|type|(exclude)?category|(exclude)?channel|command|realaudio|mp3audio|wav|raw|bandwidth|subtitles|suboffset|since|versionlist|verbose)$/, sort {lc $a cmp lc $b} keys %opt_cmdline) { - if ( defined $opt_cmdline{$_} ) { - push @options, "$_ $opt_cmdline{$_}"; - logger "DEBUG: Adding option $_ = $opt_cmdline{$_}\n" if $opt{debug}; -@@ -3715,7 +4727,7 @@ - pvr_load_list(); - # Print out list - logger "All PVR Searches:\n\n"; -- for my $name ( sort {$a <=> $b} keys %pvrsearches ) { -+ for my $name ( sort {lc $a cmp lc $b} keys %pvrsearches ) { - # Report whether disabled - if ( $pvrsearches{$name}{disable} ) { - logger "(Disabled) PVR Search '$name':\n"; -diff -ruaN mythvodka.orig/scripts/gethulu.pl mythvodka/scripts/gethulu.pl ---- mythvodka.orig/scripts/gethulu.pl 2009-01-06 19:26:30.000000000 +0000 -+++ mythvodka/scripts/gethulu.pl 2009-02-12 07:27:44.000000000 +0000 -@@ -123,7 +123,8 @@ - if($ephtml =~ m/thumbnail_url: "(.+?)"/) { $epimg=$1 } ; - - print MYTHMENU "<Stream>\n"; -- print MYTHMENU "<Name>$eptitle</Name>\n"; -+ print MYTHMENU "<Name>$title-$eptitle</Name>\n"; -+ #print MYTHMENU "<Name>$eptitle</Name>\n"; - print MYTHMENU "<Url>http://www.hulu.com/watch/$epid</Url>\n"; - print MYTHMENU "<Subtitle>$season - $epno</Subtitle>\n"; - print MYTHMENU "<Synopsis>$epdate - $epdesc</Synopsis>\n"; -diff -ruaN mythvodka.orig/scripts/hulu mythvodka/scripts/hulu ---- mythvodka.orig/scripts/hulu 2009-01-04 15:25:24.000000000 +0000 -+++ mythvodka/scripts/hulu 2009-02-12 07:27:44.000000000 +0000 -@@ -22,6 +22,7 @@ - html=get_HTML(cid) - cidSoup=BeautifulStoneSoup(html) - pid=cidSoup.findAll('pid')[0].contents[0] -+logfile="/var/log/mythtv/hulu_quality.log" - - smilURL = "http://releasegeo.hulu.com/content.select?pid=" + pid + "&mbr=true&format=smil" - print smilURL -@@ -34,18 +35,58 @@ - #label streams - i=0 - quality=0 -+qual_medium=-1; qual_high=-1; qual_h264=-1; command2="echo hulu done." -+os.system("rm -f "+logfile+".0") -+os.system("mv -f "+logfile+" "+logfile+".0") -+f=open(logfile,'w') -+os.system("chmod a+rw "+logfile) -+print >>f, "Debug debug" -+print >>f, "hulu ",url, " ", fileout -+# -+# Find the various quality choices that are available -+# - for stream in video: -- if "480K" in stream['src'] or "480k" in stream['src']: -+ print >>f, stream -+ if "H264" in stream['src'] or "h264" in stream['src'] or "h264" in stream['profile'] or "H264" in stream['profile']: -+ streams.append(['H264',stream['src']]) -+ qual_h264=i -+ print >>f, "DebugQual h264", i -+ print >>f, "" -+ elif "480K" in stream['src'] or "480k" in stream['src']: - streams.append(['Flash (480k)',stream['src']]) -+ qual_medium=i -+ print >>f, "DebugQual medium", i -+ print >>f, "" - elif "700K" in stream['src'] or "700k" in stream['src']: - streams.append(['Flash (700k)',stream['src']]) - quality=i -- elif "H264" in stream['src'] or "h264" in stream['src']: -- streams.append(['H264',stream['src']]) -+ qual_high=i -+ print >>f, "DebugQual high", i -+ print >>f, "" -+ elif "medium" in stream['profile'] or "Medium" in stream['profile']: -+ streams.append(['Flash (Medium)',stream['src']]) -+ if qual_medium==-1: qual_medium=i -+ print >>f, "DebugQual Medium", i, qual_medium -+ print >>f, "" -+ elif "high" in stream['profile'] or "High" in stream['profile']: -+ streams.append(['Flash (High)',stream['src']]) -+ if qual_high==-1: qual_high=i -+ print >>f, "DebugQual High", i, qual_high -+ print >>f, "" - else: - streams.append(['unkown quality: '+stream['src'].split('/')[-1],stream['src']]) -+ print >>f, "DebugQual Unknown", i -+ print >>f, "" - i=i+1 - -+if qual_high>-1: -+ quality=qual_high -+elif qual_medium>-1: -+ quality=qual_medium -+ -+print >>f, "DebugQualVars: h264, high, medium, selected=",qual_h264,qual_high,qual_medium,quality -+ -+ - if quality!=-1: - print "stream url" - #generate random code -@@ -104,4 +145,9 @@ - command=command.replace(';','\\;') - - print command -- os.system(command) -+ print >>f,"Command is ",command -+ print >>f,"command2 is ",command2 -+ f.close() -+ os.system(command + "; " + command2) -+else: -+ f.close() -diff -ruaN mythvodka.orig/scripts/mythvodka_player.sh mythvodka/scripts/mythvodka_player.sh ---- mythvodka.orig/scripts/mythvodka_player.sh 1970-01-01 00:00:00.000000000 +0000 -+++ mythvodka/scripts/mythvodka_player.sh 2009-02-12 07:27:44.000000000 +0000 -@@ -0,0 +1,38 @@ -+#! /bin/bash -+# -+log=/var/log/mythtv/mythvodka_player.log -+player_list="/usr/local/bin/mplayer_h264 /usr/local/bin/mplayer \ -+ /usr/bin/mplayer /bin/mplayer" -+# -+player_list="/usr/local/bin/mplayer /usr/bin/mplayer /bin/mplayer" -+f="$1" -+shift -+rm -f $log -+echo "Request to play $f on `date`" >> $log -+player="" -+for player in $player_list; do -+ if [ -x "$player" ]; then -+ echo "Found player $player" >> $log -+ break -+ fi -+done -+if [ ! -x $player ]; then -+ echo "ERROR -- not able to find mplayer on your system. " >> $log -+ echo "I searched the following list" >> $log -+ echo " $player_list" >> $log -+ exit 1 -+fi -+for pass in 1 2 4 8 10; do -+ size="0" -+ if [ -e $f ]; then -+ size=$( du --apparent-size -sD "$f" | awk '{ print $1 }' ) -+ if [ $size -gt 1500 ]; then -+ break -+ fi -+ fi -+ echo "Pass $pass, filesize is $size kbytes, sleep $pass seconds" >> $log -+ sleep $pass -+done -+size=$( du --apparent-size -sD "$f" | awk '{ print $1 }' ) -+echo "Reached $size kb on pass $pass `date`" >> $log -+$player -fs -vo xv $f |