[vlc-commits] youtube.lua: descramble signatures by parsing javascript code

Pierre Ynard git at videolan.org
Mon Dec 9 19:39:37 CET 2013


vlc/vlc-2.0 | branch: master | Pierre Ynard <linkfanel at yahoo.fr> | Sat Sep  7 19:52:04 2013 +0200| [acfe29afa106ffff993bdf4b5b4281fab3e9df26] | committer: Felix Paul Kühne

youtube.lua: descramble signatures by parsing javascript code

This should be more resilient to updates.

Fixes #9217

(cherry picked from commit 95690e162601bc1253c7cb2099cd8a3e2da64aff)
Signed-off-by: Felix Paul Kühne <fkuehne at videolan.org>

> http://git.videolan.org/gitweb.cgi/vlc/vlc-2.0.git/?a=commit;h=acfe29afa106ffff993bdf4b5b4281fab3e9df26
---

 share/lua/playlist/youtube.lua |  107 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 98 insertions(+), 9 deletions(-)

diff --git a/share/lua/playlist/youtube.lua b/share/lua/playlist/youtube.lua
index 4effd37..5778adb 100644
--- a/share/lua/playlist/youtube.lua
+++ b/share/lua/playlist/youtube.lua
@@ -67,6 +67,83 @@ function get_fmt( fmt_list )
     return fmt
 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 )
     sig = string.reverse( sig )
     local s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13 =
@@ -74,21 +151,27 @@ function descramble81( sig )
     return s3..s2..s5..s4..s1..s6..s13..s8..s7..s10..s9..s12..s11
 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... " )
-    local descrambler = descramblers[string.len( sig )]
-    if descrambler then
-        sig = descrambler( sig )
+    if js_url then
+        sig = js_descramble( sig, js_url )
     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
     return sig
 end
 
 -- Parse and pick our video URL
-function pick_url( url_map, fmt )
+function pick_url( url_map, fmt, js_url )
     local path = nil
     for stream in string.gmatch( url_map, "[^,]+" ) do
         -- Apparently formats are listed in quality order,
@@ -104,7 +187,7 @@ function pick_url( url_map, fmt )
                     -- Scrambled signature
                     sig = string.match( stream, "s=([^&,]+)" )
                     if sig then
-                        sig = descramble( sig )
+                        sig = descramble( sig, js_url )
                     end
                 end
                 local signature = ""
@@ -176,6 +259,12 @@ function parse()
             -- JSON parameters, also formerly known as "swfConfig",
             -- "SWF_ARGS", "swfArgs", "PLAYER_CONFIG", "playerConfig" ...
             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
                     fmt_list = string.match( line, "\"fmt_list\": \"(.-)\"" )
                     if fmt_list then
@@ -188,7 +277,7 @@ function parse()
                 if url_map then
                     -- FIXME: do this properly
                     url_map = string.gsub( url_map, "\\u0026", "&" )
-                    path = pick_url( url_map, fmt )
+                    path = pick_url( url_map, fmt, js_url )
                 end
 
                 if not path then



More information about the vlc-commits mailing list