def stop(self, immediate=False, fastFade=False): if self.isAlive: # If video is playing, check to see if it is a theme video if self.themePlayer.isPlayingTheme(): if immediate: log("TunesBackend: Stop playing") self.themePlayer.stop() while self.themePlayer.isPlaying(): xbmc.sleep(50) else: log("TunesBackend: Ending playing") self.themePlayer.endPlaying(fastFade) self.isAlive = False # If currently playing a video file, then we have been overridden, # and we need to restore all the settings, the player callbacks # will not be called, so just force it on stop self.themePlayer.restoreSettings() # Clear all the values stored self.newThemeFiles.clear() self.oldThemeFiles.clear() self.prevThemeFiles.clear() self.delayedStart.clear() # Clear the option used by other add-ons to work out if TvTunes is playing a theme xbmcgui.Window(10025).clearProperty("TvTunesIsRunning") # The following value is added for the Confluence skin to not show what is # currently playing, maybe change this name when we submit the pull request to # Confluence - new name: PlayingBackgroundMedia xbmcgui.Window(10025).clearProperty("TvTunesIsAlive") xbmcgui.Window(10025).clearProperty("PlayingBackgroundMedia") # Clear the Theme Player by resetting it self.themePlayer = ThemePlayer()
def _getDownloadInfoUsername(self): downloadUser = Settings.getSpecialistDownloadUser() if downloadUser in [None, '']: downloadUser = self.username log("getDownloadInfoUsername: Using username %s for ratings download" % downloadUser) return downloadUser
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 _getSecondsInTimeString(self, fullTimeString): # Start by splitting the time into sections hours = 0 minutes = 0 seconds = 0 try: timeParts = list(reversed(fullTimeString.split(':'))) if len(timeParts) > 2: hours = int(timeParts[2]) if len(timeParts) > 1: minutes = int(timeParts[1]) if len(timeParts) > 1: seconds = int(float(timeParts[0])) except: # time sections are not numbers log("FfmpegCmd: Exception Details: %s" % traceback.format_exc()) hours = 0 minutes = 0 seconds = 0 totalInSeconds = (((hours * 60) + minutes) * 60) + seconds log("FfmpegCmd: 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 __init__(self, extrasParent, videoType=None, title=None): log("VideoExtrasBase: Finding extras for %s" % extrasParent) self.videoType = videoType self.baseDirectory = extrasParent if self.baseDirectory.startswith("stack://"): self.baseDirectory = self.baseDirectory.split(" , ")[0] self.baseDirectory = self.baseDirectory.replace("stack://", "") # There is a problem if some-one is using windows shares with # \\SERVER\Name as when the addon gets called the first \ gets # removed, making an invalid path, so we add it back here elif self.baseDirectory.startswith("\\"): self.baseDirectory = "\\" + self.baseDirectory # Support special paths like smb:// means that we can not just call # os.path.isfile as it will return false even if it is a file # (A bit of a shame - but that's the way it is) fileExt = os.path.splitext(self.baseDirectory)[1] # If this is a file, then get it's parent directory if fileExt is not None and fileExt != "": self.baseDirectory = (os_path_split(self.baseDirectory))[0] self.filename = (os_path_split(extrasParent))[1] else: self.filename = None self.title = title log("VideoExtrasBase: Root directory: %s" % self.baseDirectory)
def _getCachedCover(self, fileName): cachedCover = None # check if the directory exists before searching dirs, files = xbmcvfs.listdir(Settings.getCoverCacheLocation()) for aFile in files: # Get the filename without extension coverSrc, ext = os.path.splitext(aFile) # Get the name that the cached cover will have been stored as targetSrc, bookExt = os.path.splitext(fileName) # Make sure both are utf-8 when comparing try: coverSrc = coverSrc.encode("utf-8") except: pass try: targetSrc = targetSrc.encode("utf-8") except: pass if targetSrc == coverSrc: cachedCover = os_path_join(Settings.getCoverCacheLocation(), aFile) log("AudioBookHandler: Cached cover found: %s" % cachedCover) return cachedCover
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 getTMDB_ids(self, id): log("IdLookup: Getting Ids from %s" % id) # Use the same request for tmdb as imdb url = "%s/%s/%s?api_key=%s" % (self.tmdb_url_prefix, 'movie', id, self.tmdb_api_key) json_details = self._makeCall(url) tmdb_id = None imdb_id = None if json_details not in [None, ""]: json_response = json.loads(json_details) # The results of the search come back as an array of entries if 'id' in json_response: tmdb_id = json_response.get('id', None) if tmdb_id not in [None, ""]: tmdb_id = str(tmdb_id) log("IdLookup: Found tmdb Id %s from id" % str(tmdb_id)) else: log("IdLookup: No results returned for tmdb search for tmdb from imdb id") if 'imdb_id' in json_response: imdb_id = json_response.get('imdb_id', None) if imdb_id not in [None, ""]: imdb_id = str(imdb_id) log("IdLookup: Found imdb Id %s from id" % str(imdb_id)) else: log("IdLookup: No results returned for tmdb search for imdb id") return (tmdb_id, imdb_id)
def getCoverImage(filePath, eBookFileName): # Check if there is a cached version coverTargetName = None fullpathLocalImage, bookExt = os.path.splitext(filePath) fullpathLocalImage = "%s.jpg" % fullpathLocalImage if xbmcvfs.exists(fullpathLocalImage): log("EBookBase: Found local cached image %s" % fullpathLocalImage) return fullpathLocalImage # Check for a cached cover coverTargetName = EBookBase.getCachedCover(eBookFileName) # If we reach here, then there was no cached cover image, so we need to extract one if coverTargetName in [None, ""]: ebook = EBookBase.createEBookObject(filePath) coverTargetName = ebook.extractCoverImage() ebook.tidyUp() del ebook # If there is still no cover image, check for folder.jpg in the same directory if coverTargetName in [None, ""]: baseDirectory = (os_path_split(filePath))[0] subdirs, filesInDir = xbmcvfs.listdir(baseDirectory) for fileInDir in filesInDir: if fileInDir.lower() in ['folder.jpg', 'cover.jpg', 'folder.png', 'cover.png']: coverTargetName = os_path_join(baseDirectory, fileInDir) return coverTargetName
def endPlaying(self, fastFade=False, slowFade=False): if self.isPlayingAudio() and Settings.isFadeOut(): cur_vol = self._getVolume() # Calculate how fast to fade the theme, this determines # the number of step to drop the volume in numSteps = 10 if fastFade: numSteps = numSteps / 2 elif slowFade: numSteps = numSteps * 4 vol_step = cur_vol / numSteps # do not mute completely else the mute icon shows up for step in range(0, (numSteps - 1)): # If the system is going to be shut down then we need to reset # everything as quickly as possible if WindowShowing.isShutdownMenu() or xbmc.abortRequested: log("ThemePlayer: Shutdown menu detected, cancelling fade out") break vol = cur_vol - vol_step log("ThemePlayer: fadeOut_vol: %s" % str(vol)) self._setVolume(vol) cur_vol = vol xbmc.sleep(200) # The final stop and reset of the settings will be done # outside of this "if" # Need to always stop by the end of this self.stop()
def _getNestedExtrasFiles(self, basepath, filename, exitOnFirst=False, noExtrasDirNeeded=False): extras = [] if dir_exists(basepath): dirs, files = xbmcvfs.listdir(basepath) for dirname in dirs: # Do not search inside Bluray or DVD images if (dirname == 'VIDEO_TS') or (dirname == 'BDMV'): continue dirpath = os_path_join(basepath, dirname) log("VideoExtrasFinder: Nested check in directory: %s" % dirpath) if dirname != Settings.getExtrasDirName(): log("VideoExtrasFinder: Check directory: %s" % dirpath) extras.extend(self._getExtrasDirFiles(dirpath, exitOnFirst, noExtrasDirNeeded)) # Check if we are only looking for the first entry if files and (exitOnFirst is True): break extras.extend(self._getExtrasFiles(dirpath, filename, exitOnFirst)) # Check if we are only looking for the first entry if files and (exitOnFirst is True): break extras.extend(self._getNestedExtrasFiles(dirpath, filename, exitOnFirst, noExtrasDirNeeded)) # Check if we are only looking for the first entry if files and (exitOnFirst is True): break return extras
def getAddonsToSync(self): # Start by getting all the addons installed activeAddons = self._getInstalledAddons() # Now check for any filter that is applied activeAddons = self._filterAddons(activeAddons) addonDetails = {} # Now loop each of the addons to get the details required for addonName in activeAddons.keys(): settingsDir = self._getAddonSettingsDirectory(addonName) # If there are no settings available then we have it installed # but no configuration available if settingsDir in [None, ""]: addonDetails[addonName] = None else: addonDetail = {} addonDetail['dir'] = settingsDir # Generate the hash hash = Hash() hashVal = hash.getDirhash(settingsDir) del hash log("AddonData: addon: %s path: %s hash: %s" % (addonName, settingsDir, str(hashVal))) addonDetail['hash'] = hashVal addonDetails[addonName] = addonDetail addonDetail['version'] = activeAddons[addonName] return addonDetails
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 getSelection(self, narrowSearch=True): typeFilter = 'movie' if self.isTvShow: typeFilter = 'tv' # Generate the URL and get the page search_url = "https://www.commonsensemedia.org/search/%s?f[0]=field_reference_review_ent_prod%%253Atype%%3Acsm_" + typeFilter url = search_url % urllib.quote_plus(self.videoTitle) html = self._getHtmlSource(url) soup = BeautifulSoup(''.join(html)) searchResults = soup.findAll('div', {"class": "views-field views-field-field-reference-review-ent-prod-title result-title"}) searchMatches = [] # Check each of the entries found for entries in searchResults: for link in entries.findAll('a'): # Get the link videoName = self._convertHtmlIntoKodiText(link.string) videoUrl = "https://www.commonsensemedia.org%s" % link['href'] searchMatches.append({"name": videoName, "link": videoUrl}) log("CommonSenseMediaScraper: Initial Search Match: %s {%s}" % (videoName, videoUrl)) # The Common Sense Media search can often return lots of entries that do not # contain the words in the requested video, so we can try and narrow it down if narrowSearch: searchMatches = self._narrowDownSearch(searchMatches) return searchMatches
def getUnsupportedScreensavers(screensavers): log("getUnsupportedScreensavers") # Ideally we would check each addon we have already identified using the Addons.GetAddonDetails # API, however that will only return the primary type, and we are actually looking for # the script option as just one of the supported types json_query = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Addons.GetAddons", "params": { "type": "xbmc.python.script" }, "id": 1}') json_response = json.loads(json_query) scriptaddons = [] # Extract the addon ids from the response if ("result" in json_response) and ('addons' in json_response['result']): # Check each of the screensavers that are installed on the system for addonItem in json_response['result']['addons']: addonName = addonItem['addonid'] scriptaddons.append(addonName) # Now check each of the addons we have to see if they support being launched as a script unsupportedScreensavers = [] for screensaverAddon in screensavers: if screensaverAddon not in scriptaddons: log("RandomScreensaver: Screensaver %s does not support launching as a script" % screensaverAddon) unsupportedScreensavers.append(screensaverAddon) return unsupportedScreensavers
def _getThemes(self, itemId, isTvShow): if itemId in [None, ""]: return None details = None # Check if it is in the library if isTvShow: log("ThemeLibrary: Getting TV Show theme for %s" % itemId) for tvTheme in self.tvShowList: if (tvTheme['id'] == itemId) or (tvTheme['imdb'] == itemId) or (tvTheme['tvdb'] == itemId): details = tvTheme # Only need one entry break else: log("ThemeLibrary: Getting Movie theme for %s" % itemId) for movieTheme in self.movieList: if (movieTheme['id'] == itemId) or (movieTheme['imdb'] == itemId) or (movieTheme['tmdb'] == itemId): details = movieTheme # Only need one entry break return details
def refresh(self): # Check if there is anything to do for lyrics if not Settings.isLyricsInfoLayout() or (self.listControl is None): return if not self.hasLyrics(): return timeLines = self.track['lyrics'] cur_time = self.track['position_seconds'] nums = self.listControl.size() pos = self.listControl.getSelectedPosition() # Make sure that we don't exceed the index if pos >= len(timeLines): log("Lyrics: Current Position exceeds number of entries") return # Check if we need to roll the lyrics backwards if (cur_time < timeLines[pos][0]): while ((pos > 0) and (timeLines[pos - 1][0] > cur_time)): pos = pos - 1 else: # Going forwards while ((pos < nums - 1) and (timeLines[pos + 1][0] < cur_time)): pos = pos + 1 # Now we have the correct position, but we want that to sit in the # middle of the dialog, so add on the offset if (pos + self.lyricListOffset > nums - 1): # As it's near the end - set the focus to the last one self.listControl.selectItem(nums - 1) else: self.listControl.selectItem(pos + self.lyricListOffset) self.listControl.selectItem(pos)
def __init__(self): # Start by getting the database location self.configPath = xbmc.translatePath(ADDON.getAddonInfo('profile')) self.databasefile = os_path_join(self.configPath, "ebooks_database.db") log("EbooksDB: Database file location = %s" % self.databasefile) # Check to make sure the DB has been created self._createDatabase()
def clearLyricRequest(self): log("Lyrics: Clearing lyric request") # Clear the lyrics that were stored so that the lyrics addon does not keep looking xbmcgui.Window(10000).clearProperty('culrc.manual') xbmcgui.Window(10000).clearProperty('culrc.artist') xbmcgui.Window(10000).clearProperty('culrc.track') xbmcgui.Window(10000).clearProperty('culrc.lyrics')
def _loadDetailsFromFfmpeg(self, includeCover=True): # check if the cover is required coverTargetName = None if includeCover: coverTargetName = self._getMainCoverLocation() info = self._runFFmpegCommand(self.filePath, coverTargetName) # If we needed the cover, then save the details if includeCover: if xbmcvfs.exists(coverTargetName): self.coverImage = coverTargetName if info not in [None, ""]: self.title = info['title'] # Check if the title should start with the artist name if Settings.isShowArtistInBookList() and (self.title not in [None, ""]): artist = info['artist'] if artist not in [None, ""]: # Make sure the artist name is not already in the title if (not self.title.startswith(artist)) and (not self.title.endswith(artist)): try: self.title = "%s - %s" % (artist, self.title) except: log("M4BHandler: Failed to add artist to title") self.chapters = info['chapters'] self.totalDuration = info['duration']
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 createSonosArtistSlideshow(sonosDevice): # Check the ArtistSlideshow setting to see if the biography field is set try: artistslideshow = xbmcaddon.Addon(id='script.artistslideshow') if artistslideshow.getSetting('artistinfo') != 'true': # Biography is not set, prompt the use to see if we should set it if xbmcgui.Dialog().yesno( __addon__.getLocalizedString(32001), __addon__.getLocalizedString(32060), " \"%s\"" % artistslideshow.getLocalizedString(32005), __addon__.getLocalizedString(32061)): artistslideshow.setSetting('artistinfo', 'true') if artistslideshow.getSetting('transparent') != 'true': # Transparent image is not set, prompt the use to see if we should set it if xbmcgui.Dialog().yesno( __addon__.getLocalizedString(32001), __addon__.getLocalizedString(32060), " \"%s\"" % artistslideshow.getLocalizedString(32107), __addon__.getLocalizedString(32061)): artistslideshow.setSetting('transparent', 'true') except: log( "SonosArtistSlideshow: Exception Details: %s" % traceback.format_exc(), xbmc.LOGERROR) return SonosArtistSlideshow(Settings.getArtistInfoLayout(), __cwd__, sonosDevice=sonosDevice)
def download_by_selenium(self, url): # TODO: log try: dcap = dict(DesiredCapabilities.PHANTOMJS) user_agent = UserAgent().random dcap["phantomjs.page.settings.userAgent"] = user_agent # dcap["phantomjs.page.settings.resourceTimeout"] = 5000 driver = webdriver.PhantomJS(desired_capabilities = dcap) # driver.implicitly_wait(20) driver.set_page_load_timeout(self.timeout) driver.get(url) time.sleep(self.wait_for_loading) except Exception as e: settings.logg.warning("Error while Downloading url {} \nRetrying...\n".format(url)) # to catch the error if phantomjs shutsdowns unexpectedly try: driver.quit() except: pass if (self.max_tries > 0): self.max_tries -= 1 settings.log("Number of retries left = {}".format(self.max_tries)) self.throttle.wait(url) return self.download_by_selenium(url) else: return -1 soup = BeautifulSoup(driver.page_source, "lxml") driver.quit() return soup
def __init__(self, rawPath, pathList=None, videotitle=None, debug_logging_enabled=True, audioOnly=False): self.debug_logging_enabled = debug_logging_enabled self.forceShuffle = False self.doNotShuffle = False self.audioOnly = audioOnly self.rawPath = rawPath if rawPath in [None, ""]: self.clear() else: # Check for the case where there is a custom path set so we need to use # the custom location rather than the rawPath if Settings.isCustomPathEnabled() and (videotitle not in [None, ""]): customRoot = Settings.getCustomPath() # Make sure that the path passed in has not already been converted if customRoot not in self.rawPath: self.rawPath = os_path_join(customRoot, normalize_string(videotitle)) log("ThemeFiles: Setting custom path to %s" % self.rawPath, self.debug_logging_enabled) if (pathList is not None) and (len(pathList) > 0): self.themeFiles = [] for aPath in pathList: subThemeList = self._generateThemeFilelistWithDirs(aPath) # add these files to the existing list self.themeFiles = self._mergeThemeLists(self.themeFiles, subThemeList) # If we were given a list, then we should shuffle the themes # as we don't always want the first path playing first self.forceShuffle = True else: self.themeFiles = self._generateThemeFilelistWithDirs(self.rawPath) # Check if we need to handle the ordering for video themes if not audioOnly: self.doNotShuffle = self._filterForVideoThemesRule() self.forceShuffle = False
def __init__(self): # Start by getting the database location self.configPath = xbmc.translatePath(__addon__.getAddonInfo('profile')) self.databasefile = os_path_join(self.configPath, "audiobooks_database.db") log("AudioBooksDB: Database file location = %s" % self.databasefile) # Check to make sure the DB has been created self._createDatabase()
def saveState(self): # Do not save the state on DVD Images as # this will be incorrect if not self.shouldStoreProgress(): return if self.extrasDb is None: log("ExtrasItem: Database not enabled") return log("ExtrasItem: Saving state for %s" % self.getFilename()) rowId = -1 # There are some cases where we want to remove the entries from the database # This is the case where the resume point is 0, watched is 0 if (self.resumePoint == 0) and (self.watched == 0): # There are some media files that we can only get the duration from if they have been played # so just make sure we can get the duration again before we blat this entry origDuration = self.duration if (self.totalDuration > 0) and (self.getDuration() < 1): self.duration = origDuration # We currently have a duration and can't calculate it, so just do the update rowId = self.extrasDb.insertOrUpdate(self.getFilename(), self.resumePoint, self.totalDuration, self.getWatched()) else: self.extrasDb.delete(self.getFilename()) self.duration = origDuration else: rowId = self.extrasDb.insertOrUpdate(self.getFilename(), self.resumePoint, self.totalDuration, self.getWatched()) return rowId
def _create_model(self, format_dict, prefix=""): """ generating a sql sequence to create the spefic fields for a table based on a model it should not be called by something else than itself or _create_table """ try: sql = "" for k, v in format_dict.items(): if type(v) != list: # Value is a single element with can extract the model # Complete previous element with comma or not if sql == "": comma = "" else: comma = "," sql = sql + comma + " " # add a prefix when we are actually called recursively if prefix != "": sql = sql + prefix + "_" # add the actual info as upper case sql = sql + k.upper() + " " + self.MODEL_TO_SQLITE[ v.lower()] if k == "id": # We have a primary key sql = sql + " PRIMARY KEY" else: # we have a list so we will add the elements in the list with the prefix given by the current parent element for el in v: sql = sql + self._create_model(el, prefix=k.upper()) return sql except Exception as e: log("Error in " + str(e)) return ""
def getSelection(self, narrowSearch=True): # Generate the URL and get the page search_url = "http://www.kids-in-mind.com/cgi-bin/search/search.pl?q=%s" url = search_url % urllib.quote_plus(self.videoTitle) html = self._getHtmlSource(url) soup = BeautifulSoup(''.join(html)) # findAll('p', {"style": "font-family: Verdana; font-size: 11px; line-height:19px"}) searchResults = soup.findAll('p', {"start": "1"}) searchMatches = [] # Check each of the entries found for entries in searchResults: for link in entries.findAll('a'): # Get the link videoName = self._convertHtmlIntoKodiText(link.string) videoUrl = link['href'] searchMatches.append({"name": videoName, "link": videoUrl}) log("KidsInMindScraper: Initial Search Match: %s {%s}" % (videoName, videoUrl)) # The kids in mind search can often return lots of entries that do not # contain the words in the requested video, so we can try and narrow it down if narrowSearch: searchMatches = self._narrowDownSearch(searchMatches) return searchMatches
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 getSelection(self, narrowSearch=True): # Generate the URL and get the page search_url = "https://www.movieguide.org/?s=%s" url = search_url % urllib.quote_plus(self.videoTitle) html = self._getHtmlSource(url) soup = BeautifulSoup(''.join(html)) searchResults = soup.findAll('h2') searchMatches = [] # Check each of the entries found for entries in searchResults: for link in entries.findAll('a'): # Get the link videoName = self._convertHtmlIntoKodiText(link.string) try: videoName = videoName.encode('ascii', 'ignore') except: pass videoUrl = link['href'] searchMatches.append({"name": videoName, "link": videoUrl}) log("MovieGuideOrgScraper: Initial Search Match: %s {%s}" % (videoName, videoUrl)) # The kids in mind search can often return lots of entries that do not # contain the words in the requested video, so we can try and narrow it down if narrowSearch: searchMatches = self._narrowDownSearch(searchMatches) return searchMatches
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 getImdbId_from_tvdbId(self, tvdbId): # http://thetvdb.com/api/2B8557E0CBF7D720/series/75565/en.xml url = '%s/%s/series/%s/en.xml' % (self.tvdb_url_prefix, self.tvdb_api_key, tvdbId) resp_details = self._makeCall(url) imdbId = None # The response is XML if resp_details not in [None, ""]: try: respData = ET.ElementTree(ET.fromstring(resp_details)) rootElement = respData.getroot() if rootElement not in [None, ""]: if rootElement.tag == 'Data': series = rootElement.findall('Series') # Only want to process anything if there is just a single series if (series not in [None, ""]) and (len(series) > 0): # There should only be one series as we selected by Id selectedSeries = series[0] if selectedSeries not in [None, ""]: imdbIdElem = selectedSeries.find('IMDB_ID') if imdbIdElem not in [None, ""]: imdbId = imdbIdElem.text log("IdLookup: Found IMDB_ID = %s" % imdbId) except: log("IdLookup: Failed to process data %s: %s" % (resp_details, traceback.format_exc())) return imdbId
def _doesThemeExist(self, directory): log("doesThemeExist: Checking directory: %s" % directory) # Check for custom theme directory if Settings.isThemeDirEnabled(): themeDir = os_path_join(directory, Settings.getThemeDirectory()) # Check if this directory exists if not dir_exists(themeDir): workingPath = directory # If the path currently ends in the directory separator # then we need to clear an extra one if (workingPath[-1] == os.sep) or (workingPath[-1] == os.altsep): workingPath = workingPath[:-1] # If not check to see if we have a DVD VOB if (os_path_split(workingPath)[1] == 'VIDEO_TS') or (os_path_split(workingPath)[1] == 'BDMV'): # Check the parent of the DVD Dir themeDir = os_path_split(workingPath)[0] themeDir = os_path_join(themeDir, Settings.getThemeDirectory()) directory = themeDir # check if the directory exists before searching if dir_exists(directory): # Generate the regex themeFileRegEx = Settings.getThemeFileRegEx(audioOnly=True) dirs, files = list_dir(directory) for aFile in files: m = re.search(themeFileRegEx, aFile, re.IGNORECASE) if m: log("doesThemeExist: Found match: " + aFile) return True return False
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 __init__(self): # Start by getting the database location self.configPath = xbmc.translatePath(ADDON.getAddonInfo('profile')) self.databasefile = os_path_join(self.configPath, "extras_database.db") log("ExtrasDB: Database file location = %s" % self.databasefile) # Make sure that the database exists if this is the first time self.createDatabase()
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 _getListItem(self, title, startTime=-1, chapterTitle=''): try: log("AudioBookHandler: Getting listitem for %s (Chapter: %s)" % (title, chapterTitle)) except: pass listitem = xbmcgui.ListItem() # Set the display title on the music player # Have to set this as video otherwise it will not start the audiobook at the correct Offset place listitem.setInfo('video', {'Title': title}) if chapterTitle not in [None, ""]: listitem.setInfo('music', {'album': chapterTitle}) # If both the Icon and Thumbnail is set, the list screen will choose to show # the thumbnail coverImage = self.getCoverImage() if coverImage in [None, ""]: coverImage = ADDON.getAddonInfo('icon') listitem.setIconImage(coverImage) listitem.setThumbnailImage(coverImage) # Record if the video should start playing part-way through startPoint = startTime if startTime < 0: startPoint = self.getPosition() if startPoint > 0: listitem.setProperty('StartOffset', str(startPoint)) # Stop the Lyrics addon trying to get lyrics for audiobooks listitem.setProperty('do_not_analyze', 'true') return listitem
def __init__(self): # Start by getting the database location self.configPath = xbmc.translatePath(ADDON.getAddonInfo('profile')) self.databasefile = os_path_join(self.configPath, "pinsentry_database.db") log("PinSentryDB: Database file location = %s" % self.databasefile) # Check to make sure the DB has been created self._createDatabase()
def _getMainCoverLocation(self): coverFileName, oldExt = os.path.splitext(self.fileName) targetCoverName = "%s.jpg" % coverFileName coverTargetName = os_path_join(Settings.getCoverCacheLocation(), targetCoverName) log("AudioBookHandler: Cached cover target location is %s" % coverTargetName) return coverTargetName
def _getThemesToUpload(self, id, themes): themeList = [] for theme in themes: maxFileSize = 104857600 if Settings.isVideoFile(theme): # Check if all videos are disabled if not self.isVideoEnabled: continue else: # Check if all audio are disabled if not self.isAudioEnabled: continue # Audio files have a smaller limit maxFileSize = 20971520 # Check to make sure the theme file is not too large, anything over 100 megabytes # is too large for a theme stat = xbmcvfs.Stat(theme) themeFileSize = stat.st_size() if themeFileSize > maxFileSize: log("UploadThemes: Theme %s too large %s" % (theme, themeFileSize)) continue if themeFileSize < 19460: log("UploadThemes: Theme %s too small %s" % (theme, themeFileSize)) continue # If we reach here it is not in either exclude list themeList.append(theme) return themeList
def getPlayList(self, startTime=-1, startChapter=0): log("FolderHandler: Getting playlist to start for time %d" % startTime) # Wrap the audiobook up in a playlist playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC) playlist.clear() # Add each chapter file idx = 0 startPosition = 0 if startTime > 0: startPosition = startTime # Start on the correct chapter if startChapter > 1: idx = startChapter - 1 while idx < len(self.getChapterDetails()): chapterDetail = self.chapters[idx] listitem = self._getListItem(self.getTitle(), startPosition, chapterDetail['title']) playlist.add(self.chapterFiles[idx], listitem) # Once we set the correct starting position for the main chapter, reset it # that that the next chapters start at the beginning startPosition = 0 idx += 1 return playlist
def performAction(self, actionType, itemId, title): try: if (actionType == ActionManager.ACTION_PLAY) or (actionType == ActionManager.ACTION_PLAY_NOW): self.performPlay(itemId) elif actionType == ActionManager.ACTION_ADD_TO_QUEUE: self.performAddToQueue(itemId) elif actionType == ActionManager.ACTION_REPLACE_QUEUE: self.performReplaceQueue(itemId) # Operations for the QueueIcon View elif actionType == ActionManager.ACTION_QUEUE_PLAY_ITEM: self.playQueueItem(int(itemId)) elif actionType == ActionManager.ACTION_QUEUE_REMOVE_ITEM: self.removeQueueItem(int(itemId)) elif actionType == ActionManager.ACTION_QUEUE_CLEAR: self.clearQueueItem(int(itemId)) # Radio Operations elif actionType == ActionManager.ACTION_RADIO_PLAY: self.performPlayURI(itemId, title) # Speech Operations elif actionType == ActionManager.ACTION_SPEECH_SAY_PHRASE: self.sayPhrase(itemId) elif actionType == ActionManager.ACTION_SPEECH_REMOVE_PHRASE: self.removePhrase(itemId) elif actionType == ActionManager.ACTION_UPDATE_LIBRARY: self.updateLibrary() else: # This should never be shown, so no need to translate, enabled for debug xbmcgui.Dialog().ok( __addon__.getLocalizedString(32068), "Operation %s not currently supported" % actionType ) except: log("SonosPlugin: %s" % traceback.format_exc(), xbmc.LOGERROR) xbmcgui.Dialog().ok(__addon__.getLocalizedString(32068), __addon__.getLocalizedString(32073) % actionType)
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 getLibraryTracks(self): json_query = xbmc.executeJSONRPC( '{"jsonrpc": "2.0", "method": "AudioLibrary.GetSongs", "params": {"properties": ["albumartist", "musicbrainztrackid", "rating"%s] }, "id": "libSongs"}' % self.additionalTrackValues) json_response = json.loads(json_query) libraryTracks = [] if ("result" in json_response) and ('songs' in json_response['result']): libraryTracks = json_response['result']['songs'] # Check for the case where there is no artist, but there is an album artist for track in libraryTracks: if ('artist' not in track) or (len(track['artist']) < 1) or ( len(''.join(track['artist'])) < 1): if ('albumartist' in track) and (len(track['albumartist']) > 0): track['artist'] = track['albumartist'] # We don't want the album artist left in the data if ('albumartist' in track): del track['albumartist'] # If we are running on a version earlier than v17, populate the rating if (self.kodiMajorVersion < 17) and ( 'userrating' not in track) and ('rating' in track): track['userrating'] = int(track['rating'] * self.ratingDivisor) log("MusicLibrary: Retrieved a total of %d tracks" % len(libraryTracks)) return libraryTracks
def _getMetadata(self, meta): if meta in [None, ""]: return {} done = False metaDict = {} tag = POINTER(AVDictionaryEntry)() while not done: try: tag = self.av_dict_get(meta, ''.encode('ascii'), tag, 2) except: log( "FFMpegLib: Failed to get metadata with error: %s" % traceback.format_exc(), xbmc.LOGERROR) tag = None if tag: log("FFMpegLib: Found key %s" % str(tag.contents.key)) # make sure all the keys are lower case metaDict[tag.contents.key.lower()] = tag.contents.value else: done = True # return: a dict with key, value = metadata key, metadata value return metaDict
def main(): count = 1 all_lines = read_file(filename) p = r"(([0-9]{0,}[a-zA-Z\_\-\!\.\:]+[0-9]{0,}\s?)+[;\s]?)+" res_com = re.compile(p) for each_line in all_lines: result = res_com.search(each_line) if result: movie_names = result.group() movie_name_list = [i.strip() for i in movie_names.split(";") if i.strip()] else: log("not find movie name") movie_name_list = [] chinese_name = re.split(r"\s\d+", each_line.strip())[0].strip().strip() record2 = get_movie_id("movie_trailer_movie_name_zh", chinese_name) for name in movie_name_list: record1 = get_movie_id("movie_trailer_movie_name", name) flag = True if record1 or record2: flag = has_url(record1, "movie_trailer_movie_info", flag) if flag: flag = has_url(record2, "movie_trailer_movie_info_zh", flag) if flag: if chinese_name: print chinese_name record_download_name(not_found_name, name) else: if chinese_name: print chinese_name record_download_name(not_found_name, name) del chinese_name del name
def restoreVolume(self): try: # Don't change the volume unless requested to if self.screensaverVolume > -1: self._setVolume(self.original_volume) except: log("VolumeDrop: %s" % traceback.format_exc(), xbmc.LOGERROR)
def getDisabledVideos(self): disabledVideos = [] # Check if the disabled videos file exists if not xbmcvfs.exists(self.disabledVideosFile): log("CollectSets: No disabled videos file exists") return disabledVideos try: # Load the file as a string disabledVideosFileRef = xbmcvfs.File(self.disabledVideosFile, 'r') disabledVideosStr = disabledVideosFileRef.read() disabledVideosFileRef.close() disabledVideosElem = ET.ElementTree(ET.fromstring(disabledVideosStr)) # Expected XML format: # <disabled_screensaver> # <filename></filename> # </disabled_screensaver> # Get the videos that are in the disabled list for filenameItem in disabledVideosElem.getroot().findall('filename'): disabledFile = filenameItem.text log("CollectSets: Disabled video file: %s" % disabledFile) disabledVideos.append(disabledFile) except: log("CollectSets: Failed to read collection file %s" % self.disabledVideosFile, xbmc.LOGERROR) log("CollectSets: %s" % traceback.format_exc(), xbmc.LOGERROR) log("CollectSets: Number of disabled videos is %d" % len(disabledVideos)) return disabledVideos
def save_currencies(cls): try: file = open(settings.DataDir("currencies.key"), "wb") # write mode file.write(pickle.dumps(ExchangeRate.currencies, 2)) file.close() except: settings.log("Could not save currency data. Will try again on restart")