-- quvi -- Copyright (C) 2011, 2012 quvi project -- -- This file is part of quvi . -- -- This library is free software; you can redistribute it and/or -- modify it under the terms of the GNU Lesser General Public -- License as published by the Free Software Foundation; either -- version 2.1 of the License, or (at your option) any later version. -- -- This library is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- Lesser General Public License for more details. -- -- You should have received a copy of the GNU Lesser General Public -- License along with this library; if not, write to the Free Software -- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -- 02110-1301 USA -- TODO: -- - Add support for Radio programmes -- - Add support for live streaming -- - Better error messages for geolocation errors -- - Offer the subtitles for download somehow -- Obtained with grep -oP '(?<=service=")[^"]+(?=")' on config local fmt_id_lookup = { high = 'iplayer_streaming_h264_flv_high', standard = 'iplayer_streaming_h264_flv', low = 'iplayer_streaming_h264_flv_lo', vlow = 'iplayer_streaming_h264_flv_vlo' -- iplayer_streaming_n95_3g -- iplayer_streaming_n95_wifi } -- Identify the script. function ident(self) package.path = self.script_dir .. '/?.lua' local C = require 'quvi/const' local r = {} r.domain = "www.bbc.co.uk" r.formats = "default|best" for k,_ in pairs(fmt_id_lookup) do r.formats = r.formats .."|".. k end r.categories = C.proto_rtmp local U = require 'quvi/util' r.handles = U.handles(self.page_url, {r.domain}, {"/iplayer/"}) return r end -- Parse video URL. function parse(self) function needs_new_authString(params) if not params['authString'] then return false end local found = false for _,kind in pairs{'limelight', 'akamai', 'level3', 'sis', 'iplayertok'} do if kind == params['kind'] then found = true break end end if not found then return false end -- We don't need to check for the mode, we already know it's what we want return true end function create_uri_for_limelight_level3_iplayertok(params) params.uri = params.tcurl .. '/' .. params.playpath end function process_akamai(params) params.playpath = params.identifier params.application = params.application or 'ondemand' params.application = params.application .. '?_fcs_vhost=' .. params.server .. '&undefined' params.uri = 'rtmp://' .. params.server .. ':80/' .. params.application if not params.authString:find("&aifp=") then params.authString = params.authString .. '&aifp=v001' end if not params.authString:find("&slist=") then params.identifier = params.identifier:gsub('^mp[34]:', '') params.authString = params.authString .. '&slist=' .. params.identifier end params.playpath = params.playpath .. '?' .. params.authString params.uri = params.uri .. '&' .. params.authString params.application = params.application .. '&' .. params.authString params.tcurl = 'rtmp://' .. params.server .. ':80/' .. params.application end function process_limelight_level3(params) params.application = params.application .. '?' .. params.authString params.tcurl = 'rtmp://' .. params.server .. ':1935/' .. params.application params.playpath = params.identifier create_uri_for_limelight_level3_iplayertok(params) end function process_iplayertok(params) params.identifier = params.identifier .. '?' .. params.authString params.playpath = params.identifier:gsub('^mp[34]:', '') params.tcurl = 'rtmp://' .. params.server .. ':1935/' .. params.application create_uri_for_limelight_level3_iplayertok(params) end self.host_id = 'bbc' local _,_,s = self.page_url:find('episode/(.-)/') local episode_id = s or error('no match: episode id') self.id = episode_id local playlist_uri = 'http://www.bbc.co.uk/iplayer/playlist/' .. episode_id local playlist = quvi.fetch(playlist_uri, {fetch_type = 'playlist'}) local pl_item_p,_,s = playlist:find('(.-)') self.title = s or error('no match: video title') local _,_,s = playlist:find('s local media for section in config:gmatch('') do if section:find('service="' .. fmt_id_lookup[format] .. '"') then media = section break end end if not media then error("Couldn't parse the config") end self.url = {} -- Initialise with the default values from the media local mparams = {} for _,mparam in pairs{'kind', 'service'} do _,_,mparams[mparam] = media:find(mparam .. '="(.-)"') -- print ("MEDIA: mparams[" .. mparam .. "] = " .. mparams[mparam]) end for connection in media:gmatch('') do local params, complete_uri = {}, '' for _,param in pairs{'supplier', 'server', 'application', 'identifier', 'authString', 'kind'} do _,_,params[param] = connection:find(param .. '="(.-)"') -- print ("CONNECTION: params[" .. param .. "] = " .. (params[param] or "(null)")) end -- Get authstring from more specific mediaselector if -- this mode is specified - fails sometimes otherwise if needs_new_authString(params) then local xml_url xml_uri = 'http://www.bbc.co.uk/mediaselector/4/mtis/stream/' .. media_id .. '/' .. mparams['service'] .. '/' .. params['kind'] .. "?cb=" .. math.random(10000) xml = quvi.fetch(xml_uri, {fetch_type = 'config'}) local _,_,new_authString = xml:find('authString="(.-)"') if new_authString then params['authString'] = new_authString:gsub('&', '&') end else -- Unescape the authString if params['authString'] then params['authString'] = params['authString']:gsub('&', '&') end end -- in 'application', mp has a value containing one or more entries separated by strings. -- We only keep the first entry. if params.application then params.application = params.application:gsub("&mp=([^,&]+),?.-&", "&mp=%1&") end if params.supplier == 'akamai' then process_akamai(params) end if (params.supplier == 'limelight' or params.supplier == 'level3') then process_limelight_level3(params) end params.uri = params.uri or error('Could not create RTMP URL') complete_uri = params.uri .. ' app=' .. params.application .. ' playpath=' .. params.playpath .. ' swfUrl=http://www.bbc.co.uk/emp/revisions/18269_21576_10player.swf?revision=18269_21576 swfVfy=1' .. ' tcUrl=' .. params.tcurl .. ' pageurl=' .. self.page_url self.url[#(self.url) + 1] = complete_uri end return self end