-- version 0.2 add zdf -- version 0.1 add ard function init() json = require "json" replayList = {} getReLiveList_ARD() getReLiveList_ZDF() n = neutrino() vPlay = video.new() nMisc = misc.new() repaint = false end function getdata(Url,Postfields,outputfile,pass_headers,httpheaders) if Url == nil then return nil end if Curl == nil then Curl = curl.new() end if Url:sub(1, 2) == '//' then Url = 'https:' .. Url end local ret, data = Curl:download{ url=Url, A="Mozilla/5.0",maxRedirs=5,followRedir=false,postfields=Postfields,header=pass_headers,o=outputfile,httpheader=httpheaders } if ret == CURL.OK then if outputfile then return 1 end return data else return nil end end function godirectkey(d) if d == nil then return d end local _dkey = "" if d == 1 then _dkey = RC.red elseif d == 2 then _dkey = RC.green elseif d == 3 then _dkey = RC.yellow elseif d == 4 then _dkey = RC.blue elseif d < 14 then _dkey = RC[""..d - 4 ..""] elseif d == 14 then _dkey = RC["0"] else -- rest _dkey = "" end return _dkey end function hideMenu(menu) if menu ~= nil then menu:hide() end end local Epg = nil local Title = nil function epgInfo (xres, yres, aspectRatio, framerate) local dx = n:scale2Res(800); local dy = n:scale2Res(400); local x = ((SCREEN['END_X'] - SCREEN['OFF_X']) - dx) / 2; local y = ((SCREEN['END_Y'] - SCREEN['OFF_Y']) - dy) / 2; local wh = cwindow.new{x=x, y=y, dx=dx, dy=dy, icon="" , show_footer=false }; local ct = ctext.new{parent=wh, x=20, y=20, dx=0, dy=0, text = Epg , font_text=FONT['MENU'], mode = "ALIGN_SCROLL | ALIGN_TOP"}; wh:setCaption{title=Title, alignment=TEXT_ALIGNMENT.CENTER}; wh:paint() repeat msg, data = n:GetInput(500) if msg == RC.up or msg == RC.page_up then ct:scroll{dir="up"}; elseif msg == RC.down or msg == RC.page_down then ct:scroll{dir="down"}; end msg, data = n:GetInput(500) until msg == RC.ok or msg == RC.home or msg == RC.info ; wh:hide() end function play_live(_id) local id = tonumber(_id) if replayList[id].stream == nil then getLiveStream(id) end if replayList[id].stream then hideMenu(live_listen_menu) Epg = replayList[id].epg Title = replayList[id].name if Epg and Title then vPlay:setInfoFunc("epgInfo") end local info1 = "Replay" if Title then info1 = Title end vPlay:PlayFile(replayList[id].name, replayList[id].stream, info1,"",replayList[id].audiostream or "") Epg = nil Title = nil repaint = true return MENU_RETURN.EXIT end end function getARDstream(id) local url = replayList[id].url local jdata = getdata(url) if jdata then local jnTab = json:decode(jdata) if jnTab and jnTab.diff then if jnTab.title and jnTab.subtitle then Title = jnTab.title .. ' ' .. jnTab.subtitle elseif jnTab.title then Title = jnTab.title end if jnTab.detail then Epg = jnTab.detail end replayList[id].stream = jnTab.streamurl .. jnTab.diff .. '/stream.mpd' end end end function getVideoUrlM3U8(m3u8_url) if m3u8_url == nil then return nil end local res = 0 local videoUrl = nil local audioUrl = nil local data = getdata(m3u8_url) if data then local host = m3u8_url:match('([%a]+[:]?//[_%w%-%.]+)/') local lastpos = (m3u8_url:reverse()):find("/") local hosttmp = m3u8_url:sub(1,#m3u8_url-lastpos) if hosttmp then host = hosttmp .."/" end local revision = 0 if APIVERSION ~= nil and (APIVERSION.MAJOR > 1 or ( APIVERSION.MAJOR == 1 and APIVERSION.MINOR > 82 )) then revision = nMisc:GetRevision() end local audio_url = nil if revision == 1 then -- separate audio for hd51 and co local Nconfig = configfile.new() local lang1,lang2,lang3 = nil,nil,nil Nconfig:loadConfig("/var/tuxbox/config/neutrino.conf") lang1 = Nconfig:getString("pref_lang_0", "#") lang2 = Nconfig:getString("pref_lang_1", "#") lang3 = Nconfig:getString("pref_lang_2", "#") if lang1 == "#" then lang1 = nil else lang1 = lang1:lower() lang1 = lang1:sub(1,3) end if lang2 == "#" then lang2 = nil else lang2 = lang2:lower() lang2 = lang2:sub(1,3) end if lang3 == "#" then lang3 = nil else lang3 = lang3:lower() lang3 = lang3:sub(1,3) end if lang1 == nil then lang1 = Nconfig:getString("language", "english") if lang1 == nil then lang1 = "eng" else lang1 = lang1:lower() lang1 = lang1:sub(1,3) end end local l1,l2,l3,l4,l = nil,nil,nil,nil,nil for adata in data:gmatch('TYPE%=AUDIO.GROUP%-ID=".-",(.-)\n') do local lname = adata:match('NAME="(.-)"') local lang = adata:match('LANGUAGE="(.-)"') local aurl = adata:match('URI="(.-)"') if aurl then local low_lang = lang:lower() if l1 == nil and lname and lang1 and low_lang == lang1 then l1 = aurl elseif l2 == nil and lname and lang2 and low_lang == lang2 then l2 = aurl elseif l3 == nil and lname and lang3 and low_lang == lang3 then l3 = aurl elseif l4 == nil and lname and low_lang == "deu" then l4 = aurl elseif l == nil then l = aurl end end end audio_url = l1 or l2 or l3 or l4 or l end local first = true local maxRes = 1920 for band, res1, res2, url in data:gmatch('BANDWIDTH=(%d+).-RESOLUTION=(%d+)x(%d+).-\n(.-)\n') do if url and res1 then local nr = tonumber(res1) if (nr <= maxRes and nr > res) or first then first = false res=nr if host and url:sub(1,4) ~= "http" then url = host .. url end if audio_url and host and audio_url:sub(1,4) ~= "http" then audio_url = host .. audio_url end videoUrl = url audioUrl = audio_url end end end else return m3u8_url, nil end if videoUrl then videoUrl = videoUrl:gsub("\x0d","") end if audioUrl then audioUrl = audioUrl:gsub("\x0d","") end return videoUrl, audioUrl end function getZDFstream(id) local url = replayList[id].url local jdata = getdata(url) if jdata then local jnTab = json:decode(jdata) if jnTab and jnTab.streams then local streams = jnTab.streams[1] if streams then local mp4 = streams.h264_aac_mp4_http_na_na local m3u8 = streams.h264_aac_ts_http_m3u8_http local mpd = streams.h264_aac_mp4_http_mpd_http if mp4 and mp4.main and mp4.main.deu and mp4.main.deu.q3 then replayList[id].stream = mp4.main.deu.q3 elseif m3u8 and m3u8.main and m3u8.main.deu and m3u8.main.deu.q3 then replayList[id].stream , replayList[id].audiostream = getVideoUrlM3U8(m3u8.main.deu.q3) elseif mpd and mpd.main and mpd.main.deu then replayList[id].stream = mpd.main.deu end end end end end function getLiveStream(id) local url = replayList[id].url if url and replayList[id].ch == "ard" then getARDstream(id) end if url and replayList[id].ch == "zdf" then getZDFstream(id) end return replayList[id].stream end function getReLiveList_ZDF() local h = hintbox.new{caption="Please Wait ...", text="Liste wird geladen"} if h then h:paint() end local link = 'http://hbbtv.zdf.de/zdfstart/index.php' local data = getdata(link) if data then data = data:match('filterApps%((.-)%);') local jnTab = json:decode(data) local urls = {} if jnTab then for k,v in ipairs(jnTab) do if (v.teaser and v.teaser.button =='b') or (v.teaser2 and v.teaser2.button=='b') then local appid = v.appid:match('=(.*)') if appid:find('%%3B') then appid = appid:match('(.*)%%3B') end local videoUrl = 'http://hbbtv.zdf.de/zdfm3/dyn/get.php?id=' .. appid urls[videoUrl] = v.id end end end for link,id in pairs(urls) do data = getdata(link) if data then jnTab = json:decode(data) table.insert(replayList,{name='' .. jnTab.brand .. ': ' .. jnTab.bookmark.title, url=link, stream=nil,audiostream=nil,epg=jnTab.text,ch='zdf'}) end end end if h then h:hide() end end function getReLiveList_ARD() local h = hintbox.new{caption="Please Wait ...", text="Liste wird geladen"} if h then h:paint() end -- for i=1,36 do sender={1,3,24,27,4,5,6,9,14,15,17,18,35,36} for _, i in ipairs(sender) do if i~=2 and i~=31 and i~=32 and i~=34 then local link = 'http://itv.ard.de/replay/dyn/index.php?sid=' .. i local data = getdata(link) if data then local jnTab = json:decode(data) if jnTab and jnTab.diff then if jnTab.subtitle then _hint = jnTab.subtitle end table.insert(replayList,{name="" .. jnTab.name .. ' - ' .. jnTab.title,epg=jnTab.detail, url=link, stream=nil,audiostream=nil,ch='ard'}) end end end end if h then h:hide() end end function main_menu() if repaint then replayList = {} getReLiveList_ARD() getReLiveList_ZDF() repaint = false end if #replayList == 0 then return end live_listen_menu = menu.new{name="Replay", icon="streaming"} local menu = live_listen_menu menu:addItem{type="back"} menu:addItem{type="separatorline"} local d = 0 for i, v in ipairs(replayList) do d=d+1 menu:addItem{type="forwarder", name=v.name, action="play_live",enabled=true,id=i,directkey=godirectkey(d)} end menu:exec() menu:hide() end function main() init() repeat main_menu() collectgarbage() until repaint == false end main()