Commit 95690e16 authored by Pierre Ynard's avatar Pierre Ynard

youtube.lua: descramble signatures by parsing javascript code

This should be more resilient to updates.

Fixes #9217
parent 56107241
...@@ -67,6 +67,83 @@ function get_fmt( fmt_list ) ...@@ -67,6 +67,83 @@ function get_fmt( fmt_list )
return fmt return fmt
end end
-- Descramble the URL signature using the javascript code that does that
-- in the web page
function js_descramble( sig, js_url )
-- Fetch javascript code
local js = vlc.stream( js_url )
if not js then
return sig
end
local lines = {}
-- Look for the descrambler function's name
local descrambler = nil
while not descrambler do
local line = js:readline()
if not line then
vlc.msg.err( "Couldn't process youtube video URL, please check for updates to this script" )
return sig
end
-- Buffer lines for later, so we don't have to make a second
-- HTTP request later
table.insert( lines, line )
-- c&&(b.signature=ij(c));
descrambler = string.match( line, "%.signature=(.-)%(" )
end
-- Fetch the code of the descrambler function. Example:
-- function ij(a){a=a.split("");a=a.reverse();a=jj(a,12);a=jj(a,32);a=a.reverse();a=jj(a,34);a=a.slice(3);a=jj(a,35);a=jj(a,42);a=a.slice(2);return a.join("")}
local rules = nil
while not rules do
local line
if #lines > 0 then
line = table.remove( lines )
else
line = js:readline()
if not line then
vlc.msg.err( "Couldn't process youtube video URL, please check for updates to this script" )
return sig
end
end
rules = string.match( line, "function "..descrambler.."%([^)]*%){(.-)}" )
end
-- Parse descrambling rules one by one and apply them on the
-- signature as we go
for rule in string.gmatch( rules, "[^;]+" ) do
-- a=a.reverse();
if string.match( rule, "%.reverse%(" ) then
sig = string.reverse( sig )
else
-- a=a.slice(3);
local len = string.match( rule, "%.slice%((%d+)%)" )
if len then
sig = string.sub( sig, len + 1 )
else
-- a=jj(a,32);
-- This is known to be a function swapping the first and nth
-- characters:
-- function jj(a,b){var c=a[0];a[0]=a[b%a.length];a[b]=c;return a}
local idx = string.match( rule, "=..%([^,]+,(%d+)%)" )
if idx then
idx = tonumber( idx )
if not idx then idx = 0 end
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
end end end
-- Simply ignore other statements, in particular initial split
-- and final join and return statements
end
return sig
end
function descramble81( sig ) function descramble81( sig )
sig = string.reverse( sig ) sig = string.reverse( sig )
local s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13 = local s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13 =
...@@ -74,21 +151,27 @@ function descramble81( sig ) ...@@ -74,21 +151,27 @@ function descramble81( sig )
return s3..s2..s5..s4..s1..s6..s13..s8..s7..s10..s9..s12..s11 return s3..s2..s5..s4..s1..s6..s13..s8..s7..s10..s9..s12..s11
end end
local descramblers = { [81] = descramble81 } local descramblers = {
--[81] = descramble81
}
function descramble( sig ) function descramble( sig, js_url )
vlc.msg.dbg( "Found "..string.len( sig ).."-character scrambled signature for youtube video URL, attempting to descramble... " ) vlc.msg.dbg( "Found "..string.len( sig ).."-character scrambled signature for youtube video URL, attempting to descramble... " )
local descrambler = descramblers[string.len( sig )] if js_url then
if descrambler then sig = js_descramble( sig, js_url )
sig = descrambler( sig )
else else
vlc.msg.err( "Couldn't process youtube video URL, please check for updates to this script" ) local descrambler = descramblers[string.len( sig )]
if descrambler then
sig = descrambler( sig )
else
vlc.msg.err( "Couldn't process youtube video URL, please check for updates to this script" )
end
end end
return sig return sig
end end
-- Parse and pick our video URL -- Parse and pick our video URL
function pick_url( url_map, fmt ) function pick_url( url_map, fmt, js_url )
local path = nil local path = nil
for stream in string.gmatch( url_map, "[^,]+" ) do for stream in string.gmatch( url_map, "[^,]+" ) do
-- Apparently formats are listed in quality order, -- Apparently formats are listed in quality order,
...@@ -104,7 +187,7 @@ function pick_url( url_map, fmt ) ...@@ -104,7 +187,7 @@ function pick_url( url_map, fmt )
-- Scrambled signature -- Scrambled signature
sig = string.match( stream, "s=([^&,]+)" ) sig = string.match( stream, "s=([^&,]+)" )
if sig then if sig then
sig = descramble( sig ) sig = descramble( sig, js_url )
end end
end end
local signature = "" local signature = ""
...@@ -176,6 +259,12 @@ function parse() ...@@ -176,6 +259,12 @@ function parse()
-- JSON parameters, also formerly known as "swfConfig", -- JSON parameters, also formerly known as "swfConfig",
-- "SWF_ARGS", "swfArgs", "PLAYER_CONFIG", "playerConfig" ... -- "SWF_ARGS", "swfArgs", "PLAYER_CONFIG", "playerConfig" ...
if string.match( line, "ytplayer%.config" ) then if string.match( line, "ytplayer%.config" ) then
local js_url = string.match( line, "\"js\": \"(.-)\"" )
if js_url then
js_url = string.gsub( js_url, "\\/", "/" )
end
if not fmt then if not fmt then
fmt_list = string.match( line, "\"fmt_list\": \"(.-)\"" ) fmt_list = string.match( line, "\"fmt_list\": \"(.-)\"" )
if fmt_list then if fmt_list then
...@@ -188,7 +277,7 @@ function parse() ...@@ -188,7 +277,7 @@ function parse()
if url_map then if url_map then
-- FIXME: do this properly -- FIXME: do this properly
url_map = string.gsub( url_map, "\\u0026", "&" ) url_map = string.gsub( url_map, "\\u0026", "&" )
path = pick_url( url_map, fmt ) path = pick_url( url_map, fmt, js_url )
end end
if not path then if not path then
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment