def removeContentDatabase(): import os import xbmcgui dbFile = os.path.join(kodiutil.PROFILE_PATH, 'content.db') if os.path.exists(dbFile): os.remove(dbFile) kodiutil.setSetting('content.initialized', False) xbmcgui.Dialog().ok(T(32515, 'Done'), ' ', T(32584, 'Database reset.'))
def handleClose(self): yes = True if self.modified: yes = xbmcgui.Dialog().yesno( T(32527, 'Confirm'), T(32528, 'Sequence was modified.'), '', T(32529, 'Do you really want to exit without saving changes?')) if yes: return False self.save(as_new=True) return True
def pasteLog(): import xbmcgui yes = xbmcgui.Dialog().yesno( T(32519, 'Choose'), T(32566, 'Would you like to paste the current old log?'), T( 32567, 'Paste the old log if you are in a new session, ie. after a Kodi crash.' ), '', T(32568, 'Current'), T(32569, 'Old')) if yes: _pasteLog('kodi.old.log') else: _pasteLog()
def removeItem(self): if not xbmcgui.Dialog().yesno(T(32527, 'Confirm'), '', T(32537, 'Do you really want to remove this module?')): return pos = self.sequenceControl.getSelectedPosition() if pos < 0: return self.sequenceControl.removeItem(pos) self.sequenceControl.removeItem(pos) self.updateFirstLast() self.modified = True
def _save(self, full_path, temp=False): items = [li.dataSource for li in self.sequenceControl if li.dataSource] if not cinemavision.sequence.sequenceHasFeature(items): yes = xbmcgui.Dialog().yesno( T(32603, 'No Feature'), T( 32604, 'Sequence does not have a feature module, which is required to play items selected in Kodi.' ), T(32605, 'Continue?')) if not yes: return xmlString = cinemavision.sequence.getSaveString(items) kodiutil.DEBUG_LOG('Saving to: {0}'.format(full_path)) success = True if cinemavision.util.vfs.exists(full_path): cinemavision.util.vfs.delete(full_path) with cinemavision.util.vfs.File(full_path, 'w') as f: success = f.write(xmlString) if not success: xbmcgui.Dialog().ok( T(32573, 'Failed'), T(32606, 'Failed to write sequence file!'), T(32607, 'Kodi may be unable to write to this location.')) return if not temp: self.modified = False self.saveDefault() sequence2D = kodiutil.getSetting('sequence.2D') sequence3D = kodiutil.getSetting('sequence.3D') if not sequence2D or (self.name != sequence2D and self.name != sequence3D): yes = xbmcgui.Dialog().yesno( T(32555, 'Set Default'), T( 32556, 'Would you like to set this as the default for playback?' )) if yes: as3D = xbmcgui.Dialog().yesno('2D/3D', T(32557, 'For 2D or 3D?'), nolabel='2D', yeslabel='3D') if as3D: kodiutil.setSetting('sequence.3D', self.name) else: kodiutil.setSetting('sequence.2D', self.name)
def fillSettingsList(self, update=False): sItem = self.item.dataSource items = [] for i, e in enumerate(sItem._elements): if not sItem.elementVisible(e): continue attr = e['attr'] name = e['name'] if sItem._type == 'video' and attr == 'vtype' and not sItem.getSetting( attr): name = '[COLOR FFFF0000]{0}[/COLOR]'.format(name) mli = kodigui.ManagedListItem( name, e['limits'] != cinemavision.sequence.LIMIT_ACTION and unicode(sItem.getSettingDisplay(attr)) or '', data_source=attr) mli.setProperty('name', e['name']) if sItem.getType(attr) == int: mli.setProperty('type', 'integer') items.append(mli) items.append( kodigui.ManagedListItem(u'[B]{0}[/B]'.format(T(32611)), data_source='@RESET@')) if update: self.settingsList.replaceItems(items) else: self.settingsList.reset() self.settingsList.addItems(items) self.setFocusId(self.SETTINGS_LIST_ID)
def sequenceOptions(self): while True: options = [] options.append(('conditions', 'Conditions: [B]{0}[/B]'.format( self.sequenceData._attrs.get('active') and 'ACTIVE' or 'INACTIVE'))) options.append( ('show_in_dialog', 'Show in selection dialog: [B]{0}[/B]'.format( self.sequenceData._settings.get('show_in_dialog') == False and 'NO' or 'YES'))) options.append(('close', '[B]Close[/B]')) idx = xbmcgui.Dialog().select(T(32612, 'Sequence settings'), [o[1] for o in options]) if idx < 0: return option = options[idx][0] if option == 'close': return elif option == 'conditions': self.setAttributes() elif option == 'show_in_dialog': self.sequenceData.visibleInDialog( not self.sequenceData.visibleInDialog()) self.modified = True
def load(self, import_=False): if self.abortOnModified(): return if import_: path = xbmcgui.Dialog().browse(1, T(32521, 'Select File'), 'files', '*.cvseq', False, False) if not path: return else: selection = cvutil.selectSequence() if not selection: return path = selection['path'] self._load(path) sep = cinemavision.util.getSep(path) self.path, name = path.rsplit(sep, 1) self.path += sep self.setName(name.rsplit('.', 1)[0]) self.saveDefault()
def setRatingBumperStyle(): import xbmcgui styles = cinemavision.sequence.Feature.DBChoices('ratingStyle') if not styles: xbmcgui.Dialog().ok( T(32508, 'No Content'), '', T(32509, 'No content found for current rating system.')) return idx = xbmcgui.Dialog().select(T(32510, 'Select Style'), [x[1] for x in styles]) if idx < 0: return kodiutil.setSetting('feature.ratingStyle', styles[idx][0])
def loadContent(from_settings=False, bg=False): import xbmcgui if from_settings and not kodiutil.getPathSetting('content.path'): xbmcgui.Dialog().ok(T(32503, 'No Content Path'), ' ', T(32504, 'Content path not set or not applied')) return contentPath = getContentPath(from_load=True) kodiutil.DEBUG_LOG('Loading content...') with kodiutil.Progress(T(32505, 'Loading Content'), bg=bg) as p: cinemavision.content.UserContent(contentPath, callback=p.msg, trailer_sources=kodiutil.getSetting( 'trailer.scrapers', '').split(',')) createSettingsRSDirs()
def _load(self, path): f = xbmcvfs.File(path, 'r') xmlString = f.read().decode('utf-8') f.close() sItems = cinemavision.sequence.getItemsFromString(xmlString) if sItems is None: sItems = [] xbmcgui.Dialog().ok(T(32601, 'ERROR'), T(32602, 'Error parsing sequence')) self.sequenceControl.reset() self.fillSequence() self.addItems(sItems) if self.sequenceControl.positionIsValid(1): self.sequenceControl.selectItem(1) self.setFocusId(self.ITEM_OPTIONS_LIST_ID) else: self.setFocusId(self.ADD_ITEM_LIST_ID) self.modified = False
def _save(self, full_path, temp=False): items = [li.dataSource for li in self.sequenceControl if li.dataSource] if not cinemavision.sequence.sequenceHasFeature(items): yes = xbmcgui.Dialog().yesno( T(32603, 'No Feature'), T( 32604, 'Sequence does not have a feature module, which is required to play items selected in Kodi.' ), T(32605, 'Continue?')) if not yes: return self.sequenceData.setItems(items) kodiutil.DEBUG_LOG('Saving to: {0}'.format(full_path)) try: success = self.sequenceData.save(full_path) except cinemavision.exceptions.SequenceWriteReadEmptyException: xbmcgui.Dialog().ok( T(32573, 'Failed'), 'Failed to verify sequence file after write!', 'Kodi may be unable to save to this location.') return except cinemavision.exceptions.SequenceWriteReadBadException: xbmcgui.Dialog().ok( T(32573, 'Failed'), 'Bad sequence file verification after write!', 'The sequence file seems to have been corrupted when saving.') return except cinemavision.exceptions.SequenceWriteReadUnknownException: xbmcgui.Dialog().ok( T(32573, 'Failed'), 'Unknown error when verifying sequence file after write!', 'The sequence file may not have been saved.') return if not success: xbmcgui.Dialog().ok( T(32573, 'Failed'), T(32606, 'Failed to write sequence file!'), T(32607, 'Kodi may be unable to save to this location.')) return if not temp: self.modified = False self.saveDefault()
def _load(self, path): try: sData = cinemavision.sequence.SequenceData.load(path) except cinemavision.exceptions.EmptySequenceFileException: xbmcgui.Dialog().ok( T(32573, 'Failed'), 'Failed to read sequence file!', 'Kodi may be unable to read from this location.') return except cinemavision.exceptions.BadSequenceFileException: xbmcgui.Dialog().ok(T(32573, 'Failed'), 'Failed to read sequence file!', 'The sequence file may have been corrupted.') return except: kodiutil.ERROR() xbmcgui.Dialog().ok( T(32573, 'Failed'), 'Failed to read sequence file!', 'There was an unknown error. See kodi.log for details.') return if not sData: xbmcgui.Dialog().ok(T(32601, 'ERROR'), T(32602, 'Error parsing sequence')) return self.sequenceControl.reset() self.fillSequence() self.sequenceData = sData kodiutil.setGlobalProperty('ACTIVE', self.sequenceData.active and '1' or '0') self.addItems(sData) if self.sequenceControl.positionIsValid(1): self.selectSequenceItem(1) self.setFocusId(self.ITEM_OPTIONS_LIST_ID) else: self.selectSequenceItem(0) self.setFocusId(self.ADD_ITEM_LIST_ID) self.modified = False
def updateItemSettings(self, item): sItem = item.dataSource ct = 0 item.setProperty('setting{0}'.format(ct), sItem.enabled and T(32320, 'Yes') or T(32321, 'No')) item.setProperty('setting{0}_name'.format(ct), T(32538, 'Enabled')) ct += 1 for e in sItem._elements: if not sItem.elementVisible(e): continue if e['limits'] == cinemavision.sequence.LIMIT_ACTION: continue disp = sItem.getSettingDisplay(e['attr']) item.setProperty('setting{0}'.format(ct), disp) item.setProperty('setting{0}_name'.format(ct), e['name']) ct += 1 for i in range(ct, 8): item.setProperty('setting{0}'.format(i), '') item.setProperty('setting{0}_name'.format(i), '')
def testEventActions(action): import cvutil path = None if action == 'PAUSE': if kodiutil.getSetting('action.onPause', False): path = kodiutil.getSetting('action.onPause.file', '') elif action == 'RESUME': if kodiutil.getSetting('action.onResume', 0) == 2: path = kodiutil.getSetting('action.onResume.file', '') elif action == 'ABORT': if kodiutil.getSetting('action.onAbort', False): path = kodiutil.getSetting('action.onAbort.file', '') if not path: import xbmcgui xbmcgui.Dialog().ok(T(32090, 'Not Set'), T(32330, 'This action is not set or not yet applied.')) return cvutil.evalActionFile(path)
def selectSequence(active=True, for_dialog=False): import xbmcgui contentPath = getSequencesContentPath() if not contentPath: return None sequencesPath = cinemavision.util.pathJoin(contentPath, 'Sequences') default2D = 2 default3D = 3 contentPath = getSequencesContentPath() if not contentPath: xbmcgui.Dialog().ok(T(32500, 'Not Found'), ' ', T(32501, 'No sequences found.')) return None sequences = getActiveSequences(active=active, for_dialog=for_dialog) dupNames = {} for s in sequences: if s.name in dupNames: dupNames[s.name] = True else: dupNames[s.name] = False options = [('{0}.cvseq'.format(s.pathName), '{0} ({1})'.format( s.name, s.pathName) if dupNames[s.name] else s.name) for s in sequences] options.append((default2D, u'[ {0} ]'.format(T(32599, 'Default 2D')))) options.append((default3D, u'[ {0} ]'.format(T(32600, 'Default 3D')))) if not options: xbmcgui.Dialog().ok(T(32500, 'Not Found'), ' ', T(32501, 'No sequences found.')) return None idx = xbmcgui.Dialog().select(T(32502, 'Choose Sequence'), [o[1] for o in options]) if idx < 0: return None result = options[idx][0] if result == default2D: path = defaultSavePath() elif result == default3D: path = defaultSavePath(for_3D=True) else: path = cinemavision.util.pathJoin(sequencesPath, result) return {'path': path, 'name': options[idx][1]}
def renameItem(self): item = self.sequenceControl.getSelectedItem() if not item: return sItem = item.dataSource name = xbmcgui.Dialog().input(T(32539, 'Enter a name for this item'), sItem.name) if name == sItem.name: return sItem.name = name or '' self.modified = True
def selectSequence(): import xbmcgui default2D = 2 default3D = 3 contentPath = kodiutil.getSetting('content.path') if not contentPath: xbmcgui.Dialog().ok(T(32500, 'Not Found'), ' ', T(32501, 'No sequences found.')) return None sequencesPath = cinemavision.util.pathJoin(contentPath, 'Sequences') options = cinemavision.util.vfs.listdir(sequencesPath) options = [(n, n[:-6]) for n in options if n.endswith('.cvseq')] options.append((default2D, u'[ {0} ]'.format(T(32599, 'Default 2D')))) options.append((default3D, u'[ {0} ]'.format(T(32600, 'Default 3D')))) if not options: xbmcgui.Dialog().ok(T(32500, 'Not Found'), ' ', T(32501, 'No sequences found.')) return None idx = xbmcgui.Dialog().select(T(32502, 'Choose Sequence'), [o[1] for o in options]) if idx < 0: return None result = options[idx][0] if result == default2D: path = defaultSavePath() elif result == default3D: path = defaultSavePath(for_3D=True) else: path = cinemavision.util.pathJoin(sequencesPath, options[idx][0]) return {'path': path, 'name': options[idx][1]}
def fillOptions(self): for i in cinemavision.sequence.ITEM_TYPES: item = kodigui.ManagedListItem( '{0}: {1}'.format(T(32530, 'Add'), i[1]), thumbnailImage='small/script.cinemavision-{0}.png'.format( i[2]), data_source=i[0]) item.setProperty( 'thumb.focus', 'small/script.cinemavision-{0}_selected.png'.format(i[2])) self.addItemControl.addItem(item) item = kodigui.ManagedListItem( T(32531, 'Edit'), T(32531, 'Edit'), thumbnailImage='small/script.cinemavision-edit.png', data_source='edit') item.setProperty('alt.thumb', 'small/script.cinemavision-edit.png') item.setProperty('thumb.focus', 'small/script.cinemavision-A_selected.png') self.itemOptionsControl.addItem(item) item = kodigui.ManagedListItem( T(32532, 'Rename'), T(32532, 'Rename'), thumbnailImage='small/script.cinemavision-rename.png', data_source='rename') item.setProperty('alt.thumb', 'small/script.cinemavision-rename.png') item.setProperty('thumb.focus', 'small/script.cinemavision-A_selected.png') self.itemOptionsControl.addItem(item) item = kodigui.ManagedListItem( T(32533, 'Copy'), T(32533, 'Copy'), thumbnailImage='small/script.cinemavision-copy.png', data_source='copy') item.setProperty('alt.thumb', 'small/script.cinemavision-copy.png') item.setProperty('thumb.focus', 'small/script.cinemavision-A_selected.png') self.itemOptionsControl.addItem(item) item = kodigui.ManagedListItem( T(32534, 'Move'), T(32534, 'Move'), thumbnailImage='small/script.cinemavision-move.png', data_source='move') item.setProperty('alt.thumb', 'small/script.cinemavision-move.png') item.setProperty('thumb.focus', 'small/script.cinemavision-A_selected.png') self.itemOptionsControl.addItem(item) item = kodigui.ManagedListItem( T(32535, 'Disable'), T(32535, 'Disable'), thumbnailImage='small/script.cinemavision-disable.png', data_source='enable') item.setProperty('alt.thumb', 'small/script.cinemavision-enable.png') item.setProperty('thumb.focus', 'small/script.cinemavision-A_selected.png') self.itemOptionsControl.addItem(item) item = kodigui.ManagedListItem( T(32536, 'Remove'), T(32536, 'Remove'), thumbnailImage='small/script.cinemavision-minus.png', data_source='remove') item.setProperty('alt.thumb', 'small/script.cinemavision-minus.png') item.setProperty('thumb.focus', 'small/script.cinemavision-A_selected.png') self.itemOptionsControl.addItem(item)
def _pasteLog(logName='kodi.log'): import os import re import xbmc import xbmcgui from pastebin_python import PastebinPython logPath = os.path.join(xbmc.translatePath('special://logpath').decode('utf-8'), logName) if not os.path.exists(logPath): xbmcgui.Dialog().ok(T(32570, 'No Log'), ' ', T(32571, 'That log file does not exist!')) return False def debug_log(msg): kodiutil.DEBUG_LOG('PASTEBIN: {0}'.format(msg)) replaces = ( ('//.+?:.+?@', '//USER:PASSWORD@'), ('<user>.+?</user>', '<user>USER</user>'), ('<pass>.+?</pass>', '<pass>PASSWORD</pass>'), ) apiUserKeyFile = os.path.join(kodiutil.PROFILE_PATH, 'settings.pb.key') apiUserKey = '' if os.path.exists(apiUserKeyFile): with open(apiUserKeyFile, 'r') as f: apiUserKey = f.read() or '' pb = PastebinPython(api_dev_key=kodiutil.getPeanutButter(), api_user_key=apiUserKey) apiUser = kodiutil.getSetting('pastebin.user') if apiUser and not apiUserKey: debug_log('Username set, asking user for password') password = xbmcgui.Dialog().input( T(32572, 'Enter Pastebin password (only needed 1st time - NOT stored)'), '', xbmcgui.INPUT_ALPHANUM, xbmcgui.ALPHANUM_HIDE_INPUT ) if password: debug_log('Getting API user key') apiUserKey = pb.createAPIUserKey(apiUser, password) if apiUserKey.lower().startswith('bad'): xbmcgui.Dialog().ok(T(32573, 'Failed'), u'{0}: {1}'.format(T(32574, 'Failed to create paste as user'), apiUser), '', apiUserKey) debug_log('Failed get user API key ({0}): {1}'.format(apiUser, apiUserKey)) else: with open(apiUserKeyFile, 'w') as f: f.write(apiUserKey) else: debug_log('User aborted') xbmcgui.Dialog().ok(T(32575, 'Aborted'), ' ', T(32576, 'Paste aborted!')) return False elif apiUserKey: debug_log('Creating paste with stored API key') with kodiutil.Progress('Pastebin', T(32577, 'Creating paste...')): with open(logPath, 'r') as f: content = f.read().decode('utf-8') for pattern, repl in replaces: content = re.sub(pattern, repl, content) urlOrError = pb.createPaste(content, 'Kodi CV LOG: {0}'.format(logName), api_paste_private=1, api_paste_expire_date='1W') showQR = False if urlOrError.startswith('http'): showQR = xbmcgui.Dialog().yesno( T(32515, 'Done'), T(32578, 'Paste created at:'), '', urlOrError, T(32579, 'OK'), T(32580, 'Show QR Code') ) debug_log('Paste created: {0}'.format(urlOrError)) else: xbmcgui.Dialog().ok(T(32573, 'Failed'), T(32581, 'Failed to create paste:'), '', urlOrError) debug_log('Failed to create paste: {0}'.format(urlOrError)) if showQR: showQRCode(urlOrError) return True
def evalActionFile(paths, test=True): import xbmcgui if not paths: xbmcgui.Dialog().ok(T(32511, 'None found'), T(32512, 'No action file(s) set')) return if not isinstance(paths, list): paths = [paths] messages = [] abortPath = kodiutil.getSetting('action.onAbort.file') if not kodiutil.getSetting('action.onAbort', False): abortPath = None # if not abortPath: # yes = xbmcgui.Dialog().yesno('No Abort', 'Abort action not set.', '') # Test = False for path in paths: processor = cinemavision.actions.ActionFileProcessor(path, test=True) messages.append(u'[B]VALIDATE ({0}):[/B]'.format( os.path.basename(path))) messages.append('') parseMessages = [] hasParseMessages = False hasErrors = False if not processor.fileExists: messages.append(u'{0} - [COLOR FFFF0000]{1}[/COLOR]'.format( os.path.basename(path), T(32513, 'MISSING!'))) messages.append('') continue if processor.parserLog: hasParseMessages = True for type_, msg in processor.parserLog: hasErrors = hasErrors or type_ == 'ERROR' parseMessages.append(u'[COLOR {0}]{1}[/COLOR]'.format( type_ == 'ERROR' and 'FFFF0000' or 'FFFFFF00', msg)) else: parseMessages.append('[COLOR FF00FF00]OK[/COLOR]') messages += parseMessages messages.append('') if test: if hasErrors: messages += [ u'[B]TEST ({0}):[/B]'.format(os.path.basename(path)), '' ] messages.append(u'[COLOR FFFF0000]{0}[/COLOR]'.format( 'SKIPPED DUE TO ERRORS')) else: with kodiutil.Progress('Testing', 'Executing actions...'): messages += [ u'[B]TEST ({0}):[/B]'.format(os.path.basename(path)), '' ] for line in processor.test(): if line.startswith('Action ('): lsplit = line.split(': ', 1) line = u'[COLOR FFAAAAFF]{0}:[/COLOR] {1}'.format( lsplit[0], lsplit[1]) elif line.startswith('ERROR:'): line = u'[COLOR FFFF0000]{0}[/COLOR]'.format(line) messages.append(line) messages.append('') if not test and not hasParseMessages: xbmcgui.Dialog().ok(T(32515, 'Done'), T(32516, 'Action file(s) parsed OK')) else: showText(T(32514, 'Parser Messages'), '[CR]'.join(messages)) if test and abortPath: runAbort = kodiutil.getSetting('action.test.runAbort', 0) if runAbort and (runAbort != 2 or xbmcgui.Dialog().yesno( T(32597, 'Cleanup'), T(32598, 'Run abort action?'))): processor = cinemavision.actions.ActionFileProcessor(abortPath) processor.run()
def _editItemSetting(self): item = self.settingsList.getSelectedItem() if not item or item.getProperty('type') == 'integer': return sItem = self.item.dataSource attr = item.dataSource options = sItem.getSettingOptions(attr) if options in (cinemavision.sequence.LIMIT_FILE, cinemavision.sequence.LIMIT_FILE_DEFAULT): select = True if sItem.getSetting(attr): yes = xbmcgui.Dialog().yesno( T(32517, 'Change Path'), '', T(32518, 'Would choose a new path, or clear the current path?'), '', T(32519, 'Choose'), T(32520, 'Clear')) if yes: if options == cinemavision.sequence.LIMIT_FILE: value = '' else: value = None select = False if select: value = xbmcgui.Dialog().browse(1, T(32521, 'Select File'), 'files', None, False, False, sItem.getSetting(attr)) if not value: return value = value.decode('utf-8') elif options == cinemavision.sequence.LIMIT_DB_CHOICE: options = sItem.DBChoices(attr) if not options: xbmcgui.Dialog().ok(T(32508, 'No Content'), '', T(32522, 'No matching content found.')) return False options.insert(0, (None, T(32322, 'Default'))) idx = xbmcgui.Dialog().select(T(32523, 'Options'), [x[1] for x in options]) if idx < 0: return False value = options[idx][0] elif options == cinemavision.sequence.LIMIT_DIR: select = True if sItem.getSetting(attr): yes = xbmcgui.Dialog().yesno( T(32517, 'Change Path'), '', T(32518, 'Choose a new path or clear the current path?'), '', T(32520, 'Clear'), T(32519, 'Choose')) if yes: value = None select = False if select: value = xbmcgui.Dialog().browse(0, T(32524, 'Select Directory'), 'files') if not value: return value = value.decode('utf-8') elif options == cinemavision.sequence.LIMIT_MULTI_SELECT: options = sItem.Select(attr) if not options: xbmcgui.Dialog().ok(T(32525, 'No Options'), '', T(32526, 'No options found.')) return False result = cvutil.multiSelect(options) if result is False: return False value = result elif options == cinemavision.sequence.LIMIT_BOOL_DEFAULT: curr = sItem.getSetting(attr) if curr is None: value = True elif curr is True: value = False else: value = None elif options == cinemavision.sequence.LIMIT_BOOL: value = not sItem.getSetting(attr) elif options == cinemavision.sequence.LIMIT_ACTION: if self.item.dataSource._type == 'action': cvutil.evalActionFile(self.item.dataSource.file) return False elif isinstance(options, list): idx = xbmcgui.Dialog().select(T(32523, 'Options'), [x[1] for x in options]) if idx < 0: return False value = options[idx][0] else: return False sItem.setSetting(attr, value) if sItem._type == 'video' and attr == 'vtype': if not sItem.getSetting(attr): name = '[COLOR FFFF0000]{0}[/COLOR]'.format( item.getProperty('name')) else: name = item.getProperty('name') item.setLabel(name) item.setLabel2(unicode(sItem.getSettingDisplay(attr))) self.modified = True self.main.updateItemSettings(self.item) if sItem._type == 'command' and attr == 'command': self.main.updateSpecials()
def showNoFeaturesDialog(): import xbmcgui xbmcgui.Dialog().ok(T(32561, 'No Features'), T(32562, 'No movies are in the Queue.'), '', T(32563, 'Please queue some features and try again.'))