-- version 0.2 add ard function init() json = require "json" livelist = {} getLiveList_sportscha_tokio() getLiveList_ZDF() n = neutrino() vPlay = video.new() nMisc = misc.new() 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 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 return videoUrl, audioUrl end function getARDstream(id) local url = livelist[id].url local jdata = getdata(url) if jdata then local jnTab = json:decode(jdata ) if jnTab and jnTab._mediaArray[1] and jnTab._mediaArray[1] and jnTab._mediaArray[1]._mediaStreamArray[1] and jnTab._mediaArray[1]._mediaStreamArray[1]._stream then livelist[id].stream , livelist[id].audiostream = getVideoUrlM3U8(jnTab._mediaArray[1]._mediaStreamArray[1]._stream) end end end function getZDFstream(id) local url = livelist[id].url local data = getdata(url) if data then local contentUrl = data:match('"contentUrl": "(.-)"') if contentUrl then contentUrl = contentUrl:gsub('{playerId}','ngplayer_2_4') end local apiToken = data:match('"apiToken": "(.-)"') if apiToken and contentUrl then local header_opt = { 'Api-Auth: Bearer ' .. apiToken } local js_data = getdata(contentUrl,nil,nil,nil,header_opt) if js_data then local jnTab = json:decode( js_data ) local med = false if jnTab and jnTab.priorityList[1] and jnTab.priorityList[1].formitaeten[1] and jnTab.priorityList[1].formitaeten[1].qualities then for i, v in ipairs(jnTab.priorityList[1].formitaeten[1].qualities) do if v.audio and v.audio and v.audio.tracks[1] and v.audio.tracks[1].uri then if v.audio.tracks[1].uri:find("/veryhigh") then livelist[id].stream = v.audio.tracks[1].uri return livelist[id].stream else if v.audio.tracks[1].uri:find("/med") then med = true livelist[id].stream = v.audio.tracks[1].uri end if med == false and v.audio.tracks[1].uri:find("/low") then livelist[id].stream = v.audio.tracks[1].uri end if livelist[id].stream == nil and med == false and v.audio.tracks[1].uri:find("/high") then livelist[id].stream = v.audio.tracks[1].uri end end end end end end end end end function getLiveStream(id) local url = livelist[id].url if url and livelist[id].ch == "zdf" then getZDFstream(id) end if url and livelist[id].ch == "ard" then getARDstream(id) end return livelist[id].stream end function play_live(_id) local id = tonumber(_id) if livelist[id].stream == nil then getLiveStream(id) end if livelist[id].stream then hideMenu(live_listen_menu) vPlay:PlayFile(livelist[id].name, livelist[id].stream, "Olimpia Tokio Live","",livelist[id].audiostream or "") collectgarbage(); end end function getLiveList_sportscha_tokio() local baseUrl = "https://tokio.sportschau.de/tokio2020/live/" local url2 = baseUrl .. "livestreams172-extappjson.json" local data = getdata(url2) if data then local jnTab = json:decode(data) if jnTab then local timestamp = os.time() for i, v in ipairs(jnTab.channels) do if tonumber(v.starttime) < timestamp and tonumber(v.endtime) > timestamp then if v.displayimage_uuid and v.pgm_id then local link = baseUrl .. v.pgm_id .. "-ardjson_image-" .. v.displayimage_uuid .. ".json" local channel = v.headline .. " " .. v.displaystarttime .. " " .. v.displayendtime table.insert(livelist,{name="ARD: " .. channel, url=link, stream=nil,audiostream=nil,ch="ard"}) end end end end end end function getLiveList_ZDF() local url1 = "https://www.zdf.de/sport/sport-im-zdf-livestream-live-100.html" local data = getdata(url1) if data then local ids= {} for link in data:gmatch('embed_content":%s+"(.-)"') do local channel = link:match('/([%w%-%d]+)%-%d+$') local id = channel if id and ids[id] ~= true then ids[id] = true channel = channel:gsub("%-"," ") channel = channel:gsub(" ","-") link = link:gsub('/zdf','/') link = "https://www.zdf.de" .. link .. ".html" table.insert(livelist,{name="ZDF: " .. channel, url=link, stream=nil,audiostream=nil,ch="zdf"}) end end end end function main_menu() if #livelist == 0 then return end live_listen_menu = menu.new{name="Olimpia Tokio Live", icon="streaming"} local menu = live_listen_menu menu:addItem{type="back"} menu:addItem{type="separatorline"} local d = 0 for i, v in ipairs(livelist) 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() return MENU_RETURN.EXIT_REPAINT end function main() init() main_menu() end main()