The branch, frodo has been updated
       via  83ef7583020996425860d708b8bf95dcb69d8948 (commit)
      from  e99448aeadf8f5433deb957e08e3095096be0676 (commit)

- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=83ef7583020996425860d708b8bf95dcb69d8948

commit 83ef7583020996425860d708b8bf95dcb69d8948
Author: Martijn Kaijser <[email protected]>
Date:   Wed Jul 17 23:12:30 2013 +0200

    [plugin.video.youtube] 4.4.6

diff --git a/plugin.video.youtube/YouTubeCore.py 
b/plugin.video.youtube/YouTubeCore.py
index 2ff1547..d955e49 100644
--- a/plugin.video.youtube/YouTubeCore.py
+++ b/plugin.video.youtube/YouTubeCore.py
@@ -22,7 +22,6 @@ import time
 import socket
 import urllib
 import urllib2
-#import chardet
 
 try:
     import simplejson as json
@@ -430,8 +429,8 @@ class YouTubeCore():
         else:
             request.add_header('User-Agent', self.common.USERAGENT)
 
-            if get("no-language-cookie", "false") == "false":
-                cookie += "PREF=f1=50000000&hl=en;"
+            if get("no-language-cookie", "false") == "false" and False:
+                cookie += "PREF=f1=50000000&hl=en; "
 
         if get("login", "false") == "true":
             self.common.log("got login")
@@ -442,17 +441,11 @@ class YouTubeCore():
                 return ret_obj
 
             # This should be a call to self.login._httpLogin()
-            if self.settings.getSetting("login_cookies") == "":
+            if self.settings.getSetting("cookies_saved") != "true":
                 if isinstance(self.login, str):
                     self.login = sys.modules["__main__"].login
                 self.login._httpLogin()
 
-            if self.settings.getSetting("login_cookies") != "":
-                tcookies = eval(self.settings.getSetting("login_cookies"))
-                self.common.log("Adding login cookies: " + 
repr(tcookies.keys()))
-                for key in tcookies.keys():
-                    cookie += "%s=%s;" % ( key, tcookies[key])
-
         if get("referer", "false") != "false":
             self.common.log("Added referer: %s" % get("referer"))
             request.add_header('Referer', get("referer"))
@@ -462,7 +455,6 @@ class YouTubeCore():
 
             if cookie:
                 self.common.log("Setting cookie: " + cookie)
-                request.add_header('Cookie', cookie)
 
             con = urllib2.urlopen(request)
 
@@ -489,23 +481,6 @@ class YouTubeCore():
             if e.code == 400 or True:
                 self.common.log("Unhandled HTTPError : [%s] %s " % (e.code, 
msg), 1)
 
-            if msg.find("yt:quota") > 1:
-                self.common.log("Hit quota... sleeping for 10 seconds")
-                time.sleep(10)
-            elif msg.find("too_many_recent_calls") > 1:
-                self.common.log("Hit quota... sleeping for 10 seconds")
-                time.sleep(10)
-            elif err.find("Token invalid") > -1:
-                self.common.log("refreshing token")
-                self._oRefreshToken()
-            elif err.find("User Rate Limit Exceeded") > -1:
-                self.common.log("Hit limit... Sleeping for 10 seconds")
-                time.sleep(10)
-            else:
-                if e.fp:
-                    cont = e.fp.read()
-                    self.common.log("HTTPError - Headers: " + str(e.headers) + 
" - Content: " + cont)
-
             params["error"] = get("error", 0) + 1
             ret_obj = self._fetchPage(params)
 
