The branch, frodo has been updated
via 9c522f623253ca05e788610feacf8087fc5798ed (commit)
from c059985c3bff77d67b48afa13fbff802503ecb1c (commit)
- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=9c522f623253ca05e788610feacf8087fc5798ed
commit 9c522f623253ca05e788610feacf8087fc5798ed
Author: Martijn Kaijser <[email protected]>
Date: Thu May 29 18:02:06 2014 +0200
[plugin.video.youtube] 4.4.8
diff --git a/plugin.video.youtube/YouTubeCore.py
b/plugin.video.youtube/YouTubeCore.py
index b1b3758..2bb7e09 100644
--- a/plugin.video.youtube/YouTubeCore.py
+++ b/plugin.video.youtube/YouTubeCore.py
@@ -139,7 +139,7 @@ class YouTubeCore():
def del_playlist(self, params={}):
self.common.log("")
get = params.get
- url =
u"http://gdata.youtube.com/feeds/api/users/default/playlists/{0}".format(get("playlist"))
+ url = "http://gdata.youtube.com/feeds/api/users/default/playlists/%s"
% (get("playlist"))
result = self._fetchPage({"link": url, "api": "true", "login": "true",
"auth": "true", "method": "DELETE"})
return (result["content"], result["status"])
@@ -180,9 +180,10 @@ class YouTubeCore():
folders = []
for node in entries:
folder = {}
-
- if len(self.common.parseDOM(node, "yt:deprecated")):
+ print repr(node)
+ if 'yt:deprecated' in node:
continue
+
title = self.common.parseDOM(node, "atom:category", ret="label")[0]
if title:
@@ -220,7 +221,7 @@ class YouTubeCore():
if title.find(": ") > 0:
title = title[title.find(": ") + 2:]
title = self.common.replaceHTMLCodes(title)
-
+
folder['Title'] = title
for tmp in self.common.parseDOM(node, "published"):
folder['published'] = tmp
@@ -397,13 +398,12 @@ class YouTubeCore():
return ret_obj
if get("url_data"):
- urldata = get("url_data")
- url_data = {}
-
- for key in urldata:
- url_data[key.encode('UTF-8')] = urldata[key].encode('UTF-8')
+ url_data = get("url_data")
+ url_data_encoded = {}
+ for k, v in url_data.iteritems():
+ url_data_encoded[k] = unicode(v).encode('utf-8')
- request = urllib2.Request(link, urllib.urlencode(url_data))
+ request = urllib2.Request(link, urllib.urlencode(url_data_encoded))
request.add_header('Content-Type',
'application/x-www-form-urlencoded')
elif get("request", "false") == "false":
if get("proxy"):
@@ -462,7 +462,6 @@ class YouTubeCore():
if cookie:
self.common.log("Setting cookie: " + cookie)
request.add_header('Cookie', cookie)
-
con = urllib2.urlopen(request)
inputdata = con.read()
@@ -545,6 +544,25 @@ class YouTubeCore():
self.common.log("4")
error = self.common.parseDOM(ret['content'], "div", attrs={"id":
"watch7-player-age-gate-content"})
+ if len(error) == 0:
+ self.common.log("5")
+ if len(self.common.parseDOM(ret['content'], "input", attrs={"id":
"send-code-button"})):
+ error = [self.language(30630)]
+
+ if len(error) == 0:
+ self.common.log("6")
+ if len(self.common.parseDOM(ret['content'], "h1", attrs={"id":
"login-challenge-heading"})):
+ error = [self.language(30630)]
+
+ if len(error) == 0:
+ self.common.log("7")
+ if len(self.common.parseDOM(ret['content'], "h2", attrs={"class":
"smsauth-interstitial-heading"})):
+ error = [self.language(30630)]
+
+ if len(error) == 0:
+ self.common.log("8")
+ error = self.common.parseDOM(ret['content'], "span",
attrs={"class": "error-msg"})
+
if len(error) > 0:
self.common.log("Found error: " + repr(error))
error = self.common.stripTags(error[0])
@@ -761,7 +779,6 @@ class YouTubeCore():
result = 1
for tmp in self.common.parseDOM(node, "yt:duration", ret="seconds"):
- tmp = int(tmp) / 60
if tmp:
result = tmp
diff --git a/plugin.video.youtube/YouTubeLogin.py
b/plugin.video.youtube/YouTubeLogin.py
index 3a5d0aa..f4212f5 100644
--- a/plugin.video.youtube/YouTubeLogin.py
+++ b/plugin.video.youtube/YouTubeLogin.py
@@ -188,8 +188,10 @@ class YouTubeLogin():
fetch_options = False
# Check if we are logged in.
+# nick = self.common.parseDOM(ret["content"], "p", attrs={"class":
"masthead-expanded-acct-sw-id2"})
nick = self.common.parseDOM(ret["content"], "span", attrs={"id":
"yt-masthead-user-displayname"})
+
# Check if there are any errors to report
errors = self.core._findErrors(ret, silent=True)
if errors:
@@ -224,41 +226,42 @@ class YouTubeLogin():
newurl = self.common.parseDOM(ret["content"], "meta",
attrs={"http-equiv": "refresh"}, ret="content")
if len(newurl) > 0:
newurl = newurl[0].replace("&", "&")
- newurl = newurl.replace("0; url='", "")
+ newurl = newurl[newurl.find("'") + 5:newurl.rfind("'")]
fetch_options = {"link": newurl, "referer": ret["location"]}
self.common.log("Part C: " + repr(fetch_options))
continue
## 2-factor login start
- #if ret["content"].find("smsUserPin") > -1:
- # url_data = self._fillUserPin(ret["content"])
- # if len(url_data) == 0:
- # return (False, 500)
+ if ret["content"].find("smsUserPin") > -1:
+ url_data = self._fillUserPin(ret["content"])
+ if len(url_data) == 0:
+ return (False, 500)
- # self.common.log("RETURNED CONTENT" + ret["content"])
- # new_part = self.common.parseDOM(ret["content"], "input",
attrs={"name": "continue"}, ret="value")
- # fetch_options = {"link": new_part[0].replace("&", "&"),
"url_data": url_data, "referer": ret["location"]}
+ new_part = self.common.parseDOM(ret["content"], "form",
attrs={"id": "gaia_secondfactorform"}, ret="action")
+ t_url = ret["new_url"]
+ t_url = t_url[:t_url.find("/", 10) + 1] +
new_part[0].replace("&", "&")
+ fetch_options = {"link": t_url, "url_data": url_data,
"referer": ret["new_url"]}
- # self.common.log("Part D: " + repr(fetch_options))
- # continue
+ self.common.log("Part D: " + repr(fetch_options))
+ continue
- #smsToken = self.common.parseDOM(ret["content"].replace("\n", ""),
"input", attrs={"name": "smsToken"}, ret="value")
+ smsToken = self.common.parseDOM(ret["content"].replace("\n", ""),
"input", attrs={"name": "smsToken"}, ret="value")
- #if len(smsToken) > 0 and galx != "":
- # url_data = {"smsToken": smsToken[0],
- # "PersistentCookie": "yes",
- # "service": "youtube",
- # "GALX": galx}
+ if len(smsToken) > 0 and galx != "":
+ url_data = {"smsToken": smsToken[0],
+ "PersistentCookie": "yes",
+ "service": "youtube",
+ "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,
"referer": ret["location"]}
- # self.common.log("Part E: " + repr(fetch_options))
- # continue
+ target_url = self.common.parseDOM(ret["content"], "form",
attrs={"name": "hiddenpost"}, ret="action")
+ fetch_options = {"link": target_url[0], "url_data": url_data,
"referer": ret["location"]}
+ self.common.log("Part E: " + repr(fetch_options))
+ continue
## 2-factor login finish
- #if not fetch_options:
+ if not fetch_options:
# Check for errors.
- # return (self.core._findErrors(ret), 303)
+ return (self.core._findErrors(ret), 303)
return (ret, 500)
@@ -270,7 +273,7 @@ class YouTubeLogin():
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
+ url_data[name] = self.common.makeAscii(val)
self.common.log("Extracted url_data: " + repr(url_data), 0)
url_data["Email"] = self.pluginsettings.userName()
@@ -283,12 +286,18 @@ class YouTubeLogin():
def _fillUserPin(self, content):
self.common.log("")
+ #form = self.common.parseDOM(content, "form", attrs={"id":
"gaia_secondfactorform"}, ret=True)
form = self.common.parseDOM(content, "form", attrs={"id":
"gaia_secondfactorform"}, 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
+ if name not in ["smsSend", "retry"]:
+ #for val in self.common.parseDOM(form, "input", attrs={"name":
name}, ret="value"):
+ # url_data[name] = self.common.makeAscii(val)
+ if name not in ["smsSend", "retry"]:
+ for val in self.common.parseDOM(form, "input", attrs={"name":
name}, ret="value"):
+ url_data[name] = self.common.makeAscii(val)
+
self.common.log("url_data: " + repr(form), 0)
diff --git a/plugin.video.youtube/YouTubeNavigation.py
b/plugin.video.youtube/YouTubeNavigation.py
index e4c3a7c..dcd67ad 100644
--- a/plugin.video.youtube/YouTubeNavigation.py
+++ b/plugin.video.youtube/YouTubeNavigation.py
@@ -167,7 +167,7 @@ class YouTubeNavigation():
results = []
if (get("feed") == "search" or get("scraper") == "search_disco"):
if not get("search"):
- query = self.common.getUserInput(self.language(30006), '')
+ query = self.common.getUserInput(self.language(30006),
'').strip()
if not query:
return False
params["search"] = query
@@ -442,6 +442,7 @@ class YouTubeNavigation():
listitem.setProperty("Video", "true")
listitem.setProperty("IsPlayable", "true")
+ listitem.addStreamInfo('video', {'duration': item('Duration')})
listitem.setInfo(type='Video', infoLabels=item_params)
self.xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=url,
listitem=listitem, isFolder=False, totalItems=listSize + 1)
self.common.log("Done", 5)
diff --git a/plugin.video.youtube/YouTubePlayer.py
b/plugin.video.youtube/YouTubePlayer.py
index cae01dd..539aa67 100644
--- a/plugin.video.youtube/YouTubePlayer.py
+++ b/plugin.video.youtube/YouTubePlayer.py
@@ -16,14 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
+import re
import sys
-import urllib
import cgi
+import urllib
try: import simplejson as json
except ImportError: import json
-import urllib2, re
-
class YouTubePlayer():
fmt_value = {
5: "240p h263 flv container",
@@ -52,15 +51,13 @@ class YouTubePlayer():
121: "hd1080"
}
- # MAX RECURSION Depth for security
- MAX_REC_DEPTH = 5
-
# YouTube Playback Feeds
urls = {}
urls['video_stream'] = "http://www.youtube.com/watch?v=%s&safeSearch=none"
urls['embed_stream'] = "http://www.youtube.com/get_video_info?video_id=%s"
urls['video_info'] = "http://gdata.youtube.com/feeds/api/videos/%s"
+
def __init__(self):
self.xbmcgui = sys.modules["__main__"].xbmcgui
self.xbmcplugin = sys.modules["__main__"].xbmcplugin
@@ -77,9 +74,8 @@ class YouTubePlayer():
self.core = sys.modules["__main__"].core
self.login = sys.modules["__main__"].login
self.subtitles = sys.modules["__main__"].subtitles
-
+
self.algoCache = {}
- self._cleanTmpVariables()
def playVideo(self, params={}):
self.common.log(repr(params), 3)
@@ -101,7 +97,6 @@ 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")):
@@ -317,7 +312,7 @@ class YouTubePlayer():
url = "http:" + url
return url
- def extractFlashVars(self, data, assets):
+ def extractFlashVars(self, data, assets=0):
flashvars = {}
found = False
@@ -343,7 +338,7 @@ class YouTubePlayer():
if k in flashvars:
flashvars[k] = self.normalizeUrl(flashvars[k])
- self.common.log("Step2: " + repr(data))
+ self.common.log("Step2: " + repr(data), 4)
self.common.log(u"flashvars: " + repr(flashvars), 2)
return flashvars
@@ -352,7 +347,7 @@ class YouTubePlayer():
self.common.log(u"")
links = {}
- flashvars = self.extractFlashVars(result[u"content"], 0)
+ flashvars = self.extractFlashVars(result[u"content"])
if not flashvars.has_key(u"url_encoded_fmt_stream_map"):
return links
@@ -385,24 +380,14 @@ class YouTubePlayer():
elif url_desc_map.has_key(u"s"):
sig = url_desc_map[u"s"][0]
flashvars = self.extractFlashVars(result[u"content"], 1)
- js = flashvars[u"js"]
- url = url + u"&signature=" + self.decrypt_signature(sig, js)
+ url = url + u"&signature=" + self.decrypt_signature(sig,
flashvars[u"js"])
links[key] = url
return links
- @staticmethod
- def printDBG(s):
- print(s)
-
- def _cleanTmpVariables(self):
- self.fullAlgoCode = ''
- self.allLocalFunNamesTab = []
- self.playerData = ''
-
def _jsToPy(self, jsFunBody):
- pythonFunBody = re.sub(r'function (\w*)\$(\w*)', r'function \1_S_\2',
jsFunBody)
+ pythonFunBody = re.sub(r'function (\w*)\$(\w*)', r'function \1_S_\2',
jsFunBody)
pythonFunBody = pythonFunBody.replace('function', 'def').replace('{',
':\n\t').replace('}', '').replace(';', '\n\t').replace('var ', '')
pythonFunBody = pythonFunBody.replace('.reverse()', '[::-1]')
@@ -426,10 +411,10 @@ class YouTubePlayer():
lines[i] = lines[i].replace( match.group(0), match.group(2) +
'.join(' + match.group(1) + ')' )
return "\n".join(lines)
- def _getLocalFunBody(self, funName):
+ def _getLocalFunBody(self, funName, playerData):
# get function body
funName=funName.replace('$', '\\$')
- match = re.search('(function %s\([^)]+?\){[^}]+?})' % funName,
self.playerData)
+ match = re.search('(function %s\([^)]+?\){[^}]+?})' % funName,
playerData)
if match:
# return jsFunBody
return match.group(1)
@@ -444,59 +429,56 @@ class YouTubePlayer():
return set()
def decrypt_signature(self, s, playerUrl):
- self.printDBG("decrypt_signature sign_len[%d] playerUrl[%s]" %
(len(s), playerUrl) )
-
- # clear local data
- self._cleanTmpVariables()
+ self.common.log("decrypt_signature sign_len[%d] playerUrl[%s]" %
(len(s), playerUrl) )
# use algoCache
if playerUrl not in self.algoCache:
# get player HTML 5 sript
- request = urllib2.Request(playerUrl)
+ res = self.core._fetchPage({u"link": playerUrl})
+ playerData = res["content"]
try:
- self.playerData = urllib2.urlopen(request).read()
- self.playerData = self.playerData.decode('utf-8', 'ignore')
+ playerData = playerData.decode('utf-8', 'ignore')
except Exception as ex:
- self.printDBG("Error: " + str(sys.exc_info()[0]) + " - " +
str(ex))
- self.printDBG('Unable to download playerUrl webpage')
+ self.common.log("Error: " + str(sys.exc_info()[0]) + " - " +
str(ex))
+ self.common.log('Unable to download playerUrl webpage')
return ''
- # get main function name
- match = re.search("signature=(\w+?)\([^)]\)", self.playerData)
+ # get main function name
+ match = re.search("signature=(\w+?)\([^)]\)", playerData)
if match:
mainFunName = match.group(1)
- self.printDBG('Main signature function name = "%s"' %
mainFunName)
- else:
- self.printDBG('Can not get main signature function name')
+ self.common.log('Main signature function name = "%s"' %
mainFunName)
+ else:
+ self.common.log('Can not get main signature function name')
return ''
- self._getfullAlgoCode( mainFunName )
+ fullAlgoCode = self._getfullAlgoCode( mainFunName, playerData )
# wrap all local algo function into one function
extractedSignatureAlgo()
- algoLines = self.fullAlgoCode.split('\n')
+ algoLines = fullAlgoCode.split('\n')
for i in range(len(algoLines)):
algoLines[i] = '\t' + algoLines[i]
- self.fullAlgoCode = 'def extractedSignatureAlgo(param):'
- self.fullAlgoCode += '\n'.join(algoLines)
- self.fullAlgoCode += '\n\treturn %s(param)' % mainFunName
- self.fullAlgoCode += '\noutSignature = extractedSignatureAlgo(
inSignature )\n'
+ fullAlgoCode = 'def extractedSignatureAlgo(param):'
+ fullAlgoCode += '\n'.join(algoLines)
+ fullAlgoCode += '\n\treturn %s(param)' % mainFunName
+ fullAlgoCode += '\noutSignature = extractedSignatureAlgo(
inSignature )\n'
- # after this function we should have all needed code in
self.fullAlgoCode
+ # after this function we should have all needed code in
fullAlgoCode
- self.printDBG( "---------------------------------------" )
- self.printDBG( "| ALGO FOR SIGNATURE DECRYPTION |" )
- self.printDBG( "---------------------------------------" )
- self.printDBG( self.fullAlgoCode )
- self.printDBG( "---------------------------------------" )
+ self.common.log( "---------------------------------------" )
+ self.common.log( "| ALGO FOR SIGNATURE DECRYPTION |" )
+ self.common.log( "---------------------------------------" )
+ self.common.log( fullAlgoCode )
+ self.common.log( "---------------------------------------" )
try:
- algoCodeObj = compile(self.fullAlgoCode, '', 'exec')
+ algoCodeObj = compile(fullAlgoCode, '', 'exec')
except:
- self.printDBG('decryptSignature compile algo code EXCEPTION')
+ self.common.log('decryptSignature compile algo code EXCEPTION')
return ''
else:
# get algoCodeObj from algoCache
- self.printDBG('Algo taken from cache')
+ self.common.log('Algo taken from cache')
algoCodeObj = self.algoCache[playerUrl]
# for security alow only flew python global function in algo code
@@ -509,50 +491,51 @@ class YouTubePlayer():
try:
exec( algoCodeObj, vGlobals, vLocals )
except:
- self.printDBG('decryptSignature exec code EXCEPTION')
+ self.common.log('decryptSignature exec code EXCEPTION')
return ''
- self.printDBG('Decrypted signature = [%s]' % vLocals['outSignature'])
+ self.common.log('Decrypted signature = [%s]' % vLocals['outSignature'])
# if algo seems ok and not in cache, add it to cache
if playerUrl not in self.algoCache and '' != vLocals['outSignature']:
- self.printDBG('Algo from player [%s] added to cache' % playerUrl)
+ self.common.log('Algo from player [%s] added to cache' % playerUrl)
self.algoCache[playerUrl] = algoCodeObj
- # free not needed data
- self._cleanTmpVariables()
-
return vLocals['outSignature']
# Note, this method is using a recursion
- def _getfullAlgoCode( self, mainFunName, recDepth = 0 ):
- if self.MAX_REC_DEPTH <= recDepth:
- self.printDBG('_getfullAlgoCode: Maximum recursion depth exceeded')
- return
+ def _getfullAlgoCode( self, mainFunName, playerData, recDepth = 0,
allLocalFunNamesTab=[] ):
+ # Max recursion of 5
+ if 5 <= recDepth:
+ self.common.log('_getfullAlgoCode: Maximum recursion depth
exceeded')
+ return
- funBody = self._getLocalFunBody( mainFunName )
+ funBody = self._getLocalFunBody( mainFunName, playerData)
if '' != funBody:
funNames = self._getAllLocalSubFunNames(funBody)
if len(funNames):
for funName in funNames:
- funName_=funName.replace('$','_S_')
- if funName not in self.allLocalFunNamesTab:
- funBody=funBody.replace(funName,funName_)
- self.allLocalFunNamesTab.append(funName)
- self.printDBG("Add local function %s to known
functions" % mainFunName)
- self._getfullAlgoCode( funName, recDepth + 1 )
-
- # conver code from javascript to python
+ funName_=funName.replace('$','_S_')
+ if funName not in allLocalFunNamesTab:
+ funBody=funBody.replace(funName,funName_)
+ allLocalFunNamesTab.append(funName)
+ self.common.log("Add local function %s to known
functions" % mainFunName)
+ funBody = self._getfullAlgoCode( funName, playerData,
recDepth + 1, allLocalFunNamesTab ) + "\n" + funBody
+
+ # conver code from javascript to python
funBody = self._jsToPy(funBody)
- self.fullAlgoCode += '\n' + funBody + '\n'
- return
+ return '\n' + funBody + '\n'
+ return funBody
- def getVideoPageFromYoutube(self, get):
+ def getVideoPageFromYoutube(self, get, has_verified = False):
login = "false"
+ verify = ""
if self.pluginsettings.userHasProvidedValidCredentials():
login = "true"
+ if has_verified:
+ verify = u"&has_verified=1"
- page = self.core._fetchPage({u"link": self.urls[u"video_stream"] %
get(u"videoid"), "login": login})
+ page = self.core._fetchPage({u"link": (self.urls[u"video_stream"] %
get(u"videoid")) + verify, "login": login})
self.common.log("Step1: " + repr(page["content"].find("ytplayer")))
if not page:
@@ -573,8 +556,7 @@ class YouTubePlayer():
if self.isVideoAgeRestricted(result):
self.common.log(u"Age restricted video")
if self.pluginsettings.userHasProvidedValidCredentials():
- self.login._httpLogin({"new":"true"})
- result = self.getVideoPageFromYoutube(get)
+ result = self.getVideoPageFromYoutube(get, True)
else:
video[u"apierror"] = self.language(30622)
diff --git a/plugin.video.youtube/YouTubePlaylistControl.py
b/plugin.video.youtube/YouTubePlaylistControl.py
index b35f98f..5757daf 100644
--- a/plugin.video.youtube/YouTubePlaylistControl.py
+++ b/plugin.video.youtube/YouTubePlaylistControl.py
@@ -83,7 +83,7 @@ class YouTubePlaylistControl():
playlist = self.xbmc.PlayList(self.xbmc.PLAYLIST_VIDEO)
playlist.clear()
- video_url = "%s?path=/root&action=play_video&videoid=%s"
+ video_url = "%s?path=/root/video&action=play_video&videoid=%s"
# queue all entries
for entry in result:
video = entry.get
diff --git a/plugin.video.youtube/YouTubeSubtitleControl.py
b/plugin.video.youtube/YouTubeSubtitleControl.py
index 8909440..0aa4bec 100644
--- a/plugin.video.youtube/YouTubeSubtitleControl.py
+++ b/plugin.video.youtube/YouTubeSubtitleControl.py
@@ -139,10 +139,14 @@ class YouTubeSubtitleControl():
w = self.storage.openFile(path, "w")
try:
w.write(result.encode("utf-8")) # WTF, didn't have to do this
before, did i?
- except:
- w.write(result)
- self.common.log(u"NOT utf-8 WRITE!!!: " + path + " - " +
repr(result))
- time.sleep(20)
+ except Exception as e:
+ self.common.log("Exception: " + repr(e))
+ try:
+ w.write(result)
+ self.common.log(u"NOT utf-8 WRITE!!!: " + path + " - " +
repr(result))
+ except Exception as de:
+ self.common.log("Exception2: " + repr(de))
+ time.sleep(5)
w.close()
diff --git a/plugin.video.youtube/addon.xml b/plugin.video.youtube/addon.xml
index 31c157a..1e8ea4f 100644
--- a/plugin.video.youtube/addon.xml
+++ b/plugin.video.youtube/addon.xml
@@ -1,10 +1,10 @@
-<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
-<addon id="plugin.video.youtube" name="YouTube" provider-name="TheCollective,
Skipmode A1" version="4.4.7">
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<addon id="plugin.video.youtube" name="YouTube" provider-name="TheCollective"
version="4.4.8">
<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.2" />
- <import addon="script.module.parsedom" version="2.5.1" />
+ <import addon="script.module.parsedom" version="2.5.2" />
<import addon="script.module.simple.downloader" version="1.9.4" />
</requires>
<extension library="default.py" point="xbmc.python.pluginsource">
@@ -117,12 +117,7 @@ Raadpleeg de lokale wetgeving voordat u deze plugin
installeert.</disclaimer>
<disclaimer lang="sv">Någon del av detta tillägg kanske är olagligt i
ditt land - kontrollera dina lokala lagar innan installation.</disclaimer>
<disclaimer
lang="th">à¸à¸²à¸à¸ªà¹à¸§à¸à¸à¸²à¸à¸ªà¹à¸§à¸à¸à¸¢à¸²à¸¢à¸à¸µà¹à¸à¸²à¸à¹à¸¡à¹à¸à¸¹à¸à¸à¹à¸à¸à¸à¸²à¸¡à¸à¸à¸«à¸¡à¸²à¸¢à¹à¸à¸à¸£à¸°à¹à¸à¸¨à¸à¸à¸à¸à¸¸à¸
à¸à¸£à¸¸à¸à¸²à¸à¸£à¸§à¸à¸ªà¸à¸à¸à¸à¸«à¸¡à¸²à¸¢à¹à¸à¸à¸£à¸°à¹à¸à¸¨à¸à¸à¸à¸à¸¸à¸à¸à¹à¸à¸à¸à¸³à¸à¸²à¸£à¸à¸´à¸à¸à¸±à¹à¸</disclaimer>
<disclaimer lang="zh">è¿ä¸ªæä»¶çæäºå
容å¯è½ä¸ç¬¦åä½
æå¨å½å®¶çæ³å¾è§å® - 请å¨å¨å®è£
å确认符åå½å°æ³å¾ã</disclaimer>
- <language></language>
<platform>all</platform>
- <license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
- <forum>http://forum.xbmc.org/showthread.php?tid=79487</forum>
- <website>http://www.youtube.com</website>
- <email></email>
- <source>git://github.com/skipmodea1/plugin.video.youtube.git</source>
+ <language />
</extension>
</addon>
\ No newline at end of file
diff --git a/plugin.video.youtube/changelog.txt
b/plugin.video.youtube/changelog.txt
index 7a97afe..c59d967 100644
--- a/plugin.video.youtube/changelog.txt
+++ b/plugin.video.youtube/changelog.txt
@@ -1,12 +1,23 @@
-[B]Known Errors[/B]
-- Google's two-step verification is NOT working at the moment for this addon:
until it gets fixed, either create a second google account for youtube viewing
in xbmc or turn 2-step off.
-
-[B]Version 4.4.7[/B]
-This version contains the sources by sogopot
(http://forum.xbmc.org/showthread.php?tid=79487&page=271) and jded
(http://forum.xbmc.org/showthread.php?tid=79487&page=276), thanks both :). I
(Skipmode A1) pushed it to the officials XMBC repo so it will install for
everybody.
-- Fixed: To solve the login problems, I added the fix from this post: Reply
#52 (http://code.google.com/p/youtubexbmc/issues/detail?id=115#c52). If you
continue to have login problems, you may have to find a different fix.
-- Fixed: To fix a problem with VEVO videos not playing, as well as certain
videos added to favorites not playing, I added the YouTubePlayer.py from this
post: Reply #217
(http://code.google.com/p/youtubexbmc/issues/detail?id=95#c217).
-- Fixed: To fix a problem with LIVE streams not playing, I compared an addon
sent to from member RedPenguin, and found a duplicated entry at line 362 & 363
and replaced with the necessary code in YouTubePlayer.py.
-- Fixed: Fix Login Problem with non English languages.
+[B]TODO:[/B]
+- Fix RTMP support.
+- UTF8/16 does not work consistently(Verify failures against minidom
implementation)
+- Embed playback fallback
+
+[B]Errata[/B]
+- [XBMC] Thumbnails sometimes turns into black box or Folder (XBMC not
detecting when thumbnail is set and defaulting to icon?)
+- [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] Requesting suggestions/recommendations from youtube sometimes
stalls.
+
+[B]Version 3.4.8[/B]
+- Fixed login issues for all five login methods.
+- Refactored VEVO video playback code.
+
+[B]Version 3.4.7[/B]
+- Fixed a problem with VEVO videos not playing
+- Fixed a problem with LIVE streams not playing.
+- Fixed Login Problem with non English languages.
- Fixed: When you are in a non English speaking country, youtube returns the
pages by default in your language. The plugin sometimes gave an error in this
situation.
[B]Version 3.4.6[/B]
@@ -371,17 +382,3 @@ This version contains the sources by sogopot
(http://forum.xbmc.org/showthread.p
- Cleanup of listFeedFolder
- Cleanup of listUserFolder
- Added Viewed and Downloaded overlay to video items.
-
-Not current anymore:
-[B]TODO:[/B]
-- Fix RTMP support.
-- UTF8/16 does not work consistently(Verify failures against minidom
implementation)
-- Embed playback fallback
-
-Not current anymore:
-[B]Errata[/B]
-- [XBMC] Thumbnails sometimes turns into black box or Folder (XBMC not
detecting when thumbnail is set and defaulting to icon?)
-- [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
diff --git a/plugin.video.youtube/default.py b/plugin.video.youtube/default.py
index dcfb5d0..f38ea0f 100644
--- a/plugin.video.youtube/default.py
+++ b/plugin.video.youtube/default.py
@@ -30,9 +30,9 @@ except ImportError:
import xbmcvfsdummy as xbmcvfs
# plugin constants
-version = "4.4.7"
+version = "4.4.8"
plugin = "YouTube-" + version
-author = "TheCollective, Skipmode A1"
+author = "TheCollective"
url = "www.xbmc.com"
# xbmc hooks
diff --git a/plugin.video.youtube/resources/language/English/strings.xml
b/plugin.video.youtube/resources/language/English/strings.xml
index 65dbcd7..6c2cd1a 100644
--- a/plugin.video.youtube/resources/language/English/strings.xml
+++ b/plugin.video.youtube/resources/language/English/strings.xml
@@ -220,4 +220,5 @@
<string id="30627">Please provide 2-factor authentication PIN</string>
<string id="30628">Please provide your password</string>
<string id="30629">SSL protocol not supported.</string>
+ <string id="30630">Please re-login in your browser and retry</string>
</strings>
-----------------------------------------------------------------------
Summary of changes:
plugin.video.youtube/YouTubeCore.py | 41 ++++--
plugin.video.youtube/YouTubeLogin.py | 59 +++++----
plugin.video.youtube/YouTubeNavigation.py | 3 +-
plugin.video.youtube/YouTubePlayer.py | 142 +++++++++-----------
plugin.video.youtube/YouTubePlaylistControl.py | 2 +-
plugin.video.youtube/YouTubeSubtitleControl.py | 12 +-
plugin.video.youtube/addon.xml | 13 +--
plugin.video.youtube/changelog.txt | 43 +++---
plugin.video.youtube/default.py | 4 +-
.../resources/language/English/strings.xml | 1 +
10 files changed, 163 insertions(+), 157 deletions(-)
hooks/post-receive
--
Plugins
------------------------------------------------------------------------------
Time is money. Stop wasting it! Get your web API in 5 minutes.
www.restlet.com/download
http://p.sf.net/sfu/restlet
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons