Class: IPlayer::Downloader

Inherits:
Object
  • Object
show all
Includes:
Errors
Defined in:
lib/iplayer/downloader.rb

Defined Under Namespace

Classes: Version

Constant Summary collapse

EPISODE_URL =
'http://www.bbc.co.uk/mobile/iplayer/episode/%s'
SELECTOR_URL =
'http://www.bbc.co.uk/mediaselector/3/auth/iplayer_streaming_http_mp4/%s?%s'
MAX_SEGMENT =
4 * 1024 * 1024
COPY_BUFFER =
4 * 1024 * 1024

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(browser, pid) ⇒ Downloader

Returns a new instance of Downloader.



31
32
33
34
# File 'lib/iplayer/downloader.rb', line 31

def initialize(browser, pid)
  @browser = browser
  @pid = pid
end

Instance Attribute Details

#browserObject (readonly)

Returns the value of attribute browser.



15
16
17
# File 'lib/iplayer/downloader.rb', line 15

def browser
  @browser
end

#cookiesObject

Returns the value of attribute cookies.



16
17
18
# File 'lib/iplayer/downloader.rb', line 16

def cookies
  @cookies
end

#pidObject (readonly)

Returns the value of attribute pid.



15
16
17
# File 'lib/iplayer/downloader.rb', line 15

def pid
  @pid
end

Class Method Details

.extract_pid(pid_or_url) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/iplayer/downloader.rb', line 18

def self.extract_pid(pid_or_url)
  case pid_or_url
  when %r!/(?:item|episode|programmes)/([a-z0-9]{8})!
    $1
  when %r!^[a-z0-9]{8}$!
    pid_or_url
  when %r!(b0[a-z0-9]{6})!
    $1
  else
    raise NotAPid, pid_or_url
  end
end

Instance Method Details

#available_versionsObject



56
57
58
# File 'lib/iplayer/downloader.rb', line 56

def available_versions
  .versions.map{ |name, vpid| Version.new(name, vpid) }
end

#download(version_pid, path, options = {}, &blk) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/iplayer/downloader.rb', line 60

def download(version_pid, path, options={}, &blk)
  if options[:subtitles]
    download_subtitles(version_pid, path)
  end

  if File.exist?(path)
    offset = File.size(path)
  else
    offset = 0
  end

  File.open(path, 'a+b') do |io|
    location = real_stream_location(@pid)
    content_length = content_length_from_initial_request(location)
    yield(offset, content_length) if block_given?

    offset.step(content_length - 1, MAX_SEGMENT) do |first_byte|
      last_byte = [first_byte + MAX_SEGMENT - 1, content_length - 1].min
      get(location, Browser::QT_UA, 'Range'=>"bytes=#{first_byte}-#{last_byte}") do |response|
        response.read_body do |data|
          offset += data.length
          io << data
          yield(offset, content_length) if block_given?
        end
      end
    end
  end
end

#get(url, user_agent, options = {}, &blk) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/iplayer/downloader.rb', line 40

def get(url, user_agent, options={}, &blk)
  options['User-Agent'] = user_agent
  options['Cookie'] = cookies if cookies
  response = browser.get(url, options, &blk)
  self.cookies = response.cookies.join('; ')
  response
end

#metadataObject



36
37
38
# File 'lib/iplayer/downloader.rb', line 36

def 
  @metadata = Metadata.new(@pid, @browser)
end

#post(url, data, user_agent, options = {}, &blk) ⇒ Object



48
49
50
51
52
53
54
# File 'lib/iplayer/downloader.rb', line 48

def post(url, data, user_agent, options={}, &blk)
  options['User-Agent'] = user_agent
  options['Cookie'] = cookies if cookies
  response = browser.post(url, data, options, &blk)
  self.cookies = response.cookies.join('; ')
  response
end