@@ -570,7 +545,7 @@ class YouTubeCore():
             if error.find("[") > -1:
                 error = error[0:error.find("[")]
             error = urllib.unquote(error.replace("\n", " ").replace("  ", " 
")).replace("&#39;", "'")
-            self.common.log("returning error : " + error.strip())
+            self.common.log("returning error : " + repr(error.strip()))
             return error.strip()
 
         # If no error was found. But fetchPage has an error level of 3+, 
return the fetchPage content.
diff --git a/plugin.video.youtube/YouTubeLogin.py 
b/plugin.video.youtube/YouTubeLogin.py
index 485582d..b046485 100644
--- a/plugin.video.youtube/YouTubeLogin.py
+++ b/plugin.video.youtube/YouTubeLogin.py
@@ -97,9 +97,9 @@ class YouTubeLogin():
         url = self.urls[u"oauth_api_login"]
 
         logged_in = False
-        fetch_options = {"link": url, "no-language-cookie": "true"}
+        fetch_options = {"link": url}
         step = 0
-        self.common.log("Part A")
+        self.common.log("Go to the api login page")
         while not logged_in and fetch_options and step < 6:
             self.common.log("Step : " + str(step))
             step += 1
@@ -107,6 +107,14 @@ class YouTubeLogin():
             ret = self.core._fetchPage(fetch_options)
             fetch_options = False
 
+            for accounts in self.common.parseDOM(ret["content"], "ol", 
attrs={"id": "account-list"}):
+                self.common.log("Detected google plus with page 
administrator.")
+                acurl = self.common.parseDOM(accounts, "a", ret="href")
+                acname = self.common.parseDOM(accounts, "span", 
attrs={"class": "account-name"})
+                if len(acurl):
+                    fetch_options = {"link": acurl[0].replace("&amp;", "&")}
+                    continue
+
             newurl = self.common.parseDOM(ret["content"], "form", 
attrs={"method": "POST"}, ret="action")
             state_wrapper = self.common.parseDOM(ret["content"], "input", 
attrs={"id": "state_wrapper"}, ret="value")
 
@@ -114,8 +122,8 @@ class YouTubeLogin():
                 url_data = {"state_wrapper": state_wrapper[0],
                             "submit_access": "true"}
 
-                fetch_options = {"link": newurl[0].replace("&amp;", "&"), 
"url_data": url_data, "no-language-cookie": "true"}
-                self.common.log("Part B")
+                fetch_options = {"link": newurl[0].replace("&amp;", "&"), 
"url_data": url_data}
+                self.common.log("Press 'Accept' button")
                 continue
 
             code = self.common.parseDOM(ret["content"], "input", attrs={"id": 
"code"}, ret="value")
@@ -127,22 +135,21 @@ class YouTubeLogin():
                             "redirect_uri": "urn:ietf:wg:oauth:2.0:oob",
                             "grant_type": "authorization_code"}
                 fetch_options = {"link": url, "url_data": url_data}
-                self.common.log("Part C")
+                self.common.log("Extract and use access code")
                 continue
 
             # use token
             if ret["content"].find("access_token") > -1:
-                self.common.log("Part D")
+                self.common.log("Saving access_token")
                 oauth = json.loads(ret["content"])
 
                 if len(oauth) > 0:
-                    self.common.log("Part D " + repr(oauth["expires_in"]))
                     self.settings.setSetting("oauth2_expires_at", 
str(int(oauth["expires_in"]) + time.time()))
                     self.settings.setSetting("oauth2_access_token", 
oauth["access_token"])
                     self.settings.setSetting("oauth2_refresh_token", 
oauth["refresh_token"])
 
                     logged_in = True
-                    self.common.log("Done:" + 
self.settings.getSetting("username"))
+                    self.common.log("Done: " + 
self.settings.getSetting("username"))
 
         if logged_in:
             return (self.language(30030), 200)
@@ -153,15 +160,12 @@ class YouTubeLogin():
     def _httpLogin(self, params={}):
         get = params.get
         self.common.log("")
-        status = 500
 
         if get("new", "false") == "true" or get("page", "false") != "false":
-            self.settings.setSetting("login_info", "")
-            self.settings.setSetting("SID", "")
-            self.settings.setSetting("login_cookies", "")
-        elif self.settings.getSetting("login_info") != "":
-            self.common.log("returning existing login info: " + 
self.settings.getSetting("login_info"))
-            return (self.settings.getSetting("login_info"), 200)
+            self.settings.setSetting("cookies_saved", "false")
+        elif self.settings.getSetting("cookies_saved") == "true":
+            self.common.log("Use saved cookies")
+            return (self.settings.getSetting("cookies_saved"), 200)
 
         fetch_options = {"link": get("link", "http://www.youtube.com/";)}
 
@@ -195,8 +199,9 @@ class YouTubeLogin():
 
             if len(nick) > 0 and nick[0] != "Sign In":
                 self.common.log("Logged in. Parsing data: " + repr(nick))
-                status = self._getLoginInfo(nick)
-                return(ret, status)
+                sys.modules["__main__"].cookiejar.save()
+                self.settings.setSetting("cookies_saved", "true")
+                return(ret, 200)
 
             # Click login link on youtube.com
             newurl = self.common.parseDOM(ret["content"], "button", 
attrs={"href": ".*?ServiceLogin.*?"}, ret="href")
@@ -211,7 +216,7 @@ class YouTubeLogin():
             if len(newurl) > 0:
                 (galx, url_data) = self._fillLoginInfo(ret)
                 if len(galx) > 0 and len(url_data) > 0:
-                    fetch_options = {"link": newurl[0], "no-language-cookie": 
"true", "url_data": url_data, "hidden": "true", "referer": ret["location"]}
+                    fetch_options = {"link": newurl[0], "url_data": url_data, 
"hidden": "true", "referer": ret["location"]}
                     self.common.log("Part B")
                     self.common.log("fetch options: " + repr(fetch_options), 
10)  # WARNING, SHOWS LOGIN INFO/PASSWORD
                     continue
@@ -220,7 +225,7 @@ class YouTubeLogin():
             if len(newurl) > 0:
                 newurl = newurl[0].replace("&amp;", "&")
                 newurl = newurl[newurl.find("&#39;") + 5:newurl.rfind("&#39;")]
-                fetch_options = {"link": newurl, "no-language-cookie": "true", 
"referer": ret["location"]}
+                fetch_options = {"link": newurl, "referer": ret["location"]}
                 self.common.log("Part C: "  + repr(fetch_options))
                 continue
 
@@ -231,7 +236,7 @@ class YouTubeLogin():
                     return (False, 500)
 
                 new_part = self.common.parseDOM(ret["content"], "form", 
attrs={"name": "verifyForm"}, ret="action")
-                fetch_options = {"link": new_part[0], "url_data": url_data, 
"no-language-cookie": "true", "referer": ret["location"]}
+                fetch_options = {"link": new_part[0].replace("&amp;", "&"), 
"url_data": url_data, "referer": ret["location"]}
 
                 self.common.log("Part D: " + repr(fetch_options))
                 continue
@@ -245,7 +250,7 @@ class YouTubeLogin():
                             "GALX": galx}
 
                 target_url = self.common.parseDOM(ret["content"], "form", 
attrs={"name": "hiddenpost"}, ret="action")
-                fetch_options = {"link": target_url[0], "url_data": url_data, 
"no-language-cookie": "true", "referer": ret["location"]}
+                fetch_options = {"link": target_url[0], "url_data": url_data, 
"referer": ret["location"]}
                 self.common.log("Part E: " + repr(fetch_options))
                 continue
 
@@ -254,107 +259,49 @@ class YouTubeLogin():
                 # Check for errors.
                 return (self.core._findErrors(ret), 303)
 
-        return (ret, status)
+        return (ret, 500)
 
     def _fillLoginInfo(self, ret):
+        self.common.log("")
         content = ret["content"]
-        rmShown = self.common.parseDOM(content, "input", attrs={"name": 
"rmShown"}, ret="value")
-        cont = self.common.parseDOM(content, "input", attrs={"name": 
"continue"}, ret="value")
-        uilel = self.common.parseDOM(content, "input", attrs={"name": 
"uilel"}, ret="value") # Deprecated?
-        if len(uilel) == 0: # Deprecated?
-            uilel = self.common.parseDOM(content, "input", attrs= 
{"id":"uilel"}, ret="value")
-        if len(uilel) == 0 and ret["new_url"].find("uilel=") > -1:
-            uilel = ret["new_url"][ret["new_url"].find("uilel=")+6]
-            if uilel.find("&") > -1:
-                uilel = uilel[:uilel.find("&")]
-            uilel = [uilel]
-        dsh = self.common.parseDOM(content, "input", attrs={"name": "dsh"}, 
ret="value")
-        if len(dsh) == 0:
-            dsh = self.common.parseDOM(content, "input", attrs={"id": "dsh"}, 
ret="value")
-
-        galx = self.common.parseDOM(content, "input", attrs={"name": "GALX"}, 
ret="value")
-        uname = self.pluginsettings.userName()
-        pword = self.pluginsettings.userPassword()
-
-        if pword == "":
-            pword = self.common.getUserInput(self.language(30628), hidden=True)
-
-        if len(galx) == 0 or len(cont) == 0 or len(uilel) == 0 or len(dsh) == 
0 or len(rmShown) == 0 or uname == "" or pword == "":
-            self.common.log("_fillLoginInfo missing values for login form " + 
repr(galx) + repr(cont) + repr(uilel) + repr(dsh) + repr(rmShown) + repr(uname) 
+ str(len(pword)))
-            return ("", {})
-        else:
-            galx = galx[0]
-            url_data = {"pstMsg": "0",
-                        "ltmpl": "sso",
-                        "dnConn": "",
-                        "continue": cont[0],
-                        "service": "youtube",
-                        "uilel": uilel[0],
-                        "dsh": dsh[0],
-                        "hl": "en_US",
-                        "timeStmp": "",
-                        "secTok": "",
-                        "GALX": galx,
-                        "Email": uname,
-                        "Passwd": pword,
-                        "PersistentCookie": "yes",
-                        "rmShown": rmShown[0],
-                        "signin": "Sign in",
-                        "asts": ""
-                        }
-        return (galx, url_data)
+
+        url_data = {}
+
+        for name in self.common.parseDOM(content, "input", ret="name"):
+            for val in self.common.parseDOM(content, "input", attrs={"name": 
name}, ret="value"):
+                url_data[name] = val
+
+        self.common.log("Extracted url_data: " + repr(url_data), 0)
+        url_data["Email"] = self.pluginsettings.userName()
+        url_data["Passwd"] = self.pluginsettings.userPassword()
+        if url_data["Passwd"] == "":
+            url_data["Passwd"] = 
self.common.getUserInput(self.language(30628), hidden=True)
+
+        self.common.log("Done")
+        return (url_data["GALX"], url_data)
 
     def _fillUserPin(self, content):
-        self.common.log(repr(content), 5)
-        smsToken = self.common.parseDOM(content, "input", attrs={"name": 
"smsToken"}, ret="value")
-        self.smsToken = smsToken
+        self.common.log("")
+        form = self.common.parseDOM(content, "form", attrs={"name": 
"verifyForm"}, ret=True)
+
+        url_data = {}
+        for name in self.common.parseDOM(form, "input", ret="name"):
+            for val in self.common.parseDOM(form, "input", attrs={"name": 
name}, ret="value"):
+                url_data[name] = val
+
+        self.common.log("url_data: " + repr(form), 0)
+
+        if "smsToken" in url_data:
+            self.smsToken = url_data["smsToken"]
+        if "continue" in url_data:
+            url_data["continue"] = url_data["continue"].replace("&amp;", "&")
         userpin = self.common.getUserInputNumbers(self.language(30627))
 
         if len(userpin) > 0:
-            url_data = {"smsToken": smsToken[0],
-                        "PersistentCookie": "yes",
-                        "smsUserPin": userpin,
-                        "smsVerifyPin": "Verify",
-                        "timeStmp": "",
-                        "secTok": ""}
+            url_data["smsUserPin"] = userpin
             self.common.log("Done: " + repr(url_data))
+            url_data["smsVerifyPin"] = "Verify" # Overwrite this variable 
since it might contain unicode.
             return url_data
         else:
-            self.common.log("Replace this with a message telling users that 
they didn't enter a pin")
+            self.common.log("Error")
             return {}
-
-    def _getLoginInfo(self, nick):
-        self.common.log(nick)
-        status = 303
-
-        # Save cookiefile in settings
-        cookies = self.common.getCookieInfoAsHTML()
-        login_info = self.common.parseDOM(cookies, "cookie", attrs={"name": 
"LOGIN_INFO"}, ret="value")
-        SID = self.common.parseDOM(cookies, "cookie", attrs={"name": "SID", 
"domain": ".youtube.com"}, ret="value")
-        scookies = {}
-        self.common.log("COOKIES:" + repr(cookies))
-        tnames = re.compile(" name='(.*?)' ").findall(cookies)
-        for key in tnames:
-            tval = self.common.parseDOM(cookies, "cookie", attrs={"name": 
key}, ret="value")
-            if len(tval) > 0:
-                scookies[key] = tval[0]
-        self.common.log("COOKIES:" + repr(scookies))
-
-        if len(login_info) == 1:
-            self.common.log("LOGIN_INFO: " + repr(login_info))
-            self.settings.setSetting("login_info", login_info[0])
-        else:
-            self.common.log("Failed to get LOGIN_INFO from youtube: " + 
repr(login_info))
-
-        if len(SID) == 1:
-            self.common.log("SID: " + repr(SID))
-            self.settings.setSetting("SID", SID[0])
-        else:
-            self.common.log("Failed to get SID from youtube: " + repr(SID))
-
-        if len(SID) == 1 and len(login_info) == 1:
-            status = 200
-            self.settings.setSetting("login_cookies", repr(scookies))
-
-        self.common.log("Done")
-        return status
diff --git a/plugin.video.youtube/YouTubeNavigation.py 
b/plugin.video.youtube/YouTubeNavigation.py
index 4773d0f..e4c3a7c 100644
--- a/plugin.video.youtube/YouTubeNavigation.py
+++ b/plugin.video.youtube/YouTubeNavigation.py
@@ -64,8 +64,7 @@ class YouTubeNavigation():
             {'Title':self.language(30016)  ,'path':"/root/explore/feeds/rated" 
            , 'thumbnail':"top"               , 'login':"false" , 
'feed':"feed_rated" },
             {'Title':self.language(30052)  ,'path':"/root/explore/music"       
            , 'thumbnail':"music"             , 'login':"false" , 
'store':"disco_searches", "folder":"true" },
             {'Title':self.language(30040)  ,'path':"/root/explore/music/new"   
            , 'thumbnail':"search"            , 'login':"false" , 
'scraper':"search_disco"},
-            {'Title':self.language(30055)  
,'path':"/root/explore/music/top100"            , 'thumbnail':"music"           
  , 'login':"false" , 'scraper':'music_top100'},
-            {'Title':self.language(30032)  ,'path':"/root/explore/trailers"    
            , 'thumbnail':"trailers"          , 'login':"false" , 
'scraper':'trailers'},
+            {'Title':self.language(30032)  ,'path':"/root/explore/trailers"    
            , 'thumbnail':"trailers"          , 'login':"false" , 
'feed':'uploads', 'contact':"trailers", "external":"true"},
             {'Title':self.language(30051)  ,'path':"/root/explore/live"        
            , 'thumbnail':"live"              , 'login':"false" , 
'feed':"feed_live" },
             {'Title':self.language(30019)  ,'path':"/root/recommended"         
            , 'thumbnail':"recommended"       , 'login':"true"  , 
'user_feed':"recommended" },
             {'Title':self.language(30008)  ,'path':"/root/watch_later"         
            , 'thumbnail':"watch_later"       , 'login':"true"  , 
'user_feed':"watch_later" },
@@ -574,10 +573,6 @@ class YouTubeNavigation():
             if not get("external"):
                 cm.append((self.language(30539), 
"XBMC.RunPlugin(%s?path=%s&action=delete_playlist&playlist=%s&)" % 
(sys.argv[0], item("path"), item("playlist"))))
 
-        if (item("scraper") == "music_top100"):
-            cm.append((self.language(30520), 
"XBMC.RunPlugin(%s?path=%s&action=play_all&scraper=music_top100&)" % 
(sys.argv[0], item("path"))))
-            cm.append((self.language(30522), 
"XBMC.RunPlugin(%s?path=%s&action=play_all&shuffle=true&scraper=music_top100&)" 
% (sys.argv[0], item("path"))))
-
         if (item("scraper") == "search_disco"):
             cm.append((self.language(30520), 
"XBMC.RunPlugin(%s?path=%s&action=play_all&scraper=search_disco&search=%s&)" % 
(sys.argv[0], item("path"), item("search"))))
             cm.append((self.language(30522), 
"XBMC.RunPlugin(%s?path=%s&action=play_all&shuffle=true&scraper=search_disco&search=%s&)"
 % (sys.argv[0], item("path"), item("search"))))
diff --git a/plugin.video.youtube/YouTubePlayer.py 
b/plugin.video.youtube/YouTubePlayer.py
index 280f43f..d49364c 100644
--- a/plugin.video.youtube/YouTubePlayer.py
+++ b/plugin.video.youtube/YouTubePlayer.py
@@ -93,6 +93,7 @@ class YouTubePlayer():
         self.xbmcplugin.setResolvedUrl(handle=int(sys.argv[1]), 
succeeded=True, listitem=listitem)
 
         if self.settings.getSetting("lang_code") != "0" or 
self.settings.getSetting("annotations") == "true":
+            self.common.log("BLAAAAAAAAAAAAAAAAAAAAAA: " + 
repr(self.settings.getSetting("lang_code")))
             self.subtitles.addSubtitles(video)
 
         if (get("watch_later") == "true" and get("playlist_entry_id")):
@@ -214,7 +215,7 @@ class YouTubePlayer():
             self.common.log(u"- construct_video_url failed, video_url not set")
             return video_url
 
-        if get("action") != "download":
+        if get("action") != "download" and video_url.find("rtmp") == -1:
             video_url += '|' + 
urllib.urlencode({'User-Agent':self.common.USERAGENT})
 
         self.common.log(u"Done")
@@ -254,15 +255,16 @@ class YouTubePlayer():
     def checkForErrors(self, video):
         status = 200
 
-        if video[u"video_url"] == u"":
+        if "video_url" not in video or video[u"video_url"] == u"":
             status = 303
-            vget = video.get
-            if vget(u"live_play"):
-                video[u'apierror'] = self.language(30612)
-            elif vget(u"stream_map"):
-                video[u'apierror'] = self.language(30620)
-            else:
-                video[u'apierror'] = self.language(30618)
+            if u"apierror" not in video:
+                vget = video.get
+                if vget(u"live_play"):
+                    video[u'apierror'] = self.language(30612)
+                elif vget(u"stream_map"):
+                    video[u'apierror'] = self.language(30620)
+                else:
+                    video[u'apierror'] = self.language(30618)
 
         return (video, status)
 
@@ -282,7 +284,12 @@ class YouTubePlayer():
 
         (links, video) = self.extractVideoLinksFromYoutube(video, params)
 
-        video[u"video_url"] = self.selectVideoQuality(params, links)
+        if len(links) != 0:
+            video[u"video_url"] = self.selectVideoQuality(params, links)
+        elif "hlsvp" in video:
+            #hls selects the quality based on available bitrate (adaptive 
quality), no need to select it here
+            video[u"video_url"] = video[u"hlsvp"]
+            self.common.log("Using hlsvp url %s" % video[u"video_url"])
 
         (video, status) = self.checkForErrors(video)
 
@@ -290,6 +297,13 @@ class YouTubePlayer():
 
         return (video, status)
 
+    def removeAdditionalEndingDelimiter(self, data):
+        pos = data.find("};")
+        if pos != -1:
+            self.common.log(u"found extra delimiter, removing")
+            data = data[:pos + 1]
+        return data
+
     def extractFlashVars(self, data):
         flashvars = {}
         found = False
@@ -303,10 +317,12 @@ class YouTubePlayer():
                     continue
                 data = line[p1 + 1:p2]
                 break
+        data = self.removeAdditionalEndingDelimiter(data)
 
         if found:
             data = json.loads(data)
             flashvars = data["args"]
+        self.common.log("Step2: " + repr(data))
 
         self.common.log(u"flashvars: " + repr(flashvars), 2)
         return flashvars
@@ -322,6 +338,9 @@ class YouTubePlayer():
         if flashvars.has_key(u"ttsurl"):
             video[u"ttsurl"] = flashvars[u"ttsurl"]
 
+        if flashvars.has_key(u"hlsvp"):                               
+            video[u"hlsvp"] = flashvars[u"hlsvp"]    
+
         for url_desc in flashvars[u"url_encoded_fmt_stream_map"].split(u","):
             url_desc_map = cgi.parse_qs(url_desc)
             self.common.log(u"url_map: " + repr(url_desc_map), 2)
@@ -342,11 +361,37 @@ class YouTubePlayer():
 
             if url_desc_map.has_key(u"sig"):
                 url = url + u"&signature=" + url_desc_map[u"sig"][0]
+            elif url_desc_map.has_key(u"s"):
+                sig = url_desc_map[u"s"][0]
+                url = url + u"&signature=" + self.decrypt_signature(sig)
 
             links[key] = url
 
         return links
 
+    def decrypt_signature(self, s):
+        ''' use decryption solution by Youtube-DL project '''
+        if len(s) == 88:
+            return s[48] + s[81:67:-1] + s[82] + s[66:62:-1] + s[85] + 
s[61:48:-1] + s[67] + s[47:12:-1] + s[3] + s[11:3:-1] + s[2] + s[12]
+        elif len(s) == 87:
+            return s[62] + s[82:62:-1] + s[83] + s[61:52:-1] + s[0] + 
s[51:2:-1]
+        elif len(s) == 86:
+            return s[2:63] + s[82] + s[64:82] + s[63]
+        elif len(s) == 85:
+            return s[76] + s[82:76:-1] + s[83] + s[75:60:-1] + s[0] + 
s[59:50:-1] + s[1] + s[49:2:-1]
+        elif len(s) == 84:
+            return s[83:36:-1] + s[2] + s[35:26:-1] + s[3] + s[25:3:-1] + s[26]
+        elif len(s) == 83:
+            return s[6] + s[3:6] + s[33] + s[7:24] + s[0] + s[25:33] + s[53] + 
s[34:53] + s[24] + s[54:]
+        elif len(s) == 82:
+            return s[36] + s[79:67:-1] + s[81] + s[66:40:-1] + s[33] + 
s[39:36:-1] + s[40] + s[35] + s[0] + s[67] + s[32:0:-1] + s[34]
+        elif len(s) == 81:
+            return s[6] + s[3:6] + s[33] + s[7:24] + s[0] + s[25:33] + s[2] + 
s[34:53] + s[24] + s[54:81]
+        elif len(s) == 92:
+            return s[25] + s[3:25] + s[0] + s[26:42] + s[79] + s[43:79] + 
s[91] + s[80:83];
+        else:
+            self.common.log(u'Unable to decrypt signature, key length %d not 
supported; retrying might work' % (len(s)))
+
     def getVideoPageFromYoutube(self, get):
         login = "false"
 
@@ -354,6 +399,7 @@ class YouTubePlayer():
             login = "true"
 
         page = self.core._fetchPage({u"link": self.urls[u"video_stream"] % 
get(u"videoid"), "login": login})
+        self.common.log("Step1: " + repr(page["content"].find("ytplayer")))
 
         if not page:
             page = {u"status":303}
@@ -376,7 +422,7 @@ class YouTubePlayer():
                 self.login._httpLogin({"new":"true"})
                 result = self.getVideoPageFromYoutube(get)
             else:
-                self.utils.showMessage(self.language(30600), 
self.language(30622))
+                video[u"apierror"] = self.language(30622)
 
         if result[u"status"] != 200:
             self.common.log(u"Couldn't get video page from YouTube")
@@ -384,7 +430,7 @@ class YouTubePlayer():
 
         links = self.scrapeWebPageForVideoLinks(result, video)
 
-        if len(links) == 0:
+        if len(links) == 0 and not( "hlsvp" in video ):
             self.common.log(u"Couldn't find video url- or stream-map.")
 
             if not u"apierror" in video:
diff --git a/plugin.video.youtube/YouTubePlaylistControl.py 
b/plugin.video.youtube/YouTubePlaylistControl.py
index b66ac2b..b35f98f 100644
--- a/plugin.video.youtube/YouTubePlaylistControl.py
+++ b/plugin.video.youtube/YouTubePlaylistControl.py
@@ -50,10 +50,9 @@ class YouTubePlaylistControl():
             print repr(result)
         elif get("scraper") == "liked_videos":
             (result, status) = self.getLikedVideos(params)
-        elif get("scraper") == "music_top100":
-            result = self.getYouTubeTop100(params)
         elif get("playlist"):
             params["user_feed"] = "playlist"
+            params["login"] = "true"
             result = self.getUserFeed(params)
         elif get("user_feed") in ["recommended", "watch_later", 
"newsubscriptions", "favorites"]:
             params["login"] = "true"
@@ -149,14 +148,6 @@ class YouTubePlaylistControl():
 
         return self.feeds.listAll(params)
 
-    def getYouTubeTop100(self, params={}):
-        (result, status) = self.scraper.scrapeYouTubeTop100(params)
-
-        if status == 200:
-            (result, status) = self.core.getBatchDetails(result, params)
-
-        return result
-
     def getLikedVideos(self, params={}):
         get = params.get
         if not get("scraper") or not get("login"):
diff --git a/plugin.video.youtube/YouTubePluginSettings.py 
b/plugin.video.youtube/YouTubePluginSettings.py
index e82392a..daba307 100644
--- a/plugin.video.youtube/YouTubePluginSettings.py
+++ b/plugin.video.youtube/YouTubePluginSettings.py
@@ -36,7 +36,7 @@ class YouTubePluginSettings():
         return [5, 10, 15, 20, 25][int(self.settings.getSetting("timeout"))]
 
     def userHasProvidedValidCredentials(self):
-        return (self.settings.getSetting("username") != "" and 
self.settings.getSetting("oauth2_access_token"))
+        return (self.settings.getSetting("username") != "" and 
self.settings.getSetting("oauth2_access_token") != "")
 
     def userName(self):
         return self.settings.getSetting("username")
diff --git a/plugin.video.youtube/YouTubeScraper.py 
b/plugin.video.youtube/YouTubeScraper.py
index 072b92e..04ca569 100644
--- a/plugin.video.youtube/YouTubeScraper.py
+++ b/plugin.video.youtube/YouTubeScraper.py
@@ -64,25 +64,6 @@ class YouTubeScraper():
 
         return ([], 303)
 
-#================================= trailers 
===========================================
-
-    def scraperTop100Trailers(self, params):
-        self.common.log("" + repr(params))
-        url = self.createUrl(params)
-
-        result = self.core._fetchPage({"link":url})
-
-        trailers_playlist = self.common.parseDOM(result["content"], "a", 
attrs={"class":"yt-playall-link .*?"}, ret="href")[0]
-
-        if trailers_playlist.find("list=") > 0:
-            trailers_playlist = 
trailers_playlist[trailers_playlist.find("list=") + len("list="):]
-            if (trailers_playlist.rfind("&") > 0):
-                trailers_playlist = 
trailers_playlist[:trailers_playlist.rfind("&")]
-
-            return self.feeds.listPlaylist({"user_feed": "playlist", 
"playlist" : trailers_playlist})
-
-        return ([], 303)
-
 #=================================== Music 
============================================
 
     def searchDisco(self, params={}):
@@ -103,34 +84,6 @@ class YouTubeScraper():
 
         return ([], 303)
 
-    def scrapeYouTubeTop100(self, params={}):
-        self.common.log("")
-
-        url = self.createUrl(params)
-
-        result = self.core._fetchPage({"link": url})
-
-        if result["status"] == 200:
-            list_url = self.common.parseDOM(result["content"], "a", 
attrs={"id": 'popular-tracks'}, ret="href")[0]
-            return self.scrapeWeeklyTop100Playlist(list_url)
-
-        self.common.log("Done")
-        return ([], 303)
-
-    def scrapeWeeklyTop100Playlist(self, list_url):
-        self.common.log("")
-        url = self.urls["main"] + list_url
-
-        result = self.core._fetchPage({"link":url })
-
-        if result["status"] == 200:
-            playlist = self.common.parseDOM(result["content"], "ol", 
attrs={"id": 'watch7-playlist-tray'})
-            print repr(playlist)
-            videos = self.common.parseDOM(playlist, "li", attrs={"class": 
'video-list-item.*?'}, ret="data-video-id")
-
-            return(videos, result["status"])
-
-        return ([], 303)
         #================================== Common 
============================================
     def getNewResultsFunction(self, params={}):
         get = params.get
@@ -142,13 +95,6 @@ class YouTubeScraper():
         if (get("scraper") in ["liked_videos", "watched_history"]):
             function = self.scrapeUserLikedVideos
 
-        if (get("scraper") == "music_top100"):
-            params["batch"] = "true"
-            function = self.scrapeYouTubeTop100
-
-        if get("scraper") == "trailers":
-            function = self.scraperTop100Trailers
-
         if function:
             params["new_results_function"] = function
 
@@ -168,12 +114,6 @@ class YouTubeScraper():
             else:
                 url += "?p=" + page
 
-        if get("scraper") == "music_top100":
-            url = self.urls["disco_main"]
-
-        if get("scraper") == "trailers":
-            url = self.urls["trailers"]
-
         if (get("scraper") in "search_disco"):
             url = self.urls["disco_search"] % urllib.quote_plus(get("search"))
 
@@ -238,10 +178,6 @@ class YouTubeScraper():
         return (result, status)
 
     def scrape(self, params={}):
-        get = params.get
-        if get("scraper") == "trailers":
-            return self.scraperTop100Trailers(params)
-
         self.getNewResultsFunction(params)
 
         result = self.paginator(params)
diff --git a/plugin.video.youtube/YouTubeSubtitleControl.py 
b/plugin.video.youtube/YouTubeSubtitleControl.py
index b1d9a49..8909440 100644
--- a/plugin.video.youtube/YouTubeSubtitleControl.py
+++ b/plugin.video.youtube/YouTubeSubtitleControl.py
@@ -100,36 +100,26 @@ class YouTubeSubtitleControl():
                 self.common.log(u"Code list and sublist length mismatch: " + 
repr(codelist) + " - " + repr(sublist))
                 return ""
 
-            if len(codelist) > 0:
-                # Fallback to first in list.
-                subtitle = sublist[0].replace(u" ", u"%20")
-                code = codelist[0]
-
             lang_code = ["off", "en", "es", "de", "fr", "it", 
"ja"][int(self.settings.getSetting("lang_code"))]
             self.common.log(u"selected language: " + repr(lang_code))
-            if True:
-                for i in range(0, len(codelist)):
-                    data = codelist[i].lower()
-                    if data.find("-") > -1:
-                        data = data[:data.find("-")]
-
-                    if codelist[i].find(lang_code) > -1:
-                        subtitle = sublist[i].replace(" ", "%20")
-                        code = codelist[i]
-                        self.common.log(u"found subtitle specified: " + 
subtitle + " - " + code)
-                        break
-
-                    if codelist[i].find("en") > -1:
-                        subtitle = sublist[i].replace(" ", "%20")
-                        code = "en"
-                        self.common.log(u"found subtitle default: " + subtitle 
+ " - " + code)
+
+            for i in range(0, len(codelist)):
+                if codelist[i].find(lang_code) > -1:
+                    subtitle = sublist[i].replace(u" ", u"%20")
+                    code = codelist[i]
+                    self.common.log(u"found subtitle specified: " + subtitle + 
" - " + code)
+                    break
+
+                if codelist[i].find("en") > -1:
+                    subtitle = sublist[i].replace(u" ", u"%20")
+                    code = "en"
+                    self.common.log(u"found subtitle default: " + subtitle + " 
- " + code)
 
             if code:
                 url = self.urls["close_caption_url"] % (get("videoid"), code)
                 if len(subtitle) > 0:
                     url += "&name=" + subtitle
 
-
         self.common.log(u"found subtitle url: " + repr(url))
         return url
 
diff --git a/plugin.video.youtube/addon.xml b/plugin.video.youtube/addon.xml
index d217be0..036332a 100644
--- a/plugin.video.youtube/addon.xml
+++ b/plugin.video.youtube/addon.xml
@@ -1,9 +1,9 @@
 <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
-<addon id="plugin.video.youtube" name="YouTube" provider-name="TheCollective" 
version="4.4.4">
+<addon id="plugin.video.youtube" name="YouTube" provider-name="TheCollective" 
version="4.4.6">
   <requires>
     <import addon="xbmc.python" version="2.1.0" />
     <import addon="script.module.simplejson" version="2.0.10" />
-    <import addon="script.common.plugin.cache" version="2.5.1" />
+    <import addon="script.common.plugin.cache" version="2.5.2" />
     <import addon="script.module.parsedom" version="2.5.1" />
     <import addon="script.module.simple.downloader" version="1.9.4" />
   </requires>
diff --git a/plugin.video.youtube/changelog.txt 
b/plugin.video.youtube/changelog.txt
index f3a082d..fb0e41f 100644
--- a/plugin.video.youtube/changelog.txt
+++ b/plugin.video.youtube/changelog.txt
@@ -1,9 +1,5 @@
 [B]TODO:[/B]
 - Fix RTMP support.
-- Unit test new functions in storage...
-- Replace scraper with feeds for: shows.
-- Integration tests on all user actions.
-- Use extractJS and urlparse.parse_qs.
 - UTF8/16 does not work consistently(Verify failures against minidom 
implementation)
 - Embed playback fallback
 
@@ -12,6 +8,20 @@
 - [XBMC] When sorting items, it's impossible to get them to return to their 
original order
 - [XBMC] Has Excessive Memory use after running the plugin for prolonged 
periods of time
 - [RTMPDUMP] Doesn't support handshake type 10 which is required by youtube.
+- Youtube implemented encryption of signatures, without knowing the proper 
decryption method this VEVO content will remain unstable
+
+[B]Version 3.4.6[/B]
+- Fixed: Age verification working again due
+- Added: Partial hack to support youtubes new encrypted signatures, this will 
break again.
+- Added: Support a 5th login method.
+- Changed: Much better cookie management
+- Changed: More robust http login
+
+[B]Version 3.4.5[/B]
+- Added support for hls only streams. (Thanks to simplyintricate on github)
+- Fixed edge case where youtube video object contained an extra delimiter 
breakning the video player
+- Changed: Now requires valid Login to use play all on users playlists
+- Changed: Switched trailers to use youtube api
 
 [B]Version 3.4.4[/B]
 - Fixed playback after youtube javascript site changes
diff --git a/plugin.video.youtube/default.py b/plugin.video.youtube/default.py
index fa62579..1082caa 100644
--- a/plugin.video.youtube/default.py
+++ b/plugin.video.youtube/default.py
@@ -16,20 +16,21 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 '''
 
+import os
 import sys
 import xbmc
-import xbmcplugin
-import xbmcaddon
 import xbmcgui
 import urllib2
+import xbmcaddon
 import cookielib
+import xbmcplugin
 try:
     import xbmcvfs
 except ImportError:
     import xbmcvfsdummy as xbmcvfs
 
 # plugin constants
-version = "4.4.4"
+version = "4.4.6"
 plugin = "YouTube-" + version
 author = "TheCollective"
 url = "www.xbmc.com"
@@ -51,7 +52,17 @@ login = ""
 player = ""
 cache = ""
 
-cookiejar = cookielib.LWPCookieJar()
+path = xbmc.translatePath(settings.getAddonInfo("profile"))
+path = os.path.join(path, 'yt-cookiejar.txt')
+print("Loading cookies from :" + repr(path))
+cookiejar = cookielib.LWPCookieJar(path)
+
+if xbmcvfs.exists(path):
+    try:
+        cookiejar.load()
+    except:
+        pass
+
 cookie_handler = urllib2.HTTPCookieProcessor(cookiejar)
 opener = urllib2.build_opener(cookie_handler)
 
diff --git a/plugin.video.youtube/resources/language/English/strings.xml 
b/plugin.video.youtube/resources/language/English/strings.xml
index 2ae9de2..65dbcd7 100644
--- a/plugin.video.youtube/resources/language/English/strings.xml
+++ b/plugin.video.youtube/resources/language/English/strings.xml
@@ -36,7 +36,7 @@
     <string id="30029">Contact</string>
     <string id="30030">Refreshing folder..</string>
     <string id="30031">Login success</string>
-    <string id="30032">YouTube Top 100 Trailers</string>
+    <string id="30032">YouTube Trailers</string>
     <string id="30033">Popular</string>
     <string id="30034">In Theaters</string>
     <string id="30035">Latest</string>

-----------------------------------------------------------------------

Summary of changes:
 plugin.video.youtube/YouTubeCore.py                |   33 +----
 plugin.video.youtube/YouTubeLogin.py               |  173 +++++++-------------
 plugin.video.youtube/YouTubeNavigation.py          |    7 +-
 plugin.video.youtube/YouTubePlayer.py              |   70 +++++++--
 plugin.video.youtube/YouTubePlaylistControl.py     |   11 +-
 plugin.video.youtube/YouTubePluginSettings.py      |    2 +-
 plugin.video.youtube/YouTubeScraper.py             |   64 -------
 plugin.video.youtube/YouTubeSubtitleControl.py     |   34 ++---
 plugin.video.youtube/addon.xml                     |    4 +-
 plugin.video.youtube/changelog.txt                 |   18 ++-
 plugin.video.youtube/default.py                    |   19 ++-
 .../resources/language/English/strings.xml         |    2 +-
 12 files changed, 169 insertions(+), 268 deletions(-)


hooks/post-receive
-- 
Plugins

------------------------------------------------------------------------------
See everything from the browser to the database with AppDynamics
Get end-to-end visibility with application monitoring from AppDynamics
Isolate bottlenecks and diagnose root cause in seconds.
Start your free trial of AppDynamics Pro today!
http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons

Reply via email to