Ticket #6885: pyth.updates3.patch
File pyth.updates3.patch, 20.0 KB (added by , 15 years ago) |
---|
-
MythTV/MythVideo.py
27 27 if not host: # If a hostname was not supplied then use the local host name 28 28 host = gethostname() 29 29 30 # Get storagegroup table field names 31 table_names = self.getTableFieldNames(u'storagegroup') 30 ret = [] 31 for i in self.db.getStorageGroup('Videos',host): 32 ret.append(i['dirname']) 32 33 33 cur = self.db.cursor() 34 # Check is there are storage groups for the supplied host or default to the local host 35 try: 36 cur.execute(u"select * from storagegroup") 37 except MySQLdb.Error, e: 38 log.Msg(INFO, u"! Error: Reading storagegroup MythTV table: %d: %s\n" % (e.args[0], e.args[1])) 34 if ret: 35 return ret 36 else: 39 37 return None 40 38 41 videos_dir = []42 while True:43 data_id = cur.fetchone()44 if not data_id:45 break46 record = {}47 i = 048 for elem in data_id:49 if table_names[i] == 'groupname' or table_names[i] == 'hostname' or table_names[i] == 'dirname':50 record[table_names[i]] = elem51 i+=152 if record['hostname'].lower() == host.lower() and record['groupname'] == u'Videos':53 # Add a slash if mussing to any storage group dirname54 if record['dirname'][-1:] == '/':55 videos_dir.append(record['dirname'])56 else:57 videos_dir.append(record['dirname']+u'/')58 continue59 cur.close()60 61 if not len(videos_dir):62 return None63 64 return videos_dir65 # end getStorageGroups66 67 39 def pruneMetadata(self): 68 40 """ 69 41 Removes metadata from the database for files that no longer exist. … … 115 87 This function should have been called getCategoryId 116 88 """ 117 89 c = self.db.cursor() 118 c.execute("SELECT intid FROM videocategory WHERE lower(category) = %s", (genre_name,)) 90 c.execute("""SELECT intid FROM videocategory 91 WHERE lower(category) = %s""", (genre_name,)) 119 92 row = c.fetchone() 120 93 c.close() 121 94 … … 124 97 125 98 # Insert a new genre. 126 99 c = self.db.cursor() 127 c.execute("INSERT INTO videocategory(category) VALUES (%s)", (genre_name.capitalize(),)) 100 c.execute("""INSERT INTO videocategory(category) 101 VALUES (%s)""", (genre_name.capitalize(),)) 128 102 newid = c.lastrowid 129 103 c.close() 130 104 -
MythTV/MythTV.py
207 207 """ 208 208 Returns a Program object for the current recorders recording. 209 209 """ 210 res = self.backendCommand('QUERY_RECORDER %s[]:[]GET_CURRENT_RECORDING' % recorder)210 res = self.backendCommand('QUERY_RECORDER '+BACKEND_SEP.join([recorder,'GET_CURRENT_RECORDING'])) 211 211 return Program(res.split(BACKEND_SEP)) 212 212 213 213 def isRecording(self, recorder): 214 214 """ 215 215 Returns a boolean as to whether the given recorder is recording. 216 216 """ 217 res = self.backendCommand('QUERY_RECORDER %s[]:[]IS_RECORDING' % recorder)217 res = self.backendCommand('QUERY_RECORDER '+BACKEND_SEP.join([recorder,'IS_RECORDING'])) 218 218 if res == '1': 219 219 return True 220 220 else: … … 224 224 """ 225 225 Returns a boolean as to whether the given host is an active backend 226 226 """ 227 res = self.backendCommand( 'QUERY_IS_ACTIVE_BACKEND[]:[]%s' % hostname)227 res = self.backendCommand(BACKEND_SEP.join(['QUERY_IS_ACTIVE_BACKEND',hostname])) 228 228 if res == 'TRUE': 229 229 return True 230 230 else: … … 245 245 Returns a list of all Program objects which have already recorded 246 246 """ 247 247 programs = [] 248 res = self.backendCommand('QUERY_RECORDINGS Play').split( '[]:[]')248 res = self.backendCommand('QUERY_RECORDINGS Play').split(BACKEND_SEP) 249 249 num_progs = int(res.pop(0)) 250 250 log.Msg(DEBUG, '%s total recordings', num_progs) 251 251 for i in range(num_progs): … … 253 253 + PROGRAM_FIELDS])) 254 254 return tuple(programs) 255 255 256 def getExpiring(self): 257 """ 258 Returns a tuple of all Program objects nearing expiration 259 """ 260 programs = [] 261 res = self.backendCommand('QUERY_GETEXPIRING').split(BACKEND_SEP) 262 num_progs = int(res.pop(0)) 263 for i in range(num_progs): 264 programs.append(Program(res[i * PROGRAM_FIELDS:(i * PROGRAM_FIELDS) 265 + PROGRAM_FIELDS])) 266 return tuple(programs) 267 256 268 def getCheckfile(self,program): 257 269 """ 258 270 Returns location of recording in file system 259 271 """ 260 res = self.backendCommand( 'QUERY_CHECKFILE[]:[]1[]:[]%s' % program.toString()).split(BACKEND_SEP)272 res = self.backendCommand(BACKEND_SEP.join(['QUERY_CHECKFILE',1,program.toString()])).split(BACKEND_SEP) 261 273 if res[0] == 0: 262 274 return None 263 275 else: … … 271 283 command = 'DELETE_RECORDING' 272 284 if force: 273 285 command = 'FORCE_DELETE_RECORDING' 274 return self.backendCommand( '%s%s%s' % (command,BACKEND_SEP,program.toString()))286 return self.backendCommand(BACKEND_SEP.join([command,program.toString()])) 275 287 276 288 def forgetRecording(self,program): 277 289 """ 278 290 Forgets old recording and allows it to be re-recorded 279 291 """ 280 self.backendCommand( 'FORGET_RECORDING%s%s' % (BACKEND_SEP,program.toString()))292 self.backendCommand(BACKEND_SEP.join(['FORGET_RECORDING',program.toString()])) 281 293 282 294 def deleteFile(self,file,sgroup): 283 295 """ 284 296 Deletes a file from specified storage group on the connected backend 285 297 Takes a relative file path from the root of the storage group, and returns 1 on success 286 298 """ 287 return self.backendCommand( 'DELETE_FILE%s%s%s%s' % (BACKEND_SEP,file,BACKEND_SEP,sgroup))299 return self.backendCommand(BACKEND_SEP.join(['DELETE_FILE',file,sgroup])) 288 300 289 301 def getFreeSpace(self,all=False): 290 302 """ … … 329 341 res = self.backendCommand('QUERY_LOAD').split(BACKEND_SEP) 330 342 return (float(res[0]),float(res[1]),float(res[2])) 331 343 344 def getUptime(self): 345 """ 346 Returns machine uptime in seconds 347 """ 348 return self.backendCommand('QUERY_UPTIME') 349 332 350 def getSGList(self,host,sg,path): 333 351 """ 334 352 Returns a tuple of directories and files 335 353 """ 336 res = self.backendCommand( 'QUERY_SG_GETFILELIST%s%s%s%s%s%s' % (BACKEND_SEP,host,BACKEND_SEP,sg,BACKEND_SEP,path)).split(BACKEND_SEP)354 res = self.backendCommand(BACKEND_SEP.join(['QUERY_SG_GETFILELIST',host,sg,path])).split(BACKEND_SEP) 337 355 if res[0] == 'EMPTY LIST': 338 356 return -1 339 357 if res[0] == 'SLAVE UNREACHABLE: ': … … 352 370 """ 353 371 Returns a tuple of last modification time and file size 354 372 """ 355 res = self.backendCommand( 'QUERY_SG_FILEQUERY%s%s%s%s%s%s' % (BACKEND_SEP,host,BACKEND_SEP,sg,BACKEND_SEP,path)).split(BACKEND_SEP)373 res = self.backendCommand(BACKEND_SEP.join(['QUERY_SG_FILEQUERY',host,sg,path])).split(BACKEND_SEP) 356 374 if res[0] == 'EMPTY LIST': 357 375 return -1 358 376 if res[0] == 'SLAVE UNREACHABLE: ': … … 382 400 port = self.db.getSetting("NetworkControlPort",host) 383 401 return Frontend(host,port) 384 402 403 def getLastGuideData(self): 404 """ 405 Returns the last dat for which guide data is available 406 On error, 0000-00-00 00:00 is returned 407 """ 408 return self.backendCommand('QUERY_GUIDEDATATHROUGH') 409 385 410 def joinInt(self,high,low): 386 411 """ 387 412 Returns a single long from a pair of signed integers … … 458 483 if res[0] == 'REJECT': 459 484 log.Msg(CRITICAL, 'Backend has version %s and we speak version %s', res[1], PROTO_VERSION) 460 485 sys.exit(1) 461 res = self.send('ANN FileTransfer %s %d %d % d%s%s%s%s' % (socket.gethostname(), write, False, -1, BACKEND_SEP, self.filename, BACKEND_SEP, self.sgroup))486 res = self.send('ANN FileTransfer %s %d %d %s' % (socket.gethostbyname(),write, False, BACKEND_SEP.join([-1,self.filename,self.sgroup]))) 462 487 if res.split(BACKEND_SEP)[0] != 'OK': 463 488 log.Msg(CRITICAL, 'Unexpected answer to ANN command: %s', res) 464 489 else: … … 474 499 475 500 def __del__(self): 476 501 if self.sockno: 477 self.comsock.backendCommand('QUERY_FILETRANSFER %d%sDONE' % (self.sockno, BACKEND_SEP))502 self.comsock.backendCommand('QUERY_FILETRANSFER '+BACKEND_SEP.join([self.sockno, 'JOIN'])) 478 503 if self.datsock: 479 504 self.datsock.shutdown(1) 480 505 self.datsock.close() … … 533 558 csize = self.tsize 534 559 rsize = size - csize 535 560 536 res = self.comsock.backendCommand('QUERY_FILETRANSFER %d%sREQUEST_BLOCK%s%d' % (self.sockno,BACKEND_SEP,BACKEND_SEP,csize))561 res = self.comsock.backendCommand('QUERY_FILETRANSFER '+BACKEND_SEP.join([self.sockno,'REQUEST_BLOCK',csize])) 537 562 self.pos += int(res) 538 563 # if int(res) == csize: 539 564 # if csize < size: … … 561 586 buff = data[size:] 562 587 data = data[:size] 563 588 self.pos += int(self.datsock.send(data)) 564 self.comsock.backendCommand('QUERY_FILETRANSFER %d%sWRITE_BLOCK%s%d' % (self.sockno,BACKEND_SEP,BACKEND_SEP,size))589 self.comsock.backendCommand('QUERY_FILETRANSFER '+BACKEND_SEP.join([self.sockno,'WRITE_BLOCK',size])) 565 590 self.write(buff) 566 591 return 567 592 … … 593 618 curhigh,curlow = self.comsock.splitInt(self.pos) 594 619 offhigh,offlow = self.comsock.splitInt(offset) 595 620 596 res = self.comsock.backendCommand('QUERY_FILETRANSFER %d%sSEEK%s%d%s%d%s%d%s%d%s%d' % (self.sockno, BACKEND_SEP,BACKEND_SEP,offhigh,BACKEND_SEP,offlow,BACKEND_SEP,whence,BACKEND_SEP,curhigh,BACKEND_SEP,curlow)).split(BACKEND_SEP)621 res = self.comsock.backendCommand('QUERY_FILETRANSFER '+BACKEND_SEP.join([self.sockno,'SEEK',offhigh,offlow,whence,curhigh,curlow])).split(BACKEND_SEP) 597 622 self.pos = (int(res[0]) + (int(res[1])<0))*2**32 + int(res[1]) 598 623 599 624 … … 885 910 886 911 return string 887 912 913 def setField(self,field,value): 914 db = MythDB() 915 c = db.cursor() 916 c.execute("""UPDATE recorded SET %s = %%s 917 WHERE chanid=%%s and starttime=%%s""" % field, 918 (value,self.chanid,self.starttime)) 919 c.close() 920 921 def setBasename(self,name): 922 """ 923 Change the file basename pointed to by the recording 924 """ 925 self.setField('basename',name) 926 927 def setHostname(self,name): 928 """ 929 Change the hostname of the machine which holds the recording 930 """ 931 self.setField('hostname',name) 932 933 def setSG(self,name): 934 """ 935 Change the storagegroup which holds the recording 936 """ 937 self.setField('storagegroup',name) 938 888 939 if __name__ == '__main__': 889 940 banner = '\'m\' is a MythTV instance.' 890 941 try: -
MythTV/MythDB.py
96 96 raise MythError('Unable to find MythTV configuration file') 97 97 98 98 try: 99 self.db = MySQLdb.connect(user=dbconn['user'], host=dbconn['host'], passwd=dbconn['pass'], db=dbconn['name'], use_unicode=True, charset='utf8') 100 log.Msg(INFO, 'DB Connection info (host:%s, name:%s, user:%s, pass:%s)', dbconn['host'], dbconn['name'], dbconn['user'], dbconn['pass']) 99 self.db = MySQLdb.connect(user=dbconn['user'], host=dbconn['host'], 100 passwd=dbconn['pass'], db=dbconn['name'], use_unicode=True, 101 charset='utf8') 102 log.Msg(INFO, 'DB Connection info (host:%s, name:%s, user:%s, pass:%s)', 103 dbconn['host'], dbconn['name'], dbconn['user'], dbconn['pass']) 101 104 except: 102 raise MythError('Connection failed for \'%s\'@\'%s\' to database %s using password %s' % (dbconn['user'], dbconn['host'], dbconn['name'], dbconn['pass'])) 105 raise MythError('Connection failed for \'%s\'@\'%s\' to database %s using password %s' % 106 (dbconn['user'], dbconn['host'], dbconn['name'], dbconn['pass'])) 103 107 104 108 def getAllSettings(self, hostname=None): 105 109 """ … … 119 123 c.execute(""" 120 124 SELECT value, data 121 125 FROM settings 122 WHERE hostname LIKE( '%s%%')""" %126 WHERE hostname LIKE(%s)""", 123 127 (hostname)) 124 128 rows = c.fetchall() 125 129 c.close() … … 142 146 c.execute(""" 143 147 SELECT data 144 148 FROM settings 145 WHERE value LIKE( '%s') AND hostname IS NULL LIMIT 1""" %149 WHERE value LIKE(%s) AND hostname IS NULL LIMIT 1""", 146 150 (value)) 147 151 else: 152 hostname += '%' 148 153 c.execute(""" 149 154 SELECT data 150 155 FROM settings 151 WHERE value LIKE( '%s') AND hostname LIKE('%s%%') LIMIT 1""" %156 WHERE value LIKE(%s) AND hostname LIKE(%s) LIMIT 1""", 152 157 (value, hostname)) 153 158 row = c.fetchone() 154 159 c.close() … … 166 171 c = self.db.cursor() 167 172 ws = None 168 173 ss = None 174 t = None 169 175 170 176 if hostname is None: 171 ws = "WHERE value LIKE ('%s') AND hostname IS NULL" % (value) 172 ss = "(value,data) VALUES ('%s','%s')" % (value, data) 177 ws = "WHERE value LIKE (%s) AND hostname IS NULL" 178 ss = "(data,value) VALUES (%s,%s)" 179 t = (data,value) 173 180 else: 174 ws = "WHERE value LIKE ('%s') AND hostname LIKE ('%s%%')" % (value, hostname) 175 ss = "(value,data,hostname) VALUES ('%s','%s','%s')" % (value, data, hostname) 181 hostname += '%' 182 ws = "WHERE value LIKE (%s) AND hostname LIKE (%s)" 183 ss = "(data,value,hostname) VALUES (%s,%s,%s)" 184 t = (data,value,hostname) 176 185 177 if c.execute("""UPDATE settings SET data %s LIMIT 1""" % ws) == 0:178 c.execute("""INSERT INTO settings %s""" % ss )186 if c.execute("""UPDATE settings SET data=%%s %s LIMIT 1""" % ws, t) == 0: 187 c.execute("""INSERT INTO settings %s""" % ss, t) 179 188 c.close() 180 189 181 190 def getCast(self, chanid, starttime, roles=None): … … 187 196 """ 188 197 if roles is None: 189 198 c = self.db.cursor() 190 length = c.execute("SELECT name,role FROM people,credits WHERE people.person=credits.person AND chanid=%d AND starttime=%d ORDER BY role" % (chanid, starttime)) 199 length = c.execute("""SELECT name,role 200 FROM people,credits 201 WHERE people.person=credits.person 202 AND chanid=%s AND starttime=%s 203 ORDER BY role""", (chanid, starttime)) 191 204 if length == 0: 192 205 return () 193 206 crole = None … … 208 221 return dict 209 222 elif isinstance(roles,str): 210 223 c = self.db.cursor() 211 length = c.execute("SELECT name FROM people,credits WHERE people.person=credits.person AND chanid=%d AND starttime=%d AND role='%s'" % (chanid, starttime, roles)) 224 length = c.execute("""SELECT name 225 FROM people,credits 226 WHERE people.person=credits.person 227 AND chanid=%s AND starttime=%s 228 AND role=%s""", (chanid, starttime, roles)) 212 229 if length == 0: 213 230 return () 214 231 names = [] … … 217 234 return tuple(names) 218 235 elif isinstance(roles,tuple): 219 236 c = self.db.cursor() 220 length = c.execute("SELECT name FROM people,credits WHERE people.person=credits.person AND chanid=%d AND starttime=%d AND role IN %s" % (chanid, starttime, roles)) 237 length = c.execute("""SELECT name 238 FROM people,credits 239 WHERE people.person=credits.person 240 AND chanid=%d AND starttime=%d 241 AND role IN %s""" % (chanid, starttime, roles)) 221 242 if length == 0: 222 243 return () 223 244 names = [] … … 225 246 names.append(name[0]) 226 247 return tuple(names) 227 248 249 def getStorageGroup(self, group=None, host=None): 250 """ 251 Returns tuple of dictionaries containing storage group directories 252 with the fields 'id', 'group', 'host', and 'dirname' 253 Takes an optional group and host for filtering 254 """ 255 c = self.db.cursor() 256 q1 = 'SELECT * FROM storagegroup' 257 q2 = 'ORDER BY id' 258 if host: 259 host += '%' 260 if group and host: 261 c.execute("""%s 262 WHERE groupname=%%s 263 AND hostname like %%s 264 %s""" % (q1,q2), (group, host)) 265 elif group: 266 c.execute("""%s 267 WHERE groupname=%%s 268 %s""" % (q1,q2), (group,)) 269 elif host: 270 c.execute("""%s 271 WHERE hostname like %%s 272 %s""" % (q1,q2), (host,)) 273 else: 274 c.execute("""%s %s""" % (q1,q2)) 275 ret = [] 276 for i in c.fetchall(): 277 if not i[3][-1] == '/': 278 i[3] += '/' 279 ret.append({'id':i[0], 'group':i[1], 'host':i[2], 'dirname':i[3]}) 280 return tuple(ret) 281 282 def getChannels(self): 283 """ 284 Returns a tuple of channel object defined in the database 285 """ 286 channels = [] 287 c = self.db.cursor() 288 c.execute("""SELECT * FROM channel""") 289 for row in c.fetchall(): 290 channels.append(Channel(row)) 291 c.close() 292 return tuple(channels) 293 294 def getChannel(self,chanid): 295 """ 296 Returns a single channel object for the given chanid 297 """ 298 c = self.db.cursor() 299 if c.execute("""SELECT * FROM channel 300 WHERE chanid=%s""", (chanid,)): 301 return Channel(c.fetchone()) 302 else: 303 return None 304 305 def getGuideData(self, chanid, date): 306 """ 307 Returns tuple of guide data for one channel on one date 308 """ 309 guide = [] 310 c = self.db.cursor() 311 c.execute("""SELECT * FROM program 312 WHERE chanid=%s AND DATE(starttime)=%s""", 313 (chanid,date)) 314 for show in c.fetchall(): 315 guide.append(Guide(show)) 316 c.close() 317 return tuple(guide) 318 228 319 def cursor(self): 229 320 return self.db.cursor() 230 321 … … 251 342 if self.mythdb is None: 252 343 self.mythdb = MythDB() 253 344 c = self.mythdb.cursor() 254 c.execute("SELECT chanid,starttime FROM jobqueue WHERE id=%d" % self.jobid) 345 c.execute("""SELECT chanid,starttime FROM jobqueue 346 WHERE id=%s""", (self.jobid)) 255 347 self.chanid, self.starttime = c.fetchone() 256 348 c.close() 257 349 … … 260 352 self.mythdb = MythDB() 261 353 if self.jobid is None: 262 354 c = self.mythdb.cursor() 263 c.execute("SELECT id FROM jobqueue WHERE chanid=%d AND starttime=%d" % (self.chanid, self.starttime)) 355 c.execute("""SELECT id FROM jobqueue 356 WHERE chanid=%s AND starttime=%s""", 357 (self.chanid, self.starttime)) 264 358 self.jobid = c.fetchone()[0] 265 359 c.close() 266 360 return self.jobid … … 270 364 self.mythdb = MythDB() 271 365 if self.host is None: 272 366 c = self.mythdb.cursor() 273 c.execute("SELECT hostname FROM jobqueue WHERE id=%d" % self.jobid) 367 c.execute("""SELECT hostname FROM jobqueue 368 WHERE id=%s""", (self.jobid)) 274 369 self.host = c.fetchone()[0] 275 370 c.close() 276 371 return self.host … … 279 374 if self.mythdb is None: 280 375 self.mythdb = MythDB() 281 376 c = self.mythdb.cursor() 282 c.execute("UPDATE jobqueue SET comment='%s' WHERE id=%d" % (comment,self.jobid)) 377 c.execute("""UPDATE jobqueue SET comment=%s 378 WHERE id=%s""", (comment,self.jobid)) 283 379 c.close() 284 380 285 381 def setStatus(self,status): 286 382 if self.mythdb is None: 287 383 self.mythdb = MythDB() 288 384 c = self.mythdb.cursor() 289 c.execute("UPDATE jobqueue SET status=%d WHERE id=%d" % (status,self.jobid)) 385 c.execute("""UPDATE jobqueue SET status=%s 386 WHERE id=%s""", (status,self.jobid)) 290 387 c.close() 291 388 389 class Channel: 390 """ 391 Represents a single channel from the channel table 392 """ 393 def __str__(self): 394 return "%s (%s)" % (self.chanid, self.name) 395 396 def __repr__(self): 397 return "%s (%s)" % (self.chanid, self.name) 398 399 def __init__(self,data): 400 """ 401 Load data into object 402 """ 403 self.chanid = data[0] 404 self.channum = data[1] 405 self.freqid = data[2] 406 self.sourceid = data[3] 407 self.callsign = data[4] 408 self.name = data[5] 409 self.icon = data[6] 410 self.finetune = data[7] 411 self.videofilters = data[8] 412 self.xmltvid = data[9] 413 self.recpriority = data[10] 414 self.contrast = data[11] 415 self.brightness = data[12] 416 self.colour = data[13] 417 self.hue = data[14] 418 self.tvformat = data[15] 419 self.visible = data[16] 420 self.outputfiters = data[17] 421 self.useonairguide = data[18] 422 self.mplexid = data[19] 423 self.serviceid = data[20] 424 self.tmoffset = data[21] 425 self.atsc_major_chan = data[22] 426 self.atsc_minor_chan = data[23] 427 self.last_record = data[24] 428 self.default_authority = data[25] 429 self.commmethod = data[26] 430 431 class Guide: 432 """ 433 Represents a single program from the program guide 434 """ 435 def __str__(self): 436 return "%s (%s)" % (self.title, self.starttime.strftime('%Y-%m-%d %H:%M:%S')) 437 438 def __repr__(self): 439 return "%s (%s)" % (self.title, self.starttime.strftime('%Y-%m-%d %H:%M:%S')) 440 441 def __init__(self,data): 442 """ 443 Load data into the object 444 """ 445 self.chanid = data[0] 446 self.starttime = data[1] 447 self.endtime = data[2] 448 self.title = data[3] 449 self.subtitle = data[4] 450 self.description = data[5] 451 self.category = data[6] 452 self.category_type = data[7] 453 self.airdate = data[8] 454 self.stars = data[9] 455 self.previouslyshown = data[10] 456 self.title_pronounce = data[11] 457 self.stereo = data[12] 458 self.subtitled = data[13] 459 self.hdtv = data[14] 460 self.closecaptioned = data[15] 461 self.partnumber = data[16] 462 self.parttotal = data[17] 463 self.seriesid = data[18] 464 self.originalairdate = data[19] 465 self.showtype = data[20] 466 self.colorcode = data[21] 467 self.syndicatedepisodenumber = data[22] 468 self.programid = data[23] 469 self.manualid = data[24] 470 self.generic = data[25] 471 self.listingsource = data[26] 472 self.first = data[27] 473 self.last = data[28] 474 self.audioprop = data[29] 475 self.subtitletypes = data[30] 476 self.videoprop = data[31] 477 292 478 if __name__ == '__main__': 293 479 banner = "'mdb' is a MythDB instance." 294 480 try: