def _getAllFilesInDirectory(self, baseDir, includeSubDirs=True): videoFiles = [] dirs, files = list_dir(baseDir) # Get the list of files that are to be excluded collectionCtrl = CollectSets() disabledVideos = collectionCtrl.getDisabledVideos() del collectionCtrl # Get all the files in the current directory for vidFile in files: # Check if this file is excluded if vidFile in disabledVideos: log("Ignoring disabled screensaver video %s" % vidFile) continue fullPath = os_path_join(baseDir, vidFile) videoFiles.append(fullPath) # Now check each directory if includeSubDirs and Settings.isFolderNested(): for aDir in dirs: fullPath = os_path_join(baseDir, aDir) dirContents = self._getAllFilesInDirectory(fullPath) videoFiles = videoFiles + dirContents return videoFiles
def onInit(self): # Need to clear the list of the default items self.clearList() # Start by adding an option to Play All if len(self.files) > 0: anItem = xbmcgui.ListItem(ADDON.getLocalizedString(32101), path=SourceDetails.getFilenameAndPath()) # Get the first items fanart for the play all option anItem.setProperty("Fanart_Image", self.files[0].getFanArt()) if SourceDetails.getTvShowTitle() != "": anItem.setInfo('video', {'TvShowTitle': SourceDetails.getTvShowTitle()}) if SourceDetails.getTitle() != "": anItem.setInfo('video', {'Title': SourceDetails.getTitle()}) self.addItem(anItem) # Check if we want to have YouTube Extra Support if Settings.isYouTubeSearchSupportEnabled(): # Create the message to the YouTube Plugin li = xbmcgui.ListItem(ADDON.getLocalizedString(32116)) # Need to set the title to get it in the header if SourceDetails.getTvShowTitle() != "": li.setInfo('video', {'TvShowTitle': SourceDetails.getTvShowTitle()}) if SourceDetails.getTitle() != "": li.setInfo('video', {'Title': SourceDetails.getTitle()}) li.setProperty("Fanart_Image", SourceDetails.getFanArt()) li.setProperty("search", "/search/?q=%s+Extras" % urllib.quote_plus(SourceDetails.getTitle().encode('utf8'))) self.addItem(li) # Check if we want to have Vimeo Extra Support if Settings.isVimeoSearchSupportEnabled(): # Create the message to the Vimeo Plugin li = xbmcgui.ListItem(ADDON.getLocalizedString(32122)) # Need to set the title to get it in the header if SourceDetails.getTvShowTitle() != "": li.setInfo('video', {'TvShowTitle': SourceDetails.getTvShowTitle()}) if SourceDetails.getTitle() != "": li.setInfo('video', {'Title': SourceDetails.getTitle()}) li.setProperty("Fanart_Image", SourceDetails.getFanArt()) li.setProperty("search", "/search/?q=%s+Extras" % urllib.quote_plus(SourceDetails.getTitle().encode('utf8'))) self.addItem(li) for anExtra in self.files: log("VideoExtrasWindow: filename: %s" % anExtra.getFilename()) # Create the list item anItem = anExtra.createListItem(path=SourceDetails.getFilenameAndPath(), parentTitle=SourceDetails.getTitle(), tvShowTitle=SourceDetails.getTvShowTitle()) self.addItem(anItem) # Before we return, set back the selected on screen item to the one just watched # This is in the case of a reload if self.lastRecordedListPosition > 0: self.setCurrentListPosition(self.lastRecordedListPosition) xbmcgui.WindowXML.onInit(self)
def fetchTheme(self, title, path, originaltitle=None, isTvShow=None, year=None, imdb=None): # If there is already a theme then start playing it self._startPlayingExistingTheme(path) if Settings.isThemeDirEnabled() and self._doesThemeExist(path, True): # Prompt user if we should move themes in the parent # directory into the theme directory moveExistingThemes = xbmcgui.Dialog().yesno(ADDON.getLocalizedString(32105), ADDON.getLocalizedString(32206), ADDON.getLocalizedString(32207)) # Check if we need to move a theme file if moveExistingThemes: log("fetchAllMissingThemes: Moving theme for %s" % title) self._moveToThemeFolder(path) # Stop playing any theme that started self._stopPlayingTheme() # Now reload the screen to reflect the change xbmc.executebuiltin("Container.Refresh") return if originaltitle is not None: originaltitle = normalize_string(originaltitle) # Perform the fetch videoList = [] normtitle = normalize_string(title) videoItem = {'title': normtitle, 'path': path, 'originalTitle': originaltitle, 'isTvShow': isTvShow, 'year': year, 'imdb': imdb} videoList.append(videoItem) TvTunesFetcher(videoList) # Stop playing any theme that started self._stopPlayingTheme() # Now reload the screen to reflect the change xbmc.executebuiltin("Container.Refresh")
def _addSecurityFlags(self, type, items): # Make sure we have some items to append the details to if len(items) < 1: return items # Make the call to the DB to get all the specific security settings pinDB = PinSentryDB() securityDetails = {} if type == MenuNavigator.TVSHOWS: securityDetails = pinDB.getAllTvShowsSecurity() elif type == MenuNavigator.MOVIES: securityDetails = pinDB.getAllMoviesSecurity() elif type == MenuNavigator.MOVIESETS: securityDetails = pinDB.getAllMovieSetsSecurity() elif type == MenuNavigator.MUSICVIDEOS: securityDetails = pinDB.getAllMusicVideosSecurity() elif type == MenuNavigator.PLUGINS: securityDetails = pinDB.getAllPluginsSecurity() elif type == MenuNavigator.FILESOURCE: securityDetails = pinDB.getAllFileSourcesSecurity() for item in items: # Default security to 0 (Not Set) securityLevel = 0 if item['title'] in securityDetails: title = item['title'] securityLevel = securityDetails[title] log("PinSentryPlugin: %s has security level %d" % (title, securityLevel)) item['securityLevel'] = securityLevel del pinDB return items
def _setPluginList(self): # Make the call to find out all the addons that are installed json_query = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Addons.GetAddons", "params": { "type": "xbmc.python.pluginsource", "enabled": true, "properties": ["name", "thumbnail", "fanart"] }, "id": 1}') json_query = unicode(json_query, 'utf-8', errors='ignore') json_response = simplejson.loads(json_query) log(json_response) plugins = [] if ("result" in json_response) and ('addons' in json_response['result']): # Check each of the plugins that are installed on the system for addonItem in json_response['result']['addons']: addonId = addonItem['addonid'] # Need to skip ourselves if addonId in ['script.pinsentry']: log("setPluginList: Skipping PinSentry Plugin") continue pluginDetails = {} pluginDetails['title'] = addonItem['name'] pluginDetails['dbid'] = addonId if addonItem['thumbnail'] in [None, ""]: pluginDetails['thumbnail'] = 'DefaultAddon.png' else: pluginDetails['thumbnail'] = addonItem['thumbnail'] if addonItem['fanart'] in [None, ""]: pluginDetails['fanart'] = FANART else: pluginDetails['fanart'] = addonItem['fanart'] plugins.append(pluginDetails) return plugins
def removeCollection(self, name, link): if name in [None, ""]: return collectionCtrl = CollectSets() collectionDetails = collectionCtrl.loadCollection(link) filesToDelete = [] # If the file was not processed just don't display anything if collectionDetails not in [None, ""]: screensaverFolder = Settings.getScreensaverFolder() for videoItem in collectionDetails['videos']: # If theme exists we need to check if we want to delete it if screensaverFolder not in [None, ""]: videoLocation = os_path_join(screensaverFolder, videoItem['filename']) log("VideoScreensaverPlugin: Checking if %s already downloaded to %s" % (videoItem['filename'], videoLocation)) if xbmcvfs.exists(videoLocation): filesToDelete.append(videoLocation) # If there are possible files to delete, then prompt the user to see if we should if len(filesToDelete) > 0: needDelete = xbmcgui.Dialog().yesno(ADDON.getLocalizedString(32005), ADDON.getLocalizedString(32086)) if needDelete: for vidFile in filesToDelete: xbmcvfs.delete(vidFile) # Now remove the actual collection collectionCtrl.removeCustomCollection(name) del collectionCtrl # Now reload the screen to reflect the change xbmc.executebuiltin("Container.Refresh")
def _enableKeymap(self): try: xbmcvfs.copy(self.KEYMAPSOURCEFILE, self.KEYMAPDESTFILE) xbmc.executebuiltin('Action(reloadkeymaps)') log("SonosVolumeRedirect: Installed custom keymap") except: log("SonosVolumeRedirect: Failed to copy & load custom keymap: %s" % traceback.format_exc(), xbmc.LOGERROR)
def check(self): # Check to see if we should be changing the video for the schedule scheduleEntry = self.scheduler.getScheduleEntry() # There is an item scheduled, so check to see if the item has actually changed if scheduleEntry == self.currentScheduleItem: return None log("Old Schedule %d different from new: %d" % (self.currentScheduleItem, scheduleEntry)) # Check to see if there needs to be a change in what is playing # This will also update the schedule item so we know what has been selected newPlaylist = self._getPlaylist() # If we reach here, there is a change of some sort if newPlaylist is not None: # Update the playlist with any settings such as random start time self._updatePlaylistForSettings(newPlaylist) # Start playing the new file, just override the existing one that is playing self.player.play(newPlaylist) # Also update the overlay self._setOverlayImage() # Now set the repeat option self._setRepeat() # Update any settings that need to be done after the video is playing self._updatePostPlayingForSettings(newPlaylist)
def runArtistSlideshow(self): log("SonosArtistSlideshow: runArtistSlideshow") # startup artistslideshow xbmcgui.Window(self.windowId).setProperty("ArtistSlideshow.ExternalCall", "True") # assumes addon is using suggested infolabel name of CURRENTARTIST and CURRENTTITLE artistslideshow = "RunScript(script.artistslideshow,windowid=%s&artistfield=%s&titlefield=%s&albumfield=%s&mbidfield=%s)" % (xbmcgui.getCurrentWindowId(), "CURRENTARTIST", "CURRENTTITLE", "CURRENTALBUM", "CURRENTMBID") xbmc.executebuiltin(artistslideshow)
def isClose(self): # Check if the base class has detected a need to close needToClose = SonosControllerWindow.isClose(self) # There are cases where the user could have changed the screen being # displayed, for example, if they have the following in their keymap: # <keymap> # <global> # <keyboard> # <f5>ActivateWindow(0)</f5> # </keyboard> # </global> # </keymap> # This could cause a change in window, such as loading the home screen # however we do not get a call to close - as the Sonos window will be # still running in the back-ground - just not showing on the screen # If the user then exits, the keymap file will be left, so we will # automatically close the window in this case # Note: This is not an issue with the normal controller - as it is a # dialog window, so will always remain in view if (not needToClose) and (self.windowId != -1): # Get the current window showingWindowId = xbmcgui.getCurrentWindowId() # Check if the window is no longer showing if showingWindowId != self.windowId: log("SonosArtistSlideshow: Detected change in window, sonos window = %d, new window = %d" % (self.windowId, showingWindowId)) return True return needToClose
def _moveToThemeFolder(self, directory): log("moveToThemeFolder: path = %s" % directory) # Handle the case where we have a disk image if (os_path_split(directory)[1] == 'VIDEO_TS') or (os_path_split(directory)[1] == 'BDMV'): directory = os_path_split(directory)[0] dirs, files = list_dir(directory) for aFile in files: m = re.search(Settings.getThemeFileRegEx(directory), aFile, re.IGNORECASE) if m: srcpath = os_path_join(directory, aFile) log("fetchAllMissingThemes: Found match: %s" % srcpath) targetpath = os_path_join(directory, Settings.getThemeDirectory()) # Make sure the theme directory exists if not dir_exists(targetpath): try: xbmcvfs.mkdir(targetpath) except: log("fetchAllMissingThemes: Failed to create directory: %s" % targetpath, True, xbmc.LOGERROR) break else: log("moveToThemeFolder: directory already exists %s" % targetpath) # Add the filename to the path targetpath = os_path_join(targetpath, aFile) if not xbmcvfs.rename(srcpath, targetpath): log("moveToThemeFolder: Failed to move file from %s to %s" % (srcpath, targetpath))
def _getSecondsInTimeString(self, fullTimeString): # Some services do not support duration if fullTimeString == 'NOT_IMPLEMENTED': return -1 # Start by splitting the time into sections hours = 0 minutes = 0 seconds = 0 try: hours = int(fullTimeString.split(':', 1)[0]) minutes = int(fullTimeString.split(':')[1]) seconds = int(fullTimeString.split(':')[2]) except: # time sections are not numbers log("SonosControllerWindow: Exception Details: %s" % traceback.format_exc()) hours = 0 minutes = 0 seconds = 0 totalInSeconds = (((hours * 60) + minutes) * 60) + seconds log("SonosControllerWindow: Time %s, splits into hours=%d, minutes=%d, seconds=%d, total=%d" % (fullTimeString, hours, minutes, seconds, totalInSeconds)) # Return the total time in seconds return totalInSeconds
def _setRepositoryList(self): # Make the call to find out all the addons that are installed json_query = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Addons.GetAddons", "params": { "type": "xbmc.addon.repository", "properties": ["name", "thumbnail", "fanart"] }, "id": 1}') json_query = unicode(json_query, 'utf-8', errors='ignore') json_response = simplejson.loads(json_query) log(json_response) repos = [] if ("result" in json_response) and ('addons' in json_response['result']): # Check each of the repos that are installed on the system for addonItem in json_response['result']['addons']: addonId = addonItem['addonid'] pluginDetails = {} pluginDetails['title'] = addonItem['name'] pluginDetails['dbid'] = addonId if addonItem['thumbnail'] in [None, ""]: pluginDetails['thumbnail'] = 'DefaultAddon.png' else: pluginDetails['thumbnail'] = addonItem['thumbnail'] if addonItem['fanart'] in [None, ""]: pluginDetails['fanart'] = FANART else: pluginDetails['fanart'] = addonItem['fanart'] repos.append(pluginDetails) return repos
def editPlot(self, target, path, filename): # Create the extras class that will be used to process the extras videoExtras = VideoExtrasBase(path, target) # Perform the search command # We are only updating the NFO for an entry already shown, no need for fanart files = videoExtras.findExtras() del videoExtras for anExtra in files: if anExtra.isFilenameMatch(filename): log("MenuNavigator: Found = %s" % filename) # Prompt the user for the new name keyboard = xbmc.Keyboard() keyboard.setDefault(anExtra.getPlot()) keyboard.doModal() if keyboard.isConfirmed(): try: newplot = keyboard.getText().decode("utf-8") except: newplot = keyboard.getText() # Only set the title if it has changed if (newplot != anExtra.getPlot()) and ((len(newplot) > 0) or (anExtra.getPlot() is not None)): isTv = (target == MenuNavigator.TVSHOWS) result = anExtra.setPlot(newplot, isTV=isTv) if not result: xbmcgui.Dialog().ok(ADDON.getLocalizedString(32102), ADDON.getLocalizedString(32115)) else: # Update the display xbmc.executebuiltin("Container.Refresh")
def hasVideoExtras(self, target, dbid, file, title=None): # If the service is on, then we can just check to see if the overlay image exists if Settings.isServiceEnabled(): # Get the path where the file exists rootPath = os_path_join(PROFILE_DIR, target) if not dir_exists(rootPath): # Directory does not exist yet, so can't have extras return False # Generate the name of the file that the overlay will be copied to targetFile = os_path_join(rootPath, ("%d.png" % dbid)) if xbmcvfs.exists(targetFile): return True # Otherwise, need to do the lookup the old fashioned way of looking for the # extras files on the file system (This is much slower) else: videoExtras = VideoExtrasBase(file, target, title) # We are only checking for existence of extras, no need for fanart firstExtraFile = videoExtras.findExtras(True) del videoExtras if firstExtraFile: log("MenuNavigator: Extras found for (%d) %s" % (dbid, file)) return True return False
def _updatePlaylistForSettings(self, playlist): if playlist.size() < 1: return playlist filename = playlist[0].getfilename() duration = self._getVideoDuration(filename) log("Duration is %d for file %s" % (duration, filename)) startTime = 0 # Check if we have a random start time if Settings.isRandomStart(): startTime = random.randint(0, int(duration * 0.75)) clockStart = Settings.getTimeForClock(filename, duration) if clockStart > 0: startTime = clockStart # Set the random start if (startTime > 0) and (duration > 10): listitem = xbmcgui.ListItem() # Record if the theme should start playing part-way through listitem.setProperty('StartOffset', str(startTime)) log("Setting start of %d for %s" % (startTime, filename)) # Remove the old item from the playlist playlist.remove(filename) # Add the new item at the start of the list playlist.add(filename, listitem, 0) return playlist
def clear(self, fullpath): log("AudioBooksPlugin: Clearing history for %s" % fullpath) # Remove the item from the database, it will then be rescanned audiobookDB = AudioBooksDB() audiobookDB.deleteAudioBook(fullpath) del audiobookDB xbmc.executebuiltin("Container.Refresh")
def enable(self): try: xbmcvfs.copy(self.KEYMAPSOURCEFILE, self.KEYMAPDESTFILE) self.keymapCopied = True xbmc.executebuiltin('Action(reloadkeymaps)') log("KeyMaps: Installed custom keymap") except: log("KeyMaps: Failed to copy & load custom keymap: %s" % traceback.format_exc(), xbmc.LOGERROR)
def _convertTimeToMinutes(self, strTime): if strTime in [None, ""]: log("Schedule: Time not set") return None strTimeSplit = strTime.split(':') if len(strTimeSplit) < 2: log("Schedule: Incorrect time format: %s" % strTime) return None return (int(strTimeSplit[0]) * 60) + int(strTimeSplit[1])
def onInit(self): xbmcgui.WindowXML.onInit(self) # Set the value of the dimming for the video dimLevel = Settings.getDimValue() if dimLevel is not None: log("WeatherScreen: Setting Dim Level to: %s" % dimLevel) dimControl = self.getControl(WeatherScreen.DIM_CONTROL) dimControl.setColorDiffuse(dimLevel)
def getScheduleEntry(self): # Get the current time that we are checking the schedule for localTime = time.localtime() currentTime = (localTime.tm_hour * 60) + localTime.tm_min # Get the current day of the week # 0 = Monday 6 = Sunday today = localTime.tm_wday # Make sure that the day returned is within our expected list if today not in Settings.DAY_TYPE: log("Schedule: Unknown day today %d, setting to everyday" % today) today = Settings.EVERY_DAY # Check if we need to refresh the schedule details from the file # in case they have changed if Settings.getScheduleSetting() == Settings.SCHEDULE_FILE: # Check if the file has changed scheduleFileName = Settings.getScheduleFile() if scheduleFileName not in [None, ""]: if xbmcvfs.exists(scheduleFileName): statFile = xbmcvfs.Stat(scheduleFileName) modified = statFile.st_mtime() if modified != self.lastScheduleModified: log("Schedule: Schedule file has changed (%s)" % str(modified)) # We use the offset to work out if the data has changed if self.idOffset > 0: self.idOffset = 0 else: self.idOffset = 1000 # Clear the existing schedule items self.scheduleDetails = [] # Load the new schedule items self._loadFromFile() # Check the scheduled items to see if any cover the current time for item in self.scheduleDetails: if (item['start'] <= currentTime) and (item['end'] >= currentTime): # Make sure this is for the current day if (today == Settings.EVERY_DAY) or (item['day'] in [Settings.EVERY_DAY, today]): return item['id'] # Check for the case where the time laps over midnight if item['start'] > item['end']: if (currentTime >= item['start']) or (currentTime <= item['end']): # Check to see if we are restricting to day if (today == Settings.EVERY_DAY) or (item['day'] == Settings.EVERY_DAY): return item['id'] else: if (currentTime >= item['start']) and (item['day'] in [Settings.EVERY_DAY, today]): return item['id'] else: # The day is set for the start of the time interval # so if we go over to the next day we need to update # what the expected day is nextDay = Settings.getNextDay(item['day']) if (currentTime <= item['end']) and (item['day'] in [Settings.EVERY_DAY, nextDay]): return item['id'] return -1
def restoreVolume(self): try: if Settings.isUseAudioSuspend(): xbmc.audioResume() # Don't change the volume unless requested to elif self.screensaverVolume > -1: self._setVolume(self.original_volume) except: log("VolumeDrop: %s" % traceback.format_exc(), xbmc.LOGERROR)
def _cleanupKeymap(self): if xbmcvfs.exists(self.KEYMAPDESTFILE): try: xbmcvfs.delete(self.KEYMAPDESTFILE) log("SonosVolumeRedirect: Removed custom keymap") except: log("SonosVolumeRedirect: Failed to remove & load custom keymap: %s" % traceback.format_exc(), xbmc.LOGERROR) # Force a re-load xbmc.executebuiltin('Action(reloadkeymaps)')
def createSonosPlayingPopup(track): # Creating popup for log("SonosPlayingPopup: Currently playing artist = %s, album = %s, track = %s" % (track['artist'], track['album'], track['title'])) # Get the album art if it is set (Default to the Sonos icon) albumArt = ICON if track['album_art'] != "": albumArt = track['album_art'] return SonosPlayingPopup("script-sonos-notif-popup.xml", CWD, artist=track['artist'], album=track['album'], title=track['title'], albumArt=albumArt)
def cleanup(self): if self.keymapCopied is True: try: xbmcvfs.delete(self.KEYMAPDESTFILE) log("KeyMaps: Removed custom keymap") except: log("KeyMaps: Failed to remove & load custom keymap: %s" % traceback.format_exc(), xbmc.LOGERROR) # Force a re-load xbmc.executebuiltin('Action(reloadkeymaps)')
def _setBulkSecurity(self, type, level): items = self._getVideos(type) for item in items: # Get the title of the video title = item['title'] try: title = item['title'].encode("utf-8") except: log("PinSentryPlugin: setBulkSecurity Failed to encode title %s" % title) self.setSecurity(type, title, item['dbid'], level, forceLevel=level)
def _switchToLineIn(self): # Check if we need to ensure the Sonos system is using the line-in try: # Not all speakers support line-in - so catch exception self.sonosDevice.switch_to_line_in() # Once switch to line in, some systems require that a play command is sent self.sonosDevice.play() except: log("SonosService: Failed to switch to Line-In for speaker %s" % Settings.getIPAddress()) log("SonosService: %s" % traceback.format_exc())
def _saveNewFile(self, dialogXml, dialogXmlStr): log("SaveNewFile: New file content: %s" % dialogXmlStr) # Now save the file to disk, start by backing up the old file xbmcvfs.copy(dialogXml, "%s.suitability-%s.bak" % (dialogXml, self.bak_timestamp)) # Now save the new file dialogXmlFile = xbmcvfs.File(dialogXml, 'w') dialogXmlFile.write(dialogXmlStr) dialogXmlFile.close()
def _getVolume(self): result = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": { "properties": [ "volume" ] }, "id": 1}') json_query = json.loads(result) if ("result" in json_query) and ('volume' in json_query['result']): # Get the volume value volume = json_query['result']['volume'] log("VolumeDrop: current volume: %s%%" % str(volume)) return volume
def onAction(self, action): log("Action received: %s" % str(action.getId())) # For any action we want to close, as that means activity if action.getId() in [0, "0"]: # When the refresh rate is set to change it can generate an # action with a zero Id, we need to ignore it as it is not # actually a user action log("Ignoring action %s" % str(action.getId())) else: self.close()
def _setClassificationList(self, type="", subtype=""): classifications = () securityDetails = {} # Make the call to the DB to get all the specific security settings pinDB = PinSentryDB() if type == MenuNavigator.CLASSIFICATIONS_MOVIES: classifications = Settings.movieCassificationsNames securityDetails = pinDB.getAllMovieClassificationSecurity() elif type == MenuNavigator.CLASSIFICATIONS_TV: classifications = Settings.tvCassificationsNames securityDetails = pinDB.getAllTvClassificationSecurity() del pinDB # Check if we are showing the root classification listing if type in [None, ""]: url = self._build_url({'mode': 'folder', 'foldername': MenuNavigator.CLASSIFICATIONS, 'type': MenuNavigator.CLASSIFICATIONS_MOVIES}) li = xbmcgui.ListItem(ADDON.getLocalizedString(32207), iconImage=ICON) li.setProperty("Fanart_Image", FANART) li.addContextMenuItems([], replaceItems=True) xbmcplugin.addDirectoryItem(handle=self.addon_handle, url=url, listitem=li, isFolder=True) url = self._build_url({'mode': 'folder', 'foldername': MenuNavigator.CLASSIFICATIONS, 'type': MenuNavigator.CLASSIFICATIONS_TV}) li = xbmcgui.ListItem(ADDON.getLocalizedString(32208), iconImage=ICON) li.setProperty("Fanart_Image", FANART) li.addContextMenuItems([], replaceItems=True) xbmcplugin.addDirectoryItem(handle=self.addon_handle, url=url, listitem=li, isFolder=True) elif subtype in [None, ""]: # Get all the different language that are supported languages = [] for classification in classifications: if classification['lang'] not in languages: languages.append(classification['lang']) # Check to see if we can sort all the entries alphabetically for the given language try: languages = sorted(languages, key=ADDON.getLocalizedString) except: # If it fails to sort, then we just list them unsorted log("PinSentryPlugin: Failed to sort language list") # Now print out the item for each language for lang in languages: url = self._build_url({'mode': 'folder', 'foldername': MenuNavigator.CLASSIFICATIONS, 'type': type, 'subtype': str(lang)}) iconImage = ICON for flag in Settings.flags: if flag['lang'] == lang: iconImage = os_path_join(ICON_DIR, flag['icon']) li = xbmcgui.ListItem(ADDON.getLocalizedString(lang), iconImage=iconImage) li.setProperty("Fanart_Image", FANART) li.addContextMenuItems([], replaceItems=True) xbmcplugin.addDirectoryItem(handle=self.addon_handle, url=url, listitem=li, isFolder=True) else: for classification in classifications: # Check if we are looking for a specific country if subtype != str(classification['lang']): continue fullName = classification['name'] % ADDON.getLocalizedString(classification['lang']) idStr = str(classification['id']) securityLevel = 0 if idStr in securityDetails: securityLevel = securityDetails[idStr] log("PinSentryPlugin: Classification %s has security level %d" % (fullName, securityLevel)) # Set the icon to the certificate one if available iconImage = ICON if classification['icon'] not in [None, ""]: iconImage = os_path_join(ICON_DIR, classification['icon']) li = xbmcgui.ListItem(fullName, iconImage=iconImage) # Add a tick if security is set if securityLevel > 0: li.setInfo('video', {'PlayCount': 1}) li.setProperty("Fanart_Image", FANART) li.addContextMenuItems([], replaceItems=True) url = self._build_url({'mode': 'setsecurity', 'type': type, 'id': classification['id'], 'title': classification['match'], 'level': securityLevel}) xbmcplugin.addDirectoryItem(handle=self.addon_handle, url=url, listitem=li, isFolder=False) xbmcplugin.endOfDirectory(self.addon_handle)
\t\t\t\t\t\t<include>ButtonInfoDialogsCommonValues</include> \t\t\t\t\t\t<label>$ADDON[script.tvtunes 32105]</label> \t\t\t\t\t\t<onclick>RunScript(script.tvtunes,mode=solo)</onclick> \t\t\t\t\t\t<visible>System.HasAddon(script.tvtunes) + [Container.Content(TVShows) | Container.Content(movies) | Container.Content(musicvideos)] + IsEmpty(Window(movieinformation).Property("TvTunes_HideVideoInfoButton"))</visible>''' insertTxt = previousButton + (DIALOG_VIDEO_INFO_BUTTON % idval) dialogXmlStr = dialogXmlStr.replace(previousButton, insertTxt) self._saveNewFile(dialogXml, dialogXmlStr) ######################### # Main ######################### if __name__ == '__main__': log("TvTunes: Updating Confluence Skin (version %s)" % ADDON.getAddonInfo('version')) doUpdate = xbmcgui.Dialog().yesno(ADDON.getLocalizedString(32105), ADDON.getLocalizedString(32134)) if doUpdate: try: confUp = ConfUpdate() confUp.updateSkin() del confUp except: log("VideoExtras: %s" % traceback.format_exc(), xbmc.LOGERROR) xbmcgui.Dialog().ok(ADDON.getLocalizedString(32105), ADDON.getLocalizedString(32134), ADDON.getLocalizedString(32135))
def onSettingsChanged(self): log("TvTunesMonitor: Notification of settings change received") Settings.reloadSettings()
def __init__(self): # Check if the auto update IP is enabled if not Settings.isAutoIpUpdateEnabled(): return # Get the existing zone we are trying to set the IP Address for existingZone = Settings.getZoneName() # Nothing to do if there is no Zone name set if (existingZone is None) or (existingZone == ""): return # Set up the logging before using the Sonos Device SocoLogging.enable() try: sonos_devices = discover() except: log("AutoUpdateIPAddress: Exception when getting devices") log("AutoUpdateIPAddress: %s" % traceback.format_exc()) sonos_devices = [] if sonos_devices is None: log("AutoUpdateIPAddress: Failed to find any devices") sonos_devices = [] ipaddresses = [] # Check each of the devices found for device in sonos_devices: ip = device.ip_address log("AutoUpdateIPAddress: Getting info for IP address %s" % ip) playerInfo = None # Try and get the player info, if it fails then it is not a valid # player and we should continue to the next try: playerInfo = device.get_speaker_info() except: log("AutoUpdateIPAddress: IP address %s is not a valid player" % ip) log("AutoUpdateIPAddress: %s" % traceback.format_exc()) continue # If player info was found, then print it out if playerInfo is not None: # What is the name of the zone that this speaker is in? zone_name = playerInfo['zone_name'] # Check the zone against the ones we are looking for if zone_name == existingZone: # There could be multiple IP addressing in the same group # so save them all log("AutoUpdateIPAddress: IP address %s in zone %s" % (ip, existingZone)) ipaddresses.append(ip) # Check if there is an IP Address to set if len(ipaddresses) > 0: oldIp = Settings.getIPAddress() # Check if we already have a match to the existing IP Address matchesExisting = False for newIp in ipaddresses: if newIp == oldIp: matchesExisting = True break # If no match found - then set to the first IP Address if not matchesExisting: log("AutoUpdateIPAddress: Setting IP address to %s" % ipaddresses[0]) Settings.setIPAddress(ipaddresses[0])
def onClick(self, control): WINDOW_LIST_ID = 51 # Check to make sure that this click was for the extras list if control != WINDOW_LIST_ID: return # Check the YouTube Search first, as if there are no Extras on disk # There will not be a PlayAll button and it will just be the YouTube Link youtubePosition = 0 vimeoPosition = 0 if len(self.files) > 0: youtubePosition = youtubePosition + 1 vimeoPosition = vimeoPosition + 1 if Settings.isYouTubeSearchSupportEnabled(): vimeoPosition = vimeoPosition + 1 if self.getCurrentListPosition() == youtubePosition: anItem = self.getListItem(youtubePosition) searchDetails = anItem.getProperty("search") log("VideoExtras: Running YouTube Addon/Plugin with search %s" % searchDetails) xbmc.executebuiltin("RunAddon(plugin.video.youtube,%s)" % searchDetails) return if Settings.isVimeoSearchSupportEnabled( ) and self.getCurrentListPosition() == vimeoPosition: anItem = self.getListItem(vimeoPosition) searchDetails = anItem.getProperty("search") log("VideoExtras: Running Vimeo Addon/Plugin with search %s" % searchDetails) xbmc.executebuiltin("RunAddon(plugin.video.vimeo,%s)" % searchDetails) return # Check for the Play All case if self.getCurrentListPosition() == 0: ExtrasPlayer.playAll(self.files, SourceDetails.getTitle()) return # Get the item that was clicked on extraItem = self._getCurrentSelection() if extraItem is None: # Something has gone very wrong, there is no longer the item that was selected log("VideoExtrasWindow: Unable to match item to current selection") return # If part way viewed prompt the user for resume or play from beginning if extraItem.getResumePoint() > 0: resumeWindow = VideoExtrasResumeWindow.createVideoExtrasResumeWindow( extraItem.getDisplayResumePoint()) resumeWindow.doModal() # Check the return value, if exit, then we play nothing if resumeWindow.isExit(): return # If requested to restart from beginning, reset the resume point before playing if resumeWindow.isRestart(): extraItem.setResumePoint(0) # Default is to actually resume del resumeWindow ExtrasPlayer.performPlayAction(extraItem, SourceDetails.getTitle())
def onScreensaverDeactivated(self): log("Deactivate Screensaver") self.stopScreensaver = True
return self.stopScreensaver ################################## # Main of the Video Screensaver ################################## if __name__ == '__main__': # Only allow one screensaver to run at a time if xbmcgui.Window(10000).getProperty("VideoScreensaverStarting") in ["", None]: xbmcgui.Window(10000).setProperty("VideoScreensaverStarting", "true") # Start the monitor so we can see when the screensaver quits exitMon = ScreensaverExitMonitor() log("Starting Video Screensaver %s" % ADDON.getAddonInfo('version')) # Make a special check to see if and background media is running (e.g. TvTunes) # As we want to give that time to stop before we start trying to play the video maxBackgroundMediaWait = 400 okToRunVideoScreensaver = True while maxBackgroundMediaWait > 0: maxBackgroundMediaWait = maxBackgroundMediaWait - 1 # If TvTunes is not running then stop waiting if xbmcgui.Window(10025).getProperty("PlayingBackgroundMedia") in [None, ""]: log("Background media is not playing") break else: log("Background media is currently playing")
def _report_hook(count, blocksize, totalsize): percent = int(float(count * blocksize * 100) / totalsize) downloadProgressDialog.update(percent, name, filename, destination) if downloadProgressDialog.iscanceled(): log("Download: Operation cancelled") raise ValueError('Download Cancelled')
def download(self, name, filename, downloadURL): log("VideoScreensaverPlugin: Downloading %s" % name) tmpdestination = os_path_join(Settings.getTempFolder(), filename) destination = os_path_join(Settings.getScreensaverFolder(), filename) # Check to see if there is already a file present if xbmcvfs.exists(destination): useExisting = xbmcgui.Dialog().yesno( ADDON.getLocalizedString(32005), ADDON.getLocalizedString(32301), name, ADDON.getLocalizedString(32302)) if useExisting: # Don't want to overwrite, so nothing to do log("Download: Reusing existing video file %s" % destination) return else: log("Download: Removing existing file %s ready for fresh download" % destination) xbmcvfs.delete(destination) # Create a progress dialog for the download downloadProgressDialog = xbmcgui.DialogProgress() downloadProgressDialog.create(ADDON.getLocalizedString(32303), name, filename, destination) # Callback method to report progress def _report_hook(count, blocksize, totalsize): percent = int(float(count * blocksize * 100) / totalsize) downloadProgressDialog.update(percent, name, filename, destination) if downloadProgressDialog.iscanceled(): log("Download: Operation cancelled") raise ValueError('Download Cancelled') try: log("Download: Using server: %s" % downloadURL) # Now retrieve the actual file fp, h = urllib.urlretrieve(downloadURL, tmpdestination, _report_hook) log(h) # Check to make sure that the file created downloaded correctly st = xbmcvfs.Stat(tmpdestination) fileSize = st.st_size() log("Download: Size of file %s is %d" % (tmpdestination, fileSize)) # Check for something that has a size greater than zero (in case some OSs do not # support looking at the size), but less that 1,000,000 (As all our files are # larger than that if (fileSize > 0) and (fileSize < 1000000): log("Download: Detected that file %s did not download correctly as file size is only %d" % (downloadURL, fileSize)) xbmcgui.Dialog().ok(ADDON.getLocalizedString(32005), ADDON.getLocalizedString(32306)) else: log("Download: Copy from %s to %s" % (tmpdestination, destination)) copy = xbmcvfs.copy(tmpdestination, destination) if copy: log("Download: Copy Successful") else: log("Download: Copy Failed") xbmcvfs.delete(tmpdestination) except ValueError: # This was a cancel by the user, so remove any file that may be part downloaded if xbmcvfs.exists(tmpdestination): xbmcvfs.delete(tmpdestination) except: log("Download: Theme download Failed!!!", xbmc.LOGERROR) log("Download: %s" % traceback.format_exc(), xbmc.LOGERROR) # Make sure the progress dialog has been closed downloadProgressDialog.close() # Now reload the screen to reflect the change xbmc.executebuiltin("Container.Refresh")
def checkIfVideoExtrasDisplay(self): # Check if the item that was played was a movie if xbmc.getInfoLabel("ListItem.dbtype") != 'movie': log("VideoExtrasPlayerMonitor: Was not a movie playing") return dbid = xbmc.getInfoLabel("ListItem.DBID") if dbid in [None, ""]: log("VideoExtrasPlayerMonitor: No DBID") return # Get the details for the extras title = xbmc.getInfoLabel("ListItem.Title") file = xbmc.getInfoLabel("ListItem.FilenameAndPath") if file in [None, ""]: file = xbmc.getInfoLabel("ListItem.Path") if file in [None, ""]: log("VideoExtrasPlayerMonitor: Unable to find playing file") return log("VideoExtrasPlayerMonitor: searching for: %s = %s" % (title, file)) videoExtras = VideoExtrasBase(file, Settings.MOVIES, title) # Only checking for the existence of extras - no need for DB or default Fanart firstExtraFile = videoExtras.findExtras(True) del videoExtras if not firstExtraFile: log("VideoExtrasPlayerMonitor: No extras for %s" % file) return # So there are extras, so now check to see if the movie was actually # completely viewed, we only want to display the extras for the movie # if the whole thing was viewed # It can take a little while for the database to be updated, so we need # to keep trying for a little while (3 seconds) playcount = None resumePosition = None lastResumeValue = None i = 30 while (i > 0) and (not xbmc.abortRequested): json_query = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovieDetails", "params": {"movieid":%s, "properties": ["playcount", "resume"] }, "id": 1}' % str(dbid)) json_query = unicode(json_query, 'utf-8', errors='ignore') json_query = simplejson.loads(json_query) if ("result" in json_query) and ('moviedetails' in json_query['result']): # Get the movie details from the response movieDetails = json_query['result']['moviedetails'] if movieDetails in [None, ""]: return log("VideoExtrasPlayerMonitor: Database details: %s" % str(movieDetails)) # Get the playcount playcount = movieDetails['playcount'] if playcount not in [None, "", 0, "0"]: # As well as the playcount, we want to check if there is any resume data resumePosition = movieDetails['resume']['position'] # May take a little while for the resume to be updated, so we can wait for either # it changing or the timeout expires if lastResumeValue in [None, ""]: lastResumeValue = resumePosition elif lastResumeValue != resumePosition: if resumePosition not in ["0", 0]: playcount = None break i = i - 1 xbmc.sleep(100) if (playcount in [None, "", 0, "0"]) or (resumePosition not in [None, "0", 0]): log("VideoExtrasPlayerMonitor: Movie was not completed, no need to show extras") return # If we reach here, then we should show the extras log("VideoExtras: Showing extras for %s" % file) cmd = 'RunScript(script.videoextras,display,"%s")' % file xbmc.executebuiltin(cmd)
# -*- coding: utf-8 -*- # Import the common settings from resources.lib.settings import log from resources.lib.scraper import TvTunesScraper ######################### # Main ######################### if __name__ == '__main__': log("TvTunes: Context menu called TvTunes Scraper") themeScraper = TvTunesScraper() del themeScraper
def __init__(self): # Find out where the advancedsettings.xml file is located self.advSettingsXmlFile = xbmc.translatePath( 'special://masterprofile/advancedsettings.xml').decode("utf-8") log("Advancedsettings.xml Location: %s" % self.advSettingsXmlFile) self.bak_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
import xbmcaddon import xbmcvfs import xbmcgui # Import the common settings from resources.lib.settings import log ADDON = xbmcaddon.Addon(id='script.theaudiodb.sync') ADDON_ID = ADDON.getAddonInfo('id') ######################### # Main ######################### if __name__ == '__main__': log("AudioDBSync: Clear History Called (version %s)" % ADDON.getAddonInfo('version')) trackRatingsPath = xbmc.translatePath('special://profile/addon_data/%s/trackRatings.json' % ADDON_ID).decode("utf-8") albumRatingsPath = xbmc.translatePath('special://profile/addon_data/%s/albumRatings.json' % ADDON_ID).decode("utf-8") if xbmcvfs.exists(trackRatingsPath): try: log("AudioDBSync: Removing file %s" % trackRatingsPath) xbmcvfs.delete(trackRatingsPath) except: log("AudioDBSync: %s" % traceback.format_exc(), xbmc.LOGERROR) if xbmcvfs.exists(albumRatingsPath): try: log("AudioDBSync: Removing file %s" % albumRatingsPath)
def onScreensaverActivated(self): log("Activate Screensaver") self.stopScreensaver = False
for newIp in ipaddresses: if newIp == oldIp: matchesExisting = True break # If no match found - then set to the first IP Address if not matchesExisting: log("AutoUpdateIPAddress: Setting IP address to %s" % ipaddresses[0]) Settings.setIPAddress(ipaddresses[0]) ################################ # Main of the Sonos Service ################################ if __name__ == '__main__': log("SonosService: Starting service (version %s)" % ADDON.getAddonInfo('version')) # Start by doing any auto-setting of the IP Address autoIpAdd = AutoUpdateIPAddress() del autoIpAdd # Check for the list of things that impact audio audioChanges = Settings.linkAudioWithSonos( ) or Settings.switchSonosToLineIn( ) or Settings.switchSonosToLineInOnMediaStart() displayNotice = True json_query = xbmc.executeJSONRPC( '{"jsonrpc": "2.0", "method": "Addons.GetAddonDetails", "params": { "addonid": "repository.urepo", "properties": ["enabled", "broken", "name", "author"] }, "id": 1}' ) json_response = simplejson.loads(json_query)
def loadCollection(self, collectionFile, removeDisabled=True): log("CollectSets: Loading collection %s" % collectionFile) if not xbmcvfs.exists(collectionFile): log( "CollectSets: Failed to load collection file: %s" % collectionFile, xbmc.LOGERROR) return None # Load all of the videos that are disabled disabledVideos = [] if removeDisabled: disabledVideos = self.getDisabledVideos() collectionDetails = None try: # Load the file as a string collectionFileRef = xbmcvfs.File(collectionFile, 'r') collectionStr = collectionFileRef.read() collectionFileRef.close() collectionElem = ET.ElementTree(ET.fromstring(collectionStr)) collectionName = collectionElem.find('collection') if collectionName in [None, ""]: return None collectionDetails = {'name': None, 'image': None, 'videos': []} collectionDetails['name'] = collectionName.text log("CollectSets: Collection Name is %s" % collectionDetails['name']) # Record which sets are builtin to the addon collectionDetails['builtin'] = 'false' builtinElem = collectionElem.getroot().find('builtin') if builtinElem not in [None, ""]: if builtinElem.text == 'true': collectionDetails['builtin'] = 'true' isEncoded = False encodedElem = collectionElem.getroot().find('encoded') if encodedElem not in [None, ""]: if encodedElem.text == 'true': isEncoded = True imageElem = collectionElem.getroot().find('image') if imageElem not in [None, ""]: collectionDetails['image'] = imageElem.text # Get the videos that are in the collection for elemItem in collectionElem.findall('video'): video = { 'name': None, 'filename': None, 'image': ICON, 'duration': None, 'primary': None, 'enabled': True } nameElem = elemItem.find('name') if nameElem not in [None, ""]: video['name'] = nameElem.text filenameElem = elemItem.find('filename') if filenameElem not in [None, ""]: video['filename'] = filenameElem.text imageElem = elemItem.find('image') if imageElem not in [None, ""]: video['image'] = imageElem.text durationElem = elemItem.find('duration') if durationElem not in [None, "", 0]: if durationElem.text not in [None, "", 0]: video['duration'] = int(durationElem.text) primaryElem = elemItem.find('primary') if nameElem not in [None, ""]: if isEncoded: video['primary'] = base64.b64decode(primaryElem.text) else: video['primary'] = primaryElem.text # Check if this video is in the disabled list if video['filename'] in disabledVideos: video['enabled'] = False collectionDetails['videos'].append(video) except: log( "CollectSets: Failed to read collection file %s" % collectionFile, xbmc.LOGERROR) log("CollectSets: %s" % traceback.format_exc(), xbmc.LOGERROR) return collectionDetails
CWD = ADDON.getAddonInfo('path').decode("utf-8") LIB_DIR = xbmc.translatePath(os.path.join(CWD, 'resources', 'lib').encode("utf-8")).decode("utf-8") # Class to detect when something in the system has changed class TvTunesMonitor(xbmc.Monitor): def onSettingsChanged(self): log("TvTunesMonitor: Notification of settings change received") Settings.reloadSettings() ################################## # Main of the TvTunes Service ################################## if __name__ == '__main__': log("Starting TvTunes Service %s" % ADDON.getAddonInfo('version')) # Check if the settings mean we want to reset the volume on startup startupVol = Settings.getStartupVolume() if startupVol < 0: log("TvTunesService: No Volume Change Required") else: log("TvTunesService: Setting volume to %s" % startupVol) xbmc.executebuiltin('SetVolume(%d)' % startupVol, True) # Make sure the user wants to play themes if Settings.isThemePlayingEnabled(): log("TvTunesService: Theme playing enabled") # Create a monitor so we can reload the settings if they change
def getCustomCollectionSets(self): log("CollectSets: Loading collections %s" % self.collectSetsFile) customCollections = {} if not xbmcvfs.exists(self.collectSetsFile): log("CollectSets: No custom collections file exists: %s" % self.collectSetsFile) return customCollections # <collections> # <collection> # <name></name> # <filename></filename> # <image></image> # </collection> # </collections> try: # Load the file as a string collectionFileRef = xbmcvfs.File(self.collectSetsFile, 'r') collectionStr = collectionFileRef.read() collectionFileRef.close() collectionSetElem = ET.ElementTree(ET.fromstring(collectionStr)) # Get the collections that are in the collection set for elemItem in collectionSetElem.findall('collection'): details = { 'name': None, 'filename': None, 'image': ICON, 'default': False } collectionName = None nameElem = elemItem.find('name') if nameElem not in [None, ""]: details['name'] = nameElem.text collectionName = details['name'] filenameElem = elemItem.find('filename') if filenameElem not in [None, ""]: details['filename'] = filenameElem.text imageElem = elemItem.find('image') if imageElem not in [None, ""]: details['image'] = imageElem.text if collectionName in [None, ""]: log("CollectSets: No name specified for collection set") else: log("CollectSets: Loading custom collection %s (%s)" % (collectionName, filenameElem)) customCollections[collectionName] = details except: log( "CollectSets: Failed to read collection file %s" % self.collectSetsFile, xbmc.LOGERROR) log("CollectSets: %s" % traceback.format_exc(), xbmc.LOGERROR) return customCollections
if (playcount in [None, "", 0, "0"]) or (resumePosition not in [None, "0", 0]): log("VideoExtrasPlayerMonitor: Movie was not completed, no need to show extras") return # If we reach here, then we should show the extras log("VideoExtras: Showing extras for %s" % file) cmd = 'RunScript(script.videoextras,display,"%s")' % file xbmc.executebuiltin(cmd) ################################### # Main of the Video Extras Service ################################### if __name__ == '__main__': log("VideoExtrasService: Starting service (version %s)" % ADDON.getAddonInfo('version')) # Record if the Context menu should be displayed if Settings.showOnContextMenu(): xbmcgui.Window(10025).setProperty("VideoExtrasShowContextMenu", "true") else: xbmcgui.Window(10025).clearProperty("VideoExtrasShowContextMenu") log("VideoExtrasService: Directory for overlay images is %s" % PROFILE_DIR) # This is a bit of a hack, but we want to force the default paths for the # images if they are not set. This way it will point to the directory containing # all the overlay images to start with, meaning that it will be the directory # shown to the user if they choose to change the icons if ADDON.getSetting("useCustomImages") != "true": if ADDON.getSetting('overlayImage') in [None, '']:
def addCustomCollection(self, customXmlFile): log("CollectSets: Checking custom xml file: %s" % customXmlFile) # Try and load the collection file to ensure all the data is correct collectionDetails = self.loadCollection(customXmlFile, False) if collectionDetails in [None, ""]: log("CollectSets: No collection details returned for %s" % customXmlFile) # TODO: Show error return False collectionName = collectionDetails['name'] if collectionName.lower() in [ 'aquarium', 'beach', 'clock', 'fireplace', 'miscellaneous', 'snow', 'space', 'waterfall', 'apple tv' ]: log("CollectSets: Collection name clashes %s" % collectionName) # We return True here, as we have already displayed an error msg = "%s: %s" % (ADDON.getLocalizedString(32084), collectionName) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32005), msg, ICON, 5000, False) return True # check the number of videos if len(collectionDetails['videos']) < 1: log("CollectSets: Collection contains no videos %s" % customXmlFile) # TODO: Show error return False # Check each of the settings for a video, must have name, filename and primary for videoItem in collectionDetails['videos']: if videoItem['name'] in [None, ""]: log("CollectSets: Video without a name in collection %s" % customXmlFile) # TODO: Show error return False if videoItem['filename'] in [None, ""]: log("CollectSets: Video without a filename in collection %s" % customXmlFile) # TODO: Show error return False if videoItem['primary'] in [None, ""]: log("CollectSets: Video without a primary in collection %s" % customXmlFile) # TODO: Show error return False customCollections = self.getCustomCollectionSets() # Add check to see if it clashes with a different custom collection if collectionName in customCollections.keys(): log("CollectSets: Custom collection name clashes %s" % collectionName) # We return True here, as we have already displayed an error msg = "%s: %s" % (ADDON.getLocalizedString(32084), collectionName) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32005), msg, ICON, 5000, False) return True # If we have reached here then we are OK to add the custom set, so take a copy of # it to the addon settings directory finalCustomXmlFile = os_path_join(Settings.getCustomFolder(), os_path_split(customXmlFile)[-1]) log("CollectSets: Copy from %s to %s" % (customXmlFile, finalCustomXmlFile)) copy = xbmcvfs.copy(customXmlFile, finalCustomXmlFile) if copy: # Now get the details that are required for the collection customCollections[collectionName] = { 'name': collectionName, 'filename': finalCustomXmlFile, 'image': collectionDetails['image'], 'default': False } # save the new set of custom collections self.saveCustomCollections(customCollections) return True
elif self.selectionMade == VideoPluginContextMenu.MARK_UNWATCHED__BLOOPERS: cmd = "/search/?q=%s+Blooper" % escTitle elif self.selectionMade == VideoPluginContextMenu.EDIT_TITLE__INTERVIEW: cmd = "/search/?q=%s+Interview" % escTitle elif self.selectionMade == VideoPluginContextMenu.EDIT_PLOT__VFX: cmd = "/search/?q=%s+VFX" % escTitle if cmd not in [None, ""]: xbmc.executebuiltin("RunAddon(%s,%s)" % (self.pluginName, cmd)) ######################### # Main ######################### if __name__ == '__main__': log("Starting VideoExtras %s" % ADDON.getAddonInfo('version')) try: if len(sys.argv) > 2: # get the type of operation log("Operation = %s" % sys.argv[1]) # Check to make sure that there was actually some data in the second argument # it's possible that a skin has sent us an empty string if (sys.argv[2] is None) or (len(sys.argv[2]) < 1): log("VideoExtras: Called with empty final argument", xbmc.LOGERROR) else: # Load the details of the current source of the extras SourceDetails.forceLoadDetails()
def showList(self, exList): # Get the list of display names displayNameList = [] for anExtra in exList: log("VideoExtrasDialog: filename: %s" % anExtra.getFilename()) displayNameList.append(anExtra.getDisplayName()) # Check if we are supporting YouTube Search vimeoPosition = -4 if Settings.isVimeoSearchSupportEnabled(): vimeoPosition = 0 displayNameList.insert(0, ADDON.getLocalizedString(32122)) # Check if we are supporting YouTube Search youtubePosition = -3 if Settings.isYouTubeSearchSupportEnabled(): youtubePosition = 0 vimeoPosition = vimeoPosition + 1 displayNameList.insert(0, ADDON.getLocalizedString(32116)) addPlayAll = (len(exList) > 1) if addPlayAll: youtubePosition = youtubePosition + 1 vimeoPosition = vimeoPosition + 1 # Play All Selection Option displayNameList.insert(0, ADDON.getLocalizedString(32101)) # Show the list to the user select = xbmcgui.Dialog().select(ADDON.getLocalizedString(32001), displayNameList) # User has made a selection, -1 is exit if select != -1: xbmc.executebuiltin("Dialog.Close(all, true)", True) waitLoop = 0 while xbmc.Player().isPlaying() and waitLoop < 10: xbmc.sleep(100) waitLoop = waitLoop + 1 xbmc.Player().stop() # Give anything that was already playing time to stop while xbmc.Player().isPlaying(): xbmc.sleep(100) if (select == 0) and (addPlayAll is True): ExtrasPlayer.playAll(exList, SourceDetails.getTitle()) elif select == youtubePosition: searchDetails = "/search/?q=%s+Extras" % urllib.quote_plus( SourceDetails.getTitle().encode('utf8')) log("VideoExtras: Running YouTube Addon/Plugin with search %s" % searchDetails) xbmc.executebuiltin("RunAddon(plugin.video.youtube,%s)" % searchDetails) elif select == vimeoPosition: searchDetails = "/search/?q=%s+Extras" % urllib.quote_plus( SourceDetails.getTitle().encode('utf8')) log("VideoExtras: Running Vimeo Addon/Plugin with search %s" % searchDetails) xbmc.executebuiltin("RunAddon(plugin.video.vimeo,%s)" % searchDetails) else: itemToPlay = select # If we added the PlayAll option to the list need to allow for it # in the selection, so add one if addPlayAll is True: itemToPlay = itemToPlay - 1 if vimeoPosition >= 0: itemToPlay = itemToPlay - 1 if youtubePosition >= 0: itemToPlay = itemToPlay - 1 log("VideoExtrasDialog: Start playing %s" % exList[itemToPlay].getFilename()) ExtrasPlayer.performPlayAction(exList[itemToPlay], SourceDetails.getTitle()) else: return False return True
def setSecurity(self, type, title, id, oldLevel, classBlocked=False, forceLevel=None): log("Setting security for (id:%s) %s" % (id, title)) level = 1 # Check if we need to prompt the user or the new security level has been supplied if forceLevel is None: # Set the new security level to be used if oldLevel > 0: # Default is to disable it if it was enabled level = 0 numLevels = Settings.getNumberOfLevels() if numLevels > 1 or classBlocked: # Need to prompt the user to see which pin they are trying to set displayNameList = [] # Add the option to turn it off displayNameList.append("%s %s" % (ADDON.getLocalizedString(32211), ADDON.getLocalizedString(32013))) for i in range(1, numLevels + 1): secLevStr = str(i) if numLevels < 2: # If there is only one security level, use "On" rather than the number secLevStr = ADDON.getLocalizedString(32014) displayString = "%s %s" % (ADDON.getLocalizedString(32211), secLevStr) displayNameList.append(displayString) # Check if we need the option to disable a classification restriction if classBlocked: displayNameList.append(ADDON.getLocalizedString(32212)) select = xbmcgui.Dialog().select(ADDON.getLocalizedString(32001), displayNameList) if select != -1: level = select if classBlocked and (select >= (len(displayNameList) - 1)): level = -1 log("Setting security level to %d" % level) else: log("Exiting set security as no level selected") return else: level = forceLevel # This could take a little time to set the value so show the busy dialog xbmc.executebuiltin("ActivateWindow(busydialognocancel)") if title not in [None, ""]: pinDB = PinSentryDB() if type == MenuNavigator.TVSHOWS: # Set the security level for this title, setting it to zero # will result in the entry being removed from the database # as the default for an item is unset pinDB.setTvShowSecurityLevel(title, int(id), level) elif type == MenuNavigator.MOVIES: pinDB.setMovieSecurityLevel(title, int(id), level) elif type == MenuNavigator.MOVIESETS: pinDB.setMovieSetSecurityLevel(title, int(id), level) # As well as setting the security on the Movie set, we need # to also set it on each movie in the Movie Set self._setSecurityOnMoviesInMovieSets(int(id), level) elif type == MenuNavigator.MUSICVIDEOS: pinDB.setMusicVideoSecurityLevel(title, int(id), level) elif type == MenuNavigator.PLUGINS: pinDB.setPluginSecurityLevel(title, id, level) elif type == MenuNavigator.REPOSITORIES: pinDB.setRepositorySecurityLevel(title, id, level) elif type == MenuNavigator.FILESOURCE: pinDB.setFileSourceSecurityLevel(title, id, level) elif type == MenuNavigator.CLASSIFICATIONS_MOVIES: pinDB.setMovieClassificationSecurityLevel(id, title, level) elif type == MenuNavigator.CLASSIFICATIONS_TV: pinDB.setTvClassificationSecurityLevel(id, title, level) elif type == MenuNavigator.TVCHANNELS: pinDB.setTvChannelSecurityLevel(title, id, level) del pinDB else: # Handle the bulk operations like set All security for the movies self._setBulkSecurity(type, level) xbmc.executebuiltin("Dialog.Close(busydialognocancel)") xbmc.executebuiltin("Container.Refresh")
def run(self, files): # All the files have been retrieved, now need to display them if not files and not Settings.isYouTubeSearchSupportEnabled( ) and not Settings.isVimeoSearchSupportEnabled(): # "Info", "No extras found" xbmcgui.Dialog().ok(ADDON.getLocalizedString(32102), ADDON.getLocalizedString(32103)) else: isTvTunesAlreadySet = True needsWindowReset = True # Make sure we don't leave global variables set try: # Check which listing format to use if Settings.isDetailedListScreen(): # Check if TV Tunes override is already set isTvTunesAlreadySet = (xbmcgui.Window(12000).getProperty( "TvTunesContinuePlaying").lower() == "true") # If TV Tunes is running we want to ensure that we still keep the theme going # so set this variable on the home screen if not isTvTunesAlreadySet: log("VideoExtras: Setting TV Tunes override") xbmcgui.Window(12000).setProperty( "TvTunesContinuePlaying", "True") else: log("VideoExtras: TV Tunes override already set") extrasWindow = VideoExtrasWindow.createVideoExtrasWindow( files=files) xbmc.executebuiltin("Dialog.Close(movieinformation)", True) extrasWindow.doModal() del extrasWindow else: extrasWindow = VideoExtrasDialog() needsWindowReset = extrasWindow.showList(files) del extrasWindow # The video selection will be the default return location if (not Settings.isMenuReturnVideoSelection() ) and needsWindowReset: if Settings.isMenuReturnHome(): xbmc.executebuiltin("ActivateWindow(home)", True) else: infoDialogId = 12003 # Put the information dialog back up xbmc.executebuiltin("ActivateWindow(movieinformation)") if Settings.isMenuReturnExtras(): # Wait for the Info window to open, it can take a while # this is to avoid the case where the exList dialog displays # behind the info dialog counter = 0 while (xbmcgui.getCurrentWindowDialogId() != infoDialogId) and (counter < 30): xbmc.sleep(100) counter = counter + 1 # Allow time for the screen to load - this could result in an # action such as starting TvTunes xbmc.sleep(1000) # Before showing the list, check if someone has quickly # closed the info screen while it was opening and we were waiting if xbmcgui.getCurrentWindowDialogId( ) == infoDialogId: # Reshow the exList that was previously generated self.run(files) except: log("VideoExtras: %s" % traceback.format_exc(), xbmc.LOGERROR) # Tidy up the TV Tunes flag if we set it if not isTvTunesAlreadySet: log("VideoExtras: Clearing TV Tunes override") xbmcgui.Window(12000).clearProperty("TvTunesContinuePlaying")
# dialogs (like the Addon Information Dialog) are closed if xbmc.getCondVisibility("Window.IsActive(10146)"): xbmc.executebuiltin("Dialog.Close(all, true)", True) # Get all the arguments base_url = sys.argv[0] addon_handle = int(sys.argv[1]) args = urlparse.parse_qs(sys.argv[2][1:]) # Record what the plugin deals with, files in our case xbmcplugin.setContent(addon_handle, 'files') # Get the current mode from the arguments, if none set, then use None mode = args.get('mode', None) log("PinSentryPlugin: Called with addon_handle = %d" % addon_handle) # If None, then at the root if mode is None: log("PinSentryPlugin: Mode is NONE - showing root menu") menuNav = MenuNavigator(base_url, addon_handle) menuNav.showRootMenu() del menuNav elif mode[0] == 'folder': log("PinSentryPlugin: Mode is FOLDER") # Get the actual folder that was navigated to foldername = args.get('foldername', None) type = args.get('type', None) subtype = args.get('subtype', None)
def onInit(self): # Need to clear the list of the default items self.clearList() # Start by adding an option to Play All if len(self.files) > 0: anItem = xbmcgui.ListItem(ADDON.getLocalizedString(32101), path=SourceDetails.getFilenameAndPath()) # Get the first items fanart for the play all option anItem.setProperty("Fanart_Image", self.files[0].getFanArt()) if SourceDetails.getTvShowTitle() != "": anItem.setInfo('video', {'TvShowTitle': SourceDetails.getTvShowTitle()}) if SourceDetails.getTitle() != "": anItem.setInfo('video', {'Title': SourceDetails.getTitle()}) self.addItem(anItem) # Check if we want to have YouTube Extra Support if Settings.isYouTubeSearchSupportEnabled(): # Create the message to the YouTube Plugin li = xbmcgui.ListItem(ADDON.getLocalizedString(32116)) # Need to set the title to get it in the header if SourceDetails.getTvShowTitle() != "": li.setInfo('video', {'TvShowTitle': SourceDetails.getTvShowTitle()}) if SourceDetails.getTitle() != "": li.setInfo('video', {'Title': SourceDetails.getTitle()}) li.setProperty("Fanart_Image", SourceDetails.getFanArt()) li.setProperty( "search", "/search/?q=%s+Extras" % urllib.quote_plus(SourceDetails.getTitle().encode('utf8'))) self.addItem(li) # Check if we want to have Vimeo Extra Support if Settings.isVimeoSearchSupportEnabled(): # Create the message to the Vimeo Plugin li = xbmcgui.ListItem(ADDON.getLocalizedString(32122)) # Need to set the title to get it in the header if SourceDetails.getTvShowTitle() != "": li.setInfo('video', {'TvShowTitle': SourceDetails.getTvShowTitle()}) if SourceDetails.getTitle() != "": li.setInfo('video', {'Title': SourceDetails.getTitle()}) li.setProperty("Fanart_Image", SourceDetails.getFanArt()) li.setProperty( "search", "/search/?q=%s+Extras" % urllib.quote_plus(SourceDetails.getTitle().encode('utf8'))) self.addItem(li) for anExtra in self.files: log("VideoExtrasWindow: filename: %s" % anExtra.getFilename()) # Create the list item anItem = anExtra.createListItem( path=SourceDetails.getFilenameAndPath(), parentTitle=SourceDetails.getTitle(), tvShowTitle=SourceDetails.getTvShowTitle()) self.addItem(anItem) # Before we return, set back the selected on screen item to the one just watched # This is in the case of a reload if self.lastRecordedListPosition > 0: self.setCurrentListPosition(self.lastRecordedListPosition) xbmcgui.WindowXML.onInit(self)
def updateAdvancedSettings(self): xmlFileStr = None # Check if the advancessettings.xml file already exists if xbmcvfs.exists(self.advSettingsXmlFile): log("Loading existing advanced settings file") # Read in the existing file xmlFile = xbmcvfs.File(self.advSettingsXmlFile, 'r') xmlFileStr = xmlFile.read() xmlFile.close() # The file has now been read so we need to see if # there is already a tv tunes section in it if AdvSettings.HEADER in xmlFileStr: log("Updating existing TvTunes setting") # need to strip out the existing contents and replace it with # the new contents insertTxt = AdvSettings.HEADER + "\n" insertTxt += self._getNewSettingsXml() insertTxt += ' ' + AdvSettings.FOOTER regexCompiled = re.compile( "(%s).*?(%s)" % (AdvSettings.HEADER, AdvSettings.FOOTER), re.IGNORECASE | re.DOTALL) xmlFileStr = regexCompiled.sub(insertTxt, xmlFileStr) elif AdvSettings.VIDEO_SECTION_END in xmlFileStr: log("Adding to existing video section") insertTxt = ' ' + AdvSettings.HEADER + "\n" insertTxt += self._getNewSettingsXml() insertTxt += ' ' + AdvSettings.FOOTER + "\n" insertTxt += ' ' + AdvSettings.VIDEO_SECTION_END # No Video Extras section yet, but there is a video section xmlFileStr = re.sub("(%s)" % AdvSettings.VIDEO_SECTION_END, insertTxt, xmlFileStr) elif AdvSettings.ADV_SET_END in xmlFileStr: log("Adding with new video section") # Need to add a video section as well insertTxt = ' ' + AdvSettings.VIDEO_SECTION_START + "\n" insertTxt += ' ' + AdvSettings.HEADER + "\n" insertTxt += self._getNewSettingsXml() insertTxt += ' ' + AdvSettings.FOOTER + "\n" insertTxt += ' ' + AdvSettings.VIDEO_SECTION_END + "\n" insertTxt += AdvSettings.ADV_SET_END xmlFileStr = re.sub("(%s)" % AdvSettings.ADV_SET_END, insertTxt, xmlFileStr) else: # This is an invalid advancedsettings.xml log("Invalid advancedsettings.xml detected") xmlFileStr = None # Show Error Dialog xbmcgui.Dialog().ok(ADDON.getLocalizedString(32001), ADDON.getLocalizedString(32153)) # Make a backup of the file as we are going to change it if xmlFileStr is not None: xbmcvfs.copy( self.advSettingsXmlFile, "%s.tvtunes-%s.bak" % (self.advSettingsXmlFile, self.bak_timestamp)) else: # The file didn't exist, so create it from scratch xmlFileStr = AdvSettings.ADV_SET_START + "\n" xmlFileStr += ' ' + AdvSettings.VIDEO_SECTION_START + "\n" xmlFileStr += ' ' + AdvSettings.HEADER + "\n" # Need to reduce the escaping of the forward-slash as we will not # be parsing this string again xmlFileStr += self._getNewSettingsXml().replace('\\\\\\', '\\\\') xmlFileStr += ' ' + AdvSettings.FOOTER + "\n" xmlFileStr += ' ' + AdvSettings.VIDEO_SECTION_END + "\n" xmlFileStr += AdvSettings.ADV_SET_END + "\n" # Now write the new file contents # A backup will have already been taken if there was an old file if xmlFileStr is not None: xmlFile = xbmcvfs.File(self.advSettingsXmlFile, 'w') xmlFile.write(xmlFileStr) xmlFile.close() xbmcgui.Dialog().ok(ADDON.getLocalizedString(32105), ADDON.getLocalizedString(32095)) log("New advancedsettings.xml content: %s" % xmlFileStr) else: log("advancedsettings.xml has been left unchanged")
def onAction(self, action): # actioncodes from https://github.com/xbmc/xbmc/blob/master/xbmc/guilib/Key.h ACTION_PREVIOUS_MENU = 10 ACTION_NAV_BACK = 92 ACTION_CONTEXT_MENU = 117 if (action == ACTION_PREVIOUS_MENU) or (action == ACTION_NAV_BACK): log("VideoExtrasWindow: Close Action received: %s" % str(action.getId())) self.close() elif action == ACTION_CONTEXT_MENU: youtubePosition = 0 vimeoPosition = 0 if len(self.files) > 0: youtubePosition = youtubePosition + 1 vimeoPosition = vimeoPosition + 1 if Settings.isYouTubeSearchSupportEnabled(): vimeoPosition = vimeoPosition + 1 # Check to see if the context menu has been called up for the You Tube option if self.getCurrentListPosition() == youtubePosition: contextWindow = VideoPluginContextMenu.createYouTubeContextMenu( SourceDetails.getTitle()) contextWindow.doModal() del contextWindow return if Settings.isVimeoSearchSupportEnabled( ) and self.getCurrentListPosition() == vimeoPosition: contextWindow = VideoPluginContextMenu.createVimeoContextMenu( SourceDetails.getTitle()) contextWindow.doModal() del contextWindow return # Check for the Play All case if self.getCurrentListPosition() == 0: return # Get the item that was clicked on extraItem = self._getCurrentSelection() # create the context window contextWindow = VideoExtrasContextMenu.createVideoExtrasContextMenu( extraItem) contextWindow.doModal() # Check the return value, if exit, then we play nothing if contextWindow.isExit(): return # If requested to restart from beginning, reset the resume point before playing if contextWindow.isRestart(): extraItem.setResumePoint(0) ExtrasPlayer.performPlayAction(extraItem, SourceDetails.getTitle()) if contextWindow.isResume(): ExtrasPlayer.performPlayAction(extraItem, SourceDetails.getTitle()) if contextWindow.isMarkUnwatched(): # Need to remove the row from the database if Settings.isDatabaseEnabled(): # Refresh the screen now that we have change the flag extraItem.setResumePoint(0) extraItem.saveState() self.onInit() if contextWindow.isMarkWatched(): # If marking as watched we need to set the resume time so it doesn't # start in the middle the next time it starts if Settings.isDatabaseEnabled(): extraItem.setResumePoint(extraItem.getTotalDuration()) extraItem.saveState() self.onInit() if contextWindow.isEditTitle(): # Prompt the user for the new name keyboard = xbmc.Keyboard() keyboard.setDefault(extraItem.getDisplayName()) keyboard.doModal() if keyboard.isConfirmed(): try: newtitle = keyboard.getText().decode("utf-8") except: newtitle = keyboard.getText() # Only set the title if it has changed if (newtitle != extraItem.getDisplayName()) and ( len(newtitle) > 0): result = extraItem.setTitle(newtitle, isTV=SourceDetails.isTv()) if not result: xbmcgui.Dialog().ok( ADDON.getLocalizedString(32102), ADDON.getLocalizedString(32109)) else: self.onInit() if contextWindow.isEditPlot(): # Prompt the user for the new plot description keyboard = xbmc.Keyboard() keyboard.setDefault(extraItem.getPlot()) keyboard.doModal() if keyboard.isConfirmed(): try: newplot = keyboard.getText().decode("utf-8") except: newplot = keyboard.getText() # Only set the plot if it has changed if (newplot != extraItem.getPlot()) and ( (len(newplot) > 0) or (extraItem.getPlot() is not None)): result = extraItem.setPlot(newplot, isTV=SourceDetails.isTv()) if not result: xbmcgui.Dialog().ok( ADDON.getLocalizedString(32102), ADDON.getLocalizedString(32115)) else: self.onInit() del contextWindow
###################################### # Main of the VideoScreensaver Plugin ###################################### if __name__ == '__main__': # Get all the arguments base_url = sys.argv[0] addon_handle = int(sys.argv[1]) args = urlparse.parse_qs(sys.argv[2][1:]) # Record what the plugin deals with, files in our case xbmcplugin.setContent(addon_handle, 'files') # Get the current mode from the arguments, if none set, then use None mode = args.get('mode', None) log("VideoScreensaverPlugin: Called with addon_handle = %d" % addon_handle) # If None, then at the root if mode is None: log("VideoScreensaverPlugin: Mode is NONE - showing collection list") menuNav = MenuNavigator(base_url, addon_handle) menuNav.rootMenu() del menuNav elif mode[0] == 'collection': log("VideoScreensaverPlugin: Mode is collection") name = '' link = None nameItem = args.get('name', None)
def __init__(self): # special://skin - This path points to the currently active skin's root directory. skinExtrasOverlayBase = xbmc.translatePath("special://skin").decode("utf-8") skinExtrasOverlayBase = os_path_join(skinExtrasOverlayBase, "media") # Now check to see if the user has defines a different overlay image in the # settings, as that is the main one that will be used self.skinExtrasOverlay = Settings.getCustomOverlayImage() self.skinExtrasOverlayList = Settings.getCustomListImage() # Next check the skin specific overlay if self.skinExtrasOverlay in [None, '']: self.skinExtrasOverlay = os_path_join(skinExtrasOverlayBase, "videoextras_overlay.png") if self.skinExtrasOverlayList in [None, '']: self.skinExtrasOverlayList = os_path_join(skinExtrasOverlayBase, "videoextras_overlay_list.png") log("VideoExtrasService: Looking for image overlay file: %s" % self.skinExtrasOverlay) if not xbmcvfs.exists(self.skinExtrasOverlay): log("VideoExtrasService: No custom image, using default") # Add default image setting to skinExtrasOverlay self.skinExtrasOverlay = os_path_join(RES_DIR, "skins") self.skinExtrasOverlay = os_path_join(self.skinExtrasOverlay, "icons") self.skinExtrasOverlay = os_path_join(self.skinExtrasOverlay, "overlay1.png") log("VideoExtrasService: Looking for list image overlay file: %s" % self.skinExtrasOverlayList) if not xbmcvfs.exists(self.skinExtrasOverlayList): log("VideoExtrasService: No custom wide image, using default") # Add default image setting to skinExtrasOverlay self.skinExtrasOverlayList = os_path_join(RES_DIR, "skins") self.skinExtrasOverlayList = os_path_join(self.skinExtrasOverlayList, "icons") self.skinExtrasOverlayList = os_path_join(self.skinExtrasOverlayList, "list1.png") self.forceOverlayOverwrite = False # We now know the file that we are going to use for the overlay # Check to see if this is different from the last overlay file used filename = os_path_join(PROFILE_DIR, "overlay_image_used.txt") try: previousOverlay = None if xbmcvfs.exists(filename): fileHandle = xbmcvfs.File(filename, 'r') previousOverlay = fileHandle.read() fileHandle.close() # Check if the overlay has changed if self.skinExtrasOverlay != previousOverlay: self.forceOverlayOverwrite = True # Update the record of the file we are now using if xbmcvfs.exists(filename): xbmcvfs.delete(filename) fileHandle = xbmcvfs.File(filename, 'w') fileHandle.write(self.skinExtrasOverlay.encode("UTF-8")) fileHandle.close() except: log("VideoExtrasService: Failed to write: %s" % filename, xbmc.LOGERROR) log("VideoExtrasService: %s" % traceback.format_exc(), xbmc.LOGERROR)