Enigma2 plugin to dowload and create channels picons files form lyngsat.com

get_picons.py 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. # coding: utf-8
  2. #
  3. # This file is part of GetPicons - enigma2 plugin to download picons from lyngsat.com
  4. # Copyright (c) 2016 ivars777 (ivars777@gmail.com)
  5. # Distributed under the GNU GPL v3. For full terms see http://www.gnu.org/licenses/gpl-3.0.en.html
  6. """
  7. Dowload and create Enigma2 channels' picons files form lyngsat.com
  8. Usage [options]
  9. Options:
  10. -p PACKAGE_LIST, --package=PACKAGE_LIST list of package names (html file name in lyngsat),
  11. e.g "viasat,ntvplus36"
  12. -s SAT_LIST, --sat=SAT_LIST list of sattelite positions, e.g. "4.9,-0.8"
  13. -z SIZE icon size, e.g. "100x60", default - "220x132"
  14. -f PATH, --folder PATH picon files output folder, default - "/media/hdd/picon"
  15. -e PATH, --enigma PATH enigma2 folder whera lamedb,settings are located, default - "/etc/enigma2"
  16. you can use urls, e.g. "ftp://root@receiver_address/etc/enigma2"
  17. -o, -overwrite overwrite existing picons file (default - no)
  18. -d, -debug display work progress and write debug info to file for not found services
  19. -h, --help this help file
  20. (c)Ivars777 (ivars777@gmail.com) 2013-2015, v0.2
  21. """
  22. import sys, os, os.path, getopt
  23. import re
  24. import urllib2
  25. import requests
  26. try:
  27. import Image
  28. except:
  29. from PIL import Image
  30. from StringIO import StringIO
  31. import logging
  32. options = args = None
  33. _sd= lambda x,d: d if x is None else x # set default value
  34. _sl= lambda x: False if x is None else True # set True of False for option
  35. def parse_arguments(argv,opt_short,opt_long):
  36. "Parse command line arguments"
  37. try:
  38. options, args = getopt.gnu_getopt(argv, opt_short,opt_long)
  39. options = Rec(dict([(o[0].replace("-",""),o[1]) for o in options]))
  40. except getopt.GetoptError, err:
  41. print err.msg
  42. print __doc__
  43. sys.exit(2)
  44. if options.has_key("h") or options.has_key("help"):
  45. print __doc__
  46. sys.exit(2)
  47. return options,args
  48. services = None
  49. def main(argv):
  50. global options, args, services
  51. # Parsing options
  52. opt_short = 'p:s:z:f:e:ordh'
  53. opt_long = ["package=","sat","folder=","enigma=","overwrite","hires","debug","help"]
  54. options,args = parse_arguments(argv[1:], opt_short, opt_long)
  55. options.package = _sd(options.package,_sd(options.p,""))
  56. options.sat = _sd(options.sat,_sd(options.s,""))
  57. options.enigma = _sd(options.enigma,_sd(options.e,"/etc/enigma2"))
  58. options.folder = _sd(options.folder,_sd(options.f,"/media/hdd/picon"))
  59. options.size = _sd(options.size,_sd(options.z,"220x132"))
  60. options.overwrite = _sl(_sd(options.overwrite,options.o))
  61. options.debug = _sl(_sd(options.debug,options.d))
  62. options.hires = _sl(_sd(options.hires,options.r))
  63. options.w,options.h = map(int,options.size.split("x"))
  64. if not os.path.exists(options.folder):
  65. os.makedirs(options.folder)
  66. if options.debug:
  67. #filename="get_picons.log" if not os.name == "posix" else "/var/log/get_picons.log"
  68. filename = os.path.join(options.folder,"get_picons.log")
  69. FORMAT = "%(asctime)-15s %(message)s"
  70. logging.basicConfig(filename=filename, format=FORMAT,level=logging.ERROR)
  71. print "** Analyzing lamedb"
  72. services = DBServices(options.enigma)
  73. if options.sat:
  74. package_list = []
  75. for sat in options.sat.split(","):
  76. package_list.extend(get_packages(sat))
  77. elif options.package:
  78. package_list = options.package.split(",")
  79. else: # get sattelite list from Enigma settings
  80. sat_list = []
  81. package_list = []
  82. if not "ftp:" in options.enigma:
  83. fname = os.path.join(options.enigma,"settings")
  84. f = open(fname)
  85. else:
  86. sep = "" if options.enigma[-1]=="//" else "//"
  87. fname = options.enigma + sep + "settings"
  88. f = urllib2.urlopen(fname)
  89. for line in f:
  90. if not "config.Nims.0.advanced.sat." in line: continue
  91. sat_list.append(line.split(".")[5])
  92. for sat in sat_list:
  93. package_list.extend(get_packages(sat))
  94. for package in package_list:
  95. create_picons(package)
  96. def create_picons(package):
  97. "Create picons files for package"
  98. global services,options
  99. url_package = "https://www.lyngsat.com/packages/%s.html"%package
  100. if options.debug:
  101. print "** Downloading and looking %s for picons"%url_package
  102. num_picons = 0
  103. num_skipped = 0
  104. num = 0
  105. try:
  106. #soup = html = urllib2.urlopen(url_package).read().decode("latin1")
  107. soup = html = get_page(url_package).decode("latin1")
  108. except:
  109. raise Exception("Package page '%s' does not found"%url_package)
  110. title = re.search("<title>(.+?)</title>", soup, re.DOTALL | re.IGNORECASE).group(1)
  111. pos = re.search("\d+\.\d+",title).group().replace(".","")
  112. ew = re.search("\xb0.",title).group()[1]
  113. ns = int(pos)
  114. if ew == "W":
  115. ns = 3600 - ns
  116. if ns==48: ns=49
  117. if ns==359: ns=360
  118. #pos = "%04x0000"%ns
  119. #pos_num = int(pos[0:4],16)
  120. title2 = title[0:title.index("- LyngSat")]
  121. print "** %s: "%title2.replace(u'\xb0',""),
  122. tables = re.findall(r"<table width=720 border cellspacing=0 cellpadding=0>\s*(?:(?=<tr>))(.+?)</table>", soup, re.DOTALL | re.IGNORECASE)
  123. for table in tables:
  124. tr= html_findall("tr",table)
  125. for i in range(2,len(tr)-1):
  126. td= html_findall("td",tr[i])
  127. if len(td) == 10:
  128. freq,polar = re.search(r"<b>(\d+) ([HVLR])", tr[i], re.DOTALL | re.IGNORECASE).groups()
  129. freq = int(freq)
  130. p_value = {"H":0,"V":1,"L":2,"R":3}
  131. polar = p_value[polar]
  132. b=1
  133. else:
  134. b=0
  135. icon_url = html_attr("src",td[b])
  136. if icon_url:
  137. #icon_url = "http://www.lyngsat.com" + icon_url.group(1).replace("/icon","/logo").replace(".gif",".jpg")
  138. # https://www.lyngsat.com/logo/tv/vv/viasat_history.png
  139. # https://www.lyngsat-logo.com/hires/vv/viasat_history.png
  140. if options.hires:
  141. icon_url = "https://www.lyngsat-logo.com" + icon_url.group(1).replace("/logo/tv", "/hires")
  142. else:
  143. icon_url = "https://www.lyngsat.com" + icon_url.group(1) # simple picture
  144. else:
  145. icon_url = ""
  146. name = html_text(td[b+1]).group(1)
  147. sid = html_text(td[b+5])
  148. sid = sid.group(1).replace("&nbsp;","") if sid else ""
  149. sid = int(sid) if sid and not sid == u"-" else ""
  150. vpid = html_text(td[b+6])
  151. vpid = vpid.group(1).replace("&nbsp;","") if vpid else ""
  152. #try:
  153. # vpid = int(vpid)
  154. #except Exception:
  155. # vpid = None
  156. #vpid = int(vpid) if vpid and not vpid == u"-" else None
  157. if not sid or not vpid or icon_url=="":
  158. continue
  159. if options.debug:
  160. print " package:%i/%s: service:%s "%(ns,package, name.encode("ascii","replace")),
  161. num += 1
  162. sref = find_sref(ns,freq,polar,sid)
  163. if not sref:
  164. sref = find_sref(ns,freq+1,polar,sid)
  165. if not sref:
  166. sref = find_sref(ns,freq-1,polar,sid)
  167. if not sref:
  168. sref = find_sref(ns, freq, polar-2, sid)
  169. if not sref:
  170. if options.debug:
  171. print " -- NOK (no sref in lamedb!)"
  172. logging.error(u" no sref - package:%i/%s: service:%s pos:%s/%s freq:%i polar:%s sid:%i/%x"%(ns,package, name,ns, pos_int2str(ns),freq,polar,sid,sid))
  173. continue
  174. fname = sref.replace(":","_")
  175. fname = fname[:-1]+".png"
  176. fname = os.path.join(options.folder, fname)
  177. if os.path.exists(fname) and not options.overwrite:
  178. if options.debug: print " -- skipped"
  179. num_skipped += 1
  180. continue
  181. #try:
  182. #im = Image.open(StringIO(urllib2.urlopen(icon_url).read()))
  183. data = get_page(icon_url)
  184. if not data:
  185. if options.debug: print " -- NOK (no picon image)"
  186. continue
  187. try:
  188. im = Image.open(StringIO(data))
  189. im.thumbnail((options.w,options.h), Image.ANTIALIAS)
  190. im = im.convert('P', palette=Image.ADAPTIVE)
  191. except Exception:
  192. im = None
  193. if not im:
  194. if options.debug: print " -- NOK (no picon image)"
  195. continue
  196. if options.debug: print " -- downloaded"
  197. im.save(fname,"png")
  198. del im
  199. num_picons += 1
  200. print "%i picons created, %i skipped"%(num_picons,num_skipped)
  201. def get_packages(sat):
  202. "Get packages list (names) for sattelite from lyngsat.com page"
  203. global options
  204. sat= sat.replace(".","")
  205. num = int(sat)
  206. if num>1800: num = num - 3600
  207. if num>0 and num<730: soup = get_soup("europe")
  208. elif num>730 and num<1600: soup = get_soup("asia")
  209. elif num<0 and num>-610: soup = get_soup("atlantic")
  210. elif num<-610 and num>-1600: soup = get_soup("america")
  211. else:
  212. return []
  213. i1,i2 = gr=re.search(r"<table cellspacing=0 border>\s*(?:(?=<tr>))(.+?)</table>", soup, re.DOTALL | re.IGNORECASE).span()
  214. packages = []
  215. for s in re.findall("<tr>(.+?)</tr>", soup[i1:i2], re.DOTALL | re.IGNORECASE):
  216. gr = re.search(r"(\d+.)</font><font size=1>(\d).+#176;([EW])", s,re.DOTALL | re.IGNORECASE).groups()
  217. pos = int("".join(gr[:-1]).replace(".",""))
  218. if gr[2] == "W": pos = -pos
  219. if abs(num - pos) <=2:
  220. package = re.search(r"www\.lyngsat\.com/packages/(\w.+?)\.html", s, re.DOTALL | re.IGNORECASE).group(1)
  221. packages.append(package)
  222. return packages
  223. soups = {}
  224. def get_soup(region):
  225. if soups.has_key(region):
  226. return soups[region]
  227. url = "http://www.lyngsat.com/packages/%s.html"%region
  228. if options.debug:
  229. print "** Downloading and looking %s for packages"%url
  230. try:
  231. #html = urllib2.urlopen(url).read().decode("latin1")
  232. html = get_page(url).decode("latin1")
  233. except:
  234. raise Exception("Packege page '%s' does not found"%url)
  235. soups[region] = html
  236. return soups[region]
  237. def find_sref(ns,freq,polar,sid):
  238. "Find service reference according to given parameters"
  239. global services
  240. tp = services.get_transp_by_pic((ns,freq,polar))
  241. if tp:
  242. serv = services.get_service_by_chid((sid,tp["tsid"],tp["onid"]))
  243. if serv:
  244. return serv["sref"]
  245. class DBServices(object):
  246. """Dreambox services, stored in lamedb (E2)/services(E1) file handling class"""
  247. def __init__(self,service_path,enigma=2):
  248. self.enigma = enigma
  249. self.service_path = service_path
  250. if self.enigma == 2:
  251. if not "ftp:" in service_path:
  252. self.service_fname= os.path.join(service_path,"lamedb")
  253. self.bouquets_fname = os.path.join(service_path, "bouquets.tv")
  254. else:
  255. sep = "" if service_path[-1]=="//" else "//"
  256. self.service_fname= service_path+sep+"lamedb"
  257. self.bouquets_fname = service_path+sep+"bouquets.tv"
  258. else: # ENIGMA === "1":
  259. self.service_fname = os.path.join(service_path,"services")
  260. self.bouquets_fname = os.path.join(service_path, "userbouquets.tv.epl")
  261. self.services = []
  262. self.index_sref= {}
  263. self.index_chid = {}
  264. self.index_name = {}
  265. self.index_provider = {}
  266. self.bouquets = []
  267. self.bouquets_services = {}
  268. self.transp = []
  269. self.tp_index_tpid = {}
  270. self.tp_index_pic ={}
  271. self._load_services()
  272. #self._load_bouquets()
  273. def _load_services(self):
  274. #tpref - lamedb/transponders
  275. # NS:TSID:ONID
  276. # s FREQ:SR:POLAR:FREC:49:2:0
  277. # X X X
  278. # D D D D
  279. # lref - lamedb/services
  280. # SID:NS:TSID:ONID:STYPE:UNUSED(channelnumber in enigma1)
  281. # 0 1 2 3 4 5
  282. # X X X X D D
  283. # wref bouquets/picon
  284. # REFTYPE:FLAGS:STYPE:SID:TSID:ONID:NS:PARENT_SID:PARENT_TSID:UNUSED
  285. # 0 1 2 3 4 5 6 7 8 9
  286. # D D X X X X X X X X
  287. f_services = open(self.service_fname,"r") if not self.service_fname[0:4] == "ftp:" else urllib2.urlopen(self.service_fname)
  288. line = f_services.readline()
  289. if not "eDVB services" in line:
  290. raise Exception("no correct lamedb file")
  291. line = f_services.readline()
  292. # Read transponders
  293. i = 0
  294. while True:
  295. line = f_services.readline()
  296. if "end" in line: break
  297. #tp = record.Rec()
  298. tp = {}
  299. ff = line.strip().split(":")
  300. tp["ns"] = ns = pos_str2int(ff[0])
  301. tp["tsid"] = tsid = int(ff[1],16)
  302. tp["onid"] = onid = int(ff[2],16)
  303. line = f_services.readline()
  304. tp["params"] = params = line.strip().split(" ")[1]
  305. ff = params.split(":")
  306. tp["freq"] = freq = int(ff[0][:-3])
  307. tp["polar"] = polar = int(ff[2])
  308. tp["tpid"] = tpid = (ns,tsid,onid)
  309. self.transp.append(tp)
  310. self.tp_index_tpid[tpid] = i
  311. self.tp_index_pic[(ns,freq,polar)] = i
  312. line = f_services.readline()
  313. i += 1
  314. # Reading services
  315. i= 0
  316. line = f_services.readline()
  317. while True:
  318. line = f_services.readline()
  319. if line[0:3] == "end": break
  320. if not line: break
  321. #rec = record.Rec()
  322. rec = {}
  323. rec["lref"] = line.lower().strip().decode("utf-8")
  324. line = f_services.readline()
  325. #line = line.replace('\xc2\x87', '').replace('\xc2\x86', '')
  326. #line = decode_charset(line)
  327. line = line.decode("utf-8")
  328. rec["name"] = line.strip()
  329. line = f_services.readline()
  330. pl = line.split(",")
  331. provider = ""
  332. for p in pl:
  333. if p[0:2] == "p:":
  334. provider = p[2:].strip()
  335. break
  336. if provider == "":
  337. provider = "unknown"
  338. rec["provider"] = provider.decode("utf-8")
  339. rec["sref"] = lref2sref(rec["lref"])
  340. r = lref_parse(rec["lref"])
  341. rec["stype"] = r["stype"]
  342. rec["chid"] = (r["sid"],r["tsid"],r["onid"])
  343. self.services.append(rec)
  344. self.index_sref[rec["sref"]] = i
  345. self.index_chid[rec["chid"]] = i
  346. name = rec["name"].lower()
  347. if not self.index_name.has_key(name): self.index_name[name] = []
  348. self.index_name[name].append(i)
  349. provider = rec["provider"].lower()
  350. if not self.index_provider.has_key(provider): self.index_provider[provider] = []
  351. self.index_provider[provider].append(i)
  352. i += 1
  353. f_services.close()
  354. def _load_bouquets(self):
  355. f_bouquets = open(self.bouquets_fname,"r") if not self.bouquets_fname[0:4] == "ftp:" else urllib2.urlopen(self.bouquets_fname)
  356. for line in f_bouquets.readlines():
  357. if line[0:9] == "#SERVICE:":
  358. if self.enigma==1:
  359. bn = line.strip().split("/")
  360. else:
  361. bn = line.strip().split(":")
  362. bfn = bn[-1]
  363. bf = open(os.path.join(self.service_path,bfn))
  364. for line2 in bf.readlines():
  365. if line2[0:5] == "#NAME":
  366. bname = line2.strip()[6:]
  367. self.bouquets.append(bname)
  368. self.bouquets_services[bname] = []
  369. elif line2[0:8] == "#SERVICE":
  370. sref = line2.strip()[9:]
  371. r = sref_parse(sref)
  372. if r["flags"] == 64: continue
  373. self.bouquets_services[bname].append(sref)
  374. bf.close()
  375. f_bouquets.close()
  376. def get_bouquets(self):
  377. if not self.bouquets:
  378. self._load_bouquets()
  379. return self.bouquets
  380. def get_bouquet_services(self,bn):
  381. if not self.bouquets:
  382. self._load_bouquets()
  383. return [self.get_service_by_sref(sref) for sref in self.bouquets_services[bn]]
  384. def get_service_by_sref(self,sref):
  385. return self.services[self.index_sref[sref]] if self.index_sref.has_key(sref) else None
  386. def get_service_by_chid(self,chid):
  387. return self.services[self.index_chid[chid]] if self.index_chid.has_key(chid) else None
  388. def get_service_by_name(self,name):
  389. return [self.services[i] for i in self.index_name[name]]
  390. def get_service_by_provider(self,provider):
  391. return [self.services[i] for i in self.index_provider[provider]]
  392. def get_transp_by_tpid(self,tpid):
  393. return self.transp[self.tp_index_tpid[tpid]] if self.tp_index_tpid.has_key(tpid) else None
  394. def get_transp_by_pic(self,picid):
  395. return self.transp[self.tp_index_pic[picid]] if self.tp_index_pic.has_key(picid) else None
  396. def decode_charset(s):
  397. u = None
  398. if isinstance(s,unicode):
  399. return s
  400. charset_list=('iso-8859-4','iso-8859-1','iso-8859-2''iso-8859-15')
  401. for charset in charset_list:
  402. try:
  403. u=unicode(s,charset,"strict")
  404. except:
  405. pass
  406. else:
  407. break
  408. if u == None:
  409. raise Error, "CHARSET ERROR while decoding lamedb. Aborting !"
  410. else:
  411. return(u)
  412. # lref - lamedb/services
  413. # SID:NS:TSID:ONID:STYPE:UNUSED(channelnumber in enigma1)
  414. # 0 1 2 3 4 5
  415. # X X X X D D
  416. #
  417. # wref bouquets/picon
  418. # REFTYPE:FLAGS:STYPE:SID:TSID:ONID:NS:PARENT_SID:PARENT_TSID:UNUSED
  419. # 0 1 2 3 4 5 6 7 8 9
  420. # D D X X X X X X X X
  421. def lref2sref(s):
  422. "Converts service refefence from lamedb format to bouquets/picon (standard) format"
  423. f = s.split(":")
  424. sid = int(f[0],16)
  425. ns = int(f[1], 16)
  426. tsid = int(f[2],16)
  427. onid = int(f[3],16)
  428. stype = int(f[4])
  429. s2 = "1:0:%X:%X:%X:%X:%X:0:0:0:" % (stype,sid,tsid,onid,ns)
  430. return s2
  431. def lref_parse(s):
  432. "Parse lamedb/service string, return dictionary of items"
  433. f = s.split(":")
  434. r = {}
  435. r["sid"] = int(f[0],16)
  436. r["ns"] = int(f[1], 16)
  437. r["tsid"] = int(f[2],16)
  438. r["onid"] = int(f[3],16)
  439. r["stype"] = int(f[4])
  440. return r
  441. def sref2lref(s):
  442. "Converts service refefence from bouquets/picon (standard) format to lamedb format"
  443. f = s.split(":")
  444. reftype = int(f[0])
  445. flags = int(f[1])
  446. stype = int(f[2],16)
  447. sid = int(f[3],16)
  448. tsid = int(f[4],16)
  449. onid = int(f[5],16)
  450. ns = int(f[6],16)
  451. s2 = "%04x:%08x:%04x:%04x:%i:0" % (sid,ns,tsid,onid,stype)
  452. return s2
  453. def sref_parse(s):
  454. "Parse service refefence string , return dictionary of items"
  455. f = s.split(":")
  456. r = {}
  457. r["reftype"] = int(f[0])
  458. r["flags"] = int(f[1])
  459. r["stype"] = int(f[2],16)
  460. r["sid"] = int(f[3],16)
  461. r["tsid"] = int(f[4],16)
  462. r["onid"] = int(f[5],16)
  463. r["ns"] = int(f[6],16)
  464. return r
  465. pos_int2str = lambda pos_num: "%04x0000"%pos_num
  466. pos_str2int = lambda pos: int(pos[0:4],16)
  467. def decode_charset(s):
  468. u = None
  469. charset_list = ('utf-8','iso-8859-1','iso-8859-2','iso-8859-15')
  470. for charset in charset_list:
  471. try:
  472. u = unicode(s,charset,"strict")
  473. except:
  474. pass
  475. else:
  476. break
  477. if u == None:
  478. print("CHARSET ERROR while decoding string")
  479. sys.exit(1)
  480. else:
  481. return(u)
  482. headers2dict = lambda h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
  483. headers = headers2dict("""
  484. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
  485. Referer: http://www.lyngsat.com/
  486. """)
  487. def get_page(url):
  488. r=requests.get(url,headers=headers)
  489. if r.status_code in (200,304):
  490. return r.content
  491. else:
  492. return ""
  493. html_text = lambda html: re.search(r">([^<^\n]+?)<", html, re.DOTALL | re.IGNORECASE)
  494. html_tag = lambda tag, html: re.search(r"<%s\b[^>]*>(.*?)</%s>"%(tag,tag), html, re.DOTALL | re.IGNORECASE)
  495. html_attr = lambda attr,html: re.search("""%s="?([^'" >]+)"""%(attr), html, re.DOTALL | re.IGNORECASE)
  496. html_findall_content = lambda tag,html: re.findall(r"<%s\b[^>]*>(.*?)</%s>"%(tag,tag), html, re.DOTALL | re.IGNORECASE)
  497. html_findall = lambda tag,html: re.findall(r"<%s\b[^>]*>.*?</%s>"%(tag,tag), html, re.DOTALL | re.IGNORECASE)
  498. import UserDict
  499. class Rec(UserDict.DictMixin):
  500. "Dict like class allowing a.x instead of a['x']"
  501. def __init__(self, dict=None, **kwargs):
  502. self.__dict__ = {}
  503. if dict is not None:
  504. self.update(dict)
  505. if len(kwargs):
  506. self.update(kwargs)
  507. def __setitem__(self, name, value): setattr(self, name, value)
  508. def __getitem__(self, name): return getattr(self,name,None)
  509. def __getattr__(self,key): return None
  510. def has_key(self,key): return True if self.__dict__.has_key(key) else False
  511. def keys(self): return list(self.__dict__)
  512. if __name__ == "__main__":
  513. #print "** Starting..."
  514. sys.exit(main(sys.argv))