local itags = {[37]='1920x1080',[96]='1920x1080',[22]='1280x720',[95]='1280x720',[94]='854x480',[35]='854x480', [18]='640x360',[93]='640x360',[34]='640x360',[5]='400x240',[6]='450x270',[36]='320x240', [92]='320x240',[17]='176x144',[13]='176x144', [85]='1920x1080p',[84]='1280x720',[83]='854x480',[82]='640x360' } local itags_audio = {[140]='m4a',[251]='opus',[250]='opus',[249]='opus'} -- 251,250,249 opus, bad audio sync local itags_vp9_60 = {[315]='3840x2160',[308]='2560x1440',[303]='1920x1080',[302]='1280x720'} local itags_vp9_30 = {[313]='3840x2160',[271]='2560x1440',[248]='1920x1080',[247]='1280x720',[244]='854x480'} local itags_vp9_HDR = {[337]='3840x2160',[336]='2560x1440',[335]='1920x1080',[334]='1280x720',[333]='854x480'} local itags_avc1_60 = {[299]='1920x1080',[298]='1280x720'} local itags_avc1_30 = {[137]='1920x1080',[136]='1280x720',[135]='854x480'} local itags_av01 = {[401]='3840x2160',[400]='2560x1440',[399]='1920x1080',[398]='1280x720',[397]='854x480'} -- disable or enable format local vp9_60 = true --webm local vp9_30 = false --webm local vp9_HDR = false --webm, HDR dont work on HD51 local avc1_60 = true -- mp4 local avc1_30 = false -- mp4 json = require "json" if #arg < 1 then return nil end local _url = arg[1] local ret = {} local Curl = nil function getdata(Url) if Url == nil then return nil end if Curl == nil then Curl = curl.new() end local ret, data = Curl:download{ url=Url, A="Mozilla/5.0"} if ret == CURL.OK then return data else return nil end end function hex2char(hex) return string.char(tonumber(hex, 16)) end function unescape_uri(url) if url == nil then return nil end return url:gsub("%%(%x%x)", hex2char) end function js_extract(data,patern) for line in data:gmatch("(.-};)" ) do local m = line:match(patern) if m then return m end end return nil end --- vlc youtube.lua code function js_descramble( sig, js ) local descrambler = js_extract( js, "[=%(,&|](..)%(decodeURIComponent%(.%.s%)%)" ) if descrambler == nil then return sig end local rules = js_extract( js, descrambler.."=function%([^)]*%){(.-)};" ) if rules == nil then return sig end local helper = rules:match(";(..)%...%(" ) if helper == nil then return sig end local transformations = js_extract( js, "[ ,]"..helper.."={(.-)};" ) if transformations == nil then return sig end -- Parse the helper object to map available transformations local trans = {} for meth,code in string.gmatch( transformations, "(..):function%([^)]*%){([^}]*)}" ) do -- a=a.reverse() if string.match( code, "%.reverse%(" ) then trans[meth] = "reverse" -- a.splice(0,b) elseif string.match( code, "%.splice%(") then trans[meth] = "slice" -- var c=a[0];a[0]=a[b%a.length];a[b]=c elseif string.match( code, "var c=" ) then trans[meth] = "swap" else print("Couldn't parse unknown youtube video URL signature transformation") end end -- Parse descrambling rules, map them to known transformations -- and apply them on the signature local missing = false for meth,idx in string.gmatch( rules, "..%.(..)%([^,]+,(%d+)%)" ) do idx = tonumber( idx ) if trans[meth] == "reverse" then sig = string.reverse( sig ) elseif trans[meth] == "slice" then sig = string.sub( sig, idx + 1 ) elseif trans[meth] == "swap" then if idx > 1 then sig = string.gsub( sig, "^(.)("..string.rep( ".", idx - 1 )..")(.)(.*)$", "%3%2%1%4" ) elseif idx == 1 then sig = string.gsub( sig, "^(.)(.)", "%2%1" ) end else print("Couldn't apply unknown youtube video URL signature transformation") missing = true end end if missing then print( "Couldn't process youtube video URL, please check for updates to this script" ) end return sig end local jsdata = nil function newsig(sig,js_url) if sig and js_url then if jsdata == nil then jsdata = getdata("https://www.youtube.com" .. js_url) end if jsdata then return js_descramble( sig, jsdata ) end end return nil end function add_entry(vurl,aurl,res1,res2,newname,count) entry = {} entry['url'] = vurl if aurl then entry['url2'] = aurl end entry['band'] = "1" --dummy entry['res1'] = res1 entry['res2'] = res2 entry['name'] = "" if newname then entry['name'] = newname end count = count + 1 ret[count] = {} ret[count] = entry return count end function getVideoData(yurl) if yurl == nil then return 0 end if yurl:find("www.youtube.com/user/") or yurl:find("youtube.com/channel") or yurl:find("youtube.com/c/") then --check user link or channel alias local youtube_user = getdata(yurl) if youtube_user == nil then return 0 end local youtube_live_url = youtube_user:match('"url":"(/watch.-)"') or youtube_user:match('feature=c4.-href="(/watch.-)"') if youtube_live_url == nil then return 0 end yurl = 'https://www.youtube.com' .. youtube_live_url end local revision = 0 local maxRes = 1920 if APIVERSION ~= nil and (APIVERSION.MAJOR > 1 or ( APIVERSION.MAJOR == 1 and APIVERSION.MINOR > 82 )) then M = misc.new() revision = M:GetRevision() if revision == 1 then maxRes = 3840 end end local count,countx = 0,0 local tmp_res = 0 local stop = false local urls = {} local have_itags = {} for i = 1,6 do countx = 0 local data = getdata(yurl) if data:find('player%-age%-gate%-content') then local id = yurl:match("/watch%?v=([%w+%p+]+)") if id then data = getdata('https://www.youtube.com/embed/' .. id) local sts = data:match('"sts":(%d+)') if sts then data = getdata('https://www.youtube.com/get_video_info?video_id=' .. id .. '&eurl=https%3A%2F%2Fyoutube.googleapis.com%2Fv%2F' .. id .. '&sts=' .. sts) end end end local newname = nil if data then newname = data:match('