add experimental meta parsing, not yet finished
[vuplus_dvbapp] / lib / python / Components / DreamInfoHandler.py
1 # -*- coding: iso-8859-1 -*-
2 import xml.sax
3 from Tools.Directories import crawlDirectory, resolveFilename, SCOPE_CONFIG, SCOPE_SKIN, SCOPE_METADIR, copyfile, copytree
4 from Components.NimManager import nimmanager
5 from Components.Ipkg import IpkgComponent
6 from Components.config import config, configfile
7 from Tools.HardwareInfo import HardwareInfo
8 from enigma import eConsoleAppContainer, eDVBDB
9 import os
10
11 class InfoHandlerParseError(Exception):
12         def __init__(self, value):
13                 self.value = value
14         def __str__(self):
15                 return repr(self.value)
16
17 class InfoHandler(xml.sax.ContentHandler):
18         def __init__(self, prerequisiteMet, directory, language = None):
19                 self.attributes = {}
20                 self.directory = directory
21                 self.list = []
22                 self.globalprerequisites = {}
23                 self.prerequisites = {}
24                 self.elements = []
25                 self.validFileTypes = ["skin", "config", "services", "favourites", "package"]
26                 self.prerequisitesMet = prerequisiteMet
27                 self.data = ""
28                 self.languagedata = ""
29                 self.language = language
30                 
31         def printError(self, error):
32                 print "Error in defaults xml files:", error
33                 raise InfoHandlerParseError, error
34
35         def startElement(self, name, attrs):
36                 #print name, ":", attrs.items()
37                 self.elements.append(name)
38                 if name in ("hardware", "bcastsystem", "satellite", "tag", "flag"):
39                         if not attrs.has_key("type"):
40                                         self.printError(str(name) + " tag with no type attribute")
41                         if self.elements[-3] in ("default", "package"):
42                                 prerequisites = self.globalprerequisites
43                         else:
44                                 prerequisites = self.prerequisites
45                         if not prerequisites.has_key(name):
46                                 prerequisites[name] = []
47                         prerequisites[name].append(str(attrs["type"]))
48                 if name == "files":
49                         if attrs.has_key("type"):
50                                 if attrs["type"] == "directories":
51                                         self.attributes["filestype"] = "directories"
52                                 elif attrs["type"] == "package":
53                                         self.attributes["filestype"] = "package"
54                                 # TODO add a compressed archive type
55                 if name == "file":
56                         self.prerequisites = {}
57                         if not attrs.has_key("type"):
58                                 self.printError("file tag with no type attribute")
59                         else:
60                                 if not attrs.has_key("name"):
61                                         self.printError("file tag with no name attribute")
62                                 else:   
63                                         if not attrs.has_key("directory"):
64                                                 directory = self.directory
65                                         type = attrs["type"]
66                                         if not type in self.validFileTypes:
67                                                 self.printError("file tag with invalid type attribute")
68                                         else:
69                                                 self.filetype = type
70                                                 self.fileattrs = attrs
71
72                 if name == "package":
73                         if attrs.has_key("details"):
74                                 self.attributes["details"] = str(attrs["details"])
75                         if attrs.has_key("name"):
76                                 self.attributes["name"] = str(attrs["name"].encode("utf-8"))
77                         if attrs.has_key("packagename"):
78                                 self.attributes["packagename"] = str(attrs["packagename"].encode("utf-8"))
79                         if attrs.has_key("shortdescription"):
80                                 self.attributes["shortdescription"] = str(attrs["shortdescription"].encode("utf-8"))
81
82                 if name == "screenshot":
83                         if attrs.has_key("src"):
84                                 self.attributes["screenshot"] = str(attrs["src"])
85
86         def endElement(self, name):
87                 #print "end", name
88                 #print "self.elements:", self.elements
89                 self.elements.pop()
90                 if name == "file":
91                         #print "prerequisites:", self.prerequisites
92                         if len(self.prerequisites) == 0 or self.prerequisitesMet(self.prerequisites):
93                                 if not self.attributes.has_key(self.filetype):
94                                         self.attributes[self.filetype] = []
95                                 if self.fileattrs.has_key("directory"):
96                                         directory = str(self.fileattrs["directory"])
97                                         if len(directory) < 1 or directory[0] != "/":
98                                                 directory = self.directory + directory
99                                 else:
100                                         directory = self.directory
101                                 self.attributes[self.filetype].append({ "name": str(self.fileattrs["name"]), "directory": directory })
102
103                 if name in ( "default", "package" ):
104                         self.list.append({"attributes": self.attributes, 'prerequisites': self.globalprerequisites})
105                         self.attributes = {}
106                         self.globalprerequisites = {}
107
108         def characters(self, data):
109                 if self.elements[-1] == "author":
110                         self.attributes["author"] = str(data)
111                 if self.elements[-1] == "name":
112                         self.attributes["name"] = str(data)
113                 if self.elements[-1] == "packagename":
114                         self.attributes["packagename"] = str(data.encode("utf-8"))
115                 if self.elements[-1] == "shortdescription":
116                         self.attributes["shortdescription"] = str(data.encode("utf-8"))
117                 if self.elements[-1] == "description":
118                         self.data += data.strip()
119                         self.attributes["description"] = str(self.data.encode("utf-8"))
120                 if self.language is not None:
121                         if self.elements[-1] == ("name_" + str(self.language)):
122                                 self.attributes["name"] = str(data.encode("utf-8"))
123                         if self.elements[-1] == ("shortdescription_" + str(self.language)):
124                                 self.attributes["shortdescription"] = str(data.encode("utf-8"))
125                         if self.elements[-1] == ("description_" + str(self.language)):
126                                 self.languagedata += data.strip()
127                                 self.attributes["description"] = str(self.languagedata.encode("utf-8"))
128                 #print "characters", data
129                 
130 class DreamInfoHandler:
131         STATUS_WORKING = 0
132         STATUS_DONE = 1
133         STATUS_ERROR = 2
134         STATUS_INIT = 4
135         
136         def __init__(self, statusCallback, blocking = False, neededTag = None, neededFlag = None, language = None):
137                 self.hardware_info = HardwareInfo()
138                 self.directory = "/"
139                 
140                 self.neededTag = neededTag
141                 self.neededFlag = neededFlag
142                 self.language = language
143                 
144                 # caution: blocking should only be used, if further execution in enigma2 depends on the outcome of
145                 # the installer!
146                 self.blocking = blocking
147                 
148                 self.currentlyInstallingMetaIndex = None
149                 
150                 self.console = eConsoleAppContainer()
151                 self.console.appClosed.append(self.installNext)
152                 self.reloadFavourites = False
153                 
154                 self.statusCallback = statusCallback
155                 self.setStatus(self.STATUS_INIT)
156                                 
157                 self.packageslist = []
158                 self.packagesIndexlist = []
159                 self.packageDetails = []
160
161         def readInfo(self, directory, file):
162                 print "Reading .info file", file
163                 handler = InfoHandler(self.prerequisiteMet, directory)
164                 try:
165                         xml.sax.parse(file, handler)
166                         for entry in handler.list:
167                                 self.packageslist.append((entry,file)) 
168                 except InfoHandlerParseError:
169                         print "file", file, "ignored due to errors in the file"
170                 print handler.list
171
172         def readIndex(self, directory, file):
173                 print "Reading .xml meta index file", file
174                 handler = InfoHandler(self.prerequisiteMet, directory, self.language)
175                 try:
176                         xml.sax.parse(file, handler)
177                         for entry in handler.list:
178                                 self.packagesIndexlist.append((entry,file))
179                 except InfoHandlerParseError:
180                         print "file", file, "ignored due to errors in the file"
181                 #print handler.list
182
183         def readDetails(self, directory, file):
184                 print "Reading .xml meta details file", file
185                 handler = InfoHandler(self.prerequisiteMet, directory, self.language)
186                 try:
187                         xml.sax.parse(file, handler)
188                         for entry in handler.list:
189                                 self.packageDetails.append((entry,file))
190                 except InfoHandlerParseError:
191                         print "file", file, "ignored due to errors in the file"
192                 #print handler.list
193
194         # prerequisites = True: give only packages matching the prerequisites
195         def fillPackagesList(self, prerequisites = True):
196                 self.packageslist = []
197                 packages = []
198                 if not isinstance(self.directory, list):
199                         self.directory = [self.directory]
200                 
201                 for directory in self.directory:
202                         packages += crawlDirectory(directory, ".*\.info$")
203
204                 for package in packages:
205                         self.readInfo(package[0] + "/", package[0] + "/" + package[1])
206                         
207                 if prerequisites:
208                         for package in self.packageslist[:]:
209                                 if not self.prerequisiteMet(package[0]["prerequisites"]):
210                                         self.packageslist.remove(package)
211                 return self.packageslist
212
213         # prerequisites = True: give only packages matching the prerequisites
214         def fillPackagesIndexList(self, prerequisites = True):
215                 self.packagesIndexlist = []
216                 if self.language is not None:
217                         indexfile = 'index_' + self.language + '.xml'
218                 else:
219                         indexfile = 'index.xml'
220                 if not isinstance(self.directory, list):
221                         self.directory = [self.directory]
222                 self.readIndex(self.directory[0] + "/", self.directory[0] + "/" + indexfile)
223
224                 if prerequisites:
225                         for package in self.packagesIndexlist[:]:
226                                 if not self.prerequisiteMet(package[0]["prerequisites"]):
227                                         self.packagesIndexlist.remove(package)
228                 return self.packagesIndexlist
229
230         # prerequisites = True: give only packages matching the prerequisites
231         def fillPackageDetails(self, details = None):
232                 self.packageDetails = []
233                 detailsfile = details
234                 if not isinstance(self.directory, list):
235                         self.directory = [self.directory]
236                 self.readDetails(self.directory[0] + "/", self.directory[0] + "/" + detailsfile)
237                 return self.packageDetails
238                         
239         def prerequisiteMet(self, prerequisites):
240                 # TODO: we need to implement a hardware detection here...
241                 print "prerequisites:", prerequisites
242                 met = True
243                 if self.neededTag is None:
244                         if prerequisites.has_key("tag"):
245                                 return False
246                 elif self.neededTag == 'ALL_TAGS':
247                                 return True
248                 else:
249                         if prerequisites.has_key("tag"):
250                                 if not self.neededTag in prerequisites["tag"]:
251                                         return False
252                         else:
253                                 return False
254
255                 if self.neededFlag is None:
256                         if prerequisites.has_key("flag"):
257                                 return False
258                 else:
259                         if prerequisites.has_key("flag"):
260                                 if not self.neededFlag in prerequisites["flag"]:
261                                         return False
262                         else:
263                                 return True # No flag found, assuming all flags valid
264                                 
265                 if prerequisites.has_key("satellite"):
266                         for sat in prerequisites["satellite"]:
267                                 if int(sat) not in nimmanager.getConfiguredSats():
268                                         return False                    
269                 if prerequisites.has_key("bcastsystem"):
270                         has_system = False
271                         for bcastsystem in prerequisites["bcastsystem"]:
272                                 if nimmanager.hasNimType(bcastsystem):
273                                         has_system = True
274                         if not has_system:
275                                 return False
276                 if prerequisites.has_key("hardware"):
277                         hardware_found = False
278                         for hardware in prerequisites["hardware"]:
279                                 if hardware == self.hardware_info.device_name:
280                                         hardware_found = True
281                         if not hardware_found:
282                                 return False
283                 return True
284         
285         def installPackages(self, indexes):
286                 print "installing packages", indexes
287                 if len(indexes) == 0:
288                         self.setStatus(self.STATUS_DONE)
289                         return
290                 self.installIndexes = indexes
291                 print "+++++++++++++++++++++++bla"
292                 self.currentlyInstallingMetaIndex = 0
293                 self.installPackage(self.installIndexes[self.currentlyInstallingMetaIndex])
294
295         def installPackage(self, index):
296                 print "self.packageslist:", self.packageslist
297                 if len(self.packageslist) <= index:
298                         print "no package with index", index, "found... installing nothing"
299                         return
300                 print "installing package with index", index, "and name", self.packageslist[index][0]["attributes"]["name"]
301                 
302                 attributes = self.packageslist[index][0]["attributes"]
303                 self.installingAttributes = attributes
304                 self.attributeNames = ["skin", "config", "favourites", "package", "services"]
305                 self.currentAttributeIndex = 0
306                 self.currentIndex = -1
307                 self.installNext()
308                 
309         def setStatus(self, status):
310                 self.status = status
311                 self.statusCallback(self.status, None)
312                                                 
313         def installNext(self, *args, **kwargs):
314                 if self.reloadFavourites:
315                         self.reloadFavourites = False
316                         db = eDVBDB.getInstance().reloadBouquets()
317
318                 self.currentIndex += 1
319                 attributes = self.installingAttributes
320                 #print "attributes:", attributes
321                 
322                 if self.currentAttributeIndex >= len(self.attributeNames): # end of package reached
323                         print "end of package reached"
324                         if self.currentlyInstallingMetaIndex is None or self.currentlyInstallingMetaIndex >= len(self.installIndexes) - 1:
325                                 print "set status to DONE"
326                                 self.setStatus(self.STATUS_DONE)
327                                 return
328                         else:
329                                 print "increment meta index to install next package"
330                                 self.currentlyInstallingMetaIndex += 1
331                                 self.currentAttributeIndex = 0
332                                 self.installPackage(self.installIndexes[self.currentlyInstallingMetaIndex])
333                                 return
334                 
335                 self.setStatus(self.STATUS_WORKING)             
336                 
337                 print "currentAttributeIndex:", self.currentAttributeIndex
338                 currentAttribute = self.attributeNames[self.currentAttributeIndex]
339                 
340                 print "installing", currentAttribute, "with index", self.currentIndex
341                 
342                 if attributes.has_key(currentAttribute):
343                         if self.currentIndex >= len(attributes[currentAttribute]): # all jobs done for current attribute
344                                 self.currentIndex = -1
345                                 self.currentAttributeIndex += 1
346                                 self.installNext()
347                                 return
348                 else: # nothing to install here
349                         self.currentIndex = -1
350                         self.currentAttributeIndex += 1
351                         self.installNext()
352                         return
353                         
354                 if currentAttribute == "skin":
355                         skin = attributes["skin"][self.currentIndex]
356                         self.installSkin(skin["directory"], skin["name"])
357                 elif currentAttribute == "config":
358                         if self.currentIndex == 0:
359                                 from Components.config import configfile
360                                 configfile.save()
361                         config = attributes["config"][self.currentIndex]
362                         self.mergeConfig(config["directory"], config["name"])
363                 elif currentAttribute == "favourites":
364                         favourite = attributes["favourites"][self.currentIndex]
365                         self.installFavourites(favourite["directory"], favourite["name"])
366                 elif currentAttribute == "package":
367                         package = attributes["package"][self.currentIndex]
368                         self.installIPK(package["directory"], package["name"])
369                 elif currentAttribute == "services":
370                         service = attributes["services"][self.currentIndex]
371                         self.mergeServices(service["directory"], service["name"])
372                                 
373         def readfile(self, filename):
374                 if not os.path.isfile(filename):
375                         return []
376                 fd = open(filename)
377                 lines = fd.readlines()
378                 fd.close()
379                 return lines
380                         
381         def mergeConfig(self, directory, name, merge = True):
382                 print "merging config:", directory, " - ", name
383                 if os.path.isfile(directory + name):
384                         config.loadFromFile(directory + name)
385                         configfile.save()
386                 self.installNext()
387                 
388         def installIPK(self, directory, name):
389                 if self.blocking:
390                         os.system("ipkg install " + directory + name)
391                         self.installNext()
392                 else:
393                         self.ipkg = IpkgComponent()
394                         self.ipkg.addCallback(self.ipkgCallback)
395                         self.ipkg.startCmd(IpkgComponent.CMD_INSTALL, {'package': directory + name})
396                 
397         def ipkgCallback(self, event, param):
398                 print "ipkgCallback"
399                 if event == IpkgComponent.EVENT_DONE:
400                         self.installNext()
401                 elif event == IpkgComponent.EVENT_ERROR:
402                         self.installNext()
403         
404         def installSkin(self, directory, name):
405                 print "installing skin:", directory, " - ", name
406                 print "cp -a %s %s" % (directory, resolveFilename(SCOPE_SKIN))
407                 if self.blocking:
408                         copytree(directory, resolveFilename(SCOPE_SKIN))
409                         self.installNext()
410                 else:
411                         if self.console.execute("cp -a %s %s" % (directory, resolveFilename(SCOPE_SKIN))):
412                                 print "execute failed"
413                                 self.installNext()
414
415         def mergeServices(self, directory, name, merge = False):
416                 print "merging services:", directory, " - ", name
417                 if os.path.isfile(directory + name):
418                         db = eDVBDB.getInstance()
419                         db.reloadServicelist()
420                         db.loadServicelist(directory + name)
421                         db.saveServicelist()
422                 self.installNext()
423
424         def installFavourites(self, directory, name):
425                 print "installing favourites:", directory, " - ", name
426                 self.reloadFavourites = True
427
428                 if self.blocking:
429                         copyfile(directory + name, resolveFilename(SCOPE_CONFIG))
430                         self.installNext()
431                 else:
432                         if self.console.execute("cp %s %s" % ((directory + name), resolveFilename(SCOPE_CONFIG))):
433                                 print "execute failed"
434                                 self.installNext()