home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / xbmc-9.11.exe / plugins / Programs / SVN Repo Installer / installerAPI / xbmcplugin_update.py < prev   
Encoding:
Python Source  |  2009-11-03  |  12.7 KB  |  360 lines

  1. """
  2.  
  3.     Programs/Plugin to check for SVN Repo updates of your installed scripts and Plugins
  4.  
  5.     This version as part of "SVN Repo installer" Programs/Plugin
  6.  
  7.     Written by BigBellyBilly
  8.     Contact me at BigBellyBilly at gmail dot com - Bugs reports and suggestions welcome.
  9.  
  10. """ 
  11.  
  12. import sys, os
  13. import os.path
  14. import xbmc, xbmcgui, xbmcplugin
  15. import urllib
  16. import re
  17. from string import find
  18. #from pprint import pprint
  19. from xbmcplugin_lib import *
  20.  
  21. # Script constants
  22. __plugin__ = sys.modules["__main__"].__plugin__
  23. __date__ = '14-09-2009'
  24. log("Module: %s Dated: %s loaded!" % (__name__, __date__))
  25.  
  26. # check if build is special:// aware - set roots paths accordingly
  27. XBMC_HOME = 'special://home'
  28. if not os.path.isdir(xbmc.translatePath(XBMC_HOME)):    # if fails to convert to Q:, old builds
  29.     XBMC_HOME = 'Q:'
  30. log("XBMC_HOME=%s" % XBMC_HOME)
  31.  
  32.  
  33. class Main:
  34.  
  35.     URL_BASE_SVN_SCRIPTING = "http://xbmc-scripting.googlecode.com/svn"
  36.     INSTALLED_ITEMS_FILENAME = os.path.join( os.getcwd(), "installed_items.dat" )
  37.     UPDATE_ALL_FILENAME = os.path.join( os.getcwd(), "update_all.dat" )
  38.  
  39.     def __init__( self ):
  40.         log( "%s init!" % self.__class__ )
  41.         ok = False
  42.         # set our plugin category
  43.         xbmcplugin.setPluginCategory( handle=int( sys.argv[ 1 ] ), category=xbmc.getLocalizedString( 30500 ) )
  44.  
  45.         # load settings
  46.         self.showNoSVN = bool(xbmcplugin.getSetting( "show_no_svn" ) == "true")
  47.         self.showNoVer = bool(xbmcplugin.getSetting( "show_no_ver" ) == "true")
  48.  
  49.         self.XBMC_REVISION = get_xbmc_revision()
  50.         self.SVN_URL_LIST = load_repos()
  51.  
  52.         #['plugin://programs/SVN Repo Installer/', '-1', '?download_url="%2Ftrunk%2Fplugins%2Fmusic/iTunes%2F"&repo=\'xbmc-addons\'&install=""&ioffset=2&voffset=0']
  53.         # create all XBMC script/plugin paths
  54.         paths = ("plugins/programs","plugins/video","plugins/music","plugins/pictures","plugins/weather","scripts")
  55.         self.XBMC_PATHS = []
  56.         for p in paths:
  57.             self.XBMC_PATHS.append( xbmc.translatePath( "/".join( [XBMC_HOME, p] ) ) )
  58.  
  59.         self.dialogProgress = xbmcgui.DialogProgress()
  60.         self.dialogProgress.create(__plugin__)
  61.         self.findInstalled()
  62.         if self.INSTALLED:
  63.             self.checkUpdates()
  64.         self.dialogProgress.close()
  65.  
  66. #        pprint (self.INSTALLED)
  67.         if self.INSTALLED:
  68.             ok = self.showUpdates()
  69.             if ok:
  70.                 saveFileObj(self.INSTALLED_ITEMS_FILENAME, self.INSTALLED)
  71.         else:
  72.             xbmcgui.Dialog().ok(__plugin__, "No installed Addons found!")
  73.  
  74.         log("endOfDirectory() ok=%s" % ok)
  75.         xbmcplugin.endOfDirectory( int( sys.argv[ 1 ] ), ok, cacheToDisc=False)
  76.  
  77.  
  78.     def findInstalled(self):
  79.         log("> findInstalled()")
  80.         
  81.         self.INSTALLED = []
  82.         ignoreList = (".","..",".backups",".svn")
  83.         self.dialogProgress.update(0, xbmc.getLocalizedString( 30009 )) # Looking for installed addons
  84.         TOTAL_PATHS = len(self.XBMC_PATHS)
  85.         for count, p in enumerate(self.XBMC_PATHS):
  86.             if not os.path.isdir(p): continue
  87.  
  88.             files = os.listdir(p)
  89.             for f in files:
  90.                 # ignore some
  91.                 if f in ignoreList: continue
  92.  
  93.                 percent = int( count * 100.0 / TOTAL_PATHS )
  94.                 self.dialogProgress.update(percent)
  95.  
  96.                 # extract version
  97.                 try:
  98.                     filepath = os.path.join( p, f )
  99.                     log("filepath=" + filepath)
  100.                     doc = open( os.path.join(filepath, "default.py"), "r" ).read()
  101.                     docTags = parseAllDocTags( doc )
  102.  
  103.                     if docTags and (docTags["version"] or self.showNoVer):
  104.                         thumb = os.path.join(filepath, "default.tbn")
  105.                         if not os.path.isfile( thumb ):
  106.                             thumb = "DefaultFile.png"
  107.                         cat = parseCategory(filepath)
  108.                         # if no title, parse category for it
  109.                         if not docTags["title"]:
  110.                             docTags["title"] = cat.split("/")[-1]
  111.                         tags = {"filepath": filepath,
  112.                                 "thumb": thumb,
  113.                                 "category": cat
  114.                                 }
  115.                         tags.update(docTags)
  116.                         self.INSTALLED.append( tags )
  117.                 except:
  118.                     logError()
  119.  
  120.         log("< findInstalled()  installed count=%d" % len(self.INSTALLED))
  121.  
  122.     #####################################################################################################
  123.     def checkUpdates(self):
  124.         log("> checkUpdates()")
  125.  
  126.         actionMsg = xbmc.getLocalizedString( 30010 )
  127.         self.dialogProgress.update(0, actionMsg)
  128.  
  129.         TOTAL_PATHS = len(self.INSTALLED)
  130.         quit = False
  131.         # enumarate throu each installed Addon, checking svn for update
  132.         for count, info in enumerate(self.INSTALLED):
  133.             log("%d) checking installed=%s" % (count, info))
  134.  
  135.             # unknown installed ver is OK, missing doc tag isn't
  136.             if info.get('version',None) == None:
  137.                 log("ignored, missing a version tag")
  138.                 continue
  139.  
  140.             # find installed category from filepath
  141.             installedCategory  = info['category']
  142.  
  143.             for repo in self.SVN_URL_LIST:
  144.                 try:
  145.                     REPO_URL, REPO_ROOT, REPO_STRUCTURES = get_repo_info( repo )
  146.                     repo_name, repo_noffset, repo_install, repo_ioffset, repo_voffset = REPO_STRUCTURES[0]
  147.                 except:
  148.                     continue    # repo info missing
  149.  
  150.                 base_url = REPO_URL + REPO_ROOT
  151.                 # xbmc-scripting has no 'scripts' in svn path, so remove it from installedCategory
  152.                 if base_url.startswith(self.URL_BASE_SVN_SCRIPTING):
  153.                     installedCategory = installedCategory.replace('scripts/','')
  154.                 url = "/".join( [base_url, installedCategory, "default.py"] )
  155.  
  156.                 percent = int( count * 100.0 / TOTAL_PATHS )
  157.                 self.dialogProgress.update(percent, actionMsg,"%s: %s" % (repo, installedCategory))
  158.                 if self.dialogProgress.iscanceled():
  159.                     quit = True
  160.                     break
  161.  
  162.                 # download default.py
  163.                 doc = readURL( url.replace(' ','%20' ) )
  164.                 if doc == None:    # error
  165.                     quit = True
  166.                     break
  167.                 elif doc:
  168.                     # check remote file __version__ tag
  169.                     svn_ver = parseDocTag(doc, "version")
  170.                     if svn_ver:
  171.                         try:
  172.                             svn_xbmc_rev = int(parseDocTag( doc, "XBMC_Revision" ))
  173.                         except:
  174.                             svn_xbmc_rev = 0
  175.                         self.INSTALLED[count]['svn_ver'] = svn_ver
  176.                         self.INSTALLED[count]['svn_url'] = url.replace('/default.py','')
  177.                         self.INSTALLED[count]['XBMC_Revision'] = svn_xbmc_rev
  178.                         self.INSTALLED[count]['readme'] = check_readme( "/".join( [base_url, installedCategory] ) )
  179.                         self.INSTALLED[count]['repo'] = repo
  180.                         self.INSTALLED[count]['install'] = repo_install
  181.                         self.INSTALLED[count]['ioffset'] = repo_ioffset
  182.                         self.INSTALLED[count]['voffset'] = repo_voffset
  183.                         self.INSTALLED[count]['date'] = parseDocTag( doc, "date" )
  184.                         
  185.                         break # found in svn, move to next installed
  186.  
  187.             if quit: break
  188.  
  189.         log("< checkUpdates() updated count=%d" % len(self.INSTALLED))
  190.  
  191.  
  192.     #####################################################################################################
  193.     def showUpdates(self):
  194.         log("> showUpdates()")
  195.         ok = False
  196.  
  197.         # create update_all file that contains all listitem details
  198.         deleteFile(self.UPDATE_ALL_FILENAME)
  199.         updateAllItems = []
  200.  
  201.         # create display list
  202.         sz = len(self.INSTALLED)
  203.         log("showing INSTALLED count=%s" % sz)
  204.         for info in self.INSTALLED:
  205.             try:
  206.                 svn_url = info.get('svn_url','')
  207.                 svn_ver = info.get('svn_ver','')
  208.  
  209.                 # ignore those not in SVN as per settings
  210.                 if (not svn_url or not svn_ver) and not self.showNoSVN:
  211.                     continue
  212.  
  213.                 # get addon details
  214.                 path = ""
  215.                 filepath = info.get('filepath', '')
  216.                 ver = info.get('version', '')
  217.                 xbmc_rev = info.get('XBMC_Revision', 0)
  218.                 svn_xbmc_rev = info.get('XBMC_Revision',0)
  219.                 readme = info.get('readme','')
  220.                 category = info.get('category','')
  221.                 repo = info.get('repo','SVN ?')
  222.                 labelColour = "FFFFFFFF"
  223.  
  224.                 # add ContextMenu: Delete (unless already deleted status)
  225.                 if "SVN Repo Installer" not in category:
  226.                     cm =  self._contextMenuItem( 30022, { "delete": filepath, "title": category } )    # 'Delete'
  227.                 else:
  228.                     cm = []
  229.  
  230.                 # make update state according to found information
  231.                 if ".backups" in filepath:
  232.                     verState = xbmc.getLocalizedString( 30018 )                # Deleted
  233.                     labelColour = "66FFFFFF"
  234.                 elif not svn_url:
  235.                     verState = xbmc.getLocalizedString( 30012 )             # not in SVN
  236.                 elif not svn_ver:
  237.                     verState = xbmc.getLocalizedString( 30013 )                # unknown version
  238.                 elif ver >= svn_ver:
  239.                     verState = xbmc.getLocalizedString( 30011 )                # OK
  240.                     url_args = "show_info=%s" % urllib.quote_plus( repr(filepath) )
  241.                     path = '%s?%s' % ( sys.argv[ 0 ], url_args, )
  242.                 elif (svn_xbmc_rev and self.XBMC_REVISION and self.XBMC_REVISION >= svn_xbmc_rev) or \
  243.                     (not svn_xbmc_rev or not self.XBMC_REVISION):
  244.                     # Compatible, NEW AVAILABLE - setup callback url for plugin SVN Repo Installer
  245.                     verState = "v%s (%s)" % ( svn_ver, xbmc.getLocalizedString( 30014 ) )        # eg. !New! v1.1
  246.                     trunk_url = re.search('(/(?:trunk|branch|tag).*?)$', svn_url, re.IGNORECASE).group(1)
  247. #['plugin://programs/SVN Repo Installer/', '-1', '?download_url="%2Ftrunk%2Fplugins%2Fmusic/iTunes%2F"&repo=\'xbmc-addons\'&install=""&ioffset=2&voffset=0']
  248.                     info['download_url'] = "download_url=%s&repo=%s&install=%s&ioffset=%s&voffset=%s" % \
  249.                                 (repr(urllib.quote_plus(trunk_url + "/")),
  250.                                 repr(urllib.quote_plus(repo)),
  251.                                 repr(info["install"]),
  252.                                 info["ioffset"],
  253.                                 info["voffset"],)
  254.  
  255.                     url_args = "show_info=%s" % urllib.quote_plus( repr(filepath) )
  256.  
  257.                     # exclude self update from "update all"
  258.                     if "SVN Repo Installer" not in category:
  259.                         updateAllItems.append("?" + info['download_url'])
  260.                     path = '%s?%s' % ( sys.argv[ 0 ], url_args, )
  261.                 else:
  262.                     verState = "v%s (%s)" % ( svn_ver, xbmc.getLocalizedString( 30015 ), )    # eg. Incompatible
  263.  
  264.                 if not path:
  265.                     path = os.path.join(filepath, 'default.tbn')
  266.                 if not svn_ver:
  267.                     svn_ver = '?'
  268.                 if not ver:
  269.                     ver = '?'
  270.  
  271.                 # Addon status text as label2
  272.                 text = "[COLOR=%s][%s] %s (v%s)[/COLOR]" % (labelColour, repo, category, ver)
  273.                 label2 = makeLabel2( verState )
  274.  
  275.                 # determine default icon according to addon type
  276.                 icon = ""
  277.                 if find(filepath,'scripts') != -1:
  278.                     icon = "DefaultScriptBig.png"
  279.                 elif find(filepath,'programs') != -1:
  280.                     icon = "DefaultProgramBig.png"
  281.                 elif find(filepath,'music') != -1:
  282.                     icon = "defaultAudioBig.png"
  283.                 elif find(filepath,'pictures') != -1:
  284.                     icon = "defaultPictureBig.png"
  285.                 elif find(filepath,'video') != -1:
  286.                     icon = "defaultVideoBig.png"
  287.                 # check skin for image, else fallback DefaultFile
  288.                 if not icon or not xbmc.skinHasImage(icon):
  289.                     icon = "DefaultFile.png"
  290.  
  291.                 # assign thumb, in order from: local, svn, icon
  292.                 thumb = info.get('thumb','')
  293.                 if not thumb:
  294.                     thumb = icon
  295.  
  296.                 li=xbmcgui.ListItem( text, label2, icon, thumb)
  297.                 li.setInfo( type="Video", infoLabels={ "Title": text, "Genre": label2 } )
  298.  
  299.                 # add ContextMenu: Changelog
  300.                 cm +=  self._contextMenuItem( 30600, { "showlog": True, "repo": repo, "category": category.split( "/" )[ -1 ], "revision": None, "parse": True } )        # view readme
  301.                 # add ContextMenu: Readme
  302.                 if readme:
  303.                     cm +=  self._contextMenuItem( 30610, { "showreadme": True, "repo": None, "readme": readme } )
  304.  
  305.                 li.addContextMenuItems( cm, replaceItems=True )
  306.                 ok = xbmcplugin.addDirectoryItem( handle=int( sys.argv[ 1 ] ), url=path, listitem=li, isFolder=False, totalItems=sz )
  307.                 if ( not ok ): break
  308.             except:
  309.                 # user cancelled dialog or an error occurred
  310.                 logError()
  311.                 print info
  312.  
  313.         # if New Updates; add Update All item
  314.         log("Updated Count=%d" % len(updateAllItems))
  315.         if updateAllItems:
  316.             icon = "DefaultFile.png"
  317.             text = xbmc.getLocalizedString( 30019 )
  318.             li=xbmcgui.ListItem( text, "", icon, icon)
  319.             li.setInfo( type="Video", infoLabels={ "Title": text, "Genre": "" } )
  320.             path = '%s?download_url=update_all' % ( sys.argv[ 0 ], )
  321.             xbmcplugin.addDirectoryItem( handle=int( sys.argv[ 1 ] ), url=path, listitem=li, isFolder=False, totalItems=sz )
  322.             # save update_all dict to file
  323.             saveFileObj(self.UPDATE_ALL_FILENAME, updateAllItems)
  324.  
  325.         # add list sort methods
  326.         xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_GENRE )
  327. #            xbmcplugin.setContent( handle=int( sys.argv[ 1 ] ), content="files")
  328.         ok = True
  329.  
  330.         log("< showUpdates() ok=%s" % ok)
  331.         return ok
  332.  
  333.     #####################################################################################################
  334.     def _contextMenuItem(self, stringId, args={}, runType="RunPlugin"):
  335.         """ create a tuple suitable for adding to a list of Context Menu items """
  336.         if isinstance(stringId, int):
  337.             title = xbmc.getLocalizedString(stringId)
  338.         else:
  339.             title = stringId
  340.  
  341.         if args:
  342.             argsStr = ""
  343.             for key, value in args.items():
  344.                 argsStr += "&%s=%s" % ( key, urllib.quote_plus( repr(value) ) )
  345.             runStr = ("XBMC.%s(%s?%s)" % ( runType, sys.argv[ 0 ], argsStr )).replace('?&','?')
  346.         else:
  347.             runStr = "XBMC.%s(%s)" % ( runType, sys.argv[ 0 ] )
  348.         return [ (title, runStr), ]
  349.  
  350.     def show_info( self ):
  351.         log("show_info()")
  352.         
  353.  
  354. if ( __name__ == "__main__" ):
  355.     try:
  356.         Main()
  357.     except:
  358.         handleException("Main()")
  359.  
  360.