The branch, frodo has been updated
via 55abb002a61774f1275c221824bcebc035e796b6 (commit)
from d1bd74a30055bea2de5437bf59ca6159c05cba9f (commit)
- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/scripts;a=commit;h=55abb002a61774f1275c221824bcebc035e796b6
commit 55abb002a61774f1275c221824bcebc035e796b6
Author: Martijn Kaijser <[email protected]>
Date: Sat Jul 26 12:28:55 2014 +0200
[script.module.xbmcutil] 2.1.0
diff --git a/script.module.xbmcutil/addon.xml b/script.module.xbmcutil/addon.xml
index 98ff9b3..714c30d 100644
--- a/script.module.xbmcutil/addon.xml
+++ b/script.module.xbmcutil/addon.xml
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="script.module.xbmcutil" name="XBMC Utility Pack"
provider-name="willforde" version="2.0.4">
+<addon id="script.module.xbmcutil" name="XBMC Utility Pack"
provider-name="willforde" version="2.1.0">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
- <import addon="script.module.parsedom" version="2.5.1"/>
<import addon="script.module.simple.downloader"
version="1.9.4"/>
</requires>
<extension library="lib" point="xbmc.python.module"/>
diff --git a/script.module.xbmcutil/changelog.txt
b/script.module.xbmcutil/changelog.txt
index d17ebd3..9064ded 100644
--- a/script.module.xbmcutil/changelog.txt
+++ b/script.module.xbmcutil/changelog.txt
@@ -1,3 +1,17 @@
+[B]Version 2.1.0[/B] 2014-06-30
+- Update Strings to strings.po
+- Added UnitTests
+- Added stripEnity to urlopeners
+- Added with steatment support to urlhandler
+- Added cleanup method that runs ones every 28 Days to cleanup stail url cache
+- Added a option to enable debug output when testing addons
+- Added unittest cases for all methods and fuctions
+- Removed unnecessary cache validation checks
+- Reworked debug messages and methods
+- Reworked the vimeo url scraper
+- Fix bug with youtube video id parser
+- And a lot more small improvements
+
[B]Version 2.0.4[/B]
- Changed the Video Context menu to not replace items
- Fixed bug with XBMC unexpectedly returning unicode instead of a string
diff --git a/script.module.xbmcutil/lib/fastjson.py
b/script.module.xbmcutil/lib/fastjson.py
index f613150..3dad9c1 100644
--- a/script.module.xbmcutil/lib/fastjson.py
+++ b/script.module.xbmcutil/lib/fastjson.py
@@ -1,9 +1,3 @@
import sys
-_log = sys.modules["__main__"].xbmcutil.plugin.log
-
-if sys.version_info >= (2, 7):
- _log("Importing Built in Json Library", 0)
- from json import *
-else:
- _log("Importing Older Simplejson Library", 0)
- from simplejson import *
\ No newline at end of file
+if sys.version_info >= (2, 7): from json import *
+else: from simplejson import *
\ No newline at end of file
diff --git a/script.module.xbmcutil/lib/xbmcutil/__init__.py
b/script.module.xbmcutil/lib/xbmcutil/__init__.py
index ee5e7b0..8d21595 100644
--- a/script.module.xbmcutil/lib/xbmcutil/__init__.py
+++ b/script.module.xbmcutil/lib/xbmcutil/__init__.py
@@ -19,6 +19,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
+# Manually Remove reference to the below objects to fix XBMC Bug
+def cleanup():
+ del Addon.getLocalizedString
+ del Addon.openSettings
+ del Addon.getSetting
+ del Addon.setSetting
+ del Addon._addonData
+ del Addon._scriptData
+
# Funtion for Checking the subAction Params
def sysCheck():
# Fetch SubAction Param
@@ -40,7 +49,7 @@ def sysCheck():
elif subAction == u"opensettings":
# Open Settings Dialog
plugin.openSettings()
- plugin.refreshContainer()
+ plugin.executebuiltin("Container.Refresh")
elif subAction == u"search":
# Call storageDB and exacute saved searches
@@ -48,344 +57,180 @@ def sysCheck():
storageDB.SavedSearches()
class Addon(object):
- _addonObj = __import__("xbmcaddon").Addon
- _addonName = None
+ # Create Addon Object to the System Script Module
+ import xbmcaddon
+ _scriptData = xbmcaddon.Addon("script.module.xbmcutil")
+ _addonData = xbmcaddon.Addon()
+ getLocalizedString = _addonData.getLocalizedString
+ openSettings = _addonData.openSettings
+ getSetting = _addonData.getSetting
+ setSetting = _addonData.setSetting
_profile = None
- def setAddonIDs(self, localID, globalID):
- """ Initiate Local and Global Objects """
- self._addonData = self._addonObj(localID)
- self._scriptData = self._addonObj(globalID)
- self._addonID = localID
-
- # Shortcuts
- self.openSettings = self._addonData.openSettings
- self.setSetting = self._addonData.setSetting
- self.getuni = self.getLocalizedString
+ def getAddonInfo(self, id):
+ """ # List of possible ids.
+ author, changelog, description, disclaimer,
+ fanart, icon, id, name, path, profile,
+ stars, summary, type, version
+ """
+ # Call parent method and convert response to unicode
+ return self._addonData.getAddonInfo(id).decode("utf8")
- def getLocalizedString(self, id):
+ def getuni(self, id):
""" Return localized unicode string for selected id """
- if id >= 30000 and id <= 30899: return
self._addonData.getLocalizedString(id)
- elif id >= 32900 and id <= 32999: return
self._scriptData.getLocalizedString(id)
+ if id >= 30000 and id <= 30899: return
self.getLocalizedString(id)
+ elif id >= 32800 and id <= 32999: return
self._scriptData.getLocalizedString(id)
else: return self.xbmc.getLocalizedString(id)
def getstr(self, id):
- """ Return localized string for selected id """
- return self.getLocalizedString(id).encode("utf8")
+ """ Return localized unicode string for selected id """
+ if id >= 30000 and id <= 30899: return
self.getLocalizedString(id).encode("utf8")
+ elif id >= 32800 and id <= 32999: return
self._scriptData.getLocalizedString(id).encode("utf8")
+ else: return self.xbmc.getLocalizedString(id).encode("utf8")
- def getAddonSetting(self, id, key):
- """ Return setting for selected addon """
- try: addonData = self._addonObj(id)
- except: return u""
- else: return addonData.getSetting(key)
+ def getLocalPath(self):
+ return self.getAddonInfo("path")
- def getQuality(self):
- """ Return unicode for quality setting """
- return self._addonData.getSetting("quality")
+ def getGlobalPath(self):
+ return self._scriptData.getAddonInfo("path").decode("utf8")
- def getSetting(self, id):
- """ Return unicode for setting """
- return self._addonData.getSetting(id)
+ def getAddonSetting(self, id, key):
+ """ Return setting for selected addon """
+ return self.xbmcaddon.Addon(id).getSetting(key)
def getSettingInt(self, id):
""" Return Integer for settings """
- return int(self._addonData.getSetting(id))
+ return int(self.getSetting(id))
def getSettingBool(self, id):
""" Return boolean for setting """
- return self._addonData.getSetting(id) == u"true"
+ return self.getSetting(id) == u"true"
+
+ def getIcon(self):
+ """ Return unicode path to addon icon """
+ return self.translatePath(self.getAddonInfo("icon"))
def translatePath(self, path):
""" Return translated special paths as unicode """
return self.xbmc.translatePath(path).decode("utf8")
- def getId(self):
- """ Return addon id """
- return self._addonID.decode("utf8")
-
- def getAuthor(self):
- """ Return author for current addon """
- return self._addonData.getAddonInfo("author").decode("utf8")
-
- def getChangelog(self):
- """ Return path to changelog for addon """
- return self._addonData.getAddonInfo("changelog").decode("utf8")
-
- def getDescription(self):
- """ Return full description for addon """
- return
self._addonData.getAddonInfo("description").decode("utf8")
-
- def getDisclaimer(self):
- """ Return the disclamimer for current addon if any """
- return self._addonData.getAddonInfo("disclaimer").decode("utf8")
-
- def getName(self):
- """ Return name of current addon """
- if self._addonName: return self._addonName
- else:
- self._addonName =
self._addonData.getAddonInfo("name").decode("utf8")
- return self._addonName
-
- def getStars(self):
- """ Return Start rating for addon """
- return self._addonData.getAddonInfo("stars").decode("utf8")
-
- def getSummary(self):
- """ Return description summary for addon """
- return self._addonData.getAddonInfo("summary").decode("utf8")
-
- def getType(self):
- """ Return type of addon """
- return self._addonData.getAddonInfo("type").decode("utf8")
-
- def getVersion(self):
- """ Return current addon version """
- return self._addonData.getAddonInfo("version").decode("utf8")
-
- def getTempPath(self):
- """ Return unicode path to temp folder """
- return self.translatePath("special://home/temp/")
-
def getProfile(self):
""" Returns full unicode path to the addons saved data location
"""
if self._profile: return self._profile
else:
- self._profile =
self.translatePath(self._addonData.getAddonInfo("profile"))
- if not self.os.path.exists(self._profile):
self.os.makedirs(self._profile)
- return self._profile
-
- def getLibPath(self):
- """ Returns full unicode path to the plugin library location """
- return self.os.path.join(self._addonData.getAddonInfo("path"),
"resources", "lib").decode("utf8")
-
- def getIcon(self):
- """ Return unicode path to addon icon """
- return self.translatePath(self._addonData.getAddonInfo("icon"))
-
- def getFanartImage(self):
- """ Return unicode path of addon fanart """
- return
self.translatePath(self._addonData.getAddonInfo("fanart"))
-
- def getImageLocation(self, local=True):
- """ Return unicode path to local or globale image location """
- return self.os.path.join(self.getPath(local), "resources",
"media", "%s")
-
- def getPath(self, local=True):
- """ Returns full unicode path to the plugin location """
- if local: return
self._addonData.getAddonInfo("path").decode("utf8")
- else: return
self._scriptData.getAddonInfo("path").decode("utf8")
+ self._profile = _profile =
self.translatePath(self.getAddonInfo("profile"))
+ if not self.os.path.exists(_profile):
self.os.makedirs(_profile)
+ return _profile
class Dialog(object):
- import xbmcgui
- xbmcgui.INPUT_ALPHANUM = 0
- xbmcgui.INPUT_NUMERIC = 1
- xbmcgui.INPUT_DATE = 2
- xbmcgui.INPUT_TIME = 3
- xbmcgui.INPUT_IPADDRESS = 4
- xbmcgui.INPUT_PASSWORD = 5
- xbmcgui.PASSWORD_VERIFY = 1
- xbmcgui.ALPHANUM_HIDE_INPUT = 2
-
- def dialogYesNo(self, heading, line1, line2="", line3="", nolabel="",
yeslabel=""):
- """
- Returns True if 'Yes' was pressed, else False.
-
- heading : string or unicode - dialog heading.
- line1 : string or unicode - line #1 text.
- line2 : [opt] string or unicode - line #2
text.
- line3 : [opt] string or unicode - line #3
text.
- nolabel : [opt] label to put on the no button.
- yeslabel : [opt] label to put on the yes button.
- """
- dialogbox = self.xbmcgui.Dialog()
- return dialogbox.yesno(heading, line1, line2, line3, nolabel,
yeslabel)
-
- def dialogOK(self, heading, line1, line2="", line3=""):
- """
- Returns True if 'Ok' was pressed, else False.
-
- heading : string or unicode - dialog heading.
- line1 : string or unicode - line #1 text.
- line2 : [opt] string or unicode - line #2
text.
- line3 : [opt] string or unicode - line #3
text.
- """
- dialogbox = self.xbmcgui.Dialog()
- return dialogbox.ok(heading, line1, line2, line3)
-
def dialogSelect(self, heading, list, autoclose=0):
- """
- Returns the position of the highlighted item as an
integer.
-
- heading : string or unicode - dialog heading.
- list : string list - list of items.
- autoclose : [opt] integer - milliseconds to
autoclose dialog. (default=do not autoclose)
- """
+ """ Returns the position of the highlighted item as an integer.
"""
dialogbox = self.xbmcgui.Dialog()
return dialogbox.select(heading, list, autoclose)
def dialogNumeric(self, type, heading, default=""):
- """
- Returns the entered data as a unicode string
-
- type : integer - the type of numeric dialog.
- heading : string or unicode - dialog heading.
- default : [opt] string - default value.
-
- Types:
- - 0 : ShowAndGetNumber (default format: #)
- - 1 : ShowAndGetDate (default format: DD/MM/YYYY)
- - 2 : ShowAndGetTime (default format: HH:MM)
- - 3 : ShowAndGetIPAddress (default format: #.#.#.#)
- """
+ """ Returns the entered data as a unicode string """
dialogbox = self.xbmcgui.Dialog()
return dialogbox.numeric(type, heading, default).decode("utf8")
- def dialogInput(self, heading, default="", type=0, option=0,
autoclose=0):
- """
- Returns the entered data as a unicode string
-
- heading : string - dialog heading.
- default : [opt] string - default value.
(default=empty string)
- type : [opt] integer - the type of keyboard
dialog. (default=xbmcgui.INPUT_ALPHANUM)
- option : [opt] integer - option for the
dialog. (see Options below)
- autoclose : [opt] integer - milliseconds to
autoclose dialog. (default=do not autoclose)
-
- Types:
- 0 - xbmcgui.INPUT_ALPHANUM (standard keyboard)
- 1 - xbmcgui.INPUT_NUMERIC (format: #)
- 2 - xbmcgui.INPUT_DATE (format: DD/MM/YYYY)
- 3 - xbmcgui.INPUT_TIME (format: HH:MM)
- 4 - xbmcgui.INPUT_IPADDRESS (format: #.#.#.#)
- 5 - xbmcgui.INPUT_PASSWORD (return md5 hash of input,
input is masked)
- """
- if type == 0: return self.keyBoard(default, heading, option==2)
- elif type >= 1 and type <= 4: return self.dialogNumeric(type-1,
heading, default)
- elif type == 5:
- ret = self.keyBoard(default, heading, True)
- if ret:
- from hashlib import md5
- hash = md5(ret).hexdigest().decode("utf8")
- if option == 1: return default == hash
- else: return hash
- else: return None
- else: raise ValueError("dialogInput argument type is out of
bounds")
-
- def dialogBrowse(self, type, heading, shares, mask="", useThumbs=False,
treatAsFolder=False, default="", enableMultiple=False):
- """
- Returns filename and/or path as a unicode string to the
location of the highlighted item
-
- type : integer - the type of browse dialog.
- heading : string or unicode - dialog heading.
- shares : string or unicode - from sources.xml.
(i.e. 'myprograms')
- mask : [opt] string or unicode - '|'
separated file mask. (i.e. '.jpg|.png')
- useThumbs : [opt] boolean - if True autoswitch to
Thumb view if files exist.
- treatAsFolder : [opt] boolean - if True playlists and
archives act as folders.
- default : [opt] string - default path or file.
- """
- dialogbox = self.xbmcgui.Dialog()
- return dialogbox.browse(type, heading, shares, mask, useThumbs,
treatAsFolder, default, enableMultiple)
-
def browseMultiple(self, type, heading, shares, mask="",
useThumbs=False, treatAsFolder=False, default=""):
""" Returns tuple of marked filenames as a unicode string """
- return tuple([filename.decode("utf8") for filename in
self.dialogBrowse(type, heading, shares, mask, useThumbs, treatAsFolder,
default, True)])
+ dialogbox = self.xbmcgui.Dialog()
+ return tuple([filename.decode("utf8") for filename in
dialogbox.browse(type, heading, shares, mask, useThumbs, treatAsFolder,
default, True)])
def browseSingle(self, type, heading, shares, mask="", useThumbs=False,
treatAsFolder=False, default=""):
""" Returns filename and/or path as a unicode string to the
location of the highlighted item """
- return self.dialogBrowse(type, heading, shares, mask,
useThumbs, treatAsFolder, default, False).decode("utf8")
+ dialogbox = self.xbmcgui.Dialog()
+ return dialogbox.browse(type, heading, shares, mask, useThumbs,
treatAsFolder, default, False).decode("utf8")
def keyBoard(self, default="", heading="", hidden=False):
- """
- Return User input as a unicode string
-
- default : default text entry.
- heading : keyboard heading.
- hidden : True for hidden text entry.
- """
+ """ Return User input as a unicode string """
kb = self.xbmc.Keyboard(default, heading, hidden)
kb.doModal()
if kb.isConfirmed() and kb.getText(): return
kb.getText().decode("utf8")
else: return None
- def dialogSearch(self, urlString=""):
- # Open KeyBoard Dialog
- ret = self.keyBoard("", self.getstr(16017), False)
-
- # Check if User Entered Any Data
+ def dialogSearch(self, urlString=None):
+ """ Open KeyBoard Dialog and return input with urlString """
+ ret = self.keyBoard("", self.getstr(16017), False) # 16017 =
Enter Search String
if ret and urlString: return urlString % ret
elif ret: return ret
- else: raise plugin.URLError(0, "User Cannceled The Search
KeyBoard Dialog")
+ else:
+ self.debug("User Cannceled The Search KeyBoard Dialog")
+ raise plugin.URLError(None)
- def setNotification(self, heading, message, icon="error", time=5000,
sound=True):
- """
- heading : string - dialog heading.
- message : string - dialog message.
- icon : [opt] string - icon to use.
- time : [opt] integer - time in milliseconds (default
5000)
- sound : [opt] bool - play notification sound (default
True)
- """
-
- # Check if Errors are Suppressed
- if icon == "error":
- if self._suppressErrors == True: return
- else: self._suppressErrors = True
-
- # Fetch Localized String if Needed
+ def sendNotification(self, heading, message, icon="info", time=5000,
sound=True):
+ """ Send a notification to xbmc to be displayed """
if isinstance(heading, int): heading = self.getstr(heading)
if isinstance(message, int): message = self.getstr(message)
- # Send Error Messisg to Display
- #box = self.dialogBox
- #box.notification(heading, message, icon, time, sound)
-
# Send Error Message to Display
exeString = "xbmc.Notification(%s,%s,%i)" % (heading, message,
time)
self.executebuiltin(exeString)
# Class For Fetching plugin Information
class Info(Addon, Dialog):
- import xbmcplugin, xbmc, urllib, sys, os
+ import xbmcplugin, xbmcgui, xbmc, urllib, sys, os
_suppressErrors = False
_traceback = None
_xbmcvfs = None
+ _devmode = True
+
+ # Fetch system elements
+ handleZero = sys.argv[0]
+ handleOne = int(sys.argv[1])
+ handleTwo = sys.argv[2]
+ addonID = handleZero[9:-1]
+ xbmc.log("#### %s ####" % addonID, 2)
class Error(Exception):
- exceptionName = 32909 # UnexpectedError
- def __init__(self, errorCode=0, errorMsg=""):
- if errorMsg: plugin.setDebugMsg(self.exceptionName,
errorMsg)
- self.errorCode = errorCode
+ exceptionName = 32851 # UnexpectedError
+ def __init__(self, errorMsg="", debugMsg=""):
self.errorMsg = errorMsg
-
- def __str__(self): return self.errorMsg
- def __int__(self): return self.errorCode
-
- class ScraperError(Error): exceptionName = 32915 # ScraperError
- class URLError(Error): exceptionName = 32916 # URLError
- class CacheError(Error): exceptionName = 32917 # CacheError
- class ParserError(Error): exceptionName = 32918 # ParserError
- class YoutubeAPI(Error): exceptionName = 32919 # YoutubeAPI
- class videoResolver(Error): exceptionName = 32920 # videoResolver
+ self.debugMsg = debugMsg
+
+ class URLError(Error): exceptionName = 32807 # URLError
+ class ScraperError(Error): exceptionName = 32824 # ScraperError
+ class CacheError(Error): exceptionName = 32808 # CacheError
+ class ParserError(Error): exceptionName = 32821 # ParserError
+ class YoutubeAPI(Error): exceptionName = 32822 # YoutubeError
+ class videoResolver(Error): exceptionName = 32823 # ResolverError
def error_handler(cls, function):
# Wrapper for Scraper Function
def wrapper(*arguments, **keywords):
try: response = function(*arguments, **keywords)
+ except (UnicodeEncodeError, UnicodeDecodeError) as e:
+ cls.sendNotification(32852, e.reason,
icon="error") # 32852 = Unicode Error
+ cls.severe("A Severe Unicode Encode/Decode
Error was raise, unable to continue", traceback=True)
+ cls._suppressErrors = True
+
+ except ImportError as e:
+ cls.sendNotification(32851, e.message,
icon="error") # 32852 = Unexpected Error
+ cls.severe("An unexpected python exception was
raised, unable to continue", traceback=True)
+ cls._suppressErrors = True
+
except cls.Error as e:
- if e.errorCode:
cls.setNotification(e.exceptionName, e.errorCode, icon="error")
- cls.printTraceback()
- return False
- except (UnicodeEncodeError, UnicodeDecodeError):
- cls.setNotification(32909, 32921, icon="error")
- cls.printTraceback()
- return False
+ exceptionName = cls.getstr(e.exceptionName)
+ if e.debugMsg: cls.severe("%s:%s" %
(exceptionName, e.debugMsg))
+ if e.errorMsg:
+ cls.sendNotification(exceptionName,
e.errorMsg, icon="error")
+ cls.severe("%s:%s" % (exceptionName,
e.errorMsg))
+ cls._suppressErrors = True
+
+ # print TraceBack to xbmc log
+ cls.severe("Unrecoverable Error",
traceback=True)
+
except:
- cls.setNotification(32909, 32974, icon="error")
- cls.printTraceback()
- return False
+ cls.sendNotification(32851, 32853, icon="error")
+ cls.severe("A Severe Unhandled Error was raise,
unable to continue", traceback=True)
+ cls._suppressErrors = True
+
else:
if response: return response
else:
- cls.setNotification(cls.getName(),
33077, icon="error")
- return False
+
cls.sendNotification(cls.getAddonInfo("name"), 33077, icon="error")
+ cls.debug("No Video information was
found")
# Return Wrapper
return wrapper
@@ -394,41 +239,37 @@ class Info(Addon, Dialog):
def xbmcvfs(self):
if self._xbmcvfs: return self._xbmcvfs
else:
- self._xbmcvfs = __import__("xbmcvfs")
- return self._xbmcvfs
+ self._xbmcvfs = xbmcvfs = __import__("xbmcvfs")
+ return xbmcvfs
@property
def traceback(self):
if self._traceback: return self._traceback
else:
- self._traceback = __import__("traceback")
- return self._traceback
+ self._traceback = traceback = __import__("traceback")
+ return traceback
def __init__(self):
- # Fetch system elements
- self.handleZero = self.sys.argv[0]
- self.handleOne = int(self.sys.argv[1])
- self.handleTwo = self.sys.argv[2]
-
- # Create Plugin Handle Three
- if self.handleTwo: self.handleThree = "%s%s&" %
(self.handleZero, self.handleTwo.replace("refresh","_"))
- else: self.handleThree = "%s?" %
self.handleZero.replace("refresh","_")
-
- # Fetch Dict of Params
- if self.handleTwo: self._Params =
self.get_params(self.handleTwo)
- else: self._Params = {}
+ # Check for Quary handle
+ if self.handleTwo:
+ # Fetch Dict of Params
+ self._Params = self.parse_qs(self.handleTwo)
+ # Fetch list of actions
+ self.actions =
self._Params.get("action",u"Initialize").split(".")
+ # Create Plugin Handle No Three
+ self.handleThree = "%s%s&" % (self.handleZero,
self.handleTwo.replace("refresh","_"))
+ # Log values for debug
+ self.debug(self.handleTwo)
+ else:
+ # Create empty params
+ self._Params = {}
+ # Create Initialize action
+ self.actions = [u"Initialize"]
+ # Create Plugin Handle Three
+ self.handleThree = "%s?" % self.handleZero
# Initiate Local and Global xbmcAddon Objects
- self.setAddonIDs(self.handleZero[9:-1],
"script.module.xbmcutil")
-
- # Fetch list of actions
- self.actions = self.get("action",u"Initialize").split(".")
-
- # Set addon library path
- self.sys.path.append(self.getLibPath())
-
- # Display Current ID in XBMC Log
- self.log("### %s ###" % self._addonID)
+
self.sys.path.append(self.os.path.join(self.getAddonInfo("path"), u"resources",
u"lib"))
def getSelectedViewID(self, content):
""" Returns selected View Mode setting if available """
@@ -440,8 +281,7 @@ class Info(Addon, Dialog):
contentViewMode = self.getSettingInt(content)
if contentViewMode == 0: return None
elif contentViewMode == 3:
- customView = self.getSetting("%scustom" %
content)
- try: return int(customView)
+ try: return int(self.getSetting("%scustom" %
content))
except: return None
else:
# Create Table to Sky IDs
@@ -449,15 +289,12 @@ class Info(Addon, Dialog):
'skin.aeonmq5':
{'files': {1:59, 2:56}, 'episodes': {1:59, 2:64}},
'skin.aeon.nox':
{'files': {1:52, 2:500}, 'episodes': {1:518, 2:500}},
'skin.amber':
{'files': {1:None, 2:53}, 'episodes': {1:52, 2:53}},
- #'skin.back-row':
{'files': {1:None, 2:None}, 'episodes': {1:None, 2:None}},
'skin.bello':
{'files': {1:50, 2:56}, 'episodes': {1:50, 2:561}},
'skin.carmichael':
{'files': {1:50, 2:51}, 'episodes': {1:50, 2:56}},
'skin.confluence':
{'files': {1:51, 2:500}, 'episodes': {1:51, 2:500}},
- #'skin.diffuse':
{'files': {1:None, 2:None}, 'episodes': {1:None, 2:None}},
'skin.droid':
{'files': {1:50, 2:55}, 'episodes': {1:50, 2:51}},
'skin.hybrid':
{'files': {1:50, 2:500}, 'episodes': {1:50, 2:500}},
'skin.metropolis':
{'files': {1:503, 2:None}, 'episodes': {1:55, 2:59}},
- #'skin.nbox':
{'files': {1:None, 2:None}, 'episodes': {1:None, 2:None}},
'skin.pm3-hd':
{'files': {1:550, 2:53}, 'episodes': {1:550, 2:53}},
'skin.quartz':
{'files': {1:52, 2:None}, 'episodes': {1:52, 2:None}},
'skin.re-touched':
{'files': {1:50, 2:500}, 'episodes': {1:550, 2:500}},
@@ -469,92 +306,52 @@ class Info(Addon, Dialog):
'skin.xtv-saf':
{'files': {1:50, 2:58}, 'episodes': {1:50, 2:58}}}
# Fetch IDs for current skin
- skinID = self.xbmc.getSkinDir()
- if skinID in viewModes: return
viewModes[skinID][content][contentViewMode]
- else: return None
+ try: return
viewModes[self.xbmc.getSkinDir()][content][contentViewMode]
+ except: return None
- def log(self, msg, level=2):
- """
- msg : string - text to output.
- level : [opt] integer - log level to ouput at.
(default=LOGNOTICE)
-
- Text is written to the log for the following conditions.
- 0 - LOGDEBUG
- 1 - LOGINFO
- 2 - LOGNOTICE
- 3 - LOGWARNING
- 4 - LOGERROR
- 5 - LOGSEVERE
- 6 - LOGFATAL
- 7 - LOGNONE
- """
- # Convert Unicode to UTF-8 if needed
- if isinstance(msg, unicode):
- msg = msg.encode("utf8")
-
- # Send message to xbmc log
+ def debug(self, msg): self.log(msg, 0)
+ def info(self, msg): self.log(msg, 1)
+ def notice(self, msg): self.log(msg, 2)
+ def warning(self, msg): self.log(msg, 3)
+ def error(self, msg, traceback=False): self.log(msg, 4, traceback)
+ def severe(self, msg, traceback=False): self.log(msg, 5, traceback)
+ def fatal(self, msg, traceback=False): self.log(msg, 6, traceback)
+ def log(self, msg, level=2, traceback=False):
+ if level < 2 and self._devmode is True: level = 7
+ if isinstance(msg, unicode): msg = msg.encode("utf8")
+ if traceback is True: msg = "%s\n%s" % (msg,
self.traceback.format_exc())
self.xbmc.log(msg, level)
- def setDebugMsg(self, exceptionName="", errorMsg=""):
- """ Recives a *list of mesages to print """
- if isinstance(exceptionName, int): exceptionName =
self.getstr(exceptionName)
- if isinstance(errorMsg, int): errorMsg = self.getstr(errorMsg)
- self.log("%s: %s" % (exceptionName, errorMsg), 0)
-
- def printTraceback(self):
- """ Print Exception Traceback to log """
- self.log(self.traceback.format_exc(), 6)
-
def executebuiltin(self, function):
- """ Exacute XBMC Builtin Fuction """
-
- # Convert Unicode to UTF-8 if needed
- if isinstance(function, unicode):
- function = function.encode("utf8")
-
- # Execute Builtin Function
- self.xbmc.executebuiltin(function)
-
- def executePlugin(self, pluginUrl):
- """ Execute XBMC plugin """
- self.executebuiltin(u"XBMC.RunPlugin(%s)" % pluginUrl)
-
- def executeAddon(self, addonID):
- """ Execute XBMC Addon """
- self.executebuiltin(u"XBMC.RunAddon(%s)" % addonID)
-
- def refreshContainer(self):
- """ Refresh XBMC Container Listing """
- self.xbmc.executebuiltin("Container.Refresh")
-
- def setviewMode(self, viewMode):
- """ Sets XBMC View Mode, Identified by View Mode ID """
- self.xbmc.executebuiltin("Container.SetViewMode(%d)" % viewMode)
+ """ Exacute XBMC Builtin Function """
+ if isinstance(function, unicode):
self.xbmc.executebuiltin(function.encode("utf8"))
+ else: self.xbmc.executebuiltin(function)
def urlencode(self, query):
# Create Sortcuts
quote_plus = self.urllib.quote_plus
- isinstancex = isinstance
- unicodex = unicode
- strx = str
+ isinstancel = isinstance
+ unicodel = unicode
+ strl = str
# Parse dict and return urlEncoded string of key and values
separated by &
- return "&".join([quote_plus(strx(key)) + "=" +
quote_plus(value.encode("utf8")) if isinstancex(value, unicodex) else
quote_plus(strx(key)) + "=" + quote_plus(strx(value)) for key, value in
query.iteritems()])
+ return "&".join([strl(key) + "=" +
quote_plus(value.encode("utf8")) if isinstancel(value, unicodel) else strl(key)
+ "=" + quote_plus(strl(value)) for key, value in query.iteritems()])
- def get_params(self, params):
+ def parse_qs(self, params, asList=False):
# Convert Unicode to UTF-8 if needed
if isinstance(params, unicode):
params = params.encode("utf8")
# Convert urlEncoded String into a dict and unquote
- worker = {}
+ qDict = {}
unquoter = self.urllib.unquote_plus
- for part in params[params[:1].find("?")+1:].split("&"):
- part = unquoter(part)
+ for part in params[params.find("?")+1:].split("&"):
try: key, value = part.split("=",1)
- except: continue
- else: worker[key] = value.decode("utf8")
- return worker
+ except: pass
+ else:
+ if not asList: qDict[key.lower()] =
unquoter(value).decode("utf8")
+ else: qDict[key.lower()] =
[unquoter(segment).decode("utf8") for segment in value.split(",")]
+ return qDict
def update(self, dicts):
self._Params.update(dicts)
@@ -574,20 +371,25 @@ class Info(Addon, Dialog):
def __len__(self):
return len(self._Params)
- def get(self, key, failobj=None):
+ def get(self, key, default=None):
if key in self._Params: return self._Params[key]
- else: return failobj
-
- def popitem(self, key):
- value = self._Params[key]
- del self._Params[key]
- return value
+ else: return default
def setdefault(self, key, failobj=None):
if key in self._Params: return self._Params[key]
else:
self._Params[key] = failobj
return failobj
+
+ def pop(self, key, default=None):
+ if key in self._Params:
+ value = self._Params[key]
+ del self._Params[key]
+ return value
+ elif default:
+ return default
+ else:
+ raise KeyError
# Set plugin ID
plugin = Info()
\ No newline at end of file
diff --git a/script.module.xbmcutil/lib/xbmcutil/listitem.py
b/script.module.xbmcutil/lib/xbmcutil/listitem.py
index fe2b3b2..2756a94 100644
--- a/script.module.xbmcutil/lib/xbmcutil/listitem.py
+++ b/script.module.xbmcutil/lib/xbmcutil/listitem.py
@@ -22,58 +22,10 @@
# Import Python System Modules
import os
import time
-import urllib
import functools
# Import Custom Modules
-from xbmcutil import plugin
-
-class Playlist(plugin.xbmc.PlayList):
- """ Wrapper for XBMC Playlist """
- def __init__(self, playlistType):
- """ Retrieve a reference from a valid xbmc playlist
-
- 0 : xbmc.PLAYLIST_MUSIC
- 1 : xbmc.PLAYLIST_VIDEO
- """
-
- # Initiate Overriding, in obj Classs Method
- super(Playlist, self).__init__()
- self.clear()
-
- def add_iter(self, listitems):
- """ Accepts a iterable of (url, listitem, isfolder) """
- for url, listitem, isfolder in listitems:
- if isfolder is False: self.add(url, listitem)
-
-class DialogProgress(plugin.xbmcgui.DialogProgress):
- def __init__(self, heading, line1, line2="", line3=""):
- # Initiate Overriding, in obj Classs Method
- self.subClass = super(DialogProgress, self)
- self.subClass.__init__()
-
- # Create Progress Dialog
- self.lines = [line1,line2,line3]
- self.create(heading, line1, line2, line3)
- self.update(0)
-
- def updateLine1(self, line):
- self.lines[0] = line
-
- def updateLine2(self, line):
- self.lines[1] = line
-
- def updateLine3(self, line):
- self.lines[2] = line
-
- def update(self, percent, line1=None, line2=None, line3=None):
- # Add updated line if available
- if line1 != None: self.lines[0] = line1
- if line2 != None: self.lines[1] = line2
- if line3 != None: self.lines[2] = line3
-
- # Initeate Overriding, in obj Classs Method
- self.subClass.update(int(percent), *self.lines)
+from xbmcutil import plugin, cleanup
class ListItem(plugin.xbmcgui.ListItem):
"""
@@ -84,20 +36,19 @@ class ListItem(plugin.xbmcgui.ListItem):
_strptime = time.strptime
_strftime = time.strftime
_handleZero = _plugin.handleZero
- _handelThree = _plugin.handleThree
_selfObject = _plugin.xbmcgui.ListItem
_urlencode = _plugin.urlencode
- _addonName = _plugin.getName()
- _fanartImage = _plugin.getFanartImage()
- _imageGlobal = _plugin.getImageLocation(local=False)
- _imageLocal = _plugin.getImageLocation(local=True)
- _stringDownload = _plugin.getuni(33003)
- _strRelated = _plugin.getuni(32966)
- _staticMenu = ([(_plugin.getuni(20159), "XBMC.Action(Info)"),
- (_plugin.getuni(13347),
"XBMC.Action(Queue)"),
- (_plugin.getuni(184),
"XBMC.Container.Update(%srefresh=true)" % _handelThree)],
- [(_plugin.getuni(1045),
"XBMC.RunPlugin(%s?action=system.opensettings)" % _handleZero),
- (_plugin.getuni(184),
"XBMC.Container.Update(%srefresh=true)" % _handelThree)])
+ _addonName = _plugin.getAddonInfo("name")
+ _fanartImage = _plugin.translatePath(_plugin.getAddonInfo("fanart"))
+ _imageGlobal = os.path.join(_plugin.getGlobalPath(), "resources",
"media", "%s")
+ _imageLocal = os.path.join(_plugin.getLocalPath(), "resources",
"media", "%s")
+ _strRelated = _plugin.getuni(32904) # 32904 = Related Videos
+ _folderMenu = [("$LOCALIZE[1045]",
"XBMC.RunPlugin(%s?action=system.opensettings)" % _handleZero), # 1045 = Add-on
Settings
+ ("$LOCALIZE[184]",
"XBMC.Container.Update(%srefresh=true)" % _plugin.handleThree)] # 184 = Refresh
+ _videoMenu = [("$LOCALIZE[20159]", "XBMC.Action(Info)"), # 20159 =
Video Information
+ ("$LOCALIZE[13347]", "XBMC.Action(Queue)"),
# 13347 = Queue Item
+ ("$LOCALIZE[13350]",
"XBMC.ActivateWindow(videoplaylist)"), # 13350 = Now Playing...
+ ("$LOCALIZE[184]",
"XBMC.Container.Update(%srefresh=true)" % _plugin.handleThree)] # 184 = Refresh
def __init__(self):
""" Initialize XBMC ListItem Object """
@@ -107,20 +58,16 @@ class ListItem(plugin.xbmcgui.ListItem):
# Set class wide variables
self.infoLabels = {"studio":self._addonName}
- self.contextMenu = []
self.urlParams = {}
self.streamInfo = {}
- self.isFolder = True
- self.isIconSet = False
- self.isFanartSet = False
- self.isPlayableSet = False
+ self.contextMenu = []
+
+ # Pre Define Vars
+ self.icon = None
+ self.fanartImg = None
def setLabel(self, label):
- """ Sets the listitem's label
-
- label: string or unicode - text string
- """
- self.urlParams["title"] = label.encode("ascii", "ignore")
+ """ Sets the listitem's label """
self.infoLabels["title"] = label
self._selfObject.setLabel(self, label)
@@ -128,20 +75,19 @@ class ListItem(plugin.xbmcgui.ListItem):
""" Returns the listitem label as a unicode string"""
return self._selfObject.getLabel(self).decode("utf8")
- def setIconImage(self, icon=None):
- """ Sets ListItem's Icon Image
-
- icon: string -
(DefaultFolder.png/DefaultVideo.png/DefaultVideoPlaylists.png)
- """
- if icon is None: icon =
("DefaultVideo.png","DefaultFolder.png")[self.isFolder]
- self._selfObject.setIconImage(self, icon)
- self.isIconSet = True
+ def setIconImage(self, icon):
+ """ Sets ListItem's Icon Image """
+ self.icon = icon
+
+ def setFanartImage(self, fanart):
+ """ Sets ListItem's Fanart Image """
+ self.fanartImg = fanart
def setThumbnailImage(self, image, local=0):
""" Sets ListItem's Thumbnail Image
image: string - Path to thumbnail image, (local or
remote)
- local: integer - (0/1/2) - Changes image path to point
to (Remote/Local/Global) Filesystem
+ local: integer - (0/1/2) - Changes image path to point
to (Remote/Local/Global)
"""
if local is 0: self._selfObject.setThumbnailImage(self, image)
elif local is 1: self._selfObject.setThumbnailImage(self,
self._imageLocal % image)
@@ -162,20 +108,17 @@ class ListItem(plugin.xbmcgui.ListItem):
if key and value: self.streamInfo[key] = value
if kwargs: self.streamInfo.update(kwargs)
- def getInfoItem(self, key, fbObject=None):
- """ Return specifiyed Key form infolabels """
- if key in self.infoLabels: return self.infoLabels[key]
- else: return fbObject
-
- def getParamItem(self, key, fbObject=None):
- """ Return specifiyed key from urlparams """
- if key in self.urlParams: return self.urlParams[key]
- else: return fbObject
-
- def getStreamItem(self, key, fbObject=None):
- """ Return specifiyed key from streamInfo """
- if key in self.streamInfo: return self.streamInfo[key]
- else: return fbObject
+ def setDateInfo(self, date, dateFormat):
+ """ Sets Date Info Label
+
+ date: string - Date of list item
+ dateFormat: string - Format of date string for strptime
conversion
+ """
+ convertedDate = self._strptime(date, dateFormat)
+ self.infoLabels["date"] = self._strftime("%d.%m.%Y",
convertedDate)
+ self.infoLabels["aired"] = self._strftime("%Y-%m-%d",
convertedDate)
+ self.infoLabels["year"] = self._strftime("%Y", convertedDate)
+ self.infoLabels["dateadded"] = self._strftime("%Y-%m-%d
%H-%M-%S", convertedDate)
def setDurationInfo(self, duration):
""" Sets Date duration Label """
@@ -198,30 +141,9 @@ class ListItem(plugin.xbmcgui.ListItem):
# Set Duration
self.streamInfo["duration"] = duration
- def setResumePoint(self, startPoint, totalTime=None):
- """ Set Resume Pont for xbmc to start playing video """
- self.setProperty("TotalTime", totalTime or
str(self.streamInfo.get("duration","1")))
- self.setProperty("ResumeTime", startPoint)
-
- def setDateInfo(self, date, dateFormat):
- """ Sets Date Info Label
-
- date: string - Date of list item
- dateFormat: string - Format of date string for strptime
conversion
- """
- convertedDate = self._strptime(date, dateFormat)
- self.infoLabels["date"] = self._strftime("%d.%m.%Y",
convertedDate)
- self.infoLabels["aired"] = self._strftime("%Y-%m-%d",
convertedDate)
- self.infoLabels["year"] = self._strftime("%Y", convertedDate)
-
- def setFanartImage(self, fanart=None):
- """ Sets ListItem's Fanart Image
-
- fanart: string - Path to fanart image, if not set
defaults to fanart of addon
- """
- if fanart is None: fanart = self._fanartImage
- self.setProperty("Fanart_Image", fanart)
- self.isFanartSet = True
+ def setAudioInfo(self, codec="aac", language="en", channels=2):
+ """ Set Default Audio Info """
+ self.addStreamInfo("audio", {"codec":codec,
"language":language, "channels":channels})
def setQualityIcon(self, HD=False):
""" Enable Listitem's HD|SD Overlay Iron """
@@ -234,9 +156,10 @@ class ListItem(plugin.xbmcgui.ListItem):
self.streamInfo["width"] = 768
self.streamInfo["height"] = 576
- def setAudioInfo(self, codec="aac", language="en", channels=2):
- """ Set Default Audio Info """
- self.addStreamInfo("audio", {"codec":codec,
"language":language, "channels":channels})
+ def setResumePoint(self, startPoint, totalTime=None):
+ """ Set Resume Pont for xbmc to start playing video """
+ self.setProperty("totaltime", totalTime or
str(self.streamInfo.get("duration","1")))
+ self.setProperty("resumetime", startPoint)
def addRelatedContext(self, **params):
""" Adds a context menu item to link to related videos """
@@ -254,140 +177,155 @@ class ListItem(plugin.xbmcgui.ListItem):
if params: command += "(%s?%s)" % (self._handleZero,
self._urlencode(params))
self.contextMenu.append((label, command))
- def setIsPlayable(self, isPlayable=False):
- """ Sets the listitem's playable flag """
- self.isPlayableSet = True
- self.isFolder = not isPlayable
- self.setProperty("IsPlayable", str(isPlayable).lower())
- self.setProperty(("Folder","Video")[isPlayable], "true")
-
def setIdentifier(self, identifier):
""" Sets Unique Identifier for Watched Flags """
self.urlParams["identifier"] = identifier
- def getPath(self):
- """ Returns urlParams as a string """
- return self._handleZero + "?" + self._urlencode(self.urlParams)
-
- def finalize(function):
- """ Wrapper for get Listitem to finalize creation of listitem
for XBMC
+ def getListitemTuple(self, isPlayable=False):
+ """ Returns a tuple of listitem properties, (path, listitem,
isFolder) """
+ infoLabels = self.infoLabels
+ urlParams = self.urlParams
+
+ # Set XBMC InfoLabels and StreamInfo
+ self.setInfo("video", infoLabels)
+ if self.streamInfo: self.addStreamInfo("video", self.streamInfo)
+
+ # Set Listitem Fanart Image
+ if self.fanartImg: self.setProperty("fanart_image",
self.fanartImg)
+ else: self.setProperty("fanart_image", self._fanartImage)
+
+ if isPlayable is True:
+ # Change XBMC Propertys to mark as Playable
+ self.setProperty("isplayable","true")
+ self.setProperty("video","true")
+
+ # Add title to urlParams for the Download title
+ urlParams["title"] =
infoLabels["title"].encode("ascii","ignore")
+
+ # If not a live video then add Download option in
context menu
+ path = "%s?%s" % (self._handleZero,
self._urlencode(urlParams))
+ if not "live" in urlParams:
self.contextMenu.append(("$LOCALIZE[33003]", "XBMC.RunPlugin(%s&download=true)"
% path))
+
+ # Set XBMC icon image
+ if self.icon: self._selfObject.setIconImage(self,
self.icon)
+ else: self._selfObject.setIconImage(self,
"DefaultVideo.png")
- isPlayable: boolean - (True/False) - Lets XBMC know if
listitem is a playable source - Default=False
- infoType: string - (video/music/pictures) - Lets XBMC
know the type of content been listed - Default="video"
- """
- def wrapped(self, isPlayable=False, infoType="video"):
- # If markers are not set, set sections
- if not self.isPlayableSet:
self.setIsPlayable(isPlayable)
- if not self.isFanartSet: self.setFanartImage()
- if not self.isIconSet: self.setIconImage()
- # Set info, steam into and path where available
- if self.infoLabels: self.setInfo(infoType,
self.infoLabels)
- if self.streamInfo: self.addStreamInfo(infoType,
self.streamInfo)
- self.path = self.getPath()
# Add context menu items
- if not self.isFolder and not "live" in self.urlParams:
self.addContextMenuItem(self._stringDownload,
"XBMC.RunPlugin(%s&download=true)" % (self.path))
- self.addContextMenuItems(self.contextMenu +
self._staticMenu[self.isFolder], replaceItems=not isPlayable)
- # Call Decorated Function ad return it response
- return function(self)
- return wrapped
-
- @finalize
- def getListitemTuple(self):
- """ Returns a tuple of listitem properties, (path, _listitem,
isFolder) """
- return self.path, self, self.isFolder
-
- @finalize
- def getListitem(self):
- """ Returns the wrapped xbmcgui.ListItem """
- return self
+ self.addContextMenuItems(self.contextMenu +
self._videoMenu, replaceItems=False)
+
+ # Return Tuple of url, listitem, isFolder
+ return (path, self, False)
+
+ else:
+ # Change XBMC Propertys to mark as Folder
+ self.setProperty("isplayable","false")
+ self.setProperty("video","true")
+
+ # Set XBMC icon image
+ if self.icon: self._selfObject.setIconImage(self,
self.icon)
+ else: self._selfObject.setIconImage(self,
"DefaultFolder.png")
+
+ # Add context menu items
+ self.addContextMenuItems(self.contextMenu +
self._folderMenu, replaceItems=True)
+
+ # Return Tuple of url, listitem, isFolder
+ return ("%s?%s" % (self._handleZero,
self._urlencode(urlParams)), self, True)
@classmethod
- def add_item(cls, label=None, label2=None, icon=None, thumbnail=None,
url={}, info={}, isPlayable=False, infoType="video"):
+ def add_item(cls, label=None, icon=None, thumbnail=None, url={},
info={}, isPlayable=False):
""" A Listitem constructor for creating a XBMC listitem object
label: string - Title of listitem
- label2: string - Secondary lable of listitem
icon: string - Image for listitem icon
thumbnail: list/tuple - (image/0) Thumbnail Image for
listitem / Image location identifier
url: dict - Dictionary containing url params to control
addon
info: dict - Dictionary containing information about
video
isPlayable: boolean - (True/False) - Lets XBMC know if
listitem is a playable source - Default=False
- infoType: string - (video/music/pictures) - Lets XBMC
know the type of content been listed - Default="video"
"""
listitem = cls()
if label: listitem.setLabel(label)
- if label2: listitem.setLabel2(label2)
- if icon: listitem.setIconImage(icon)
+ if icon: listitem.icon = icon
if thumbnail: listitem.setThumbnailImage(*thumbnail)
if url: listitem.urlParams.update(url)
if info: listitem.infoLabels.update(info)
- return listitem.getListitemTuple(isPlayable, infoType)
+ return listitem.getListitemTuple(isPlayable)
@classmethod
- def add_next_page(cls, url={}, infoType="video"):
+ def add_next_page(cls, url={}):
""" A Listitem constructor for Next Page Item
url: dict - Dictionary containing url params to control
addon
- infoType: string - (video/music/pictures) - Lets XBMC
know the type of content been listed - Default="video"
"""
- nextCount = int(cls._plugin.get("NextPageCount",1)) + 1
+ nextCount = int(cls._plugin.get("nextpagecount",1)) + 1
if not "action" in url and "action" in cls._plugin:
url["action"] = cls._plugin["action"]
- url["NextPageCount"] = nextCount
+ url["nextpagecount"] = nextCount
url["updatelisting"] = "true"
- label = u"%s %i" % (cls._plugin.getuni(33078), nextCount)
- return cls.add_item(label, thumbnail=(u"next.png", 2), url=url,
infoType=infoType)
+ label = u"%s %i" % (cls._plugin.getuni(33078), nextCount) #
33078 = Next Page
+ listitem = cls()
+ listitem.setLabel(label)
+ listitem.setThumbnailImage(u"next.png", 2)
+ listitem.urlParams.update(url)
+ return listitem.getListitemTuple(False)
@classmethod
- def add_search(cls, forwarding, url, label=u"-Search"):
+ def add_search(cls, forwarding, url, label=None):
""" A Listitem constructor to add Saved Search Support to addon
forwarding: string - Addon Action to farward on to
url: string - Base url to combine with search term
label: string - Lable of Listitem
"""
- return cls.add_item(label, thumbnail=(u"search.png", 2),
url={"action":"system.search", "forwarding":forwarding, "url":url})
+ listitem = cls()
+ if label: listitem.setLabel(label)
+ else: listitem.setLabel(u"-%s" % cls._plugin.getuni(137)) # 137
= Search
+ listitem.setThumbnailImage(u"search.png", 2)
+ listitem.urlParams.update({"action":"system.search",
"forwarding":forwarding, "url":url})
+ return listitem.getListitemTuple(False)
@classmethod
- def add_youtube_channel(cls, channelID, label=None, hasPlaylist=False):
+ def add_youtube_channel(cls, channelID, label=None, hasPlaylist=False,
hasHD=None):
""" A Listitem constructor to add a youtube channel to addon
channelID: string - Youtube channel ID to add
label: string - Title of listitem - default (-Youtube
Channel)
hasPlaylist: boolean - True/False if channel ID
contains any playlists - default (False) - (soon to be obsolete)
"""
- if label is None: label = u"-" + cls._plugin.getuni(32963)
- return cls.add_item(label, thumbnail=(u"youtube.png", 2),
url={"action":"system.videohosts.YTChannelUploads", "url":channelID,
"hasPlaylists":unicode(hasPlaylist).lower()})
+ listitem = cls()
+ if label: listitem.setLabel(label)
+ else: listitem.setLabel(u"-" + cls._plugin.getuni(32901)) #
32901 = Youtube Channel
+ listitem.setThumbnailImage(u"youtube.png", 2)
+
listitem.urlParams.update({"action":"system.videohosts.YTChannelUploads",
"url":channelID, "hasplaylists":str(hasPlaylist).lower(),
"hashd":str(hasHD).lower()})
+ return listitem.getListitemTuple(False)
@classmethod
- def add_youtube_playlist(cls, playlistID, label=None):
+ def add_youtube_playlist(cls, playlistID, label=None, hasHD=None):
""" A Listitem constructor to add a youtube playlist to addon
playlistID: string - Youtube playlist ID to add
label: string - Title of listitem - default (-Youtube
Playlist)
"""
- if label is None: label = u"-" + cls._plugin.getuni(32964)
- return cls.add_item(label, icon="DefaultVideoPlaylists.png",
thumbnail=(u"youtubeplaylist.png", 2),
url={"action":"system.videohosts.YTPlaylistVideos", "url":playlistID})
+ listitem = cls()
+ if label: listitem.setLabel(label)
+ else: listitem.setLabel(u"-" + cls._plugin.getuni(32902)) #
32902 = Youtube Playlist
+ listitem.icon = "DefaultVideoPlaylists.png"
+ listitem.setThumbnailImage(u"youtubeplaylist.png", 2)
+
listitem.urlParams.update({"action":"system.videohosts.YTPlaylistVideos",
"url":playlistID, "hashd":str(hasHD).lower()})
+ return listitem.getListitemTuple(False)
@classmethod
- def add_youtube_playlists(cls, channelID, label=None):
+ def add_youtube_playlists(cls, channelID, label=None, hasHD=None):
""" A Listitem constructor to add a youtube playlist to addon
channelID: string - Youtube channel ID to list
playlists from
label: string - Title of listitem - default (-Youtube
Playlist)
"""
- if label is None: label = u"-" + cls._plugin.getuni(32965)
- return cls.add_item(label, icon="DefaultVideoPlaylists.png",
thumbnail=(u"youtubeplaylist.png", 2),
url={"action":"system.videohosts.YTChannelPlaylists", "url":channelID})
-
- @classmethod
- def add_vimeo_user(cls, channelID, label=None):
- """ A Listitem constructor to add a youtube channel to addon
-
- channelID: string - Youtube channel ID to add
- label: string - Title of listitem - default (-Youtube
Channel)
- hasPlaylist: boolean - True/False if channel ID
contains any playlists - default (False) - (soon to be obsolete)
- """
- if label is None: label = u"-" + cls._plugin.getuni(32967)
- return cls.add_item(label, thumbnail=(u"vimeo.png", 2),
url={"action":"system.videohosts.VUserVideos", "url":channelID})
+ listitem = cls()
+ if label: listitem.setLabel(label)
+ else: listitem.setLabel(u"-" + cls._plugin.getuni(32903)) #
32903 = Youtube Playlists
+ listitem.icon = "DefaultVideoPlaylists.png"
+ listitem.setThumbnailImage(u"youtubeplaylist.png", 2)
+
listitem.urlParams.update({"action":"system.videohosts.YTChannelPlaylists",
"url":channelID, "hashd":str(hasHD).lower()})
+ return listitem.getListitemTuple(False)
class VirtualFS(object):
""" Wrapper for XBMC Virtual Directory Listings """
@@ -413,30 +351,33 @@ class VirtualFS(object):
self.cacheToDisc = True
# Start Scraper Script
+ self.extraItems = []
self.add_item = self.item_add(self._listitem.add_item)
self.add_next_page = self.item_add(self._listitem.add_next_page)
self.add_search = self.item_add(self._listitem.add_search)
self.add_youtube_channel =
self.item_add(self._listitem.add_youtube_channel)
self.add_youtube_playlist =
self.item_add(self._listitem.add_youtube_playlist)
self.add_youtube_playlists =
self.item_add(self._listitem.add_youtube_playlists)
- self.add_vimeo_user =
self.item_add(self._listitem.add_vimeo_user)
- # If a directory listings exists then add to XBMC and Finalize
- #start = time.time()
+ # Add Listitems to xbmc
listitems = self.scraper()
- #self._plugin.log("Elapsed Time: %s" % (time.time() - start))
- if listitems and listitems is not True:
self.add_dir_items(listitems)
+ extraItems = self.extraItems
+ if isinstance(listitems, list): extraItems.extend(listitems)
+ if extraItems: self.add_dir_items(extraItems)
+
+ # Finalize the script
self.finalize(bool(listitems), self.updateListing,
self.cacheToDisc)
+ self.cleanup()
def item_add(self, function):
# Wrap Listitem classmethods to redirect the output to
add_dir_item
@functools.wraps(function)
- def wrapped(*args, **kwargs): self.add_dir_item(function(*args,
**kwargs))
+ def wrapped(*args, **kwargs):
self.extraItems.append(function(*args, **kwargs))
return wrapped
def add_dir_item(self, listitem):
""" Add Directory List Item to XBMC """
- self._plugin.xbmcplugin.addDirectoryItem(self._handleOne,
*listitem)
+ self.extraItems.append(listitem)
def add_dir_items(self, listitems):
""" Add Directory List Items to XBMC """
@@ -454,19 +395,36 @@ class VirtualFS(object):
def finalize(self, succeeded=True, updateListing=False,
cacheToDisc=False):
""" Make the end of directory listings """
- if succeeded and self.viewID:
self._plugin.setviewMode(self.viewID)
+ if succeeded and self.viewID:
self._plugin.executebuiltin("Container.SetViewMode(%d)" % self.viewID)
self._plugin.xbmcplugin.setPluginFanart(self._handleOne,
self._listitem._fanartImage)
self._plugin.xbmcplugin.endOfDirectory(self._handleOne,
succeeded, updateListing, cacheToDisc)
+
+ def scraper_speed_check(self):
+ try:
+ start = time.time()
+ return self.scraper()
+ finally:
+ self._plugin.info("Elapsed Time: %s" % (time.time() -
start))
+
+ def cleanup(self):
+ currentTime = time.time()
+ try: lastTime = float(plugin.getSetting("lastcleanup")) +
2419200
+ except ValueError: lastTime = 0
+ if lastTime < currentTime:
+ plugin.debug("Initiating Cache Cleanup")
+ import urlhandler
+ try: urlhandler.CachedResponse.cleanup(604800)
+ except: plugin.error("Cache Cleanup Failed")
+ else: plugin.setSetting("lastcleanup", str(currentTime))
class PlayMedia(object):
""" Class to handle the resolving and playing of video url """
_plugin = plugin
- _videoData = _plugin._Params
- _quotePlus = urllib.quote_plus
+ _quotePlus = _plugin.urllib.quote_plus
def __init__(self):
# Fetch Common Vars
- downloadRequested = self._videoData.get("download") == u"true"
- vaildFilename = self.validate_filename(self._videoData["title"])
+ downloadRequested = self._plugin.get("download") == u"true"
+ vaildFilename = self.validate_filename(self._plugin["title"])
downloadPath = self._plugin.getSetting("downloadpath")
# Check if Video has already been downlaoded
@@ -474,7 +432,7 @@ class PlayMedia(object):
else: downloads = None
# Select witch Video Resolver to use
- if downloads: self._videoData["url"] = downloads
+ if downloads: self._plugin["url"] = downloads
elif self.video_resolver() is not True: return None
self.process_video(downloadRequested, downloadPath,
vaildFilename)
@@ -509,10 +467,10 @@ class PlayMedia(object):
# Resolve Video Url using Plugin Resolver
resolvedData = self.resolve()
if resolvedData and isinstance(resolvedData, dict):
- self._videoData.update(resolvedData)
+ self._plugin.update(resolvedData)
return True
elif resolvedData:
- self._videoData["url"] = resolvedData
+ self._plugin["url"] = resolvedData
return True
else:
# Resolve Video Url using Video Hosts sources
@@ -521,22 +479,27 @@ class PlayMedia(object):
elif subaction == u"source":
resolvedData =
self._plugin.error_handler(self.sources)()
if resolvedData and isinstance(resolvedData,
dict):
- self._videoData.update(resolvedData)
+ self._plugin.update(resolvedData)
return True
elif resolvedData:
- self._videoData["url"] = resolvedData
+ self._plugin["url"] = resolvedData
return True
- def sources(self, url=None, urls=None):
+ def sources(self, url=None, urls=None, sourcetype=None):
# Import Video Resolver
import videoResolver
- if url is None: url = self._videoData["url"]
+ if url is None: url = self._plugin["url"]
# Call Specified Source Decoder if Set
- if "sourcetype" in self._videoData:
+ sourcetype = self._plugin.get("sourcetype", sourcetype)
+ if sourcetype and hasattr(videoResolver, sourcetype.lower()):
# Fetch Specified Class
- classObject = getattr(videoResolver,
self._videoData["sourcetype"].lower())()
+ classObject = getattr(videoResolver,
sourcetype.lower())()
return classObject.decode(url)
+ elif sourcetype:
+ # Use urlresolver to fetch video url
+ import urlresolver
+ return {"url": urlresolver.HostedMediaFile(url,
sourcetype.replace(u"_",u".").lower()).resolve()}
else:
# Parse WebPage and Find Video Sources
sources = videoResolver.VideoParser()
@@ -550,15 +513,25 @@ class PlayMedia(object):
except self._plugin.videoResolver: pass
except self._plugin.URLError: pass
+ # Failed to find a playable video using my own parser,
Not trying urlResolver
+ try: import urlresolver
+ except ImportError: pass
+ else:
+ for url in sources.sourceUrls:
+ urlObj =
urlresolver.HostedMediaFile(url)
+ if urlObj:
+ mediaUrl = urlObj.resolve()
+ if mediaUrl: return
{"url":mediaUrl}
+
# Unable to Resolve Video Source
- raise self._plugin.videoResolver(33077, "Was unable to
Find Video Url for: %s" % repr(sources.get()))
+ raise
self._plugin.videoResolver(self._plugin.getstr(33077), "Was unable to Find
Video Url for: %s" % repr(sources.get()))
def process_video(self, downloadRequested, downloadPath, vaildFilename):
# Fetch Video Url / List and Create Listitem
listitemObj = self._plugin.xbmcgui.ListItem
- videoTitle = self._videoData["title"].encode("utf8")
+ videoTitle = self._plugin["title"].encode("utf8")
vaildFilename = vaildFilename.encode("utf8")
- videoUrl = self._videoData["url"]
+ videoUrl = self._plugin["url"]
# Add Each url to a Playlist
isIterable = hasattr(videoUrl, "__iter__")
@@ -583,8 +556,8 @@ class PlayMedia(object):
listitem.setLabel(videoTitle %
count)
# Add Content Type and urlpath to
listitem
- if "type" in self._videoData:
listitem.setMimeType(self._videoData["type"])
- url = self.add_header_pips(url,
self._videoData.get("useragent"), self._videoData.get("referer"))
+ if "type" in self._plugin:
listitem.setMimeType(self._plugin["type"])
+ url = self.add_header_pips(url,
self._plugin.get("useragent"), self._plugin.get("referer"))
listitem.setPath(url)
prepappend((url, listitem, False))
@@ -593,8 +566,10 @@ class PlayMedia(object):
downloader.add_batch_job(prepList)
else:
# Create Playlist and add items
- playlist = Playlist(1)
- playlist.add_iter(prepList)
+ playlist = self._plugin.xbmc.PlayList(1)
+ playlist.clear()
+ for url, listitem, isfolder in prepList:
+ if isfolder is False: playlist.add(url,
listitem)
# Resolve to first element of playlist
self.set_resolved_url(prepList[0][1])
@@ -611,11 +586,11 @@ class PlayMedia(object):
downloader.add_job(videoUrl, vaildFilename,
videoTitle)
else:
# Add Content Type and Header Pips if any
- if "item" in self._videoData: listitem =
self._videoData["item"]
+ if "item" in self._plugin: listitem =
self._plugin["item"]
else: listitem = listitemObj()
- if "type" in self._videoData:
listitem.setMimeType(self._videoData["type"])
- listitem.setPath(self.add_header_pips(videoUrl,
self._videoData.get("useragent"), self._videoData.get("referer")))
+ if "type" in self._plugin:
listitem.setMimeType(self._plugin["type"])
+ listitem.setPath(self.add_header_pips(videoUrl,
self._plugin.get("useragent"), self._plugin.get("referer")))
self.set_resolved_url(listitem)
def add_header_pips(self, url, useragent, referer):
@@ -663,7 +638,7 @@ class DownloadMGR(object):
def get_download_path(self):
""" Asks for Download Path """
- downloadPath = self._plugin.browseSingle(3,
self._plugin.getuni(32933), u"video", u"", False, False, "")
+ downloadPath = self._plugin.browseSingle(3,
self._plugin.getuni(33010), u"video", u"", False, False, "") # 33010 = Set
Download Directory
if downloadPath: self._plugin.setSetting("downloadpath",
downloadPath)
return downloadPath
@@ -676,7 +651,7 @@ class DownloadMGR(object):
def add_job(self, url, filename, title=None):
# Check if url is a Plugin instead of a video Url
- if url.startswith("plugin:"): return
self._plugin.executePlugin(url)
+ if url.startswith("plugin:"): return
self._plugin.executebuiltin("XBMC.RunPlugin(%s)" % url)
elif not self.params["download_path"]: return None
else:
# Guess Full Filename
diff --git a/script.module.xbmcutil/lib/xbmcutil/storageDB.py
b/script.module.xbmcutil/lib/xbmcutil/storageDB.py
index f8f3312..20a126c 100644
--- a/script.module.xbmcutil/lib/xbmcutil/storageDB.py
+++ b/script.module.xbmcutil/lib/xbmcutil/storageDB.py
@@ -93,7 +93,7 @@ class Metadata(dictStorage):
def __init__(self):
# Check if UserData Exists
from xbmcutil import plugin
- systemMetaData = os.path.join(plugin.getPath(), u"resources",
u"metadata.json")
+ systemMetaData = os.path.join(plugin.getLocalPath(),
u"resources", u"metadata.json")
userMetaData = os.path.join(plugin.getProfile(),
u"metadata.json")
if os.path.isfile(systemMetaData) and not
os.path.isfile(userMetaData):
import shutil
@@ -118,7 +118,7 @@ class SavedSearches(listitem.VirtualFS):
# Call Search Dialog if Required
if "remove" in plugin and plugin["remove"] in self.searches:
- self.searches.remove(plugin.popitem("remove"))
+ self.searches.remove(plugin.pop("remove"))
self.searches.sync()
elif "search" in plugin:
self.search_dialog(plugin["url"])
@@ -131,7 +131,7 @@ class SavedSearches(listitem.VirtualFS):
params["search"] = "true"
params["updatelisting"] = "true"
params["cachetodisc"] = "true"
- self.add_item(label=u"-Search", url=params, isPlayable=False)
+ self.add_item(label=u"-%s" % plugin.getuni(137), url=params,
isPlayable=False) # 137 = Search
# Set Content Properties
self.set_sort_methods(self.sort_method_video_title)
@@ -145,11 +145,8 @@ class SavedSearches(listitem.VirtualFS):
self.searches.close()
def search_dialog(self, urlString):
- # Call Search Dialog
- searchTerm = plugin.dialogSearch()
-
# Add searchTerm to database
- self.searches.add(searchTerm)
+ self.searches.add(plugin.dialogSearch())
self.searches.sync()
def list_searches(self):
@@ -163,7 +160,7 @@ class SavedSearches(listitem.VirtualFS):
baseAction = plugin["forwarding"]
# Create Context Menu item Params
- strRemove = plugin.getuni(1210)
+ strRemove = plugin.getuni(1210) # 1210 = Remove
params = plugin._Params.copy()
params["updatelisting"] = "true"
diff --git a/script.module.xbmcutil/lib/xbmcutil/urlhandler.py
b/script.module.xbmcutil/lib/xbmcutil/urlhandler.py
index 8affb42..b24058b 100644
--- a/script.module.xbmcutil/lib/xbmcutil/urlhandler.py
+++ b/script.module.xbmcutil/lib/xbmcutil/urlhandler.py
@@ -29,17 +29,17 @@ from hashlib import md5
#################################################################
-def urlopen(url, maxAge=None, data=None, headers={}, userAgent=1):
+def urlopen(url, maxAge=None, data=None, headers={}, userAgent=1,
stripEntity=False):
''' Makes Request and Return Response Object '''
handle = HttpHandler()
- handle.add_response_handler(userAgent)
+ handle.add_response_handler(userAgent, stripEntity=stripEntity)
if maxAge is not None: handle.add_cache_handler(maxAge)
return handle.open(url, data, headers)
-def urlread(url, maxAge=None, data=None, headers={}, userAgent=1):
+def urlread(url, maxAge=None, data=None, headers={}, userAgent=1,
stripEntity=True):
''' Makes Request and Return Response Data '''
handle = HttpHandler()
- handle.add_response_handler(userAgent, stripEntity=True)
+ handle.add_response_handler(userAgent, stripEntity=stripEntity)
if maxAge is not None: handle.add_cache_handler(maxAge)
resp = handle.open(url, data, headers)
data = resp.read()
@@ -62,12 +62,6 @@ def urlretrieve(url, filename):
#################################################################
-def parse_qs(url):
- # Strip url down to Query String
- if u"#" in url: url = url[:url.find(u"#")]
- if u"?" in url: url = url[url.find(u"?")+1:]
- return dict(part.split(u"=",1) for part in url.split(u"&"))
-
def strip_tags(html):
# Strips out html code and return plan text
sub_start = html.find(u"<")
@@ -78,28 +72,19 @@ def strip_tags(html):
sub_end = html.find(u">")
return html
-def search(urlString=u""):
- # Open KeyBoard Dialog
- ret = plugin.keyBoard("", plugin.getstr(16017), False)
-
- # Check if User Entered Any Data
- if ret and urlString: return urlString % ret
- elif ret: return ret
- else: raise plugin.URLError(0, "User Cannceled The Search KeyBoard
Dialog")
-
def redirect(url, data=None, headers={}):
# Convert url to ascii if needed
if isinstance(url, unicode): url = url.encode("ascii")
# Log for Debuging
- plugin.log(url + " - Redirected To:", 0)
+ plugin.debug(url + " - Redirected To:")
# Split url into Components
splitUrl = urlparse.urlsplit(url)
# Create Connection Object, HTTP or HTTPS
- if splitUrl[0] == "http": conn = httplib.HTTPConnection(splitUrl[1])
- elif splitUrl[0] == "https": conn = httplib.HTTPSConnection(splitUrl[1])
+ if splitUrl[0] == "http": conn = httplib.HTTPConnection(splitUrl[1],
timeout=10)
+ elif splitUrl[0] == "https": conn =
httplib.HTTPSConnection(splitUrl[1], timeout=10)
# Set Request Mothods
if data is not None:
@@ -111,23 +96,28 @@ def redirect(url, data=None, headers={}):
# Make Request to Server
try: conn.request(method, urlparse.urlunsplit(splitUrl), data, headers)
- except: raise plugin.URLError(32910, "Failed to Make Request for
Redirected Url")
+ except httplib.HTTPException as e: raise plugin.URLError(str(e),
"Failed to Make Request for Redirected Url")
# Fetch Headers from Server
try:
resp = conn.getresponse()
- plugin.log("%s - %s" % (resp.status, resp.reason), 0)
+ plugin.debug("%s - %s" % (resp.status, resp.reason))
headers = dict(resp.getheaders())
conn.close()
- except: raise plugin.URLError(32910, "Failed to Read Server Response")
+ except httplib.HTTPException as e: raise plugin.URLError(str(e),
"Failed to Read Redirected Server Response")
# Fetch Redirect Location
if "location" in headers: url = headers["location"]
elif "uri" in headers: url = headers["uri"]
else: url = ""
- plugin.log(url, 0)
+ plugin.debug(url)
return url.decode("ascii")
+class withaddinfourl(urllib2.addinfourl):
+ # Methods to add support for with statement
+ def __enter__(self): return self
+ def __exit__(self, *exc_info): self.close()
+
#################################################################
class HttpHandler:
@@ -142,9 +132,9 @@ class HttpHandler:
''' Adds Response Handler to Urllib to Handle Compression and
unescaping '''
self.handleList.append(ResponseHandler(userAgent, compressed,
stripEntity))
- def add_cache_handler(self, maxAge=0, cacheLocal=u"urlcache",
asUrl=None):
+ def add_cache_handler(self, maxAge=0, asUrl=None):
''' Adds Cache Handler to Urllib to Handle Caching of Source '''
- self.handleList.append(CacheHandler(maxAge, cacheLocal, asUrl))
+ self.handleList.append(CacheHandler(maxAge, asUrl))
def add_authorization(self, username, password):
''' Adds Basic Authentication to Requst '''
@@ -162,13 +152,13 @@ class HttpHandler:
# Make Url Connection, Save Cookie If Set and Return Response
opener = urllib2.build_opener(*self.handleList)
try: return opener.open(request, timeout=timeout)
- except socket.timeout: raise plugin.URLError(32904, "Server
Request Timed Out")
- except urllib2.URLError: raise plugin.URLError(32909, "An
Unexpected UrlError Occurred")
+ except socket.timeout as e: raise plugin.URLError(str(e))
+ except urllib2.URLError as e: raise plugin.URLError(str(e))
class ErrorHandler(urllib2.HTTPDefaultErrorHandler):
''' Default Error Handler for Reporting The Error Code to XBMC '''
def http_error_default(self, req, fp, code, msg, hdrs):
- raise plugin.URLError(msg, "HTTPError %s:%s" % (code, msg))
+ raise plugin.URLError("HTTPError %s:%s" % (code, msg))
class Authorization(urllib2.BaseHandler):
def __init__(self, username, password):
@@ -222,11 +212,11 @@ class HTTPCookieProcessor(urllib2.BaseHandler):
loginPage = self.loginPage
if loginPage and (headers.get("location") == loginPage or
headers.get("uri") == loginPage):
# Check if Logon has already happend within current
session
- if self.logonAtempted is True: raise
plugin.URLError(32914, "Logon Already Atempted, Stopping infinite loop")
+ if self.logonAtempted is True: raise
plugin.URLError(plugin.getstr(32806), "Logon Already Atempted, Stopping
infinite loop")
else: self.logonAtempted = True
# Login to site and create session cookie
- plugin.log("Sending Login Data")
+ plugin.debug("Sending Login Data")
urllogin(**self.loginData)
# Resend Request for Data with new session cookie
@@ -270,7 +260,7 @@ class ResponseHandler(urllib2.BaseHandler):
if self.userAgent and not request.has_header("User-Agent"):
request.add_header("User-Agent", self.userAgent)
if self.compressed: request.add_header("Accept-Encoding",
"gzip, deflate")
request.add_header("Accept-Language", "en-gb,en-us,en")
- plugin.log(request.get_full_url())
+ plugin.notice(request.get_full_url())
return request
def handle_response(self, response):
@@ -286,8 +276,8 @@ class ResponseHandler(urllib2.BaseHandler):
elif contentEncoding and "deflate" in contentEncoding:
data = zlib.decompress(response.read())
else: data = response.read()
- except zlib.error:
- raise plugin.URLError(32912, "Failed to Decompress
Response Body")
+ except zlib.error as e:
+ raise plugin.URLError(plugin.getstr(32804), str(e))
else:
# Convert content to unicode and back to utf-8 to fix
any issues
@@ -298,13 +288,13 @@ class ResponseHandler(urllib2.BaseHandler):
if charset: contentCharset = charset[0]
# Attempt to decode Response to unicode
- if not contentCharset: contentCharset =
"utf-8"; plugin.log("Response encoding not specified, defaulting to UTF-8", 0)
+ if not contentCharset: contentCharset =
"utf-8"; plugin.debug("Response encoding not specified, defaulting to UTF-8")
try: data = unicode(data,
contentCharset.lower())
except:
# Attempt to decode using iso-8859-1
(latin-1)
- plugin.log("Specified encoding failed,
reverting to iso-8859-1 (latin-1)", 0)
+ plugin.debug("Specified encoding
failed, reverting to iso-8859-1 (latin-1)")
try: data = unicode(data, "iso-8859-1")
- except: raise plugin.URLError(32913,
"Unable to Decode response to unicode")
+ except UnicodeDecodeError as e: raise
plugin.URLError(plugin.getstr(32805), str(e))
# Unescape the content if requested
if self.stripEntity: data =
self.unescape(data).encode("utf-8")
@@ -315,7 +305,7 @@ class ResponseHandler(urllib2.BaseHandler):
response.close()
# Return Data Wraped in an addinfourl Object
- addInfo = urllib2.addinfourl(StringIO.StringIO(data), headers,
response.url, response.code)
+ addInfo = withaddinfourl(StringIO.StringIO(data), headers,
response.url, response.code)
addInfo.msg = response.msg
return addInfo
@@ -337,12 +327,11 @@ class ResponseHandler(urllib2.BaseHandler):
except KeyError: return text
# Return Clean string using accepted encoding
- try: return re.sub("&#?\w+;", fixup, text)
- except: raise plugin.URLError(32913, "HTML Entity Decoding
Failed")
+ return re.sub("&#?\w+;", fixup, text)
def http_response(self, request, response):
''' Returns a Decompress Version of the response '''
- plugin.log("%s - %s" % (response.code, response.msg), 0)
+ plugin.debug("%s - %s" % (response.code, response.msg))
if response.code is not 200 or response.info().get("X-Cache")
== "HIT": return response
else: return self.handle_response(response)
@@ -352,10 +341,9 @@ class ResponseHandler(urllib2.BaseHandler):
class CacheHandler(urllib2.BaseHandler):
'''Stores responses in a persistant on-disk cache'''
- def __init__(self, maxAge=0, cacheLocal=u"urlcache", asUrl=None):
+ def __init__(self, maxAge=0, asUrl=None):
global time
import time
- self.cacheLocal = cacheLocal
self.maxAge = maxAge
self.redirect = False
self.url = asUrl
@@ -378,37 +366,50 @@ class CacheHandler(urllib2.BaseHandler):
# Create Cache Path
urlHash = md5(url).hexdigest()
- plugin.log("UrlHash = " + urlHash, 0)
- self.CachePath = os.path.join(plugin.getProfile(),
self.cacheLocal, urlHash + u".%s")
-
- # Check Status of Cache
- if CachedResponse.exists(self.CachePath):
- # If Refresh Param Exists Then Reset
- if "refresh" in plugin:
CachedResponse.reset(self.CachePath, (0,0))
+ plugin.debug("UrlHash = %s" % urlHash)
+ self.CachePath = CachePath =
os.path.join(plugin.getProfile(), "urlcache", urlHash + u".%s")
- # Check if Cache is Valid
- if self.maxAge == -1 or
CachedResponse.isValid(self.CachePath, self.maxAge):
- plugin.log("Cached")
- # Return Cached Response
- return CachedResponse(self.CachePath)
- else:
- plugin.log("Cache Not Valid")
+ # Check Status of Cache
+ if CachedResponse.exists(CachePath):
+ # If Refresh Param Exists Then Reset
+ if "refresh" in plugin:
CachedResponse.reset(CachePath, (0,0))
+ maxAge = self.maxAge
+
+ # Check if Cache is Valid and return Cached
Response if valid
+ if not maxAge == 0:
+ if maxAge == -1 or
CachedResponse.isValid(CachePath, maxAge):
+ plugin.notice("Cached")
+ try: return
CachedResponse(CachePath)
+ except plugin.CacheError as e:
+
CachedResponse.remove(self.CachePath)
+ plugin.error(e.debugMsg)
+ return None
+ else:
+ plugin.notice("Cache Not Valid")
+
# Set If-Modified-Since & If-None-Match Headers
- cacheHeaders =
CachedResponse.loadHeaders(self.CachePath)
+ cacheHeaders =
CachedResponse.loadHeaders(CachePath)
if "Last-Modified" in cacheHeaders:
# Add If-Modified-Since Date to Request
Headers
request.add_header("If-Modified-Since",
cacheHeaders["Last-Modified"])
if "ETag" in cacheHeaders:
# Add If-None-Match Etag to Request
Headers
request.add_header("If-None-Match",
cacheHeaders["ETag"])
+ else:
+ plugin.debug("Cache Not Found")
def http_response(self, request, response):
''' Store Server Response into Cache '''
# Save response to cache and return it if status is 200 else
return response untouched
self.redirect = False
if response.code is 200 and not response.info().get("X-Cache")
== "HIT":
- CachedResponse.store_in_cache(self.CachePath, response)
- return CachedResponse(self.CachePath)
+ try:
+ CachedResponse.store_in_cache(self.CachePath,
response)
+ return CachedResponse(self.CachePath)
+ except plugin.CacheError as e:
+ CachedResponse.remove(self.CachePath)
+ plugin.error(e.debugMsg)
+ return response
elif response.code in (301,302,303,307):
self.redirect = True
return response
@@ -436,6 +437,18 @@ class CachedResponse(StringIO.StringIO):
To determine wheter a response is cached or coming directly from
the network, check the x-cache header rather than the object type.
'''
+ @staticmethod
+ def cleanup(maxAge):
+ # Loop each file within urlcache folder
+ cachePath = os.path.join(plugin.getProfile(), "urlcache")
+ for urlFile in os.listdir(cachePath):
+ # Check if file is a Body file then proceed
+ if urlFile.endswith(".body"):
+ # Fetch urlHash and check if Cache is Stale,
then remove
+ fullPath = os.path.join(cachePath,
urlFile.replace(".body", ".%s"))
+ if not CachedResponse.isValid(fullPath, maxAge):
+ # If file is not valid then Remove
+ CachedResponse.remove(fullPath)
@staticmethod
def exists(cachePath):
@@ -445,19 +458,20 @@ class CachedResponse(StringIO.StringIO):
@staticmethod
def isValid(cachePath, maxAge):
''' Returns True if Cache is Valid, Else Return False '''
- return maxAge and time.time() - os.stat(cachePath %
u"body").st_mtime < maxAge and time.time() - os.stat(cachePath %
u"headers").st_mtime < maxAge
+ return time.time() - os.stat(cachePath % u"body").st_mtime <
maxAge and time.time() - os.stat(cachePath % u"headers").st_mtime < maxAge
@staticmethod
def loadHeaders(cachePath):
''' Returns Only the headers to chack If-Modified-Since &
If-None-Match '''
- try: return
httplib.HTTPMessage(StringIO.StringIO(CachedResponse.readFile(cachePath %
u"headers")))
- except: raise plugin.CacheError(32911, "Loading of Cache
Headers Failed")
+ return
httplib.HTTPMessage(StringIO.StringIO(CachedResponse.readFile(cachePath %
u"headers")))
@staticmethod
def readFile(filename):
''' Return content of file and auto close file '''
- with open(filename, "rb") as fileObject:
- return fileObject.read()
+ try:
+ with open(filename, "rb") as fileObject: return
fileObject.read()
+ except (IOError, OSError) as e:
+ raise plugin.CacheError(plugin.getstr(32803), str(e))
@staticmethod
def reset(cachePath, times=None):
@@ -468,6 +482,7 @@ class CachedResponse(StringIO.StringIO):
@staticmethod
def remove(cachePath):
''' Remove Cache Items '''
+ plugin.debug("Removing Cache item: %s" % cachePath[:-3])
# Remove Headers
try: os.remove(cachePath % u"headers")
except: pass
@@ -488,13 +503,13 @@ class CachedResponse(StringIO.StringIO):
headers["X-Cache"] = "HIT"
headers["X-Location"] = response.url
try: outputFile.write(str(headers))
- except: raise plugin.CacheError(32911, "Failed to Save Headers
to Cache")
+ except (IOError, OSError) as e: raise
plugin.CacheError(plugin.getstr(32803), str(e))
finally: outputFile.close()
# Save Response to Cache
outputFile = open(cachePath % u"body", "wb")
try: outputFile.write(response.read())
- except: raise plugin.CacheError(32911, "Failed to Save Body to
Cache")
+ except (IOError, OSError) as e: raise
plugin.CacheError(plugin.getstr(32803), str(e))
finally: outputFile.close()
# Close Response Connection
@@ -502,8 +517,7 @@ class CachedResponse(StringIO.StringIO):
def __init__(self, cachePath):
# Read in Both Body and Header Responses
- try: StringIO.StringIO.__init__(self, self.readFile(cachePath %
u"body"))
- except: raise plugin.CacheError(32911, "Loading of Cache Body
Failed")
+ StringIO.StringIO.__init__(self, self.readFile(cachePath %
u"body"))
self.headers = self.loadHeaders(cachePath)
# Set Response Codes
@@ -518,3 +532,7 @@ class CachedResponse(StringIO.StringIO):
def geturl(self):
''' Returns original Url '''
return self.url
+
+ # Methods to add support for with statement
+ def __enter__(self): return self
+ def __exit__(self, *exc_info): self.close()
diff --git a/script.module.xbmcutil/lib/xbmcutil/videoResolver.py
b/script.module.xbmcutil/lib/xbmcutil/videoResolver.py
index 382b1c3..4b68e68 100644
--- a/script.module.xbmcutil/lib/xbmcutil/videoResolver.py
+++ b/script.module.xbmcutil/lib/xbmcutil/videoResolver.py
@@ -48,15 +48,15 @@ class MainParser(HTMLParser.HTMLParser):
elif tag == u"embed": self.handle_embed(dict(attrs))
def handle_iframe(self, attrs):
- if u"src" in attrs and attrs[u"src"][:4] == u"http":
+ if u"src" in attrs:# and attrs[u"src"][:4] == u"http":
self.sourceUrls.add(attrs[u"src"])
def handle_param(self, attrs):
- if u"name" in attrs and u"value" in attrs and attrs[u"name"] ==
u"movie" and attrs[u"value"][:4] == u"http":
+ if u"name" in attrs and u"value" in attrs and attrs[u"name"] ==
u"movie":# and attrs[u"value"][:4] == u"http":
self.sourceUrls.add(attrs[u"value"])
def handle_embed(self, attrs):
- if u"src" in attrs and attrs[u"src"][:4] == u"http":
+ if u"src" in attrs:# and attrs[u"src"][:4] == u"http":
self.sourceUrls.add(attrs[u"src"])
class RegexParser:
@@ -87,15 +87,15 @@ class RegexParser:
for attrs in re.findall('<embed(.*?)[/]*>', self.html,
re.DOTALL | re.IGNORECASE): self.handle_embed(self.dict(attrs))
def handle_iframe(self, attrs):
- if u"src" in attrs and attrs[u"src"][:4] == u"http":
+ if u"src" in attrs:# and attrs[u"src"][:4] == u"http":
self.sourceUrls.add(attrs[u"src"])
def handle_param(self, attrs):
- if u"name" in attrs and u"value" in attrs and attrs[u"name"] ==
u"movie" and attrs[u"value"][:4] == u"http":
+ if u"name" in attrs and u"value" in attrs and attrs[u"name"] ==
u"movie":# and attrs[u"value"][:4] == u"http":
self.sourceUrls.add(attrs[u"value"])
def handle_embed(self, attrs):
- if u"src" in attrs and attrs[u"src"][:4] == u"http":
+ if u"src" in attrs:# and attrs[u"src"][:4] == u"http":
self.sourceUrls.add(attrs[u"src"])
class SoupParser:
@@ -145,7 +145,7 @@ class VideoParser:
[{'domain':'videobb.com', 'vodepid':'ZaibwUjYLlxJ',
'function':videobb_com, 'isplaylist':False, 'priority':71},
{'domain':'www.youtube.com', 'vodepid':'st40Gps08KI', 'function':youtube_com,
'isplaylist':False, 'priority':100}]
'''
- def parse(self, arg, maxAge=86400):
+ def parse(self, arg, maxAge=57600):
'''
Takes in a url to a online resource or the HTML to that online
resource and
Passes it into the Custom Parsers to Find Embeded Sources
@@ -159,18 +159,14 @@ class VideoParser:
htmlSource = arg
# Feed the HTML into the HTMLParser and acquire the source urls
- sourceUrls = self.htmlparser(htmlSource)
- if not sourceUrls: raise plugin.ParserError(32973, "No Video
Sources ware found")
+ sourceUrls = list(self.htmlparser(htmlSource))
+ if not sourceUrls: raise
plugin.ParserError(plugin.getstr(32827), "No Video Sources ware found")
else: self.setUrls(sourceUrls)
def setUrls(self, sourceUrls):
# Process Available Sources
- plugin.log("Source url's: " + str(sourceUrls))
- parsedUrls = self.processUrls(sourceUrls)
-
- # Check for Supported Sources
- if not parsedUrls: raise plugin.ParserError(32970, "No
Supported Video Sources ware found")
- else: self.parsedUrls = parsedUrls
+ plugin.debug("Source url's: " + str(sourceUrls))
+ self.parsedUrls = self.processUrls(sourceUrls)
def htmlparser(self, htmlSource):
'''
@@ -183,36 +179,45 @@ class VideoParser:
customParser = MainParser()
customParser.parse(htmlSource)
return customParser.sourceUrls
- except: plugin.log("HTML Parser Failed: Falling Back to Regex",
0)
+ except: plugin.debug("HTML Parser Failed: Falling Back to
Regex")
try:
# Try Parse HTML Using Regex
customParser = RegexParser()
customParser.parse(htmlSource)
return customParser.sourceUrls
- except: plugin.log("Regex Parser Failed: Falling Beautiful
Soup", 0)
+ except: plugin.debug("Regex Parser Failed: Falling Beautiful
Soup")
try:
# Try Parse HTML Using Beautiful Soup
customParser = SoupParser()
customParser.parse(htmlSource)
return customParser.sourceUrls
- except: plugin.log("Beautiful Soup Parser Failed: All Out Total
Failure", 0)
+ except: plugin.debug("Beautiful Soup Parser Failed: All Out
Total Failure")
# Raise ParserError when all three parsers have failded
- raise plugin.ParserError(32972, "HTML Parsing Failed")
+ raise plugin.ParserError(plugin.getstr(32826), "HTML Parsing
Failed")
def processUrls(self, sourceUrls):
'''
Take in a list of Embeded Source Urls and Create a list
Containing
information about the Sources found and the required Decoder.
'''
+ blockList = ("platform.twitter.com", "google.com",
"megavideo.com", "myspace.com")
# Filter Video Sources
parsedUrls = []
+ self.sourceUrls = []
+ appendUrl = self.sourceUrls.append
for url in sourceUrls:
- filtered = self.process(urlparse.urlsplit(url))
- if filtered: parsedUrls.append(filtered)
+ urlParts = urlparse.urlsplit(url)
+ if urlParts[1] in blockList:
+ plugin.debug("Ignoring domain %s" % urlParts[1])
+ continue
+ else:
+ appendUrl(url)
+ filtered = self.process(urlParts)
+ if filtered: parsedUrls.append(filtered)
# Return list of processed Urls
return parsedUrls
@@ -229,7 +234,7 @@ class VideoParser:
return pluginObject().strip(*urlObject)
# Log The UnSupported Video Source for Future Identification
- plugin.log(u"Video Source UnSupported\nDomain = %s\nurl =
%s" % (urlObject[1], urlObject.geturl()), 0)
+ plugin.debug(u"Video Source UnSupported\nDomain = %s\nurl =
%s" % (urlObject[1], urlObject.geturl()))
def get(self, sort=True):
'''
@@ -251,63 +256,26 @@ def check_arg(pluginName):
def wrapper(self, oarg):
arg = oarg
if arg[:4] == u"http": arg =
self.strip(*urlparse.urlsplit(arg), returnID=True)
- if not arg: raise plugin.videoResolver(32918, u"Unable
to Strip out videoID ==> %s ==> %s" % (pluginName, oarg))
+ if not arg: raise
plugin.videoResolver(plugin.getstr(32821), u"Unable to Strip out videoID ==> %s
==> %s" % (pluginName, oarg))
else:
# Call Function and return response
- plugin.setDebugMsg("VideoResolver", u"Select
Source ==> %s ==> %s" % (pluginName, arg))
+ plugin.debug("VideoResolver, Select Source ==>
%s ==> %s" % (pluginName, arg))
return function(self, arg)
return wrapper
return decorator
-def parse_qs(query):
- '''
- Takes in a Query Stirng and Converts to a Dictionary with Lower Case
Keys
- '''
- qDict = {}
- for key,value in urlparse.parse_qsl(query):
- if key in qDict: qDict[key.lower()].append(value)
- else: qDict[key.lower()] = [value]
- return qDict
-
-def jsUnpack(sJavascript):
- aSplit = sJavascript.split(";',")
- p = str(aSplit[0])
- aSplit = aSplit[1].split(",")
- a = int(aSplit[0])
- c = int(aSplit[1])
- k = aSplit[2].split(".")[0].replace("'", '').split('|')
- e = ''
- d = ''
- sUnpacked = str(__unpack(p, a, c, k, e, d))
- return sUnpacked.replace('\\', '')
-
-def __unpack(p, a, c, k, e, d):
- while (c > 1):
- c = c -1
- if (k[c]):
- p = re.sub('\\b' + str(__itoa(c, a)) +'\\b', k[c], p)
- return p
-
-def __itoa(num, radix):
- result = ""
- while num > 0:
- result = "0123456789abcdefghijklmnopqrstuvwxyz"[num % radix] +
result
- num /= radix
- return result
-
#######################################################################################
class youtube_com(object):
'''
- Takes a video id from a Youtube video page url, or takes a complete url
- to a Youtube swf and returns the url to the actual video resource.
- Raises videoResolver Error if no match is found.
+ Takes a full youtube url or video ID and returns the url to the actual
+ video resource. Raises video Resolver Error if no resource is found.
>>> youtube_com.decode('http://www.youtube.com/v/c64Aia4XE1Y')
-
'http://o-o.preferred.dub06s01.v22.lscache8.c.youtube.com/videoplayback?sparams=id%2Cexpire%2Cip%2Cipbits%2Citag%2Csource%2Calgorithm%2Cburst%2Cfactor%2Ccp&fexp=909513%2C914051%2C910102%2C913601%2C914102%2C902516&algorithm=throttle-factor&itag=34&ip=93.0.0.0&burst=40&sver=3&signature=CB2444C0FE8F08F9C8A5187CD8A7ECB9300F7D08.84A27889772123BA3F53BA5F1D2B6390E9EFF14E&source=youtube&expire=1327129325&key=yt1&ipbits=8&factor=1.25&cp=U0hRTFFNV19HT0NOMV9JR0FEOk5FVGY5blpiSjQx&id=73ae0089ae171356'
+
'http://o-o.preferred.dub06s01.v22.lscache8.c.youtube.com/videoplayback?sparams=id%2Cexpi...'
>>> youtube_com.decode('c64Aia4XE1Y')
-
'http://o-o.preferred.dub06s01.v22.lscache8.c.youtube.com/videoplayback?sparams=id%2Cexpire%2Cip%2Cipbits%2Citag%2Csource%2Calgorithm%2Cburst%2Cfactor%2Ccp&fexp=909513%2C914051%2C910102%2C913601%2C914102%2C902516&algorithm=throttle-factor&itag=34&ip=93.0.0.0&burst=40&sver=3&signature=CB2444C0FE8F08F9C8A5187CD8A7ECB9300F7D08.84A27889772123BA3F53BA5F1D2B6390E9EFF14E&source=youtube&expire=1327129325&key=yt1&ipbits=8&factor=1.25&cp=U0hRTFFNV19HT0NOMV9JR0FEOk5FVGY5blpiSjQx&id=73ae0089ae171356'
+
'http://o-o.preferred.dub06s01.v22.lscache8.c.youtube.com/videoplayback?sparams=id%2Cexpi...'
'''
__metaclass__ = Plugin
def __init__(self, priority=100):
@@ -316,20 +284,28 @@ class youtube_com(object):
self.priority = priority
def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- if path.lower() is u"/embed/videoseries":
- playlistID = parse_qs(query)[u"list"][0]
+ if path.lower().startswith(u"/embed/videoseries"):
+ playlistID = plugin.parse_qs(query)["list"]
+ if returnID: return playlistID
+ else: return {"domain":netloc, "vodepid":playlistID,
"function":self.decode_playlist, "priority":self.priority-1}
+ elif path.lower().startswith("/playlist"):
+ playlistID = plugin.parse_qs(query)["list"]
if returnID: return playlistID
- else: return {"domain":netloc, "vodepid":playlistID,
"function":self.decode_playlist, "isplaylist":True, "priority":self.priority-1}
+ else: return {"domain":netloc, "vodepid":playlistID,
"function":self.decode_playlist, "priority":self.priority-1}
+ elif path.lower().startswith(u"/embed/") and "list" in query:
+ playlistID = plugin.parse_qs(query)["list"]
+ if returnID: return playlistID
+ else: return {"domain":netloc, "vodepid":playlistID,
"function":self.decode_playlist, "priority":self.priority-1}
elif path.lower().startswith(u"/embed/") or
path.lower().startswith(u"/v/"):
- videoID = path[path.rfind(u"/")+1:]
+ videoID = path[path.rfind(u"/")+1:].split("&")[0]
if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
+ else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "priority":self.priority}
elif path.lower().startswith(u"/watch"):
- videoID = parse_qs(query)[u"v"][0]
+ videoID = plugin.parse_qs(query)["v"]
if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
+ else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "priority":self.priority}
else:
- plugin.log("Unable to Strip out video id from youtube
source url")
+ plugin.notice("Unable to Strip out video id from
youtube.com source url")
return None
@check_arg(u"Youtube")
@@ -347,64 +323,11 @@ class youtube_com(object):
# Fetch list of urls and listiems
markForDownload = plugin.get("download",u"false") == u"true"
return [{"url":url + "&download=true", "item":listitem} if
markForDownload else {"url":url, "item":listitem} for url, listitem, isfolder
in Gdata.VideoGenerator()]
-
- def checker(self, testcard=u"c64Aia4XE1Y"):
- plugin.log("Checking Youtube Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
class youtu_be(youtube_com): pass
#######################################################################################
-class vimeo_com(object):
- '''
- Takes a video id from a Vimeo video page url, or takes a complete url
- to a Vimeo swf and returns the url to the actual video resource.
- Raises videoResolver Error if no match is found.
-
- >>>
vimeo_com.decode('http://player.vimeo.com/video/34242816?title=0&byline=0&portrait=0')
-
'http://av.vimeo.com/68932/441/78039067.mp4?token=1327120954_425c5e7baa06f349386b5479f9cb6d0e'
-
- >>> vimeo_com.decode('34242816')
-
'http://av.vimeo.com/68932/441/78039067.mp4?token=1327120954_425c5e7baa06f349386b5479f9cb6d0e'
- '''
- __metaclass__ = Plugin
- def __init__(self, priority=97):
- self.priority = priority
-
- def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path[path.rfind(u"/")+1:]
- if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
-
- @check_arg(u"Vimeo")
- def decode(self, arg):
- # Fetch Video Processer Object and Return Decoded Url
- if plugin.get("download",u"false") == u"true": return
{"url":u"plugin://plugin.video.vimeo/?action=download&videoid=%s" % arg}
- else: return
{"url":u"plugin://plugin.video.vimeo/?action=play_video&videoid=%s" % arg}
-
- def checker(self, testcard=u"34242816"):
- plugin.log("Checking Vimeo Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
-
-#######################################################################################
-
class dailymotion_com(object):
'''
Takes a video id from a DailyMotion video page url, or takes a complete
url
@@ -422,23 +345,30 @@ class dailymotion_com(object):
self.priority = priority
def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path[path.rfind(u"/")+1:].split(u"_",1)[0]
- if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
+ if "video/" in path.lower():
+ videoID = path[path.rfind(u"/")+1:].split(u"_",1)[0]
+ if returnID: return videoID
+ else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "priority":self.priority}
+ else:
+ plugin.notice("Unable to Strip out video id from
dailymotion.com source url")
+ return None
@check_arg(u"DailyMotion")
def decode(self, arg):
# Fetch Redirected Url
url = u"http://www.dailymotion.com/embed/video/%s" % arg
- sourceCode = urlhandler.urlread(url)
+ handle = urlhandler.HttpHandler()
+ handle.add_response_handler()
+ with handle.open(url) as resp:
+ sourceCode = resp.read().decode("utf-8")
# Fetch list of Urls
sourceCode = re.findall('var info = (\{.+?\}),', sourceCode)[0]
Sources = dict([[part for part in match if part] for match in
re.findall('"(stream_h264_url)":"(\S+?)",|"(stream_h264_ld_url)":"(\S+?)",|"(stream_h264_hq_url)":"(\S+?)",|"(stream_h264_hd_url)":"(\S+?)",|"(stream_h264_hd1080_url)":"(\S+?)",',
sourceCode)])
- if not Sources: raise plugin.videoResolver(33077, "Unable to
Find Video Url")
+ if not Sources: raise
plugin.videoResolver(plugin.getstr(33077), "Unable to Find Video Url")
# Fetch Video Quality
- Quality = plugin.getQuality()
+ Quality = plugin.getSetting("quality")
if Quality == u"1080p": Quality = 5
elif Quality == u"720p": Quality = 4
else: Quality = 3
@@ -450,18 +380,6 @@ class dailymotion_com(object):
# Fallback to any quality if unable to find format
return {"url":Sources[Sources.keys()[0]]}
-
- def checker(self, testcard=u"x162i8b"):
- plugin.log("Checking DailyMotion Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
#######################################################################################
@@ -471,36 +389,45 @@ class blip_tv(object):
to a BlipTV swf and returns the url to the actual video resource.
Raises videoResolver Error if no match is found.
- >>> blip_tv.decode('http://blip.tv/play/AYOW3REC.html')
-
'http://blip.tv/file/get/Linuxjournal-GetFirefoxMenuButtonInLinux474.m4v'
+ >>> blip_tv.decode('http://blip.tv/play/AYKvk2QC.html')
+ 'http://blip.tv/file/get/8bitredcat-....m4v'
- >>> blip_tv.decode('AYOW3REC'|'6663725')
-
'http://blip.tv/file/get/Linuxjournal-GetFirefoxMenuButtonInLinux474.m4v'
+ >>> blip_tv.decode('AYKvk2QC'|'4966784')
+ 'http://blip.tv/file/get/8bitredcat-....m4v'
'''
__metaclass__ = Plugin
def __init__(self, priority=95):
self.priority = priority
def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path[path.rfind(u"/")+1:].split(u".")[0]
+ if "api.swf" in path.lower(): videoID = fragment
+ elif "play/" in path.lower(): videoID =
path[path.rfind(u"/")+1:].split(u".")[0]
+ elif str.isdigit(path.rsplit("-", 1)[-1]): videoID =
path.rsplit("-", 1)[-1]
+ else:
+ plugin.notice("Unable to Strip out video id from
blip.tv source url")
+ return None
+
+ # Return Ether Video id or dict of objects
if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
+ else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "priority":self.priority}
@check_arg(u"BlipTV")
def decode(self, arg):
# Check if arg is the embeded ID or VideoID
- if arg.isdigit():
- # Fetch Redirected Url
+ if arg.isdigit(): url = "http://blip.tv/rss/flash/%s" % arg
+ else:
+ # Fetch Redirected Url with the numeric id
url = u"http://blip.tv/play/%s" % arg
reUrl = urlhandler.redirect(url)
# Filter out VideoCode
- try: arg = re.findall('flash%2F(.+?)&', reUrl)[0]
- except: raise plugin.videoResolver(33077, "Unable to
filter out Video Code")
+ try: url =
plugin.parse_qs(reUrl[reUrl.find("?")+1:])["file"]
+ except (ValueError, KeyError) as e: raise
plugin.videoResolver(plugin.getstr(33077), str(e))
# Fetch XMLSource
- url = u"http://blip.tv/rss/flash/%s" % arg
- sourceObj = urlhandler.urlopen(url)
+ handle = urlhandler.HttpHandler()
+ handle.add_response_handler()
+ sourceObj = handle.open(url)
import xml.etree.ElementTree as ElementTree
media = u"http://search.yahoo.com/mrss/"
tree = ElementTree.parse(sourceObj)
@@ -508,30 +435,20 @@ class blip_tv(object):
# Fetch list of Media Content Found
filtered = ((int(node.get(u"height",0)), node.attrib) for node
in tree.getiterator(u"{%s}content" % media) if not node.get(u"type") ==
u"text/plain")
+ filtered = sorted(filtered, key=lambda x: x[0], reverse=True)
+ filtered = sorted(filtered, key=lambda x: x[1]["isDefault"] ==
"true", reverse=True)
# Fetch Video Quality and return video
- quality = int(plugin.getQuality().replace(u"p",u""))
- qualitySorted = sorted(filtered, key=lambda x: x[0],
reverse=True)
- for content in qualitySorted:
- if content[0] <= quality:
- videoInfo = content[1]
- return {"url":videoInfo[u"url"],
"type":videoInfo[u"type"]}
+ try: quality =
int(plugin.getSetting("quality").replace(u"p",u""))
+ except ValueError: quality = 720
+
+ for res, content in filtered:
+ if res <= quality:
+ return {"url":content[u"url"],
"type":content[u"type"]}
# Fallback to hightest available quality
- videoInfo = qualitySorted[0][1]
+ videoInfo = filtered[0][1]
return {"url":videoInfo[u"url"], "type":videoInfo[u"type"]}
-
- def checker(self, testcard=u"AYOW3REC"):
- plugin.log("Checking BlipTV Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
#######################################################################################
@@ -541,10 +458,10 @@ class veehd_com(object):
to a veehd swf and returns the url to the actual video resource.
Raises videoResolver Error if no match is found.
- >>>
veehd_com.decode('http://veehd.com/embed?v=4700076&w=720&h=406&t=2&s=6000&p=divx')
+ >>>
veehd_com.decode('http://veehd.com/embed?v=4686958&w=720&h=405&t=1&s=0&p=flash')
'http://v33.veehd.com/dl/92c8e1b53f19f27697e16d186c28f17b/1327678479/5000.4686958.mp4&b=390'
- >>> veehd_com.decode('4700076')
+ >>> veehd_com.decode('4686958')
'http://v33.veehd.com/dl/92c8e1b53f19f27697e16d186c28f17b/1327678479/5000.4686958.mp4&b=390'
'''
__metaclass__ = Plugin
@@ -552,14 +469,15 @@ class veehd_com(object):
self.priority = priority
def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- if path.lower().startswith(u"/embed"): videoID =
parse_qs(query)[u"v"][0]
+ if path.lower().startswith(u"/embed"): videoID =
plugin.parse_qs(query)[u"v"]
elif path.lower().startswith(u"/video/"): videoID =
path.strip(u"/video/").split(u"_")[0]
else:
- plugin.log("Unable to Strip out video id from veehd.com
source url")
+ plugin.notice("Unable to Strip out video id from
veehd.com source url")
return None
+ # Return Ether Video id or dict of objects
if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
+ else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "priority":self.priority}
@check_arg(u"Veehd")
def decode(self, arg):
@@ -567,375 +485,77 @@ class veehd_com(object):
url = u"http://veehd.com/video/%s" % arg
handle = urlhandler.HttpHandler()
handle.add_response_handler()
- htmlSource = handle.open(url).read().decode("utf8")
+ with handle.open(url) as resp:
+ htmlSource = resp.read().decode("utf-8")
# Fetch Video Info Page Url and Download HTML Page
- try: url = u"http://veehd.com" +
re.findall('load_stream\(\)\{\s+\$\("#playeriframe"\).attr\(\{src :
"(/vpi.+?)"\}\);\s+\}',htmlSource)[0]
- except: raise plugin.videoResolver(33077, "Was unable to Find
Veehd Video Info Page Url")
- else: htmlSource = handle.open(url).read().decode("utf8")
-
- # Search for Video Url Using Params Method
- try: return
{"url":re.findall('<param\s+name="src"\svalue="(http://\S+?)">',htmlSource)[0]}
- except: plugin.setDebugMsg("VideoResolver", "Video Url Search
using params Failed")
-
- # search for Video Url Using Embed Method
- try: return
{"url":re.findall('<embed\s+type="video/divx"\s+src="(http://\S+?)"',htmlSource)[0]}
- except: plugin.setDebugMsg("VideoResolver", "Video Url Search
using embed Failed")
+ try: url = [u"http://veehd.com%s" % url for url in
re.findall('attr\(\{src\s:\s"(/vpi\S+?)"\}\)',htmlSource) if u"do=d" in url][0]
+ except IndexError as e: raise
plugin.videoResolver(plugin.getstr(33077), str(e))
+ else:
+ with handle.open(url) as resp:
+ htmlSource = resp.read().decode("utf-8")
- # Search for Video Url Javascript Method
- import urllib
- try: return
{"url":urllib.unquote_plus(re.findall('"url":"(\S+?)"',htmlSource)[0].encode("ascii"))}
- except: raise plugin.videoResolver(33077, "Was unable to Find
Veehd Video Url or Decode It")
-
- def checker(self, testcard=u"4700076"):
- plugin.log("Checking Veehd Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
+ # Search for Video Url
+ try: return
{"url":re.findall('<a\sstyle="color\:#31A0FF;"\shref="(http://.+?)">',
htmlSource)[0]}
+ except IndexError as e: raise
plugin.videoResolver(plugin.getstr(33077), "Was unable to Find Veehd Video Url
or Decode It")
#######################################################################################
-class www_4shared_com(object):
+class vimeo_com(object):
'''
- Takes a video id from a 4shared video page url, or takes a complete url
- to a 4shared swf and returns the url to the actual video resource.
+ Takes a video id from a vimeo video page url, or takes a complete url
+ to a vimeo swf and returns the url to the actual video resource.
Raises videoResolver Error if no match is found.
- >>>
4shared_com.decode('http://www.4shared.com/embed/488295743/e62f15bd')
-
'http://dc171.4shared.com/img/260186666/598f38a8/dlink__2Fdownload_2FsRLkhHko_3Ftsid_3D20120126-030811-665332a8/preview.flv?file=http://dc171.4shared.com/img/260186666/598f38a8/dlink__2Fdownload_2FsRLkhHko_3Ftsid_3D20120126-030811-665332a8/preview.flv&start=0'
+ >>> vimeo_com.decode('http://player.vimeo.com/video/6368439')
+
'http://av.vimeo.com/30750/741/12016117.mp4?token2=1403188386_b1960dae0fb3a6eafedc717134285fbe&aksessionid=5281d14cce9e052f&ns=4'
- >>> 4shared_com.decode('488295743/e62f15bd')
-
'http://dc171.4shared.com/img/260186666/598f38a8/dlink__2Fdownload_2FsRLkhHko_3Ftsid_3D20120126-030811-665332a8/preview.flv?file=http://dc171.4shared.com/img/260186666/598f38a8/dlink__2Fdownload_2FsRLkhHko_3Ftsid_3D20120126-030811-665332a8/preview.flv&start=0'
+ >>> vimeo_com.decode('6368439')
+
'http://av.vimeo.com/30750/741/12016117.mp4?token2=1403188386_b1960dae0fb3a6eafedc717134285fbe&aksessionid=5281d14cce9e052f&ns=4'
'''
__metaclass__ = Plugin
- def __init__(self, priority=72):
+ def __init__(self, priority=94):
self.priority = priority
def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path.strip(u"/embed/")
- if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
-
- @check_arg(u"4Shared")
- def decode(self, arg):
- # Fetch Redirected Url
- url = u"http://www.4shared.com/embed/%s" % arg
- reUrl = urlhandler.redirect(url)
-
- # Parse Url String and Create Dict
- urlDict = parse_qs(urlparse.urlsplit(reUrl)[3])
+ """
+ http://www.vimeo.com/moogaloop.swf?clip_id=45517759
+ http://player.vimeo.com/video/6368439
+ http://vimeo.com/6368439
+ """
+ if "/video/" in path.lower(): videoID = path[path.rfind("/")+1:]
+ elif "/moogaloop.swf" in path: videoID =
plugin.parse_qs(query)[u"clip_id"]
+ elif path[1:].isdigit(): videoID = path[1:]
+ else:
+ plugin.notice("Unable to Strip out video id from
veehd.com source url")
+ return None
- # Check if the Required Keys Exist
- if u"file" in urlDict and u"streamer" in urlDict: return
{"url":u"%s?file=%s&start=0" % (urlDict[u"streamer"][0], urlDict[u"file"][0])}
- else: raise plugin.videoResolver(33077, "Required Key Not Found
in Redirected Url")
-
- def checker(self, testcard=u"488295743/e62f15bd"):
- plugin.log("Checking 4Shared Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
-
-#######################################################################################
-
-class uploadc_com(object):
- '''
- Takes a video id from a uploadc video page url, or takes a complete url
- to a uploadc swf and returns the url to the actual video resource.
- Raises videoResolver Error if no match is found.
-
- >>> uploadc_com.decode('http://www.uploadc.com/embed-mcc8uenje34i.html')
-
'http://www1.uploadc.com:182/d/rqgabh5gvsulzrqm3ln6pcvoamzh5zsdbpurbtaty6adou55pzhyjqt3/Zegapain+-+Episode+25.mkv'
-
- >>> uploadc_com.decode('mcc8uenje34i')
-
'http://www1.uploadc.com:182/d/rqgabh5gvsulzrqm3ln6pcvoamzh5zsdbpurbtaty6adou55pzhyjqt3/Zegapain+-+Episode+25.mkv'
- '''
- __metaclass__ = Plugin
- def __init__(self, priority=71):
- self.priority = priority
-
- def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path[path.find(u"embed-")+6:path.rfind(u".")]
+ # Return Ether Video id or dict of objects
if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
+ else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "priority":self.priority}
- @check_arg(u"UploadC")
- def decode(self, arg):
- # Create Url String and Fetch HTML Page
- url = u"http://www.uploadc.com/embed-%s.html" % arg
- sourceObj = urlhandler.urlopen(url)
- htmlSource = sourceObj.read()
- sourceObj.close()
-
- # Fetch JavaScript Code and Unpack
- try: jsCode = jsUnpack(re.findall("<script
type='text/javascript'>eval\((.+?)\)\s+</script>", htmlSource)[0])
- except: raise plugin.videoResolver(33077, "Was unable to Find
JavaScript and Unpack It")
-
- # Fetch Video ID From JavaScript Code
- import urllib
- try: return {"url":re.findall('<param
name="src"0="(http://\S+?)"/>', jsCode)[0], "referer":url.encode("ascii")}
- except: raise plugin.videoResolver(33077, "Video Url Was Not
Found in JavaScript Code")
-
- def checker(self, testcard=u"mcc8uenje34i"):
- plugin.log("Checking Uploadc Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
-
-#######################################################################################
-
-class putlocker_com(object):
- '''
- Takes a video id from a PutLocker video page url, or takes a complete
url
- to a PutLocker swf and returns the url to the actual video resource.
- Raises videoResolver Error if no match is found.
-
- >>> putlocker_com.decode('http://www.putlocker.com/embed/21NRHEZN4Z17')
-
'http://media-b30.putlocker.com/download/69/imbtcd1_arc_56c02.flv/daac7e476510246bb24ca320e6468969/4f1a63dc'
-
- >>> putlocker_com.decode('21NRHEZN4Z17')
-
'http://media-b30.putlocker.com/download/69/imbtcd1_arc_56c02.flv/daac7e476510246bb24ca320e6468969/4f1a63dc'
- '''
- __metaclass__ = Plugin
- def __init__(self, priority=70):
- self.priority = priority
-
- def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path[path.rfind(u"/")+1:]
- if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
-
- @check_arg(u"PutLocker")
+ @check_arg(u"Veehd")
def decode(self, arg):
- # Construct Embedded Url and Fetch Server Response
- url = u"http://www.putlocker.com/embed/%s" % arg
+ # Create Url String and Download HTML Page
+ url = u"http://player.vimeo.com/video/%s" % arg
handle = urlhandler.HttpHandler()
handle.add_response_handler()
- response = handle.open(url).read()
-
- # Fetch Cookie and Hash Values
- try: hash =
"fuck_you=%s&confirm=Close+Ad+and+Watch+as+Free+User" % (re.findall('<input
type="hidden" value="(\S+?)" name="fuck_you">', response)[0])
- except: raise plugin.videoResolver(33077, "Failed to Read Hash
Value used for validating the Cookie")
-
- # Validate Cookie with the Hash Value
- response = handle.open(url, hash,
{"Referer":url.encode("ascii")}).read()
-
- # Construct Embedded Url and Fetch Video Info
- try: url = "http://www.putlocker.com/get_file.php?stream=%s" %
(re.findall('/get_file.php\?stream=(\S+)', response)[0])
- except: raise plugin.videoResolver(33077, "Failed to Validate
Cookie and Fetch Embed Code")
-
- # Connect to Server to Download Video Source Data
- response = handle.open(url)
-
- # Fetch and Return Video Url
- import xml.etree.ElementTree as ElementTree
- media = u"http://search.yahoo.com/mrss/"
- tree = ElementTree.parse(response)
- response.close()
-
- # Loop each media content element and find video url
- for node in tree.getiterator(u"{%s}content" % media):
- url = node.get(u"url")
- type = node.get(u"type")
- if url is not None and type[:5] == u"video":
- return {"url":url, "type":type}
+ with handle.open(url) as resp:
+ htmlSource = resp.read()
- # Raise Error if unable to return any video url
- raise plugin.videoResolver(33077, "Failed to Read Video Url")
-
- def checker(self, testcard=u"21NRHEZN4Z17"):
- plugin.log("Checking PutLocker Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
-
-#######################################################################################
-
-class kqed_org(object):
- '''
- Takes a video id from a kqed.org video page url, or takes a complete
url
- to a kqed.org swf and returns the url to the actual video resource.
- Raises videoResolver Error if no match is found.
-
- >>> kqed_org.decode('http://www.kqed.org/quest/television/embed/502')
- 'http://www.kqed.org/.stream/anon/quest/116b_exoplanets_e.flv'
-
- >>> kqed_org.decode('502')
- 'http://www.kqed.org/.stream/anon/quest/116b_exoplanets_e.flv'
- '''
- __metaclass__ = Plugin
- def __init__(self, priority=51):
- self.priority = priority
-
- def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path[path.rfind(u"/")+1:]
- if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
-
- @check_arg(u"kqed.org")
- def decode(self, arg):
- # Create Url String and Fetch HTML Page
- url = u"http://www.kqed.org/quest/television/embed/%s" % arg
- htmlSource = urlhandler.urlread(url)
-
- # Fetch the Video Url And Return It
- try: return {"url":re.findall("source=(http\S+?)&",
htmlSource)[0]}
- except: raise plugin.videoResolver(33077, "Was unable to Find
kqed.org Video Url")
-
- def checker(self, testcard=u"502"):
- plugin.log("Checking kqed Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
-
-#######################################################################################
-
-class stagevu_com(object):
- '''
- WARNING: Very Slow to Start Video Source
- Takes a video id from a StageView video page url, or takes a complete
url
- to a StageView swf and returns the url to the actual video resource.
- Raises videoResolver Error if no match is found.
-
- >>>
stagevu_com.decode('http://stagevu.com/embed?width=720&height=405&background=000&uid=srpfmbeqxlwe')
- 'http://n47.stagevu.com/v/e848f783c2a9046f44cb93e32ae5d878/43289.avi'
-
- >>> stagevu_com.decode('srpfmbeqxlwe')
- 'http://n47.stagevu.com/v/e848f783c2a9046f44cb93e32ae5d878/43289.avi'
- '''
- __metaclass__ = Plugin
- def __init__(self, priority=20):
- self.priority = priority
-
- def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = parse_qs(query)[u"uid"][0]
- if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
-
- @check_arg(u"StageView")
- def decode(self, arg):
- # Create Url String and Fetch HTML Page
- url = u"http://stagevu.com/embed?uid=%s" % arg
- htmlSource = urlhandler.urlread(url)
+ # Fetch Video Info Page Url and Download HTML Page
+ try: jsonData = re.findall('"h264":({.+?}),"hls"',
htmlSource)[0]
+ except IndexError as e: raise
plugin.videoResolver(plugin.getstr(33077), str(e))
- # Search for Video Url and Return
- try: return {"url":re.findall("url\[\d+]\ = '(http://\S+?)';",
htmlSource)[0]}
- except: raise plugin.videoResolver(33077, "Was unable to Find
StageView Video Url")
-
- def checker(self, testcard=u"srpfmbeqxlwe"):
- plugin.log("Checking StageView Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
-
-#######################################################################################
-
-class rutube_ru(object):
- '''
- Warning: Video Stream Failes when a 3rd of the way into the Video
- Takes a video id from a rutube.ru video page url, or takes a complete
url
- to a rutube.ru swf and returns the url to the actual video resource.
- Raises videoResolver Error if no match is found.
-
- >>>
rutube_ru.decode('http://video.rutube.ru/65110c1ab97f073835033d2b4a9c3bd2')
- 'http://bl.rutube.ru/65110c1ab97f073835033d2b4a9c3bd2.m3u8'
-
- >>> rutube_ru.decode('65110c1ab97f073835033d2b4a9c3bd2')
- 'http://bl.rutube.ru/65110c1ab97f073835033d2b4a9c3bd2.m3u8'
- '''
- #__metaclass__ = Plugin
- def __init__(self, priority=-1):
- self.priority = priority
-
- def strip(self, scheme, netloc, path, query, fragment, returnID=False):
- videoID = path[path.rfind(u"/")+1:]
- if returnID: return videoID
- else: return {"domain":netloc, "vodepid":videoID,
"function":self.decode, "isplaylist":False, "priority":self.priority}
-
- @check_arg(u"rutube.ru")
- def decode(self, arg):
- # Fetch Video Information
- url = u"http://rutube.ru/api/play/options/%s/?format=json" % arg
- sourceObj = urlhandler.urlopen(url)
+ # Convert String to json Object
+ import fastjson as json
+ try: urlData = sorted([(value["height"], value["url"]) for
value in json.loads(jsonData).values()], key=lambda x: x[0], reverse=True)
+ except (ValueError, KeyError) as e: raise
plugin.videoResolver(plugin.getstr(33077), str(e))
- # Load Json Object
- from fastjson import load
- jsonObject = load(sourceObj)
- sourceObj.close()
+ # Fetch Video Quality and return video
+ try: quality =
int(plugin.getSetting("quality").replace(u"p",u""))
+ except ValueError: quality = 720
- # Return Video Url
- return {"url":jsonObject[u"video_balancer"][u"m3u8"]}
-
- def checker(self, testcard=u"65110c1ab97f073835033d2b4a9c3bd2"):
- plugin.log("Checking RuTube.ru Decoder", 0)
- try:
- videoUrl = self.decode(testcard)
- plugin.log("#########################################",
0)
- plugin.log(u"PASS: Successfully Decoded %s" % testcard,
0)
- plugin.log( videoUrl)
- return videoUrl
- except:
- plugin.log("#######################################", 0)
- plugin.log(u"FAILED: Unable to Devode %s" % testcard, 0)
-
-#######################################################################################
-
-'''
- Dummy Class to Stop Shutdown Sources from Been Identified as
UnSupported,
- Google Video has been Shut Down and is no longer available.
- Megavideo has been Shut Down and is no longer available.
- Myspace has Totally Changed and is no longer serving videos
-'''
-
-class google_com(object):
- __metaclass__ = Plugin
- def strip(self, *args, **kwargs):
- plugin.log("google.com: Google Video has been Shut Down and is
no longer available.")
- return None
-
-class megavideo_com(object):
- __metaclass__ = Plugin
- def strip(self, *args, **kwargs):
- plugin.log("megavideo.com: Megavideo has been Shut Down and is
no longer available.")
- return None
-
-class myspace_com(object):
- __metaclass__ = Plugin
- def strip(self, *args, **kwargs):
- plugin.log("myspace.com: Myspace has Totally Changed and is no
longer serving videos.")
- return None
+ for res, url in urlData:
+ if res <= quality:
+ return {"url":url}
\ No newline at end of file
diff --git a/script.module.xbmcutil/lib/xbmcutil/videohostsAPI.py
b/script.module.xbmcutil/lib/xbmcutil/videohostsAPI.py
index f3f8e50..758dbf9 100644
--- a/script.module.xbmcutil/lib/xbmcutil/videohostsAPI.py
+++ b/script.module.xbmcutil/lib/xbmcutil/videohostsAPI.py
@@ -28,10 +28,10 @@ from fastjson import load
class YTChannelUploads(listitem.VirtualFS):
@plugin.error_handler
- def scraper(self, contentID=None):
+ def scraper(self):
# Create Url Source
urlString =
u"http://gdata.youtube.com/feeds/api/users/%s/uploads"
- contentID = plugin.get("url", contentID)
+ contentID = plugin["url"]
url = urlString % contentID
# Initialize Gdata API
@@ -42,10 +42,10 @@ class YTChannelUploads(listitem.VirtualFS):
videoItems = Gdata.VideoGenerator()
# Add Next Page and/or Playlist
- if plugin.get("hasPlaylists",u"false") == u"true":
self.add_youtube_playlists(contentID, label=u"Playlists")
+ if plugin.get("hasplaylists",u"false") == u"true":
self.add_youtube_playlists(contentID, label=plugin.getuni(136),
hasHD=plugin.get("hashd","none"))
# Add Next Page
- if Gdata.processed:
self.add_next_page({"action":plugin["action"], "url":contentID,
"processed":Gdata.processed})
+ if Gdata.processed:
self.add_next_page({"action":plugin["action"], "url":contentID,
"processed":Gdata.processed, "hashd":plugin.get("hashd","none")})
# Set SortMethods and Content
self.set_sort_methods(self.sort_method_date,
self.sort_method_video_runtime, self.sort_method_program_count,
self.sort_method_video_rating, self.sort_method_genre, self.sort_method_studio,
self.sort_method_video_title)
@@ -56,10 +56,10 @@ class YTChannelUploads(listitem.VirtualFS):
class YTChannelPlaylists(listitem.VirtualFS):
@plugin.error_handler
- def scraper(self, contentID=None):
+ def scraper(self):
# Create Url Source
urlString =
u"http://gdata.youtube.com/feeds/api/users/%s/playlists"
- contentID = plugin.get("url", contentID)
+ contentID = plugin["url"]
url = urlString % contentID
# Initialize Gdata API
@@ -70,7 +70,7 @@ class YTChannelPlaylists(listitem.VirtualFS):
videoItems = Gdata.PlaylistGenerator(loop=True)
# Add Next Page
- if Gdata.processed:
self.add_next_page({"action":plugin["action"], "url":contentID,
"processed":Gdata.processed})
+ if Gdata.processed:
self.add_next_page({"action":plugin["action"], "url":contentID,
"processed":Gdata.processed, "hashd":plugin.get("hashd","none")})
# Set SortMethods and Content
self.set_sort_methods(self.sort_method_video_title,
self.sort_method_date)
@@ -81,10 +81,10 @@ class YTChannelPlaylists(listitem.VirtualFS):
class YTPlaylistVideos(listitem.VirtualFS):
@plugin.error_handler
- def scraper(self, contentID=None):
+ def scraper(self):
# Create Url Source
urlString = u"http://gdata.youtube.com/feeds/api/playlists/%s"
- contentID = plugin.get("url", contentID)
+ contentID = plugin["url"]
url = urlString % contentID
# Initialize Gdata API
@@ -95,8 +95,8 @@ class YTPlaylistVideos(listitem.VirtualFS):
videoItems = Gdata.VideoGenerator()
# Add Next Page
- if Gdata.processed:
self.add_next_page({"action":plugin["action"], "url":contentID,
"processed":Gdata.processed})
-
+ if Gdata.processed:
self.add_next_page({"action":plugin["action"], "url":contentID,
"processed":Gdata.processed, "hashd":plugin.get("hashd","none")})
+
# Set Content Properties
self.set_sort_methods(self.sort_method_date,
self.sort_method_video_runtime, self.sort_method_program_count,
self.sort_method_video_rating, self.sort_method_genre, self.sort_method_studio,
self.sort_method_video_title)
self.set_content("episodes")
@@ -106,10 +106,10 @@ class YTPlaylistVideos(listitem.VirtualFS):
class YTRelatedVideos(listitem.VirtualFS):
@plugin.error_handler
- def scraper(self, contentID=None):
+ def scraper(self):
# Create Url Source
urlString =
u"http://gdata.youtube.com/feeds/api/videos/%s/related"
- contentID = plugin.get("url", contentID)
+ contentID = plugin["url"]
url = urlString % contentID
# Initialize Gdata API
@@ -133,11 +133,21 @@ class YoutubeAPI:
# Fetch Filter String Based of Filter Mode
if filterMode == "video": self.filterString =
u"openSearch:totalResults,entry(yt:statistics,gd:rating,media:group(yt:videoid,media:title,yt:aspectRatio,yt:duration,media:credit,media:category,media:description,yt:uploaded))"
elif filterMode == "playlists": self.filterString =
u"openSearch:totalResults,entry(yt:playlistId,yt:countHint,title,summary,published,media:group(media:thumbnail))"
+
+ # Fetch Youtube Video Quality Setting
+ try: setting =
int(plugin.getAddonSetting("plugin.video.youtube", "hd_videos"))
+ except: self.isHD = None
+ else:
+ # Set HD Flag based of Youtube Setting
+ hashd = plugin.get("hashd", "none")
+ if setting == 1 or hashd == "false": self.isHD = False
+ elif (setting == 0 or setting >= 2) and hashd ==
u"true": self.isHD = True
+ else: self.isHD = None
def ProcessUrl(self):
# Fetch SourceCode
url =
u"%s?v=2&max-results=%i&start-index=%i&alt=json&fields=%s" % (self.baseUrl,
self.maxResults, self.processed+1, self.filterString)
- sourceObj = urlhandler.urlopen(url, 28800)
+ sourceObj = urlhandler.urlopen(url, 14400) # TTL = 4 Hours
# Fetch List of Entries
feed = load(sourceObj)[u"feed"]
@@ -155,10 +165,12 @@ class YoutubeAPI:
# Setup Converter and Optimizations
results = []
localInt = int
+ isHD = self.isHD
localFloat = float
imagePath = u"http://img.youtube.com/vi/%s/0.jpg"
localListItem = listitem.ListItem
addItem = results.append
+ hashd = plugin.get("hashd","none")
while 1:
# Loop Entries
@@ -200,10 +212,14 @@ class YoutubeAPI:
if u"yt$aspectRatio" in mediaGroup and
mediaGroup[u"yt$aspectRatio"][u"$t"] == u"widescreen":
item.setStreamDict("aspect", 1.78)
# Add Context item to link to related videos
-
item.addRelatedContext(action="system.videohosts.YTRelatedVideos", url=videoID)
+
item.addRelatedContext(action="system.videohosts.YTRelatedVideos", url=videoID,
hashd=hashd)
+
+ # Set Quality and Audio Overlays
+ item.setQualityIcon(isHD)
+ item.setAudioInfo()
# Add InfoLabels and Data to Processed List
- addItem(item.getListitemTuple(isPlayable=True))
+ addItem(item.getListitemTuple(True))
# Fetch Next Set of Pages if Available
if (loop == True) and (self.processed):
self.ProcessUrl()
@@ -215,6 +231,7 @@ class YoutubeAPI:
def PlaylistGenerator(self, loop=False):
results = []
addItem = results.append
+ hashd = plugin.get("hashd", "none")
while True:
# Loop Entries
for node in self.entries:
@@ -224,6 +241,7 @@ class YoutubeAPI:
# Fetch Video ID
item.setParamDict("url",
node[u"yt$playlistId"][u"$t"])
+ item.setParamDict("hashd", hashd)
# Fetch Title and Video Cound for combining
Title
item.setLabel(u"%s (%s)" %
(node[u"title"][u"$t"], node[u"yt$countHint"][u"$t"]))
@@ -238,7 +256,7 @@ class YoutubeAPI:
if u"published" in node:
item.setDateInfo(node[u"published"][u"$t"].split(u"T")[0], "%Y-%m-%d")
# Add InfoLabels and Data to Processed List
- addItem(item.getListitemTuple())
+ addItem(item.getListitemTuple(False))
# Fetch Next Set of Pages if Available
if (loop == True) and (self.processed):
self.ProcessUrl()
@@ -246,112 +264,3 @@ class YoutubeAPI:
# Return List of Listitems
return results
-
-############################## VimeoAPI ##############################
-
-class VUserVideos(listitem.VirtualFS):
- @plugin.error_handler
- def scraper(self, contentID=None):
- # Create Url Source
- urlString = u"http://vimeo.com/api/v2/%s/videos.json"
- contentID = plugin.get("url", contentID)
- url = urlString % contentID
-
- # Fetch User Info
- pagelimit = int(plugin.get("pagelimit", 0))
- if pagelimit == 0:
- info = VimeoAPI.user_info(contentID)
- pagelimit = (info[u"total_videos_uploaded"] -1) / 20 + 1
-
- # Initialize Vimeo API
- Vimeo = VimeoAPI(url, int(plugin.get("currentpage",1)),
pagelimit)
- Vimeo.ProcessUrl()
-
- # Fetch Video Results
- videoItems = Vimeo.VideoGenerator()
-
- # Add Next Page
- #if Vimeo.currentPage: self.add_next_page({"url":contentID,
"pagelimit":Vimeo.pageLimit, "currentpage":Vimeo.currentPage})
-
- # Set Content Properties
- self.set_sort_methods(self.sort_method_date,
self.sort_method_video_runtime, self.sort_method_genre,
self.sort_method_video_title)
- self.set_content("episodes")
-
- # Return List of Video Listitems
- return videoItems
-
-class VimeoAPI:
- @classmethod
- def user_info(self, userID):
- # Create Url String
- url = u"http://vimeo.com/api/v2/%s/info.json" % userID
- sourceObj = urlhandler.urlopen(url, 604800)
-
- # Fetch User Info
- info = load(sourceObj)
- sourceObj.close()
- return info
-
- def __init__(self, baseUrl, currentPage=1, pageLimit=3):
- # Set Global Vars
- self.baseUrl = baseUrl
- self.currentPage = currentPage
-
- # Restrict Page Limit to 3, Only 3 Pages allowed for Simple API
- if pageLimit > 3: self.pageLimit = 3
- else: self.pageLimit = pageLimit
-
- def ProcessUrl(self):
- # Fetch SourceCode
- sourceObj = urlhandler.urlopen(u"%s?page=%i" % (self.baseUrl,
self.currentPage), 28800)
-
- # Fetch List of Entries
- self.entries = load(sourceObj)
- sourceObj.close()
-
- # Increment Page Counter
- if self.currentPage >= self.pageLimit: self.currentPage = 0
- else: self.currentPage += 1
-
- def VideoGenerator(self, loop=False):
- # Setup Converter and Optimizations
- results = []
- localListItem = listitem.ListItem
- addItem = results.append
-
- while 1:
- # Loop Entries
- for node in self.entries:
- # Create listitem object
- item = localListItem()
-
- # Fetch Title
- item.setLabel(node[u"title"])
-
- # Fetch Video ID and Set Actions
- item.setParamDict(action="system.source",
sourcetype="vimeo_com", url=node[u"id"])
-
- # Fetch Plot Description
- item.setInfoDict(plot=node[u"description"],
genre=node[u"tags"].replace(u'"',u'').split(u",")[0])
-
- # Fetch Duration and Convert to String
- item.setDurationInfo(node[u"duration"])
-
- # Fetch Date of Video
- item.setDateInfo(node[u"upload_date"].split(u"
")[0], "%Y-%m-%d")
-
- # Add Thumbnail Image
- item.setThumbnailImage(node[u"thumbnail_large"])
-
- # Add Quality Overlay
- item.setStreamDict(codec="h264",
width=node[u"width"], height=node[u"height"])
-
- # Add InfoLabels and Data to Processed List
- addItem(item.getListitemTuple(isPlayable=True))
-
- # Fetch Next Set of Pages if Available
- if (loop == True) and (self.currentPage):
self.ProcessUrl()
- else: break
-
- # Return List of Listitems
- return results
-----------------------------------------------------------------------
Summary of changes:
script.module.xbmcutil/addon.xml | 3 +-
script.module.xbmcutil/changelog.txt | 14 +
script.module.xbmcutil/lib/fastjson.py | 10 +-
script.module.xbmcutil/lib/xbmcutil/__init__.py | 542 +++++-----------
script.module.xbmcutil/lib/xbmcutil/listitem.py | 431 ++++++-------
script.module.xbmcutil/lib/xbmcutil/storageDB.py | 13 +-
script.module.xbmcutil/lib/xbmcutil/urlhandler.py | 160 +++--
.../lib/xbmcutil/videoResolver.py | 674 +++++---------------
.../lib/xbmcutil/videohostsAPI.py | 161 +----
.../resources/language/English/strings.po | 151 +++++
.../resources/language/English/strings.xml | 40 --
11 files changed, 819 insertions(+), 1380 deletions(-)
create mode 100644 script.module.xbmcutil/resources/language/English/strings.po
delete mode 100644
script.module.xbmcutil/resources/language/English/strings.xml
hooks/post-receive
--
Scripts
------------------------------------------------------------------------------
Want fast and easy access to all the code in your enterprise? Index and
search up to 200,000 lines of code with a free copy of Black Duck
Code Sight - the same software that powers the world's largest code
search on Ohloh, the Black Duck Open Hub! Try it now.
http://p.sf.net/sfu/bds
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons