Project ION: remove unvanted files
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.program.repo.installer"
|
||||
name="Repositories Installer"
|
||||
version="1.0.3"
|
||||
provider-name="Temhil and Frost (passion-xbmc.org)">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="1.0"/>
|
||||
</requires>
|
||||
<extension point="xbmc.python.pluginsource"
|
||||
library="default.py">
|
||||
<provides>executable</provides>
|
||||
</extension>
|
||||
<extension point="xbmc.addon.metadata">
|
||||
<summary>Installer of Add-on Repositories for XBMC</summary>
|
||||
<description>This Add-on allow to select (from XBMC Wiki) and install Repositories of XBMC Add-ons</description>
|
||||
<platform>all</platform>
|
||||
</extension>
|
||||
</addon>
|
@ -1,17 +0,0 @@
|
||||
2011-03-21 Version 1.0.3 by Temhil
|
||||
- Added Repository info window
|
||||
- Set default title display option without description
|
||||
|
||||
2011-03-17 Version 1.0.2 by Temhil
|
||||
- Added option to add or not description from title
|
||||
- Added option for activating or not color of description (set it by default)
|
||||
- Removed Bold Title
|
||||
|
||||
2011-03-15 - Version 1.0.1 by Temhil
|
||||
- Added Icon (thank to Willynuisance)
|
||||
- Added settings allowing to change color of description
|
||||
|
||||
2011-03-13 - Version 1.0.0 by Temhil and Frost
|
||||
- Creation (installation part based on Frost work with script.addon.installer)
|
||||
|
||||
|
@ -1,376 +0,0 @@
|
||||
# -*- coding: cp1252 -*-
|
||||
"""
|
||||
Repository Installer Addon (plugin type) allowing to find and install addon repositories for XBMC
|
||||
|
||||
Changelog:
|
||||
|
||||
03-21-2011 Version 1.0.3 by Temhil
|
||||
- Added Repository info window
|
||||
- Set default title display option without description
|
||||
|
||||
03-17-2011 Version 1.0.2 by Temhil
|
||||
- Added option to add or not description from title
|
||||
- Added option for activating or not color of description (set it by default)
|
||||
- Removed Bold Title
|
||||
|
||||
03-15-2011 Version 1.0.1 by Temhil
|
||||
- Added Icon (thank to Willynuisance)
|
||||
- Added settings allowing to change color of description
|
||||
|
||||
03-13-2011 Version 1.0.0 by Temhil and Frost
|
||||
- Creation (installation part based on Frost work with script.addon.installer)
|
||||
"""
|
||||
|
||||
REMOTE_DBG = False # For remote debugging with PyDev (Eclipse)
|
||||
|
||||
|
||||
__script__ = "Unknown"
|
||||
__plugin__ = "Repositories Installer"
|
||||
__addonID__ = "plugin.program.repo.installer"
|
||||
__author__ = "Temhil and Frost (http://passion-xbmc.org)"
|
||||
__url__ = "http://passion-xbmc.org/index.php"
|
||||
__svn_url__ = "http://passion-xbmc.googlecode.com/svn/trunk/addons/plugin.program.repository.installer/"
|
||||
__credits__ = "Team XBMC Passion"
|
||||
__platform__ = "xbmc media center"
|
||||
__date__ = "03-21-2011"
|
||||
__version__ = "1.0.3"
|
||||
__svn_revision__ = 0
|
||||
|
||||
|
||||
import os
|
||||
import urllib
|
||||
from traceback import print_exc
|
||||
|
||||
# xbmc modules
|
||||
import xbmc
|
||||
import xbmcplugin
|
||||
import xbmcgui
|
||||
import xbmcaddon
|
||||
|
||||
|
||||
|
||||
__addon__ = xbmcaddon.Addon( __addonID__ )
|
||||
__settings__ = __addon__
|
||||
__language__ = __addon__.getLocalizedString
|
||||
__addonDir__ = __settings__.getAddonInfo( "path" )
|
||||
|
||||
|
||||
# Remote debugger using Eclipse and Pydev
|
||||
if REMOTE_DBG:
|
||||
# Note pydevd module need to be copied in XBMC\system\python\Lib\pysrc
|
||||
try:
|
||||
import pysrc.pydevd as pydevd
|
||||
pydevd.settrace('localhost', stdoutToServer=True, stderrToServer=True)
|
||||
except ImportError:
|
||||
sys.stderr.write("Error: " +
|
||||
"You must add org.python.pydev.debug.pysrc to XBMC\system\python\Lib\pysrc")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
ROOTDIR = os.getcwd()
|
||||
BASE_RESOURCE_PATH = os.path.join( ROOTDIR, "resources" )
|
||||
MEDIA_PATH = os.path.join( BASE_RESOURCE_PATH, "media" )
|
||||
ADDON_DATA = xbmc.translatePath( "special://profile/addon_data/%s/" % __addonID__ )
|
||||
REPO_LIST_URL = "http://wiki.xbmc.org/index.php?title=Unofficial_Add-on_Repositories"
|
||||
REPO_PACKAGE_DIR = "special://home/addons/packages/"
|
||||
REPO_INSTALL_DIR = "special://home/addons/"
|
||||
|
||||
DIALOG_PROGRESS = xbmcgui.DialogProgress()
|
||||
|
||||
#modules custom
|
||||
try:
|
||||
import resources.lib.wikiparser as wikiparser
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
|
||||
|
||||
|
||||
class RepoInstallerPlugin:
|
||||
"""
|
||||
main plugin class
|
||||
"""
|
||||
# define param key names
|
||||
PARAM_NAME = 'name'
|
||||
PARAM_ACTION = 'action'
|
||||
PARAM_URL = 'url'
|
||||
VALUE_INSTALL_FROM_ZIP = 'installfromzip'
|
||||
VALUE_INSTALL_FROM_REPO = 'installfromrepo'
|
||||
VALUE_INSTALL_ALL = 'installfromzip'
|
||||
VALUE_DISPLAY_INFO = 'displayinfo'
|
||||
|
||||
# Constant
|
||||
colorList = ["red", "green", "yellow", "lightblue", None]
|
||||
debugMode = False
|
||||
shortTitleDisplay = False
|
||||
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
|
||||
# Parse plugin parameters
|
||||
self.parameters = self._parse_params()
|
||||
|
||||
# Check settings
|
||||
#if ( __settings__.getSetting('first_run') == 'true' ):
|
||||
# #xbmcplugin.openSettings(sys.argv[0])
|
||||
#else:
|
||||
# self.select()
|
||||
self._set_title_display()
|
||||
self.select()
|
||||
|
||||
|
||||
def create_root_dir ( self ):
|
||||
print "createRootDir"
|
||||
xbmcplugin.setPluginCategory( handle=int( sys.argv[ 1 ] ), category=__language__( 30001 ) )
|
||||
print "Loading wiki page: %s"%REPO_LIST_URL
|
||||
wikiparser.getRepoList(REPO_LIST_URL, addItemFunc=self._addLink, progressBar=None, msgFunc=None )
|
||||
self._add_sort_methods( True )
|
||||
self._end_of_directory( True )
|
||||
|
||||
|
||||
def install_repo(self, repoName, repoURL):
|
||||
"""
|
||||
Install a repository in XBMC
|
||||
-> will need XBMC restart in order to have the new Repo taken in account by XBMC
|
||||
"""
|
||||
continueInstall = True
|
||||
dialogYesNo = xbmcgui.Dialog()
|
||||
if dialogYesNo.yesno(repoName, __language__( 30100 ), __language__( 30101 )):
|
||||
if continueInstall:
|
||||
ri = RepoInstaller()
|
||||
|
||||
newRepo = ri.download( repoURL )
|
||||
print newRepo
|
||||
|
||||
if newRepo:
|
||||
fp, ok = ri.install( newRepo )
|
||||
print "---"
|
||||
print fp, ok
|
||||
xbmc.executebuiltin( 'XBMC.UpdateAddonRepos()' )
|
||||
try:
|
||||
_N_ = Addon( os.path.basename( fp ) )
|
||||
print "Addon %s Installed"%s_N_
|
||||
ri.notification( _N_.getAddonInfo( "name" ), __language__( 24065 ).encode( "utf-8" ), 5000, _N_.getAddonInfo( "icon" ) )
|
||||
except:
|
||||
xbmcgui.Dialog().ok( __settings__.getAddonInfo( "name" ), __language__( 30007 ) + " : " + repoName, __language__( 30010 ) )
|
||||
self._end_of_directory( True, update=False )
|
||||
|
||||
|
||||
|
||||
def select( self ):
|
||||
try:
|
||||
print "select"
|
||||
print self.parameters
|
||||
if len(self.parameters) < 1:
|
||||
self.create_root_dir()
|
||||
|
||||
elif self.PARAM_ACTION in self.parameters.keys():
|
||||
if self.parameters[self.PARAM_ACTION] == self.VALUE_INSTALL_FROM_ZIP:
|
||||
repoName = self.parameters[self.PARAM_NAME]
|
||||
repoURL = self.parameters[self.PARAM_URL]
|
||||
#print repoName
|
||||
#print repoURL
|
||||
#xbmc.executebuiltin('XBMC.ActivateWindow(146)')
|
||||
#xbmc.executebuiltin( "Action(Info)")
|
||||
|
||||
self.install_repo(repoName, repoURL)
|
||||
elif self.parameters[self.PARAM_ACTION] == self.VALUE_DISPLAY_INFO:
|
||||
try:
|
||||
from resources.lib.DialogRepoInfo import DialogRepoInfo
|
||||
repoWindow = DialogRepoInfo( "DialogRepoInfo.xml", os.getcwd(), "Default", "720p" )
|
||||
del repoWindow
|
||||
except:
|
||||
print_exc()
|
||||
self._end_of_directory( False )
|
||||
else:
|
||||
self._end_of_directory( True, update=False )
|
||||
|
||||
except:
|
||||
print_exc()
|
||||
self._end_of_directory( False )
|
||||
|
||||
|
||||
def _parse_params( self ):
|
||||
"""
|
||||
Parses Plugin parameters and returns it as a dictionary
|
||||
"""
|
||||
paramDic={}
|
||||
# Parameters are on the 3rd arg passed to the script
|
||||
paramStr=sys.argv[2]
|
||||
print paramStr
|
||||
if len(paramStr)>1:
|
||||
paramStr = paramStr.replace('?','')
|
||||
|
||||
# Ignore last char if it is a '/'
|
||||
if (paramStr[len(paramStr)-1]=='/'):
|
||||
paramStr=paramStr[0:len(paramStr)-2]
|
||||
|
||||
# Processing each parameter splited on '&'
|
||||
for param in paramStr.split("&"):
|
||||
try:
|
||||
# Splitting couple key/value
|
||||
key,value=param.split("=")
|
||||
except:
|
||||
key=param
|
||||
value=""
|
||||
|
||||
key = urllib.unquote_plus(key)
|
||||
value = urllib.unquote_plus(value)
|
||||
|
||||
# Filling dictionary
|
||||
paramDic[key]=value
|
||||
print paramDic
|
||||
return paramDic
|
||||
|
||||
|
||||
def _create_param_url(self, paramsDic):
|
||||
"""
|
||||
Create an plugin URL based on the key/value passed in a dictionary
|
||||
"""
|
||||
url = sys.argv[ 0 ]
|
||||
sep = '?'
|
||||
print paramsDic
|
||||
try:
|
||||
for param in paramsDic:
|
||||
#TODO: solve error on name with non ascii char (generate exception)
|
||||
url = url + sep + urllib.quote_plus( param ) + '=' + urllib.quote_plus( paramsDic[param] )
|
||||
sep = '&'
|
||||
except:
|
||||
url = None
|
||||
print_exc()
|
||||
return url
|
||||
|
||||
def _set_title_display(self):
|
||||
descriptInTitle =__settings__.getSetting('desintitle')
|
||||
if descriptInTitle == 'true':
|
||||
self.shortTitleDisplay = False
|
||||
else:
|
||||
self.shortTitleDisplay = True
|
||||
|
||||
def _addLink( self, itemInfo ):
|
||||
"""
|
||||
Add a link to the list of items
|
||||
"""
|
||||
ok=True
|
||||
|
||||
print itemInfo
|
||||
|
||||
if itemInfo["ImageUrl"]:
|
||||
icon = itemInfo["ImageUrl"]
|
||||
else:
|
||||
#icon = "DefaultFolder.png"
|
||||
#icon = "DefaultAddon.png"
|
||||
icon = os.path.join(MEDIA_PATH, "DefaultAddonRepository.png")
|
||||
|
||||
descriptColor = self.colorList[ int( __settings__.getSetting( "descolor" ) ) ]
|
||||
|
||||
if self.shortTitleDisplay:
|
||||
labelTxt = itemInfo["name"]
|
||||
else:
|
||||
labelTxt = itemInfo["name"] + ": " + self._coloring( itemInfo["description"], descriptColor )
|
||||
liz=xbmcgui.ListItem( label=labelTxt, iconImage=icon, thumbnailImage=icon )
|
||||
liz.setInfo( type="addons",
|
||||
infoLabels={ "title": itemInfo["name"], "Plot": itemInfo["description"] } )
|
||||
liz.setProperty("Addon.Name",itemInfo["name"])
|
||||
liz.setProperty("Addon.Version"," ")
|
||||
liz.setProperty("Addon.Summary", "")
|
||||
liz.setProperty("Addon.Description", itemInfo["description"])
|
||||
liz.setProperty("Addon.Type", __language__( 30011 ))
|
||||
liz.setProperty("Addon.Creator", itemInfo["owner"])
|
||||
liz.setProperty("Addon.Disclaimer","")
|
||||
liz.setProperty("Addon.Changelog", "")
|
||||
liz.setProperty("Addon.ID", "")
|
||||
liz.setProperty("Addon.Status", "Stable")
|
||||
liz.setProperty("Addon.Broken", "Stable")
|
||||
liz.setProperty("Addon.Path","")
|
||||
liz.setProperty("Addon.Icon",icon)
|
||||
|
||||
|
||||
|
||||
#dirItem.addContextMenuItem( self.Addon.getLocalizedString( 30900 ), "XBMC.RunPlugin(%s?showtimes=%s)" % ( sys.argv[ 0 ], urllib.quote_plus( repr( video[ "title" ] ) ), ) )
|
||||
paramsMenu = {}
|
||||
paramsMenu[self.PARAM_NAME] = itemInfo["name"]
|
||||
paramsMenu[self.PARAM_ACTION] = self.VALUE_DISPLAY_INFO
|
||||
urlMenu = self._create_param_url( paramsMenu )
|
||||
if urlMenu:
|
||||
c_items = [ ( __language__( 30012 ), "XBMC.RunPlugin(%s)" % ( urlMenu)) ]
|
||||
liz.addContextMenuItems( c_items )
|
||||
params = {}
|
||||
params[self.PARAM_NAME] = itemInfo["name"]
|
||||
params[self.PARAM_ACTION] = self.VALUE_INSTALL_FROM_ZIP
|
||||
params[self.PARAM_URL] = itemInfo["repoUrl"]
|
||||
urlRepo = self._create_param_url( params )
|
||||
if urlRepo:
|
||||
ok=xbmcplugin.addDirectoryItem( handle=int(sys.argv[1]), url=urlRepo, listitem=liz, isFolder=False )
|
||||
return ok
|
||||
|
||||
|
||||
def _end_of_directory( self, OK, update=False ):
|
||||
xbmcplugin.endOfDirectory( handle=int( sys.argv[ 1 ] ), succeeded=OK, updateListing=update )#, cacheToDisc=True )#updateListing = True,
|
||||
|
||||
def _add_sort_methods( self, OK ):
|
||||
if ( OK ):
|
||||
try:
|
||||
xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_UNSORTED )
|
||||
xbmcplugin.addSortMethod( handle=int( sys.argv[ 1 ] ), sortMethod=xbmcplugin.SORT_METHOD_LABEL )
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def _coloring( self, text , color ):
|
||||
if color:
|
||||
if color == "red": color="FFFF0000"
|
||||
if color == "green": color="FF00FF00"
|
||||
if color == "yellow": color="FFFFFF00"
|
||||
if color == "lightblue": color="FFB1C7EC"
|
||||
colored_text = "[COLOR=%s]%s[/COLOR]" % ( color , text )
|
||||
else:
|
||||
colored_text = text
|
||||
return colored_text
|
||||
|
||||
def _bold_text( self, text ):
|
||||
""" FONCTION POUR METTRE UN MOT GRAS """
|
||||
return "[B]%s[/B]" % ( text, )
|
||||
|
||||
|
||||
|
||||
class RepoInstaller:
|
||||
"""
|
||||
main plugin class
|
||||
"""
|
||||
def download( self, url, destination=REPO_PACKAGE_DIR ):
|
||||
try:
|
||||
DIALOG_PROGRESS.create( __settings__.getAddonInfo( "name" ) )
|
||||
destination = xbmc.translatePath( destination ) + os.path.basename( url )
|
||||
def _report_hook( count, blocksize, totalsize ):
|
||||
percent = int( float( count * blocksize * 100 ) / totalsize )
|
||||
DIALOG_PROGRESS.update( percent, __language__( 30005 ) % url, __language__( 30006 ) % destination )
|
||||
fp, h = urllib.urlretrieve( url, destination, _report_hook )
|
||||
print fp, h
|
||||
return fp
|
||||
except:
|
||||
print_exc()
|
||||
DIALOG_PROGRESS.close()
|
||||
return ""
|
||||
|
||||
|
||||
def install( self, filename ):
|
||||
from resources.lib.extractor import extract
|
||||
return extract( filename, xbmc.translatePath( REPO_INSTALL_DIR ) )
|
||||
|
||||
|
||||
def notification( self, header="", message="", sleep=5000, icon=__settings__.getAddonInfo( "icon" ) ):
|
||||
""" Will display a notification dialog with the specified header and message,
|
||||
in addition you can set the length of time it displays in milliseconds and a icon image.
|
||||
"""
|
||||
xbmc.executebuiltin( "XBMC.Notification(%s,%s,%i,%s)" % ( header, message, sleep, icon ) )
|
||||
|
||||
|
||||
#######################################################################################################################
|
||||
# BEGIN !
|
||||
#######################################################################################################################
|
||||
|
||||
if ( __name__ == "__main__" ):
|
||||
try:
|
||||
RepoInstallerPlugin()
|
||||
except:
|
||||
print_exc()
|
Before Width: | Height: | Size: 53 KiB |
@ -1 +0,0 @@
|
||||
# Dummy file to make this directory a package.
|
@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<strings>
|
||||
<!-- Les strings de 30000 à 30999 sont réservées aux paramètres plug-in -->
|
||||
<!--GENERAL STRINGS -->
|
||||
<string id="30000">Repositories Installer</string>
|
||||
<string id="30001">Add-ons Repositories</string>
|
||||
|
||||
<string id="30005">Downloading: %s </string>
|
||||
<string id="30006">to: %s</string>
|
||||
<string id="30007">Repository Installed</string>
|
||||
<string id="30010">XBMC requires to restart!</string>
|
||||
<string id="30011">Repository</string>
|
||||
<string id="30012">Repository Info</string>
|
||||
|
||||
<!--CONTEXT MENU STRINGS -->
|
||||
|
||||
<!--DIALOG STRINGS -->
|
||||
<string id="30100">Are you sure you want to Install this repository?</string>
|
||||
<string id="30101">This will overwrite any repository with the same name</string>
|
||||
<string id="30110">This repository is already installed</string>
|
||||
<string id="30111">Do you want to continue and overwrite the existing repository?</string>
|
||||
|
||||
<string id="30200">Error!</string>
|
||||
<string id="30201">Error during %s repository install</string>
|
||||
<string id="30202">Please check the logs</string>
|
||||
|
||||
<!--SETTINGS STRINGS -->
|
||||
<string id="30500">Appearance</string>
|
||||
<string id="30501">Text color of description:</string>
|
||||
<string id="30520">Red</string>
|
||||
<string id="30521">Green</string>
|
||||
<string id="30522">Yellow</string>
|
||||
<string id="30523">Light Blue</string>
|
||||
<string id="30524">None</string>
|
||||
|
||||
<string id="30530">Display description with title</string>
|
||||
</strings>
|
@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<strings>
|
||||
<!-- Les strings de 30000 à 30999 sont réservées aux paramètres plug-in -->
|
||||
<!--GENERAL STRINGS -->
|
||||
<string id="30000">Repositories Installer</string>
|
||||
<string id="30001">Repositories d'Add-ons </string>
|
||||
|
||||
<string id="30005">Téléchargement: %s </string>
|
||||
<string id="30006">vers: %s</string>
|
||||
<string id="30007">Repository Installé</string>
|
||||
<string id="30010">XBMC doit redémarrer!</string>
|
||||
<string id="30011">Repository</string>
|
||||
<string id="30012">Infos Repository</string>
|
||||
|
||||
<!--CONTEXT MENU STRINGS -->
|
||||
|
||||
<!--DIALOG STRINGS -->
|
||||
<string id="30100">Etes vous sure de vouloir installer ce repository?</string>
|
||||
<string id="30101">Cela écrasera tout repository du meme nom</string>
|
||||
<string id="30110">CE repository est déja installé</string>
|
||||
<string id="30111">Voulez-vous continuer et écraser le repository existant?</string>
|
||||
|
||||
<string id="30200">Erreur!</string>
|
||||
<string id="30201">Erreur durant l'installation du repository %s</string>
|
||||
<string id="30202">Veuillez vérifier les logs</string>
|
||||
|
||||
<!--SETTINGS STRINGS -->
|
||||
<string id="30500">Apparence</string>
|
||||
<string id="30501">Couleur du texte de description:</string>
|
||||
<string id="30520">Rouge</string>
|
||||
<string id="30521">Vert</string>
|
||||
<string id="30522">Jaune</string>
|
||||
<string id="30523">Bleu Ciel</string>
|
||||
<string id="30524">Aucune</string>
|
||||
|
||||
<string id="30530">Afficher la description avec le titre</string>
|
||||
</strings>
|
@ -1,121 +0,0 @@
|
||||
|
||||
import os
|
||||
from traceback import print_exc
|
||||
|
||||
import xbmc
|
||||
import xbmcgui
|
||||
from xbmcaddon import Addon
|
||||
|
||||
|
||||
############################################################################
|
||||
#get actioncodes from keymap.xml
|
||||
############################################################################
|
||||
#ACTION_MOVE_LEFT = 1
|
||||
#ACTION_MOVE_RIGHT = 2
|
||||
#ACTION_MOVE_UP = 3
|
||||
#ACTION_MOVE_DOWN = 4
|
||||
#ACTION_PAGE_UP = 5
|
||||
#ACTION_PAGE_DOWN = 6
|
||||
#ACTION_SELECT_ITEM = 7
|
||||
#ACTION_HIGHLIGHT_ITEM = 8
|
||||
ACTION_PARENT_DIR = 9
|
||||
ACTION_PREVIOUS_MENU = 10
|
||||
#ACTION_SHOW_INFO = 11
|
||||
#ACTION_PAUSE = 12
|
||||
#ACTION_STOP = 13
|
||||
#ACTION_NEXT_ITEM = 14
|
||||
#ACTION_PREV_ITEM = 15
|
||||
#ACTION_MUSIC_PLAY = 79
|
||||
#ACTION_MOUSE_CLICK = 100
|
||||
ACTION_CONTEXT_MENU = 117
|
||||
|
||||
|
||||
#__settings__ = Addon( "repository.xbmc.builds" )
|
||||
#__addonDir__ = __settings__.getAddonInfo( "path" )
|
||||
|
||||
#PROFILE_PATH = xbmc.translatePath( __settings__.getAddonInfo( "profile" ) )
|
||||
#DL_INFO_PATH = os.path.join( PROFILE_PATH, "iddl_data" )
|
||||
|
||||
|
||||
class DialogRepoInfo( xbmcgui.WindowXMLDialog ):
|
||||
|
||||
Addon = Addon( id=os.path.basename( os.getcwd() ) )
|
||||
ACTION_CLOSE_DIALOG_LIST = [ ACTION_PARENT_DIR, ACTION_PREVIOUS_MENU, ACTION_CONTEXT_MENU ]
|
||||
|
||||
def __init__( self, *args, **kwargs ):
|
||||
print "Creating DialogRepoInfo"
|
||||
xbmcgui.WindowXMLDialog.__init__( self, *args, **kwargs )
|
||||
|
||||
# Get information about the repository
|
||||
self._get_repo_info()
|
||||
# show dialog
|
||||
self.doModal()
|
||||
|
||||
def onInit( self ):
|
||||
# Show repo info
|
||||
self._show_repo_info()
|
||||
|
||||
def onFocus( self, controlID ):
|
||||
pass
|
||||
|
||||
def onClick( self, controlID ):
|
||||
try:
|
||||
kill = None
|
||||
if controlID == 199:
|
||||
self._close_dialog()
|
||||
# elif controlID == 99:
|
||||
# pass
|
||||
except:
|
||||
print_exc()
|
||||
|
||||
def onAction( self, action ):
|
||||
if action in self.ACTION_CLOSE_DIALOG_LIST:
|
||||
self._close_dialog()
|
||||
|
||||
def _get_repo_info( self ):
|
||||
# initialize our dictionary
|
||||
print "_get_repo_info"
|
||||
self.repo = {}
|
||||
self.repo[ "Name" ] = unicode( xbmc.getInfoLabel( "ListItem.Property(Addon.Name)" ), "utf-8" )
|
||||
self.repo[ "Description" ] = unicode( xbmc.getInfoLabel( "ListItem.Property(Addon.Description)" ), "utf-8" )
|
||||
self.repo[ "Icon" ] = xbmc.getInfoLabel( "ListItem.Property(Addon.Icon)" )
|
||||
self.repo[ "Type" ] = unicode( xbmc.getInfoLabel( "ListItem.Property(Addon.Type)" ), "utf-8" )
|
||||
self.repo[ "Creator" ] = unicode( xbmc.getInfoLabel( "ListItem.Property(Addon.Creator)" ), "utf-8" )
|
||||
#self.repo[ "Version" ] = unicode( xbmc.getInfoLabel( "ListItem.Property(Addon.Version)" ), "utf-8" )
|
||||
self.repo[ "Version" ] = None
|
||||
|
||||
print self.repo[ "Name" ]
|
||||
print self.repo
|
||||
|
||||
def _show_repo_info( self ):
|
||||
# set initial apple trailer info
|
||||
self._set_repo_info( name=self.repo[ "Name" ],
|
||||
description=self.repo[ "Description" ],
|
||||
creator=self.repo[ "Creator" ],
|
||||
type=self.repo[ "Type" ],
|
||||
version=self.repo[ "Version" ],
|
||||
icon=self.repo[ "Icon" ],
|
||||
)
|
||||
|
||||
def _set_repo_info( self, name="", description="", creator="", type="", version="", icon="" ):
|
||||
# grab the window
|
||||
wId = xbmcgui.Window( xbmcgui.getCurrentWindowDialogId() )
|
||||
|
||||
# set our info
|
||||
wId.setProperty( "Name", name )
|
||||
wId.setProperty( "Description", description )
|
||||
wId.setProperty( "Creator", creator )
|
||||
wId.setProperty( "Type", type )
|
||||
if version:
|
||||
wId.setProperty( "Version", version )
|
||||
wId.setProperty( "Icon", icon )
|
||||
|
||||
def _close_dialog( self ):
|
||||
self.close()
|
||||
|
||||
|
||||
if ( __name__ == "__main__" ):
|
||||
s = DialogRepoInfo( "DialogRepoInfo.xml", os.path.dirname( os.path.dirname( os.getcwd() ) ), "Default", "720p")
|
||||
del s
|
||||
#if ( __name__ == "__main__" ):
|
||||
# DialogDownloadProgress( "DialogRepoInfo.xml", __addonDir__ )
|
@ -1 +0,0 @@
|
||||
# Dummy file to make this directory a package.
|
@ -1,242 +0,0 @@
|
||||
"""
|
||||
extractor for zip and rar file and a future support file 7-zip.
|
||||
|
||||
frost
|
||||
"""
|
||||
|
||||
# Modules general
|
||||
import os
|
||||
import sys
|
||||
from tarfile import is_tarfile
|
||||
from zipfile import is_zipfile
|
||||
from traceback import print_exc
|
||||
|
||||
|
||||
# Modules XBMC
|
||||
from xbmcgui import DialogProgress
|
||||
from xbmc import executebuiltin, sleep
|
||||
|
||||
# Modules Custom
|
||||
import shutil2
|
||||
|
||||
|
||||
DIALOG_PROGRESS = DialogProgress()
|
||||
|
||||
try:
|
||||
#FONCTION POUR RECUPERER LES LABELS DE LA LANGUE.
|
||||
_ = sys.modules[ "__main__" ].__language__
|
||||
except:
|
||||
lang = { 110: "Please wait...", 187: "UnRar: %i of %i items", 188: "UnZip: %i of %i items" }
|
||||
def _( id ): return lang[ id ]
|
||||
|
||||
|
||||
def is_rarfile( filename ):
|
||||
RAR_ID = "Rar!\x1a\x07\x00"
|
||||
buf = open( filename, "rb" ).read( len( RAR_ID ) )
|
||||
return buf == RAR_ID
|
||||
|
||||
|
||||
def get_time_sleep( filename ):
|
||||
# faut vraiment laisser xbmc le temps d'extraire l'archive environ 1 seconde pour 1 mo
|
||||
# plus l'archive est grosse plus cela va etre long
|
||||
try:
|
||||
slp = int( os.path.getsize( filename ) / 1000 )
|
||||
except:
|
||||
print_exc()
|
||||
slp = 0
|
||||
if slp < 1000: slp = 1000
|
||||
return slp
|
||||
|
||||
|
||||
def unrar( filename, destination=None, report=False ):
|
||||
from rarfile import RarFile
|
||||
base_dir = ""
|
||||
if destination is None:
|
||||
destination = os.path.dirname( filename )
|
||||
try:
|
||||
rar = RarFile( filename, "r" )
|
||||
namelist = rar.namelist()
|
||||
total_items = len( namelist ) or 1
|
||||
diff = 100.0 / total_items
|
||||
percent = 0
|
||||
# nom du dossier racine
|
||||
root_dir = namelist[ -1 ]
|
||||
is_root_dir = True
|
||||
# si root_dir n'est pas un dossier ou n'est pas la racine, on se base sur le nom de l'archive
|
||||
#print root_dir
|
||||
if not rar.getinfo( root_dir ).isdir():
|
||||
is_root_dir = False
|
||||
else:
|
||||
for i in namelist:
|
||||
#print root_dir in i, i
|
||||
if not root_dir in i:
|
||||
is_root_dir = False
|
||||
break
|
||||
if not is_root_dir:#rar.getinfo( root_dir ).isdir():
|
||||
root_dir = os.path.basename( os.path.splitext( filename )[ 0 ] )
|
||||
base_dir = os.path.join( destination, root_dir )
|
||||
if os.path.isdir( base_dir ):
|
||||
shutil2.rmtree( base_dir )
|
||||
os.makedirs( base_dir )
|
||||
time_sleep = get_time_sleep( filename )
|
||||
# avec cette methode on extract dans le dossier ou est l'archive
|
||||
ok = executebuiltin( 'XBMC.Extract(%s)' % ( filename, ) )
|
||||
#sleep une seconde ou plus selon la drosseur du fichier le temps que le builtin est fini car l'action suivante
|
||||
# "os.listdir" est excecuter avant la fin du builtin
|
||||
sleep( time_sleep )
|
||||
# si le dossier base_dir est vide on move les items de namelist dedans
|
||||
if not os.listdir( base_dir ):
|
||||
for item in namelist:
|
||||
src = os.path.normpath( os.path.join( os.path.dirname( filename ), item ) )
|
||||
dst = os.path.normpath( os.path.join( base_dir, item ) )
|
||||
if not rar.getinfo( item ).isdir():
|
||||
if not os.path.isdir( os.path.dirname( dst ) ):
|
||||
os.makedirs( os.path.dirname( dst ) )
|
||||
shutil2.move( src, dst, overwrite=True )
|
||||
elif os.path.exists( src ) and not os.listdir( src ):
|
||||
shutil2.rmtree( src )
|
||||
#maintenant on verifier l'extraction d'xbmc avec la liste de la lib rarfile
|
||||
if os.path.isdir( base_dir ):
|
||||
size = 0
|
||||
list_size = 0
|
||||
if not root_dir in namelist:
|
||||
list_size -= 1
|
||||
namelist = [ os.path.split( item )[ 1 ] for item in namelist ]
|
||||
for root, dirs, files in os.walk( base_dir, topdown=False ):
|
||||
percent += diff
|
||||
list_size += 1
|
||||
for file in files:
|
||||
percent += diff
|
||||
list_size += 1
|
||||
if report:
|
||||
if DIALOG_PROGRESS.iscanceled():
|
||||
break
|
||||
DIALOG_PROGRESS.update( int( percent ), _( 187 ) % ( list_size, total_items ), file, _( 110 ) )
|
||||
#print round( percent, 2 ), file
|
||||
if file in namelist:
|
||||
size += os.path.getsize( os.path.join( root, file ) )
|
||||
else:
|
||||
print "Error %s est dans la liste de depart!" % file
|
||||
#print size
|
||||
if not size:
|
||||
print "Error for extracting rar: %s" % filename
|
||||
rar.close()
|
||||
del rar
|
||||
# si list_size est pas declarer une erreur automatique est creer ;)
|
||||
return base_dir, list_size == total_items
|
||||
except:
|
||||
print_exc()
|
||||
return "", False
|
||||
|
||||
|
||||
def unzip( filename, destination=None, report=False ):
|
||||
from zipfile import ZipFile
|
||||
base_dir = ""
|
||||
if destination is None:
|
||||
destination = os.path.dirname( filename ) #=> extraction in current directory
|
||||
try:
|
||||
zip = ZipFile( filename, "r" )
|
||||
namelist = zip.namelist()
|
||||
total_items = len( namelist ) or 1
|
||||
diff = 100.0 / total_items
|
||||
percent = 0
|
||||
# nom du dossier racine
|
||||
root_dir = namelist[ 0 ]
|
||||
is_root_dir = True
|
||||
# si root_dir n'est pas un dossier ou n'est pas la racine, on se base sur le nom de l'archive
|
||||
#print root_dir
|
||||
if not root_dir.endswith( "/" ) and ( zip.getinfo( root_dir ).file_size > 0 ):
|
||||
is_root_dir = False
|
||||
else:
|
||||
for i in namelist:
|
||||
#print root_dir in i, i
|
||||
if not root_dir in i:
|
||||
is_root_dir = False
|
||||
break
|
||||
base_dir = os.path.join( destination, root_dir.rstrip( "/" ) )
|
||||
if not is_root_dir:#root_dir.endswith( "/" ) and ( zip.getinfo( root_dir ).file_size > 0 ):
|
||||
root_dir = os.path.basename( os.path.splitext( filename )[ 0 ] )
|
||||
destination = os.path.join( destination, root_dir )
|
||||
base_dir = destination
|
||||
if os.path.isdir( base_dir ):
|
||||
shutil2.rmtree( base_dir )
|
||||
os.makedirs( base_dir )
|
||||
for count, item in enumerate( namelist ):
|
||||
percent += diff
|
||||
if report:
|
||||
if DIALOG_PROGRESS.iscanceled():
|
||||
break
|
||||
DIALOG_PROGRESS.update( int( percent ), _( 188 ) % ( count + 1, total_items ), item, _( 110 ) )
|
||||
#print round( percent, 2 ), item
|
||||
if not item.endswith( "/" ):
|
||||
root, name = os.path.split( item )
|
||||
directory = os.path.normpath( os.path.join( destination, root ) )
|
||||
if not os.path.isdir( directory ): os.makedirs( directory )
|
||||
file( os.path.join( directory, name ), "wb" ).write( zip.read( item ) )
|
||||
zip.close()
|
||||
del zip
|
||||
return base_dir, True
|
||||
except:
|
||||
print_exc()
|
||||
return "", False
|
||||
|
||||
|
||||
def extract_tarfile( filename, destination=None ):
|
||||
import tarfile
|
||||
base_dir = ""
|
||||
try:
|
||||
# is_tarfile, Return True if name is a tar archive file, that the tarfile module can read.
|
||||
if tarfile.is_tarfile( filename ):
|
||||
# if not destination, set destination to current filename
|
||||
if destination is None:
|
||||
destination = os.path.dirname( filename )
|
||||
# open tarfile
|
||||
tar = tarfile.open( filename )#, 'r:gz' )
|
||||
# extractall, New in version 2.5 or greater
|
||||
if hasattr( tar, 'extractall' ):
|
||||
tar.extractall( destination )
|
||||
else:
|
||||
# if not extractall use standard extract
|
||||
[ tar.extract( tarinfo , destination ) for tarinfo in tar ]
|
||||
root_dir = tar.getnames()[ 0 ].strip( "/" )
|
||||
base_dir = os.path.join( destination, root_dir )
|
||||
# close tarfile
|
||||
tar.close()
|
||||
return base_dir, True
|
||||
except:
|
||||
print_exc()
|
||||
return "", False
|
||||
|
||||
|
||||
def filetype( filename ):
|
||||
try:
|
||||
#Check quickly whether file is rar archive.
|
||||
if is_rarfile( filename ): return "is_rar"
|
||||
#Quickly see if file is a ZIP file by checking the magic number.
|
||||
if is_zipfile( filename ): return "is_zip"
|
||||
#Return True if name points to a tar archive that we are able to handle, else return False.
|
||||
if is_tarfile( filename ): return "is_tar"
|
||||
except:
|
||||
print_exc()
|
||||
return "Inconnue"
|
||||
|
||||
|
||||
def extract( filename, destination=None, report=False ):
|
||||
type = filetype( filename )
|
||||
if type == "is_zip":
|
||||
return unzip( filename, destination, report )
|
||||
elif type == "is_rar":
|
||||
return unrar( filename, destination, report )
|
||||
elif type == "is_tar":
|
||||
return extract_tarfile( filename, destination )
|
||||
#elif type == "is_7z":
|
||||
# # test for future support 7-zip archive, not supported for a moment
|
||||
# # mais il semblerais que le librairie "pylzma" marche bien, http://www.joachim-bauch.de/projects/python/pylzma/
|
||||
# # reste a compiler cette lib pour xbmc linux, win32/xbox et osx semble pas etre supporter
|
||||
# # Note faut compiler cette lib avec python 2.4, sinon elle sera pas compatible avec xbmc, pas certain a 100 pour 100.
|
||||
# #ok = executebuiltin( 'XBMC.Extract(%s)' % ( filename, ) )
|
||||
# print "L'archive '%s' n'est pas pris en charge..." % os.path.basename( filename )
|
||||
else:
|
||||
print "L'archive '%s' n'est pas pris en charge..." % os.path.basename( filename )
|
||||
|
||||
return "", False
|
@ -1,488 +0,0 @@
|
||||
# rarfile.py, http://grue.l-t.ee/~marko/src/rarfile/
|
||||
#
|
||||
# Copyright (c) 2005 Marko Kreen <marko@l-t.ee>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import os, re
|
||||
from struct import pack, unpack
|
||||
from binascii import crc32
|
||||
from cStringIO import StringIO
|
||||
|
||||
# whether to speed up decompression by using tmp archive
|
||||
_use_extract_hack = 1
|
||||
|
||||
# command line to use for extracting
|
||||
_extract_cmd = 'unrar p -inul "%s" "%s"'
|
||||
|
||||
#
|
||||
# rar constants
|
||||
#
|
||||
|
||||
RAR_ID = "Rar!\x1a\x07\x00"
|
||||
|
||||
# block types
|
||||
RAR_BLOCK_MARK = 0x72 # r
|
||||
RAR_BLOCK_MAIN = 0x73 # s
|
||||
RAR_BLOCK_FILE = 0x74 # t
|
||||
RAR_BLOCK_OLD_COMMENT = 0x75 # u
|
||||
RAR_BLOCK_OLD_EXTRA = 0x76 # v
|
||||
RAR_BLOCK_OLD_SUB = 0x77 # w
|
||||
RAR_BLOCK_OLD_RECOVERY = 0x78 # x
|
||||
RAR_BLOCK_OLD_AUTH = 0x79 # y
|
||||
RAR_BLOCK_SUB = 0x7a # z
|
||||
RAR_BLOCK_ENDARC = 0x7b # {
|
||||
|
||||
# main header flags
|
||||
RAR_MAIN_VOLUME = 0x0001
|
||||
RAR_MAIN_COMMENT = 0x0002
|
||||
RAR_MAIN_LOCK = 0x0004
|
||||
RAR_MAIN_SOLID = 0x0008
|
||||
RAR_MAIN_NEWNUMBERING = 0x0010
|
||||
RAR_MAIN_AUTH = 0x0020
|
||||
RAR_MAIN_RECOVERY = 0x0040
|
||||
RAR_MAIN_PASSWORD = 0x0080
|
||||
RAR_MAIN_FIRSTVOLUME = 0x0100
|
||||
|
||||
# file header flags
|
||||
RAR_FILE_SPLIT_BEFORE = 0x0001
|
||||
RAR_FILE_SPLIT_AFTER = 0x0002
|
||||
RAR_FILE_PASSWORD = 0x0004
|
||||
RAR_FILE_COMMENT = 0x0008
|
||||
RAR_FILE_SOLID = 0x0010
|
||||
RAR_FILE_DICTMASK = 0x00e0
|
||||
RAR_FILE_DICT64 = 0x0000
|
||||
RAR_FILE_DICT128 = 0x0020
|
||||
RAR_FILE_DICT256 = 0x0040
|
||||
RAR_FILE_DICT512 = 0x0060
|
||||
RAR_FILE_DICT1024 = 0x0080
|
||||
RAR_FILE_DICT2048 = 0x00a0
|
||||
RAR_FILE_DICT4096 = 0x00c0
|
||||
RAR_FILE_DIRECTORY = 0x00e0
|
||||
RAR_FILE_LARGE = 0x0100
|
||||
RAR_FILE_UNICODE = 0x0200
|
||||
RAR_FILE_SALT = 0x0400
|
||||
RAR_FILE_VERSION = 0x0800
|
||||
RAR_FILE_EXTTIME = 0x1000
|
||||
RAR_FILE_EXTFLAGS = 0x2000
|
||||
|
||||
RAR_ENDARC_NEXT_VOLUME = 0x0001
|
||||
RAR_ENDARC_DATACRC = 0x0002
|
||||
RAR_ENDARC_REVSPACE = 0x0004
|
||||
|
||||
# flags common to all blocks
|
||||
RAR_SKIP_IF_UNKNOWN = 0x4000
|
||||
RAR_LONG_BLOCK = 0x8000
|
||||
|
||||
# Host OS types
|
||||
RAR_OS_MSDOS = 0
|
||||
RAR_OS_OS2 = 1
|
||||
RAR_OS_WIN32 = 2
|
||||
RAR_OS_UNIX = 3
|
||||
|
||||
#
|
||||
# Public interface
|
||||
#
|
||||
def is_rarfile(fn):
|
||||
'''Check quickly whether file is rar archive.'''
|
||||
buf = open(fn, "rb").read(len(RAR_ID))
|
||||
return buf == RAR_ID
|
||||
|
||||
class RarInfo:
|
||||
'''An entry in rar archive.'''
|
||||
|
||||
def isdir(self):
|
||||
'''Returns True if the entry is a directory.'''
|
||||
if self.type == RAR_BLOCK_FILE:
|
||||
return (self.flags & RAR_FILE_DIRECTORY) == RAR_FILE_DIRECTORY
|
||||
return False
|
||||
|
||||
class RarFile:
|
||||
'''Rar archive handling.'''
|
||||
def __init__(self, rarfile, mode="r", charset=None, info_callback=None):
|
||||
self.rarfile = rarfile
|
||||
self.charset = charset
|
||||
|
||||
self.info_list = []
|
||||
self.is_solid = 0
|
||||
self.uses_newnumbering = 0
|
||||
self.uses_volumes = 0
|
||||
self.info_callback = info_callback
|
||||
self.got_mainhdr = 0
|
||||
self._gen_volname = self._gen_oldvol
|
||||
|
||||
if mode != "r":
|
||||
raise Exception("Only mode=r supported")
|
||||
|
||||
self._parse()
|
||||
|
||||
def namelist(self):
|
||||
'''Return list of filenames in rar'''
|
||||
res = []
|
||||
for f in self.info_list:
|
||||
res.append(f.filename)
|
||||
return res
|
||||
|
||||
def infolist(self):
|
||||
'''Return rar entries.'''
|
||||
return self.info_list
|
||||
|
||||
def getinfo(self, fname):
|
||||
'''Return RarInfo for fname.'''
|
||||
fx = fname.replace("/", "\\")
|
||||
for f in self.info_list:
|
||||
if fname == f.filename or fx == f.filename:
|
||||
return f
|
||||
|
||||
def read(self, fname):
|
||||
'''Return decompressed data.'''
|
||||
inf = self.getinfo(fname)
|
||||
if not inf:
|
||||
raise Exception("No such file")
|
||||
|
||||
if inf.isdir():
|
||||
raise Exception("No data in directory")
|
||||
|
||||
if inf.compress_type == 0x30:
|
||||
res = self._extract_clear(inf)
|
||||
elif _use_extract_hack and not self.is_solid and not self.uses_volumes:
|
||||
res = self._extract_hack(inf)
|
||||
else:
|
||||
res = self._extract_unrar(self.rarfile, inf)
|
||||
return res
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def printdir(self):
|
||||
for f in self.info_list:
|
||||
print f.filename
|
||||
|
||||
# store entry
|
||||
def _process_entry(self, item):
|
||||
# RAR_BLOCK_NEWSUB has files too: CMT, RR
|
||||
if item.type == RAR_BLOCK_FILE:
|
||||
# use only first part
|
||||
if (item.flags & RAR_FILE_SPLIT_BEFORE) == 0:
|
||||
self.info_list.append(item)
|
||||
|
||||
if self.info_callback:
|
||||
self.info_callback(item)
|
||||
|
||||
# read rar
|
||||
def _parse(self):
|
||||
fd = open(self.rarfile, "rb")
|
||||
id = fd.read(len(RAR_ID))
|
||||
if id != RAR_ID:
|
||||
raise Exception("Not a Rar")
|
||||
|
||||
volume = 0 # first vol (.rar) is 0
|
||||
more_vols = 0
|
||||
while 1:
|
||||
h = self._parse_header(fd)
|
||||
if not h:
|
||||
if more_vols:
|
||||
volume += 1
|
||||
fd = open(self._gen_volname(volume), "rb")
|
||||
more_vols = 0
|
||||
if fd:
|
||||
continue
|
||||
break
|
||||
h.volume = volume
|
||||
|
||||
if h.type == RAR_BLOCK_MAIN and not self.got_mainhdr:
|
||||
if h.flags & RAR_MAIN_NEWNUMBERING:
|
||||
self.uses_newnumbering = 1
|
||||
self._gen_volname = self._gen_newvol
|
||||
self.uses_volumes = h.flags & RAR_MAIN_VOLUME
|
||||
self.is_solid = h.flags & RAR_MAIN_SOLID
|
||||
self.got_mainhdr = 1
|
||||
elif h.type == RAR_BLOCK_ENDARC:
|
||||
more_vols = h.flags & RAR_ENDARC_NEXT_VOLUME
|
||||
|
||||
# store it
|
||||
self._process_entry(h)
|
||||
|
||||
# skip data
|
||||
if h.add_size > 0:
|
||||
fd.seek(h.add_size, 1)
|
||||
|
||||
def _parse_header(self, fd):
|
||||
h = self._parse_block_header(fd)
|
||||
if h and (h.type == RAR_BLOCK_FILE or h.type == RAR_BLOCK_SUB):
|
||||
self._parse_file_header(h)
|
||||
return h
|
||||
|
||||
# common header
|
||||
def _parse_block_header(self, fd):
|
||||
HDRLEN = 7
|
||||
h = RarInfo()
|
||||
h.header_offset = fd.tell()
|
||||
buf = fd.read(HDRLEN)
|
||||
if not buf:
|
||||
return None
|
||||
|
||||
t = unpack("<HBHH", buf)
|
||||
h.header_crc, h.type, h.flags, h.header_size = t
|
||||
h.header_unknown = h.header_size - HDRLEN
|
||||
|
||||
if h.header_size > HDRLEN:
|
||||
h.data = fd.read(h.header_size - HDRLEN)
|
||||
else:
|
||||
h.data = ""
|
||||
h.file_offset = fd.tell()
|
||||
|
||||
if h.flags & RAR_LONG_BLOCK:
|
||||
h.add_size = unpack("<L", h.data[:4])[0]
|
||||
else:
|
||||
h.add_size = 0
|
||||
|
||||
# no crc check on that
|
||||
if h.type == RAR_BLOCK_MARK:
|
||||
return h
|
||||
|
||||
# check crc
|
||||
if h.type == RAR_BLOCK_MAIN:
|
||||
crcdat = buf[2:] + h.data[:6]
|
||||
elif h.type == RAR_BLOCK_OLD_AUTH:
|
||||
crcdat = buf[2:] + h.data[:8]
|
||||
else:
|
||||
crcdat = buf[2:] + h.data
|
||||
calc_crc = crc32(crcdat) & 0xFFFF
|
||||
|
||||
# return good header
|
||||
if h.header_crc == calc_crc:
|
||||
return h
|
||||
|
||||
# crc failed
|
||||
print "CRC mismatch! ofs =", h.header_offset
|
||||
# instead panicing, send eof
|
||||
return None
|
||||
|
||||
# read file-specific header
|
||||
def _parse_file_header(self, h):
|
||||
HDRLEN = 4+4+1+4+4+1+1+2+4
|
||||
fld = unpack("<LLBLLBBHL", h.data[ : HDRLEN])
|
||||
h.compress_size = long(fld[0]) & 0xFFFFFFFFL
|
||||
h.file_size = long(fld[1]) & 0xFFFFFFFFL
|
||||
h.host_os = fld[2]
|
||||
h.CRC = fld[3]
|
||||
h.date_time = self._parse_dos_time(fld[4])
|
||||
h.extract_version = fld[5]
|
||||
h.compress_type = fld[6]
|
||||
h.name_size = fld[7]
|
||||
h.mode = fld[8]
|
||||
pos = HDRLEN
|
||||
|
||||
if h.flags & RAR_FILE_LARGE:
|
||||
h1, h2 = unpack("<LL", h.data[pos:pos+8])
|
||||
h.compress_size |= long(h1) << 32
|
||||
h.file_size |= long(h2) << 32
|
||||
pos += 8
|
||||
|
||||
name = h.data[pos : pos + h.name_size ]
|
||||
pos += h.name_size
|
||||
if h.flags & RAR_FILE_UNICODE:
|
||||
nul = name.find("\0")
|
||||
h.filename = name[:nul]
|
||||
u = _UnicodeFilename(h.filename, name[nul + 1 : ])
|
||||
h.unicode_filename = u.decode()
|
||||
else:
|
||||
h.filename = name
|
||||
h.unicode_filename = None
|
||||
if self.charset:
|
||||
h.unicode_filename = name.decode(self.charset)
|
||||
else:
|
||||
# just guessing...
|
||||
h.unicode_filename = name.decode("iso-8859-1", "replace")
|
||||
|
||||
if h.flags & RAR_FILE_SALT:
|
||||
h.salt = h.data[pos : pos + 8]
|
||||
pos += 8
|
||||
else:
|
||||
h.salt = None
|
||||
|
||||
# unknown contents
|
||||
if h.flags & RAR_FILE_EXTTIME:
|
||||
h.ext_time = h.data[pos : ]
|
||||
else:
|
||||
h.ext_time = None
|
||||
|
||||
h.header_unknown -= pos
|
||||
|
||||
return h
|
||||
|
||||
def _parse_dos_time(self, stamp):
|
||||
sec = stamp & 0x1F; stamp = stamp >> 5
|
||||
min = stamp & 0x3F; stamp = stamp >> 6
|
||||
hr = stamp & 0x1F; stamp = stamp >> 5
|
||||
day = stamp & 0x1F; stamp = stamp >> 5
|
||||
mon = stamp & 0x0F; stamp = stamp >> 4
|
||||
yr = (stamp & 0x7F) + 1980
|
||||
return (yr, mon, day, hr, min, sec)
|
||||
|
||||
# new-style volume name
|
||||
def _gen_newvol(self, volume):
|
||||
# allow % in filenames
|
||||
fn = self.rarfile.replace("%", "%%")
|
||||
|
||||
m = re.search(r"([0-9][0-9]*)[^0-9]*$", fn)
|
||||
if not m:
|
||||
raise Exception("Cannot construct volume name")
|
||||
n1 = m.start(1)
|
||||
n2 = m.end(1)
|
||||
fmt = "%%0%dd" % (n2 - n1)
|
||||
volfmt = fn[:n1] + fmt + fn[n2:]
|
||||
return volfmt % (volume + 1)
|
||||
|
||||
# old-style volume naming
|
||||
def _gen_oldvol(self, volume):
|
||||
if volume == 0: return self.rarfile
|
||||
i = self.rarfile.rfind(".")
|
||||
base = self.rarfile[:i]
|
||||
if volume <= 100:
|
||||
ext = ".r%02d" % (volume - 1)
|
||||
else:
|
||||
ext = ".s%02d" % (volume - 101)
|
||||
return base + ext
|
||||
|
||||
# read uncompressed file
|
||||
def _extract_clear(self, inf):
|
||||
volume = inf.volume
|
||||
buf = ""
|
||||
cur = None
|
||||
while 1:
|
||||
f = open(self._gen_volname(volume), "rb")
|
||||
if not cur:
|
||||
f.seek(inf.header_offset)
|
||||
|
||||
while 1:
|
||||
cur = self._parse_header(f)
|
||||
if cur.type in (RAR_BLOCK_MARK, RAR_BLOCK_MAIN):
|
||||
if cur.add_size:
|
||||
f.seek(cur.add_size, 1)
|
||||
continue
|
||||
if cur.filename == inf.filename:
|
||||
buf += f.read(cur.add_size)
|
||||
break
|
||||
|
||||
raise RuntimeException("file not found?")
|
||||
|
||||
# no more parts?
|
||||
if (cur.flags & RAR_FILE_SPLIT_AFTER) == 0:
|
||||
break
|
||||
|
||||
volume += 1
|
||||
|
||||
return buf
|
||||
|
||||
# put file compressed data into temporary .rar archive, and run
|
||||
# unrar on that, thus avoiding unrar going over whole archive
|
||||
def _extract_hack(self, inf):
|
||||
BSIZE = 32*1024
|
||||
|
||||
size = inf.compress_size + inf.header_size
|
||||
rf = open(self.rarfile, "rb")
|
||||
rf.seek(inf.header_offset)
|
||||
tmpname = os.tempnam() + ".rar"
|
||||
tmpf = open(tmpname, "wb")
|
||||
|
||||
# create main header: crc, type, flags, size, res1, res2
|
||||
mh = pack("<HBHHHL", 0x90CF, 0x73, 0, 13, 0, 0)
|
||||
tmpf.write(RAR_ID + mh)
|
||||
|
||||
while size > 0:
|
||||
if size > BSIZE:
|
||||
buf = rf.read(BSIZE)
|
||||
else:
|
||||
buf = rf.read(size)
|
||||
tmpf.write(buf)
|
||||
size -= len(buf)
|
||||
tmpf.close()
|
||||
|
||||
buf = self._extract_unrar(tmpname, inf)
|
||||
os.unlink(tmpname)
|
||||
return buf
|
||||
|
||||
# extract using unrar
|
||||
def _extract_unrar(self, rarfile, inf):
|
||||
fn = inf.filename
|
||||
# linux unrar wants '/', not '\'
|
||||
fn = fn.replace("\\", "/")
|
||||
# shell escapes
|
||||
fn = fn.replace("`", "\\`")
|
||||
fn = fn.replace('"', '\\"')
|
||||
fn = fn.replace("$", "\\$")
|
||||
|
||||
cmd = _extract_cmd % (rarfile, fn)
|
||||
fd = os.popen(cmd, "r")
|
||||
buf = fd.read()
|
||||
err = fd.close()
|
||||
if err > 0:
|
||||
raise Exception("Error reading file")
|
||||
return buf
|
||||
|
||||
class _UnicodeFilename:
|
||||
def __init__(self, name, encdata):
|
||||
self.std_name = name
|
||||
self.encdata = encdata
|
||||
self.pos = self.encpos = 0
|
||||
self.buf = StringIO()
|
||||
|
||||
def enc_byte(self):
|
||||
c = self.encdata[self.encpos]
|
||||
self.encpos += 1
|
||||
return ord(c)
|
||||
|
||||
def std_byte(self):
|
||||
return ord(self.std_name[self.pos])
|
||||
|
||||
def put(self, lo, hi):
|
||||
self.buf.write(chr(lo) + chr(hi))
|
||||
self.pos += 1
|
||||
|
||||
def decode(self):
|
||||
hi = self.enc_byte()
|
||||
flagbits = 0
|
||||
while self.encpos < len(self.encdata):
|
||||
if flagbits == 0:
|
||||
flags = self.enc_byte()
|
||||
flagbits = 8
|
||||
flagbits -= 2
|
||||
t = (flags >> flagbits) & 3
|
||||
if t == 0:
|
||||
self.put(self.enc_byte(), 0)
|
||||
elif t == 1:
|
||||
self.put(self.enc_byte(), hi)
|
||||
elif t == 2:
|
||||
self.put(self.enc_byte(), self.enc_byte())
|
||||
else:
|
||||
n = self.enc_byte()
|
||||
if n & 0x80:
|
||||
c = self.enc_byte()
|
||||
for i in range((n & 0x7f) + 2):
|
||||
lo = (self.std_byte() + c) & 0xFF
|
||||
self.put(lo, hi)
|
||||
else:
|
||||
for i in range(n + 2):
|
||||
self.put(self.std_byte(), 0)
|
||||
return self.buf.getvalue().decode("utf-16le", "replace")
|
||||
|
||||
# ignore os.tempnam() warning
|
||||
try:
|
||||
import warnings
|
||||
warnings.filterwarnings(action = 'ignore', category = RuntimeWarning,
|
||||
module = 'rarfile')
|
||||
except Exception, det:
|
||||
pass
|
||||
|
@ -1,305 +0,0 @@
|
||||
"""Utility functions for copying files and directory trees.
|
||||
|
||||
XXX The functions here don't copy the resource fork or other metadata on Mac.
|
||||
|
||||
|
||||
shutil2 (22/12/08)
|
||||
by Frost and Temhil
|
||||
Version unofficial of shutil, shutil2 is based on current SVN revision 65644.
|
||||
This version includes 'overwrite' file and 'reportcopy' during process
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import stat
|
||||
from os.path import abspath
|
||||
import fnmatch
|
||||
|
||||
__all__ = ["copyfileobj","copyfile","copymode","copystat","copy","copy2",
|
||||
"copytree","move","rmtree","Error"]
|
||||
|
||||
class Error(EnvironmentError):
|
||||
pass
|
||||
|
||||
try:
|
||||
WindowsError
|
||||
except NameError:
|
||||
WindowsError = None
|
||||
|
||||
# exception raised when copied size does not match content-length
|
||||
class ContentTooShortError(IOError):
|
||||
def __init__(self, message, content):
|
||||
IOError.__init__(self, message)
|
||||
self.content = content
|
||||
|
||||
def copyfileobj(fsrc, fdst, reportcopy=None, size=1, length=16*1024):
|
||||
"""copy data from file-like object fsrc to file-like object fdst"""
|
||||
result = "", str(size)
|
||||
read = 0
|
||||
blocknum = 0
|
||||
if reportcopy:
|
||||
reportcopy(blocknum, length, size)
|
||||
while 1:
|
||||
buf = fsrc.read(length)
|
||||
if not buf:
|
||||
break
|
||||
read += len(buf)
|
||||
fdst.write(buf)
|
||||
blocknum += 1
|
||||
if reportcopy:
|
||||
reportcopy(blocknum, length, size)
|
||||
|
||||
# raise exception if actual size does not match
|
||||
if size >= 0 and read < size:
|
||||
raise ContentTooShortError("copying incomplete: got only %i out "
|
||||
"of %i bytes" % (read, size), result)
|
||||
|
||||
def _samefile(src, dst):
|
||||
# Macintosh, Unix.
|
||||
if hasattr(os.path,'samefile'):
|
||||
try:
|
||||
return os.path.samefile(src, dst)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
# All other platforms: check for same pathname.
|
||||
return (os.path.normcase(os.path.abspath(src)) ==
|
||||
os.path.normcase(os.path.abspath(dst)))
|
||||
|
||||
def copyfile(src, dst, reportcopy=None, overwrite=False):
|
||||
"""Copy data from src to dst"""
|
||||
if _samefile(src, dst):
|
||||
raise Error, "`%s` and `%s` are the same file" % (src, dst)
|
||||
|
||||
fsrc = None
|
||||
fdst = None
|
||||
try:
|
||||
fsrc = open(src, 'rb')
|
||||
if os.path.exists(dst) and overwrite:
|
||||
os.remove(dst)
|
||||
fdst = open(dst, 'wb')
|
||||
try: size = os.stat(src)[6]/1024.0
|
||||
except: size = 1
|
||||
copyfileobj(fsrc, fdst, reportcopy, size)
|
||||
finally:
|
||||
if fdst:
|
||||
fdst.close()
|
||||
if fsrc:
|
||||
fsrc.close()
|
||||
|
||||
def copymode(src, dst):
|
||||
"""Copy mode bits from src to dst"""
|
||||
if hasattr(os, 'chmod'):
|
||||
st = os.stat(src)
|
||||
mode = stat.S_IMODE(st.st_mode)
|
||||
os.chmod(dst, mode)
|
||||
|
||||
def copystat(src, dst):
|
||||
"""Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
|
||||
st = os.stat(src)
|
||||
mode = stat.S_IMODE(st.st_mode)
|
||||
if hasattr(os, 'utime'):
|
||||
os.utime(dst, (st.st_atime, st.st_mtime))
|
||||
if hasattr(os, 'chmod'):
|
||||
os.chmod(dst, mode)
|
||||
if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
|
||||
os.chflags(dst, st.st_flags)
|
||||
|
||||
def copy(src, dst, reportcopy=None, overwrite=False):
|
||||
"""Copy data and mode bits ("cp src dst").
|
||||
|
||||
The destination may be a directory.
|
||||
|
||||
"""
|
||||
if os.path.isdir(dst):
|
||||
dst = os.path.join(dst, os.path.basename(src))
|
||||
copyfile(src, dst, reportcopy, overwrite)
|
||||
copymode(src, dst)
|
||||
|
||||
def copy2(src, dst, reportcopy=None, overwrite=False):
|
||||
"""Copy data and all stat info ("cp -p src dst").
|
||||
|
||||
The destination may be a directory.
|
||||
|
||||
"""
|
||||
if os.path.isdir(dst):
|
||||
dst = os.path.join(dst, os.path.basename(src))
|
||||
copyfile(src, dst, reportcopy, overwrite)
|
||||
copystat(src, dst)
|
||||
|
||||
def ignore_patterns(*patterns):
|
||||
"""Function that can be used as copytree() ignore parameter.
|
||||
|
||||
Patterns is a sequence of glob-style patterns
|
||||
that are used to exclude files"""
|
||||
def _ignore_patterns(path, names):
|
||||
ignored_names = []
|
||||
for pattern in patterns:
|
||||
ignored_names.extend(fnmatch.filter(names, pattern))
|
||||
return set(ignored_names)
|
||||
return _ignore_patterns
|
||||
|
||||
def copytree(src, dst, symlinks=False, ignore=None, reportcopy=None, overwrite=False):
|
||||
"""Recursively copy a directory tree using copy2().
|
||||
|
||||
The destination directory must not already exist.
|
||||
If exception(s) occur, an Error is raised with a list of reasons.
|
||||
|
||||
If the optional symlinks flag is true, symbolic links in the
|
||||
source tree result in symbolic links in the destination tree; if
|
||||
it is false, the contents of the files pointed to by symbolic
|
||||
links are copied.
|
||||
|
||||
The optional ignore argument is a callable. If given, it
|
||||
is called with the `src` parameter, which is the directory
|
||||
being visited by copytree(), and `names` which is the list of
|
||||
`src` contents, as returned by os.listdir():
|
||||
|
||||
callable(src, names) -> ignored_names
|
||||
|
||||
Since copytree() is called recursively, the callable will be
|
||||
called once for each directory that is copied. It returns a
|
||||
list of names relative to the `src` directory that should
|
||||
not be copied.
|
||||
|
||||
XXX Consider this example code rather than the ultimate tool.
|
||||
|
||||
"""
|
||||
names = os.listdir(src)
|
||||
if ignore is not None:
|
||||
ignored_names = ignore(src, names)
|
||||
else:
|
||||
ignored_names = set()
|
||||
if not os.path.exists(dst) or not overwrite:
|
||||
os.makedirs(dst)
|
||||
errors = []
|
||||
for name in names:
|
||||
if name in ignored_names:
|
||||
continue
|
||||
srcname = os.path.join(src, name)
|
||||
dstname = os.path.join(dst, name)
|
||||
try:
|
||||
if symlinks and os.path.islink(srcname):
|
||||
linkto = os.readlink(srcname)
|
||||
os.symlink(linkto, dstname)
|
||||
elif os.path.isdir(srcname):
|
||||
copytree(srcname, dstname, symlinks, ignore, reportcopy, overwrite)
|
||||
else:
|
||||
copy2(srcname, dstname, reportcopy, overwrite)
|
||||
# XXX What about devices, sockets etc.?
|
||||
except (IOError, os.error), why:
|
||||
errors.append((srcname, dstname, str(why)))
|
||||
# catch the Error from the recursive copytree so that we can
|
||||
# continue with other files
|
||||
except Error, err:
|
||||
errors.extend(err.args[0])
|
||||
try:
|
||||
copystat(src, dst)
|
||||
except OSError, why:
|
||||
if WindowsError is not None and isinstance(why, WindowsError):
|
||||
# Copying file access times may fail on Windows
|
||||
pass
|
||||
elif overwrite:
|
||||
pass
|
||||
else:
|
||||
errors.extend((src, dst, str(why)))
|
||||
if errors:
|
||||
raise Error, errors
|
||||
|
||||
def rmtree(path, ignore_errors=False, onerror=None):
|
||||
"""Recursively delete a directory tree.
|
||||
|
||||
If ignore_errors is set, errors are ignored; otherwise, if onerror
|
||||
is set, it is called to handle the error with arguments (func,
|
||||
path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
|
||||
path is the argument to that function that caused it to fail; and
|
||||
exc_info is a tuple returned by sys.exc_info(). If ignore_errors
|
||||
is false and onerror is None, an exception is raised.
|
||||
|
||||
"""
|
||||
if ignore_errors:
|
||||
def onerror(*args):
|
||||
pass
|
||||
elif onerror is None:
|
||||
def onerror(*args):
|
||||
raise
|
||||
try:
|
||||
if os.path.islink(path):
|
||||
# symlinks to directories are forbidden, see bug #1669
|
||||
raise OSError("Cannot call rmtree on a symbolic link")
|
||||
except OSError:
|
||||
onerror(os.path.islink, path, sys.exc_info())
|
||||
# can't continue even if onerror hook returns
|
||||
return
|
||||
names = []
|
||||
try:
|
||||
names = os.listdir(path)
|
||||
except os.error, err:
|
||||
onerror(os.listdir, path, sys.exc_info())
|
||||
for name in names:
|
||||
fullname = os.path.join(path, name)
|
||||
try:
|
||||
mode = os.lstat(fullname).st_mode
|
||||
except os.error:
|
||||
mode = 0
|
||||
if stat.S_ISDIR(mode):
|
||||
rmtree(fullname, ignore_errors, onerror)
|
||||
else:
|
||||
try:
|
||||
os.remove(fullname)
|
||||
except os.error, err:
|
||||
onerror(os.remove, fullname, sys.exc_info())
|
||||
try:
|
||||
os.rmdir(path)
|
||||
except os.error:
|
||||
onerror(os.rmdir, path, sys.exc_info())
|
||||
|
||||
|
||||
def _basename(path):
|
||||
# A basename() variant which first strips the trailing slash, if present.
|
||||
# Thus we always get the last component of the path, even for directories.
|
||||
return os.path.basename(path.rstrip(os.path.sep))
|
||||
|
||||
def move(src, dst, reportcopy=None, overwrite=False):
|
||||
"""Recursively move a file or directory to another location. This is
|
||||
similar to the Unix "mv" command.
|
||||
|
||||
If the destination is a directory or a symlink to a directory, the source
|
||||
is moved inside the directory. The destination path must not already
|
||||
exist.
|
||||
|
||||
If the destination already exists but is not a directory, it may be
|
||||
overwritten depending on os.rename() semantics.
|
||||
|
||||
If the destination is on our current filesystem, then rename() is used.
|
||||
Otherwise, src is copied to the destination and then removed.
|
||||
A lot more could be done here... A look at a mv.c shows a lot of
|
||||
the issues this implementation glosses over.
|
||||
|
||||
"""
|
||||
real_dst = dst
|
||||
if os.path.isdir(dst):
|
||||
real_dst = os.path.join(dst, _basename(src))
|
||||
if os.path.exists(real_dst):
|
||||
raise Error, "Destination path '%s' already exists" % real_dst
|
||||
try:
|
||||
os.rename(src, real_dst)
|
||||
except OSError:
|
||||
if os.path.isdir(src):
|
||||
if destinsrc(src, dst):
|
||||
raise Error, "Cannot move a directory '%s' into itself '%s'." % (src, dst)
|
||||
#copytree(src, dst, symlinks=True)
|
||||
copytree(src, dst, True, reportcopy, overwrite)
|
||||
rmtree(src)
|
||||
else:
|
||||
copy2(src, real_dst, reportcopy, overwrite)
|
||||
os.unlink(src)
|
||||
|
||||
def reportcopy(blocknum, blocksize, totalsize):
|
||||
# Report during remote transfers
|
||||
print "Block number: %d, Block size: %d, Total size: %d" % (
|
||||
blocknum, blocksize, totalsize)
|
||||
|
||||
def destinsrc(src, dst):
|
||||
return abspath(dst).startswith(abspath(src))
|
@ -1,71 +0,0 @@
|
||||
"""
|
||||
Module retrieving repositories info from XBMC Wiki
|
||||
by Temhil
|
||||
"""
|
||||
|
||||
from BeautifulSoup import SoupStrainer
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
from traceback import print_exc
|
||||
import re
|
||||
import urllib
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
#__settings__ = sys.modules[ "__main__" ].__settings__
|
||||
#__language__ = sys.modules[ "__main__" ].__language__
|
||||
|
||||
|
||||
|
||||
def getRepoList(pageUrl, destination=None, addItemFunc=None, progressBar=None, msgFunc=None ):
|
||||
"""
|
||||
Retrieve Blogs list passing each item to the cb addItemFunc
|
||||
return Next page URL
|
||||
"""
|
||||
#print "getRepoList"
|
||||
result = "OK"
|
||||
|
||||
# Get HTML page...
|
||||
htmlSource = urllib.urlopen( pageUrl ).read()
|
||||
#print htmlSource
|
||||
|
||||
# Parse response...
|
||||
beautifulSoup = BeautifulSoup( htmlSource )
|
||||
itemRepoList = beautifulSoup.findAll("tr")
|
||||
print itemRepoList
|
||||
for repo in itemRepoList:
|
||||
try:
|
||||
#print repo
|
||||
#print "----"
|
||||
repoInfo = {}
|
||||
tdList = repo.findAll ("td")
|
||||
if tdList:
|
||||
repoInfo["name"] = tdList[0].a.string.strip()
|
||||
repoInfo["description"] = tdList[1].string.strip()
|
||||
repoInfo["owner"] = tdList[2].string.strip()
|
||||
repoInfo["repoUrl"] = tdList[3].a["href"]
|
||||
try:
|
||||
repoInfo["ImageUrl"] = tdList[4].a["href"]
|
||||
except:
|
||||
repoInfo["ImageUrl"] = None
|
||||
#print repoInfo
|
||||
|
||||
#if progressBar != None:
|
||||
if addItemFunc != None:
|
||||
if repoInfo["repoUrl"].endswith("zip"):
|
||||
addItemFunc( repoInfo )
|
||||
else:
|
||||
print "Invalid URL for the repository %s - URL=%s"%(repoInfo["name"], repoInfo["repoUrl"])
|
||||
except:
|
||||
print "getRepoList - error parsing html - impossible to retrieve Blog info"
|
||||
print_exc()
|
||||
result = "ERROR"
|
||||
return result
|
||||
|
||||
|
||||
|
||||
if ( __name__ == "__main__" ):
|
||||
print "Wiki parser test"
|
||||
|
||||
repoListUrl = "http://wiki.xbmc.org/index.php?title=Unofficial_Add-on_Repositories"
|
||||
print repoListUrl
|
||||
getRepoList(repoListUrl)
|
Before Width: | Height: | Size: 33 KiB |
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<settings>
|
||||
<!-- GENERAL -->
|
||||
<category label="$LOCALIZE[128]">
|
||||
<setting label="30500" type="lsep" />
|
||||
<setting label="30530" type="bool" id="desintitle" default="false" />
|
||||
<setting label="30501" type="enum" id="descolor" enable="eq(-1,true)" lvalues="30520|30521|30522|30523|30524" default="4" />
|
||||
</category>
|
||||
</settings>
|
@ -1,476 +0,0 @@
|
||||
<window id="146">
|
||||
<defaultcontrol always="true">9000</defaultcontrol>
|
||||
<coordinates>
|
||||
<system>1</system>
|
||||
<posx>185</posx>
|
||||
<posy>60</posy>
|
||||
<origin x="185" y="10">!IsEmpty(ListItem.Property(Addon.broken))</origin>
|
||||
</coordinates>
|
||||
<include>dialogeffect</include>
|
||||
<controls>
|
||||
<control type="group">
|
||||
<include>VisibleFadeEffect</include>
|
||||
<visible>!Window.isVisible(AddonSettings) + !Window.IsActive(TextViewer)</visible>
|
||||
<control type="image">
|
||||
<description>background image</description>
|
||||
<posx>0</posx>
|
||||
<posy>0</posy>
|
||||
<width>920</width>
|
||||
<height>600</height>
|
||||
<texture border="40">DialogBack.png</texture>
|
||||
</control>
|
||||
<control type="image">
|
||||
<posx>260</posx>
|
||||
<posy>10</posy>
|
||||
<width>620</width>
|
||||
<height>578</height>
|
||||
<texture border="5">black-back2.png</texture>
|
||||
</control>
|
||||
<control type="image">
|
||||
<posx>260</posx>
|
||||
<posy>10</posy>
|
||||
<width>610</width>
|
||||
<height>100</height>
|
||||
<aspectratio>stretch</aspectratio>
|
||||
<texture>GlassTitleBar.png</texture>
|
||||
</control>
|
||||
<control type="button">
|
||||
<description>Close Window button</description>
|
||||
<posx>810</posx>
|
||||
<posy>9</posy>
|
||||
<width>64</width>
|
||||
<height>32</height>
|
||||
<label>-</label>
|
||||
<font>-</font>
|
||||
<onclick>PreviousMenu</onclick>
|
||||
<texturefocus>DialogCloseButton-focus.png</texturefocus>
|
||||
<texturenofocus>DialogCloseButton.png</texturenofocus>
|
||||
<onleft>3</onleft>
|
||||
<onright>3</onright>
|
||||
<onup>3</onup>
|
||||
<ondown>3</ondown>
|
||||
<visible>system.getbool(input.enablemouse)</visible>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>header label</description>
|
||||
<posx>280</posx>
|
||||
<posy>20</posy>
|
||||
<width>580</width>
|
||||
<height>30</height>
|
||||
<font>font30_title</font>
|
||||
<label>24003</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<textcolor>white</textcolor>
|
||||
<shadowcolor>black</shadowcolor>
|
||||
</control>
|
||||
<control type="image">
|
||||
<posx>35</posx>
|
||||
<posy>40</posy>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
<aspectratio>keep</aspectratio>
|
||||
<!--texture>$INFO[ListItem.Icon]</texture-->
|
||||
<texture>$INFO[Window.Property(Icon)]</texture>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>Addon Title value</description>
|
||||
<posx>280</posx>
|
||||
<posy>80</posy>
|
||||
<width>580</width>
|
||||
<height>30</height>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13caps</font>
|
||||
<!-- >label>[B]$INFO[ListItem.Property(Addon.Name)][/B]</label-->
|
||||
<label>[B]$INFO[Window.Property(Name)][/B]</label>
|
||||
<textcolor>white</textcolor>
|
||||
<scroll>true</scroll>
|
||||
</control>
|
||||
<control type="group">
|
||||
<posx>270</posx>
|
||||
<posy>130</posy>
|
||||
<control type="label">
|
||||
<description>Type txt</description>
|
||||
<posx>150</posx>
|
||||
<posy>0</posy>
|
||||
<width>140</width>
|
||||
<height>25</height>
|
||||
<label>$LOCALIZE[146]</label>
|
||||
<align>right</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13_title</font>
|
||||
<textcolor>blue</textcolor>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>Type Value</description>
|
||||
<posx>160</posx>
|
||||
<posy>0</posy>
|
||||
<width>440</width>
|
||||
<height>25</height>
|
||||
<label fallback="416">$INFO[Window.Property(Type)]</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13</font>
|
||||
<scroll>true</scroll>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>Author txt</description>
|
||||
<posx>150</posx>
|
||||
<posy>30</posy>
|
||||
<width>140</width>
|
||||
<height>25</height>
|
||||
<label>$LOCALIZE[21863]:</label>
|
||||
<align>right</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13_title</font>
|
||||
<textcolor>blue</textcolor>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>Author Value</description>
|
||||
<posx>160</posx>
|
||||
<posy>30</posy>
|
||||
<width>440</width>
|
||||
<height>25</height>
|
||||
<label fallback="416">$INFO[Window.Property(Creator)]</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13</font>
|
||||
<scroll>true</scroll>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>Version txt</description>
|
||||
<posx>150</posx>
|
||||
<posy>60</posy>
|
||||
<width>140</width>
|
||||
<height>25</height>
|
||||
<label>$LOCALIZE[24051]</label>
|
||||
<align>right</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13_title</font>
|
||||
<textcolor>blue</textcolor>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>Version Value</description>
|
||||
<posx>160</posx>
|
||||
<posy>60</posy>
|
||||
<width>440</width>
|
||||
<height>25</height>
|
||||
<label fallback="416">$INFO[Window.Property(Version)]</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13</font>
|
||||
<scroll>true</scroll>
|
||||
</control>
|
||||
<!-- control type="label">
|
||||
<description>Rating txt</description>
|
||||
<posx>150</posx>
|
||||
<posy>90</posy>
|
||||
<width>140</width>
|
||||
<height>25</height>
|
||||
<label>$LOCALIZE[563]:</label>
|
||||
<align>right</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13_title</font>
|
||||
<textcolor>blue</textcolor>
|
||||
</control>
|
||||
<control type="image">
|
||||
<description>Rating value</description>
|
||||
<posx>160</posx>
|
||||
<posy>90</posy>
|
||||
<width>160</width>
|
||||
<height>32</height>
|
||||
<texture>LeftRating/$INFO[ListItem.Property(Addon.StarRating)]</texture>
|
||||
</control -->
|
||||
<!--control type="label">
|
||||
<description>Summary txt</description>
|
||||
<posx>150</posx>
|
||||
<posy>90</posy>
|
||||
<width>140</width>
|
||||
<height>25</height>
|
||||
<label>$LOCALIZE[20037]:</label>
|
||||
<align>right</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13_title</font>
|
||||
<textcolor>blue</textcolor>
|
||||
</control>
|
||||
<control type="fadelabel">
|
||||
<description>Summary Value</description>
|
||||
<posx>160</posx>
|
||||
<posy>90</posy>
|
||||
<width>440</width>
|
||||
<height>25</height>
|
||||
<label fallback="416">$INFO[ListItem.Property(Addon.Summary)]</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13</font>
|
||||
<scrollout>false</scrollout>
|
||||
<pauseatend>2000</pauseatend>
|
||||
</control-->
|
||||
<control type="image">
|
||||
<posx>0</posx>
|
||||
<posy>160</posy>
|
||||
<width>600</width>
|
||||
<height>4</height>
|
||||
<texture>separator.png</texture>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>Description txt</description>
|
||||
<posx>0</posx>
|
||||
<posy>170</posy>
|
||||
<width>300</width>
|
||||
<height>25</height>
|
||||
<label>$LOCALIZE[21821]</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13</font>
|
||||
<textcolor>blue</textcolor>
|
||||
</control>
|
||||
<control type="group">
|
||||
<!--visible>!IsEmpty(ListItem.Property(Addon.Disclaimer))</visible-->
|
||||
<control type="label">
|
||||
<description>Description Page Count</description>
|
||||
<posx>600</posx>
|
||||
<posy>170</posy>
|
||||
<width>300</width>
|
||||
<height>25</height>
|
||||
<label>$INFO[Container(400).CurrentPage, $LOCALIZE[31024] [COLOR=blue]([/COLOR]]$INFO[Container(400).NumPages,/,[COLOR=blue])[/COLOR]]</label>
|
||||
<align>right</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font12</font>
|
||||
<textcolor>grey</textcolor>
|
||||
</control>
|
||||
<control type="textbox" id="400">
|
||||
<description>Description</description>
|
||||
<posx>10</posx>
|
||||
<posy>195</posy>
|
||||
<width>580</width>
|
||||
<height>160</height>
|
||||
<font>font12</font>
|
||||
<align>-</align>
|
||||
<textcolor>white</textcolor>
|
||||
<!--label>$INFO[ListItem.Property(Addon.Description)]</label-->
|
||||
<label>$INFO[Window.Property(Name)]</label>
|
||||
<pagecontrol>60</pagecontrol>
|
||||
</control>
|
||||
<control type="scrollbar" id="60">
|
||||
<posx>610</posx>
|
||||
<posy>190</posy>
|
||||
<width>25</width>
|
||||
<height>175</height>
|
||||
<texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
|
||||
<texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
|
||||
<texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
|
||||
<textureslidernib>ScrollBarNib.png</textureslidernib>
|
||||
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
|
||||
<onleft>9000</onleft>
|
||||
<onright>61</onright>
|
||||
<showonepage>false</showonepage>
|
||||
<orientation>vertical</orientation>
|
||||
</control>
|
||||
<!--control type="label">
|
||||
<description>Disclaimer txt</description>
|
||||
<posx>0</posx>
|
||||
<posy>370</posy>
|
||||
<width>600</width>
|
||||
<height>25</height>
|
||||
<label>$LOCALIZE[24052]</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font13</font>
|
||||
<textcolor>orange</textcolor>
|
||||
</control>
|
||||
<control type="textbox">
|
||||
<description>Disclaimer</description>
|
||||
<posx>10</posx>
|
||||
<posy>395</posy>
|
||||
<width>580</width>
|
||||
<height>40</height>
|
||||
<font>font12</font>
|
||||
<align>-</align>
|
||||
<textcolor>white</textcolor>
|
||||
<label>$INFO[ListItem.Property(Addon.Disclaimer)]</label>
|
||||
<autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll>
|
||||
</control-->
|
||||
</control>
|
||||
<control type="group">
|
||||
<!--visible>IsEmpty(ListItem.Property(Addon.Disclaimer))</visible-->
|
||||
<control type="label">
|
||||
<description>Description Page Count</description>
|
||||
<posx>600</posx>
|
||||
<posy>170</posy>
|
||||
<width>300</width>
|
||||
<height>25</height>
|
||||
<label>$INFO[Container(401).CurrentPage, $LOCALIZE[31024] [COLOR=blue]([/COLOR]]$INFO[Container(401).NumPages,/,[COLOR=blue])[/COLOR]]</label>
|
||||
<align>right</align>
|
||||
<aligny>center</aligny>
|
||||
<font>font12</font>
|
||||
<textcolor>grey</textcolor>
|
||||
</control>
|
||||
<control type="textbox" id="401">
|
||||
<description>Description</description>
|
||||
<posx>10</posx>
|
||||
<posy>195</posy>
|
||||
<width>580</width>
|
||||
<height>250</height>
|
||||
<font>font12</font>
|
||||
<align>-</align>
|
||||
<textcolor>white</textcolor>
|
||||
<label>$INFO[ListItem.Property(Addon.Description)]</label>
|
||||
<pagecontrol>61</pagecontrol>
|
||||
</control>
|
||||
<control type="scrollbar" id="61">
|
||||
<posx>610</posx>
|
||||
<posy>190</posy>
|
||||
<width>25</width>
|
||||
<height>250</height>
|
||||
<texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
|
||||
<texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
|
||||
<texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
|
||||
<textureslidernib>ScrollBarNib.png</textureslidernib>
|
||||
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
|
||||
<onleft>60</onleft>
|
||||
<onright>9000</onright>
|
||||
<showonepage>false</showonepage>
|
||||
<orientation>vertical</orientation>
|
||||
</control>
|
||||
</control>
|
||||
</control>
|
||||
<control type="group" id="9000">
|
||||
<posx>10</posx>
|
||||
<posy>280</posy>
|
||||
<!--control type="button" id ="6">
|
||||
<description>Enable Addon button</description>
|
||||
<posx>0</posx>
|
||||
<posy>0</posy>
|
||||
<width>255</width>
|
||||
<height>51</height>
|
||||
<textoffsetx>20</textoffsetx>
|
||||
<label>24022</label>
|
||||
<font>font13_title</font>
|
||||
<align>right</align>
|
||||
<texturenofocus border="5">MenuItemNF.png</texturenofocus>
|
||||
<texturefocus border="5">MenuItemFO.png</texturefocus>
|
||||
<onleft>60</onleft>
|
||||
<onright>60</onright>
|
||||
<onup>10</onup>
|
||||
<ondown>7</ondown>
|
||||
</control>
|
||||
<control type="button" id="7">
|
||||
<description>Disable Addon button</description>
|
||||
<posx>0</posx>
|
||||
<posy>50</posy>
|
||||
<width>255</width>
|
||||
<height>51</height>
|
||||
<textoffsetx>20</textoffsetx>
|
||||
<label>24021</label>
|
||||
<font>font13_title</font>
|
||||
<align>right</align>
|
||||
<texturenofocus border="5">MenuItemNF.png</texturenofocus>
|
||||
<texturefocus border="5">MenuItemFO.png</texturefocus>
|
||||
<onleft>60</onleft>
|
||||
<onright>60</onright>
|
||||
<onup>6</onup>
|
||||
<ondown>9</ondown>
|
||||
</control>
|
||||
<control type="button" id="9">
|
||||
<description>Addon Settings</description>
|
||||
<posx>0</posx>
|
||||
<posy>100</posy>
|
||||
<width>255</width>
|
||||
<height>51</height>
|
||||
<textoffsetx>20</textoffsetx>
|
||||
<label>24020</label>
|
||||
<font>font13_title</font>
|
||||
<align>right</align>
|
||||
<texturenofocus border="5">MenuItemNF.png</texturenofocus>
|
||||
<texturefocus border="5">MenuItemFO.png</texturefocus>
|
||||
<onleft>60</onleft>
|
||||
<onright>60</onright>
|
||||
<onup>7</onup>
|
||||
<ondown>8</ondown>
|
||||
</control>
|
||||
<control type="button" id="8">
|
||||
<description>Update Addon button</description>
|
||||
<posx>0</posx>
|
||||
<posy>150</posy>
|
||||
<width>255</width>
|
||||
<height>51</height>
|
||||
<textoffsetx>20</textoffsetx>
|
||||
<label>24069</label>
|
||||
<font>font13_title</font>
|
||||
<align>right</align>
|
||||
<texturenofocus border="5">MenuItemNF.png</texturenofocus>
|
||||
<texturefocus border="5">MenuItemFO.png</texturefocus>
|
||||
<onleft>60</onleft>
|
||||
<onright>60</onright>
|
||||
<onup>9</onup>
|
||||
<ondown>10</ondown>
|
||||
</control>
|
||||
<control type="button" id="10">
|
||||
<description>Changelog button</description>
|
||||
<posx>0</posx>
|
||||
<posy>200</posy>
|
||||
<width>255</width>
|
||||
<height>51</height>
|
||||
<textoffsetx>20</textoffsetx>
|
||||
<label>24036</label>
|
||||
<font>font13_title</font>
|
||||
<align>right</align>
|
||||
<texturenofocus border="5">MenuItemNF.png</texturenofocus>
|
||||
<texturefocus border="5">MenuItemFO.png</texturefocus>
|
||||
<onleft>60</onleft>
|
||||
<onright>60</onright>
|
||||
<onup>8</onup>
|
||||
<ondown>6</ondown>
|
||||
</control -->
|
||||
</control>
|
||||
<control type="group">
|
||||
<visible>!IsEmpty(ListItem.Property(Addon.broken))</visible>
|
||||
<posx>0</posx>
|
||||
<posy>600</posy>
|
||||
<control type="image">
|
||||
<description>background image</description>
|
||||
<posx>0</posx>
|
||||
<posy>0</posy>
|
||||
<width>920</width>
|
||||
<height>100</height>
|
||||
<texture border="20">OverlayDialogBackground.png</texture>
|
||||
</control>
|
||||
<control type="image">
|
||||
<description>Icon image</description>
|
||||
<posx>18</posx>
|
||||
<posy>18</posy>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
<texture>DefaultIconError.png</texture>
|
||||
</control>
|
||||
<control type="label">
|
||||
<description>header label</description>
|
||||
<posx>100</posx>
|
||||
<posy>15</posy>
|
||||
<width>800</width>
|
||||
<height>25</height>
|
||||
<font>font13_title</font>
|
||||
<label>24096</label>
|
||||
<align>left</align>
|
||||
<aligny>center</aligny>
|
||||
<textcolor>selected</textcolor>
|
||||
<shadowcolor>black</shadowcolor>
|
||||
</control>
|
||||
<control type="textbox">
|
||||
<description>Reason label</description>
|
||||
<posx>100</posx>
|
||||
<posy>35</posy>
|
||||
<width>800</width>
|
||||
<height>50</height>
|
||||
<font>font13</font>
|
||||
<label>$INFO[ListItem.Property(Addon.broken)]</label>
|
||||
<align>left</align>
|
||||
<textcolor>white</textcolor>
|
||||
<shadowcolor>black</shadowcolor>
|
||||
</control>
|
||||
</control>
|
||||
</control>
|
||||
</controls>
|
||||
</window>
|
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.9 KiB |