def __init__(self): self.networkList = [] self.studioList = [] self.mixedGenreList = [] self.showGenreList = [] self.movieGenreList = [] self.showList = [] self.channels = [] self.videoParser = VideoParser() self.sleepTime = 0 self.threadPaused = False self.runningActionChannel = 0 self.runningActionId = 0 self.enteredChannelCount = 0 self.background = True random.seed()
def getThemePlaylist(self): # Take the list of files and create a playlist from them # Needs to be a Music playlist otherwise repeat will not work # via the JSON interface playlist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC) playlist.clear() for aFile in self.themeFiles: # Add the theme file to a playlist playlist.add(url=aFile) # Check if we have more than one item in the playlist if playlist.size() > 1: playlist.shuffle() # Check if we are only supposed to play one theme when there are multiple # available if Settings.onlyPlaySingleTheme(): firstTheme = playlist[0].getfilename() playlist.clear() playlist.add(url=firstTheme) # Now we have the playlist, and it has been shuffled if needed # Check if we need to have a random start time for the first track # Note: The following method (rather than seek) should prevent # the seek dialog being displayed on the screen and also prevent # the need to start the theme playing before changing the start point if Settings.isRandomStart() and playlist.size() > 0: filename = playlist[0].getfilename() duration = int(playlist[0].getduration()) log( "MusicThemeFiles: Duration is %d for file %s" % (duration, filename), self.debug_logging_enabled) # Check if the duration of the file is showing as Zero, this means we need to # try and find out the duration ourself if duration < 1: duration = VideoParser().getVideoLength(filename) log( "MusicThemeFiles: Manual find of duration is %d for file %s" % (duration, filename), self.debug_logging_enabled) if duration > 10: listitem = xbmcgui.ListItem() # Check if there is a fixed start position randomStart = Settings.getRandomFixedOffset(filename) if (randomStart < 1) or (randomStart >= duration): # Record if the theme should start playing part-way through randomStart = random.randint(0, int(duration * 0.75)) listitem.setProperty('StartOffset', str(randomStart)) log( "MusicThemeFiles: Setting Random start of %d for %s" % (randomStart, filename), self.debug_logging_enabled) # 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 getDuration(self): if self.duration is None: try: # Parse the video file for the duration self.duration = VideoParser().getVideoLength(self.filename) log("BaseExtrasItem: Duration retrieved is = %d" % self.duration) except: log("BaseExtrasItem: Failed to get duration from %s" % self.filename) log("BaseExtrasItem: %s" % traceback.format_exc()) self.duration = 0 return self.duration
def __init__(self): self.networkList = [] self.studioList = [] self.mixedGenreList = [] self.showGenreList = [] self.movieGenreList = [] self.showList = [] self.videoParser = VideoParser() self.fileLock = FileLock() self.httpJSON = True self.sleepTime = 0 self.exitThread = False self.discoveredWebServer = False
def _getVideoDuration(self, filename): duration = 0 try: # Parse the video file for the duration duration = VideoParser().getVideoLength(filename) except: log("Failed to get duration from %s" % filename, xbmc.LOGERROR) log("Error: %s" % traceback.format_exc(), xbmc.LOGERROR) duration = 0 log("Duration retrieved is = %d" % duration) return duration
class ChannelList: def __init__(self): self.networkList = [] self.studioList = [] self.mixedGenreList = [] self.showGenreList = [] self.movieGenreList = [] self.showList = [] self.channels = [] self.videoParser = VideoParser() self.sleepTime = 0 self.threadPaused = False self.runningActionChannel = 0 self.runningActionId = 0 self.enteredChannelCount = 0 self.background = True random.seed() def readConfig(self): self.channelResetSetting = int(ADDON.getSetting("ChannelResetSetting")) self.log('Channel Reset Setting is ' + str(self.channelResetSetting)) self.forceReset = ADDON.getSetting('ForceChannelReset') == "true" self.log('Force Reset is ' + str(self.forceReset)) self.updateDialog = xbmcgui.DialogProgress() self.startMode = int(ADDON.getSetting("StartMode")) self.log('Start Mode is ' + str(self.startMode)) self.backgroundUpdating = int(ADDON.getSetting("ThreadMode")) self.mediaLimit = MEDIA_LIMIT[int(ADDON.getSetting("MediaLimit"))] self.YearEpInfo = ADDON.getSetting('HideYearEpInfo') self.findMaxChannels() if self.forceReset: ADDON.setSetting('ForceChannelReset', "False") self.forceReset = False try: self.lastResetTime = int( ADDON_SETTINGS.getSetting("LastResetTime")) except: self.lastResetTime = 0 try: self.lastExitTime = int(ADDON_SETTINGS.getSetting("LastExitTime")) except: self.lastExitTime = int(time.time()) def setupList(self): self.readConfig() self.updateDialog.create(ADDON_NAME, "Updating channel list") self.updateDialog.update(0, "Updating channel list") self.updateDialogProgress = 0 foundvalid = False makenewlists = False self.background = False if self.backgroundUpdating > 0 and self.myOverlay.isMaster == True: makenewlists = True # Go through all channels, create their arrays, and setup the new playlist for i in range(self.maxChannels): self.updateDialogProgress = i * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(i + 1), "waiting for file lock") self.channels.append(Channel()) # If the user pressed cancel, stop everything and exit if self.updateDialog.iscanceled(): self.log('Update channels cancelled') self.updateDialog.close() return None self.setupChannel(i + 1, False, makenewlists, False) if self.channels[i].isValid: foundvalid = True if makenewlists == True: ADDON.setSetting('ForceChannelReset', 'false') if foundvalid == False and makenewlists == False: for i in range(self.maxChannels): self.updateDialogProgress = i * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(i + 1), "waiting for file lock", '') self.setupChannel(i + 1, False, True, False) if self.channels[i].isValid: foundvalid = True break self.updateDialog.update(100, "Update complete") self.updateDialog.close() return self.channels def log(self, msg, level=xbmc.LOGDEBUG): log('ChannelList: ' + msg, level) # Determine the maximum number of channels by opening consecutive # playlists until we don't find one def findMaxChannels(self): self.log('findMaxChannels') self.maxChannels = 0 self.enteredChannelCount = 0 for i in range(999): chtype = 9999 chsetting1 = '' chsetting2 = '' try: chtype = int( ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_2') except: pass if chtype == 0: if FileAccess.exists(xbmc.translatePath(chsetting1)): self.maxChannels = i + 1 self.enteredChannelCount += 1 elif chtype < 8: if len(chsetting1) > 0: self.maxChannels = i + 1 self.enteredChannelCount += 1 if self.forceReset and (chtype != 9999): ADDON_SETTINGS.setSetting('Channel_' + str(i + 1) + '_changed', "True") self.log('findMaxChannels return ' + str(self.maxChannels)) def sendJSON(self, command): data = xbmc.executeJSONRPC(command) return unicode(data, 'utf-8', errors='ignore') def setupChannel(self, channel, background=False, makenewlist=False, append=False): self.log('setupChannel ' + str(channel)) returnval = False createlist = makenewlist chtype = 9999 chsetting1 = '' chsetting2 = '' needsreset = False self.background = background self.settingChannel = channel try: chtype = int( ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_2') except: pass while len(self.channels) < channel: self.channels.append(Channel()) if chtype == 9999: self.channels[channel - 1].isValid = False return False self.channels[channel - 1].isSetup = True self.channels[channel - 1].loadRules(channel) self.runActions(RULES_ACTION_START, channel, self.channels[channel - 1]) try: needsreset = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_changed') == 'True' if needsreset: self.channels[channel - 1].isSetup = False except: pass # If possible, use an existing playlist # Don't do this if we're appending an existing channel # Don't load if we need to reset anyway if FileAccess.exists(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u' ) and append == False and needsreset == False: try: self.channels[channel - 1].totalTimePlayed = int( ADDON_SETTINGS.getSetting( 'Channel_' + str(channel) + '_time', True)) createlist = True if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(channel), "reading playlist", '') if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: self.channels[channel - 1].isValid = True self.channels[ channel - 1].fileName = CHANNELS_LOC + 'channel_' + str( channel) + '.m3u' returnval = True # If this channel has been watched for longer than it lasts, reset the channel if self.channelResetSetting == 0 and self.channels[ channel - 1].totalTimePlayed < self.channels[ channel - 1].getTotalDuration(): createlist = False if self.channelResetSetting > 0 and self.channelResetSetting < 4: timedif = time.time() - self.lastResetTime if self.channelResetSetting == 1 and timedif < ( 60 * 60 * 24): createlist = False if self.channelResetSetting == 2 and timedif < ( 60 * 60 * 24 * 7): createlist = False if self.channelResetSetting == 3 and timedif < ( 60 * 60 * 24 * 30): createlist = False if timedif < 0: createlist = False if self.channelResetSetting == 4: createlist = False except: pass if createlist or needsreset: self.channels[channel - 1].isValid = False if makenewlist: try: os.remove(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') except: pass append = False if createlist: ADDON_SETTINGS.setSetting('LastResetTime', str(int(time.time()))) if append == False: if chtype == 6 and chsetting2 == str(MODE_ORDERAIRDATE): self.channels[channel - 1].mode = MODE_ORDERAIRDATE # if there is no start mode in the channel mode flags, set it to the default if self.channels[channel - 1].mode & MODE_STARTMODES == 0: if self.startMode == 0: self.channels[channel - 1].mode |= MODE_RESUME elif self.startMode == 1: self.channels[channel - 1].mode |= MODE_REALTIME elif self.startMode == 2: self.channels[channel - 1].mode |= MODE_RANDOM if ((createlist or needsreset) and makenewlist) or append: if self.background == False: self.updateDialogProgress = ( channel - 1) * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(channel), "adding videos", '') if self.makeChannelList(channel, chtype, chsetting1, chsetting2, append) == True: if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: returnval = True self.channels[ channel - 1].fileName = CHANNELS_LOC + 'channel_' + str( channel) + '.m3u' self.channels[channel - 1].isValid = True # Don't reset variables on an appending channel if append == False: self.channels[channel - 1].totalTimePlayed = 0 ADDON_SETTINGS.setSetting( 'Channel_' + str(channel) + '_time', '0') if needsreset: ADDON_SETTINGS.setSetting( 'Channel_' + str(channel) + '_changed', 'False') self.channels[channel - 1].isSetup = True self.runActions(RULES_ACTION_BEFORE_CLEAR, channel, self.channels[channel - 1]) # Don't clear history when appending channels if self.background == False and append == False and self.myOverlay.isMaster: self.updateDialogProgress = (channel - 1) * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(channel), "clearing history", '') self.clearPlaylistHistory(channel) if append == False: self.runActions(RULES_ACTION_BEFORE_TIME, channel, self.channels[channel - 1]) if self.channels[channel - 1].mode & MODE_ALWAYSPAUSE > 0: self.channels[channel - 1].isPaused = True if self.channels[channel - 1].mode & MODE_RANDOM > 0: self.channels[channel - 1].showTimeOffset = random.randint( 0, self.channels[channel - 1].getTotalDuration()) if self.channels[channel - 1].mode & MODE_REALTIME > 0: timedif = int(self.myOverlay.timeStarted) - self.lastExitTime self.channels[channel - 1].totalTimePlayed += timedif if self.channels[channel - 1].mode & MODE_RESUME > 0: self.channels[channel - 1].showTimeOffset = self.channels[ channel - 1].totalTimePlayed self.channels[channel - 1].totalTimePlayed = 0 while self.channels[channel - 1].showTimeOffset > self.channels[ channel - 1].getCurrentDuration(): self.channels[channel - 1].showTimeOffset -= self.channels[ channel - 1].getCurrentDuration() self.channels[channel - 1].addShowPosition(1) self.channels[channel - 1].name = self.getChannelName( chtype, chsetting1) if ((createlist or needsreset) and makenewlist) and returnval: self.runActions(RULES_ACTION_FINAL_MADE, channel, self.channels[channel - 1]) else: self.runActions(RULES_ACTION_FINAL_LOADED, channel, self.channels[channel - 1]) return returnval def clearPlaylistHistory(self, channel): self.log("clearPlaylistHistory") if self.channels[channel - 1].isValid == False: self.log("channel not valid, ignoring") return # if we actually need to clear anything if self.channels[channel - 1].totalTimePlayed > (60 * 60 * 24 * 2): try: fle = FileAccess.open( CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', 'w') except: self.log( "clearPlaylistHistory Unable to open the smart playlist", xbmc.LOGERROR) return flewrite = uni("#EXTM3U\n") tottime = 0 timeremoved = 0 for i in range(self.channels[channel - 1].Playlist.size()): tottime += self.channels[channel - 1].getItemDuration(i) if tottime > (self.channels[channel - 1].totalTimePlayed - (60 * 60 * 12)): tmpstr = str( self.channels[channel - 1].getItemDuration(i)) + ',' tmpstr += self.channels[channel - 1].getItemTitle( i) + "//" + self.channels[ channel - 1].getItemEpisodeTitle(i) + "//" + self.channels[ channel - 1].getItemDescription(i) tmpstr = uni(tmpstr[:2036]) tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace( "\\\"", "\"") tmpstr = uni(tmpstr) + uni('\n') + uni( self.channels[channel - 1].getItemFilename(i)) flewrite += uni("#EXTINF:") + uni(tmpstr) + uni("\n") else: timeremoved = tottime fle.write(flewrite) fle.close() if timeremoved > 0: if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == False: self.channels[channel - 1].isValid = False else: self.channels[channel - 1].totalTimePlayed -= timeremoved # Write this now so anything sharing the playlists will get the proper info ADDON_SETTINGS.setSetting( 'Channel_' + str(channel) + '_time', str(self.channels[channel - 1].totalTimePlayed)) def getChannelName(self, chtype, setting1): self.log('getChannelName ' + str(chtype)) if len(setting1) == 0: return '' if chtype == 0: return self.getSmartPlaylistName(setting1) elif chtype == 1 or chtype == 2 or chtype == 5 or chtype == 6: return setting1 elif chtype == 3: return setting1 + " TV" elif chtype == 4: return setting1 + " Movies" elif chtype == 7: if setting1[-1] == '/' or setting1[-1] == '\\': return os.path.split(setting1[:-1])[1] else: return os.path.split(setting1)[1] return '' # Open the smart playlist and read the name out of it...this is the channel name def getSmartPlaylistName(self, fle): self.log('getSmartPlaylistName') fle = xbmc.translatePath(fle) try: xml = FileAccess.open(fle, "r") except: self.log( "getSmartPlaylisyName Unable to open the smart playlist " + fle, xbmc.LOGERROR) return '' try: dom = parse(xml) except: self.log('getSmartPlaylistName Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return '' xml.close() try: plname = dom.getElementsByTagName('name') self.log('getSmartPlaylistName return ' + plname[0].childNodes[0].nodeValue) return plname[0].childNodes[0].nodeValue except: self.log("Unable to get the playlist name.", xbmc.LOGERROR) return '' # Based on a smart playlist, create a normal playlist that can actually be used by us def makeChannelList(self, channel, chtype, setting1, setting2, append=False): self.log('makeChannelList ' + str(channel)) israndom = False fileList = [] if chtype == 7: fileList = self.createDirectoryPlaylist(setting1) israndom = True else: if chtype == 0: if FileAccess.copy(setting1, MADE_CHAN_LOC + os.path.split(setting1)[1]) == False: if FileAccess.exists(MADE_CHAN_LOC + os.path.split(setting1)[1]) == False: self.log("Unable to copy or find playlist " + setting1) return False fle = MADE_CHAN_LOC + os.path.split(setting1)[1] else: fle = self.makeTypePlaylist(chtype, setting1, setting2) fle = uni(fle) if len(fle) == 0: self.log( 'Unable to locate the playlist for channel ' + str(channel), xbmc.LOGERROR) return False try: xml = FileAccess.open(fle, "r") except: self.log( "makeChannelList Unable to open the smart playlist " + fle, xbmc.LOGERROR) return False try: dom = parse(xml) except: self.log('makeChannelList Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return False xml.close() if self.getSmartPlaylistType(dom) == 'mixed': fileList = self.buildMixedFileList(dom, channel) else: fileList = self.buildFileList(fle, channel) try: order = dom.getElementsByTagName('order') if order[0].childNodes[0].nodeValue.lower() == 'random': israndom = True except: pass try: if append == True: channelplaylist = FileAccess.open( CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "r") channelplaylist.seek(0, 2) channelplaylist.close() else: channelplaylist = FileAccess.open( CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "w") except: self.log( 'Unable to open the cache file ' + CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', xbmc.LOGERROR) return False if append == False: channelplaylist.write(uni("#EXTM3U\n")) if israndom: random.shuffle(fileList) if len(fileList) > 16384: fileList = fileList[:16384] fileList = self.runActions(RULES_ACTION_LIST, channel, fileList) self.channels[channel - 1].isRandom = israndom if append: if len(fileList) + self.channels[channel - 1].Playlist.size() > 16384: fileList = fileList[:( 16384 - self.channels[channel - 1].Playlist.size())] else: if len(fileList) > 16384: fileList = fileList[:16384] # Write each entry into the new playlist for string in fileList: channelplaylist.write(uni("#EXTINF:") + uni(string) + uni("\n")) channelplaylist.close() self.log('makeChannelList return') return True def makeTypePlaylist(self, chtype, setting1, setting2): if chtype == 1: if len(self.networkList) == 0: self.fillTVInfo() return self.createNetworkPlaylist(setting1) elif chtype == 2: if len(self.studioList) == 0: self.fillMovieInfo() return self.createStudioPlaylist(setting1) elif chtype == 3: if len(self.showGenreList) == 0: self.fillTVInfo() return self.createGenrePlaylist('episodes', chtype, setting1) elif chtype == 4: if len(self.movieGenreList) == 0: self.fillMovieInfo() return self.createGenrePlaylist('movies', chtype, setting1) elif chtype == 5: if len(self.mixedGenreList) == 0: if len(self.showGenreList) == 0: self.fillTVInfo() if len(self.movieGenreList) == 0: self.fillMovieInfo() self.mixedGenreList = self.makeMixedList( self.showGenreList, self.movieGenreList) self.mixedGenreList.sort(key=lambda x: x.lower()) return self.createGenreMixedPlaylist(setting1) elif chtype == 6: if len(self.showList) == 0: self.fillTVInfo() return self.createShowPlaylist(setting1, setting2) self.log('makeTypePlaylists invalid channel type: ' + str(chtype)) return '' def createNetworkPlaylist(self, network): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Network_' + network + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "episodes", self.getChannelName(1, network)) network = network.lower() added = False fle.write(' <rule field="tvshow" operator="is">\n') for i in range(len(self.showList)): if self.showList[i][1].lower() == network: theshow = self.cleanString(self.showList[i][0]) fle.write(' <value>' + theshow + '</value>\n') added = True fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() if added == False: return '' return flename def createShowPlaylist(self, show, setting2): order = 'random' try: setting = int(setting2) if setting & MODE_ORDERAIRDATE > 0: order = 'episode' except: pass flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Show_' + show + '_' + order + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, 'episodes', self.getChannelName(6, show)) show = self.cleanString(show) fle.write(' <rule field="tvshow" operator="is">\n') fle.write(' <value>' + show + '</value>\n') fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, order) fle.close() return flename def createGenreMixedPlaylist(self, genre): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Mixed_' + genre + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' epname = os.path.basename( self.createGenrePlaylist('episodes', 3, genre)) moname = os.path.basename(self.createGenrePlaylist('movies', 4, genre)) self.writeXSPHeader(fle, 'mixed', self.getChannelName(5, genre)) fle.write(' <rule field="playlist" operator="is">' + epname + '</rule>\n') fle.write(' <rule field="playlist" operator="is">' + moname + '</rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createGenrePlaylist(self, pltype, chtype, genre): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + pltype + '_' + genre + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, pltype, self.getChannelName(chtype, genre)) genre = self.cleanString(genre) fle.write(' <rule field="genre" operator="is">\n') fle.write(' <value>' + genre + '</value>\n') fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createStudioPlaylist(self, studio): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Studio_' + studio + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "movies", self.getChannelName(2, studio)) studio = self.cleanString(studio) fle.write(' <rule field="studio" operator="is">\n') fle.write(' <value>' + studio + '</value>\n') fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createDirectoryPlaylist(self, setting1): self.log("createDirectoryPlaylist " + setting1) fileList = [] filecount = 0 def listdir_fullpath(dir): return [uni(os.path.join(dir, f)) for f in xbmcvfs.listdir(dir)[1]] if self.background == False: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "getting file list") file_detail = listdir_fullpath(setting1) for f in file_detail: if self.threadPause() == False: del fileList[:] break duration = self.videoParser.getVideoLength(f) if duration > 0: filecount += 1 if self.background == False: if filecount == 1: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entry") else: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entries") afile = os.path.basename(f) afile, ext = os.path.splitext(afile) tmpstr = str(duration) + ',' tmpstr += afile + "//" + "//" + xbmc.getLocalizedString( 21801) + ': ' + setting1 + "\n" tmpstr += setting1 + os.path.basename(f) tmpstr = tmpstr[:2036] fileList.append(tmpstr) if filecount == 0: self.log('Unable to access Videos files in ' + setting1) return fileList def writeXSPHeader(self, fle, pltype, plname): fle.write('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n') fle.write('<smartplaylist type="' + pltype + '">\n') plname = self.cleanString(plname) fle.write(' <name>' + plname + '</name>\n') fle.write(' <match>one</match>\n') def writeXSPFooter(self, fle, limit, order): if self.mediaLimit > 0: fle.write(' <limit>' + str(self.mediaLimit) + '</limit>\n') fle.write(' <order direction="ascending">' + order + '</order>\n') fle.write('</smartplaylist>\n') def cleanString(self, string): newstr = uni(string) newstr = newstr.replace('&', '&') newstr = newstr.replace('>', '>') newstr = newstr.replace('<', '<') return uni(newstr) def fillTVInfo(self, sortbycount=False): self.log("fillTVInfo") json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": {"properties":["studio", "genre"]}, "id": 1}' if self.background == False: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "reading TV data") json_folder_detail = self.sendJSON(json_query) detail = re.compile("{(.*?)}", re.DOTALL).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return match = re.search('"studio" *: *\[(.*?)\]', f) network = '' if match: network = (match.group(1).split(','))[0] network = network.strip('"').strip() found = False for item in range(len(self.networkList)): if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return itm = self.networkList[item] if sortbycount: itm = itm[0] if itm.lower() == network.lower(): found = True if sortbycount: self.networkList[item][1] += 1 break if found == False and len(network) > 0: if sortbycount: self.networkList.append([network, 1]) else: self.networkList.append(network) match = re.search('"label" *: *"(.*?)",', f) if match: show = match.group(1).strip() self.showList.append([show, network]) match = re.search('"genre" *: *\[(.*?)\]', f) if match: genres = match.group(1).split(',') for genre in genres: found = False curgenre = genre.lower().strip('"').strip() for g in range(len(self.showGenreList)): if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return itm = self.showGenreList[g] if sortbycount: itm = itm[0] if curgenre == itm.lower(): found = True if sortbycount: self.showGenreList[g][1] += 1 break if found == False: if sortbycount: self.showGenreList.append( [genre.strip('"').strip(), 1]) else: self.showGenreList.append(genre.strip('"').strip()) if sortbycount: self.networkList.sort(key=lambda x: x[1], reverse=True) self.showGenreList.sort(key=lambda x: x[1], reverse=True) else: self.networkList.sort(key=lambda x: x.lower()) self.showGenreList.sort(key=lambda x: x.lower()) if (len(self.showList) == 0) and (len( self.showGenreList) == 0) and (len(self.networkList) == 0): self.log(json_folder_detail) self.log("found shows " + str(self.showList)) self.log("found genres " + str(self.showGenreList)) self.log("fillTVInfo return " + str(self.networkList)) def fillMovieInfo(self, sortbycount=False): self.log("fillMovieInfo") studioList = [] json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": {"properties":["studio", "genre"]}, "id": 1}' if self.background == False: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "reading movie data") json_folder_detail = self.sendJSON(json_query) detail = re.compile("{(.*?)}", re.DOTALL).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.movieGenreList[:] del self.studioList[:] del studioList[:] break match = re.search('"genre" *: *\[(.*?)\]', f) if match: genres = match.group(1).split(',') for genre in genres: found = False curgenre = genre.lower().strip('"').strip() for g in range(len(self.movieGenreList)): itm = self.movieGenreList[g] if sortbycount: itm = itm[0] if curgenre == itm.lower(): found = True if sortbycount: self.movieGenreList[g][1] += 1 break if found == False: if sortbycount: self.movieGenreList.append( [genre.strip('"').strip(), 1]) else: self.movieGenreList.append( genre.strip('"').strip()) match = re.search('"studio" *: *\[(.*?)\]', f) if match: studios = match.group(1).split(',') for studio in studios: curstudio = studio.strip('"').strip() found = False for i in range(len(studioList)): if studioList[i][0].lower() == curstudio.lower(): studioList[i][1] += 1 found = True break if found == False and len(curstudio) > 0: studioList.append([curstudio, 1]) maxcount = 0 for i in range(len(studioList)): if studioList[i][1] > maxcount: maxcount = studioList[i][1] bestmatch = 1 lastmatch = 1000 counteditems = 0 for i in range(maxcount, 0, -1): itemcount = 0 for j in range(len(studioList)): if studioList[j][1] == i: itemcount += 1 if abs(itemcount + counteditems - 8) < abs(lastmatch - 8): bestmatch = i lastmatch = itemcount counteditems += itemcount if sortbycount: studioList.sort(key=lambda x: x[1], reverse=True) self.movieGenreList.sort(key=lambda x: x[1], reverse=True) else: studioList.sort(key=lambda x: x[0].lower()) self.movieGenreList.sort(key=lambda x: x.lower()) for i in range(len(studioList)): if studioList[i][1] >= bestmatch: if sortbycount: self.studioList.append( [studioList[i][0], studioList[i][1]]) else: self.studioList.append(studioList[i][0]) if (len(self.movieGenreList) == 0) and (len(self.studioList) == 0): self.log(json_folder_detail) self.log("found genres " + str(self.movieGenreList)) self.log("fillMovieInfo return " + str(self.studioList)) def makeMixedList(self, list1, list2): self.log("makeMixedList") newlist = [] for item in list1: curitem = item.lower() for a in list2: if curitem == a.lower(): newlist.append(item) break self.log("makeMixedList return " + str(newlist)) return newlist def buildFileList(self, dir_name, channel): self.log("buildFileList") fileList = [] seasoneplist = [] filecount = 0 json_query = '{"jsonrpc": "2.0", "method": "Files.GetDirectory", "params": {"directory": "%s", "media": "video", "properties":["duration","runtime","showtitle","plot","plotoutline","season","episode","year","playcount"]}, "id": 1}' % ( self.escapeDirJSON(dir_name)) if self.background == False: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "querying database") json_folder_detail = self.sendJSON(json_query) file_detail = re.compile("{(.*?)}", re.DOTALL).findall(json_folder_detail) for f in file_detail: if self.threadPause() == False: del fileList[:] break f = uni(f) match = re.search('"file" *: *"(.*?)",', f) if match: if (match.group(1).endswith("/") or match.group(1).endswith("\\")): fileList.extend(self.buildFileList(match.group(1), channel)) else: f = self.runActions(RULES_ACTION_JSON, channel, f) duration = re.search('"duration" *: *([0-9]*?),', f) try: dur = int(duration.group(1)) except: dur = 0 if dur == 0: duration = re.search('"runtime" *: *([0-9]*?),', f) try: dur = int(duration.group(1)) except: dur = 0 if dur == 0: try: dur = self.videoParser.getVideoLength( uni(match.group(1)).replace("\\\\", "\\")) except: dur = 0 try: if dur > 0: filecount += 1 if self.background == False: if filecount == 1: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entry") else: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entries") tmpstr = str(dur) + ',' title = re.search('"label" *: *"(.*?)"', f) showtitle = re.search('"showtitle" *: *"(.*?)"', f) plot = re.search('"plot" *: *"(.*?)","', f) plotoutline = re.search( '"plotoutline" *: *"(.*?)","', f) if len(plotoutline.group(1)) > 0: theplot = plotoutline.group(1) elif len(plotoutline.group(1)) == 0 and len( plot.group(1)) > 0: theplot = plot.group(1) else: theplot = LANGUAGE(30023) # This is a TV show if showtitle != None and len( showtitle.group(1)) > 0: swtitle = title.group(1) param, swtitle = swtitle.split(". ", 1) swtitle = ('"{}"'.format(swtitle)) season = re.search('"season" *: *(.*?),', f) episode = re.search('"episode" *: *(.*?),', f) seasonval = season.group(1) epval = episode.group(1) sxexx = (' ({})'.format(seasonval + 'x' + epval.zfill(2))) if epval != None and len(episode.group( 1)) > 0 and self.YearEpInfo == 'false': swtitle = swtitle + sxexx tmpstr += showtitle.group( 1) + "//" + swtitle + "//" + theplot else: # This is a movie if showtitle == None or len( showtitle.group(1)) == 0: tmpstr += title.group(1) years = re.search('"year" *: *([\d.]*\d+)', f) year = ('({})'.format(years.group(1))) if len( years.group(1) ) > 0 and self.YearEpInfo == 'false': tmpstr += "//" + year + "//" + theplot else: tmpstr += "//" + "//" + theplot tmpstr = tmpstr[:2036] tmpstr = tmpstr.replace("\\n", " ").replace( "\\r", " ").replace("\\\"", "\"") tmpstr = tmpstr + '\n' + match.group(1).replace( "\\\\", "\\") if self.channels[channel - 1].mode & MODE_ORDERAIRDATE > 0: seasoneplist.append([seasonval, epval, tmpstr]) else: fileList.append(tmpstr) except: pass else: continue if self.channels[channel - 1].mode & MODE_ORDERAIRDATE > 0: seasoneplist.sort(key=lambda seep: seep[1]) seasoneplist.sort(key=lambda seep: seep[0]) for seepitem in seasoneplist: fileList.append(seepitem[2]) if filecount == 0: self.log(json_folder_detail) self.log("buildFileList return") return fileList def buildMixedFileList(self, dom1, channel): fileList = [] self.log('buildMixedFileList') try: rules = dom1.getElementsByTagName('rule') order = dom1.getElementsByTagName('order') except: self.log('buildMixedFileList Problem parsing playlist ' + filename, xbmc.LOGERROR) xml.close() return fileList for rule in rules: rulename = rule.childNodes[0].nodeValue if FileAccess.exists( xbmc.translatePath('special://profile/playlists/video/') + rulename): FileAccess.copy( xbmc.translatePath('special://profile/playlists/video/') + rulename, MADE_CHAN_LOC + rulename) fileList.extend( self.buildFileList(MADE_CHAN_LOC + rulename, channel)) else: fileList.extend( self.buildFileList(GEN_CHAN_LOC + rulename, channel)) self.log("buildMixedFileList returning") return fileList # Run rules for a channel def runActions(self, action, channel, parameter): self.log("runActions " + str(action) + " on channel " + str(channel)) if channel < 1: return self.runningActionChannel = channel index = 0 for rule in self.channels[channel - 1].ruleList: if rule.actions & action > 0: self.runningActionId = index if self.background == False: self.updateDialog.update( self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "processing rule " + str(index + 1), '') parameter = rule.runAction(action, self, parameter) index += 1 self.runningActionChannel = 0 self.runningActionId = 0 return parameter def threadPause(self): if threading.activeCount() > 1: while self.threadPaused == True and self.myOverlay.isExiting == False: time.sleep(self.sleepTime) # This will fail when using config.py try: if self.myOverlay.isExiting == True: self.log("IsExiting") return False except: pass return True def escapeDirJSON(self, dir_name): mydir = uni(dir_name) if (mydir.find(":")): mydir = mydir.replace("\\", "\\\\") return mydir def getSmartPlaylistType(self, dom): self.log('getSmartPlaylistType') try: pltype = dom.getElementsByTagName('smartplaylist') return pltype[0].attributes['type'].value except: self.log("Unable to get the playlist type.", xbmc.LOGERROR) return ''
class ChannelList: def __init__(self): self.networkList = [] self.studioList = [] self.mixedGenreList = [] self.showGenreList = [] self.movieGenreList = [] self.showList = [] self.videoParser = VideoParser() self.fileLock = FileLock() self.httpJSON = True self.sleepTime = 0 self.exitThread = False self.discoveredWebServer = False def setupList(self): self.channels = [] self.findMaxChannels() self.channelResetSetting = int(REAL_SETTINGS.getSetting("ChannelResetSetting")) self.log('Channel Reset Setting is ' + str(self.channelResetSetting)) self.forceReset = REAL_SETTINGS.getSetting('ForceChannelReset') == "true" self.log('Force Reset is ' + str(self.forceReset)) self.updateDialog = xbmcgui.DialogProgress() self.startMode = int(REAL_SETTINGS.getSetting("StartMode")) self.log('Start Mode is ' + str(self.startMode)) self.updateDialog.create("PseudoTV", "Updating channel list") self.updateDialog.update(0, "Updating channel list") try: self.lastResetTime = int(ADDON_SETTINGS.getSetting("LastResetTime")) except: self.lastResetTime = 0 try: self.lastExitTime = int(ADDON_SETTINGS.getSetting("LastExitTime")) except: self.lastExitTime = int(time.time()) # Go through all channels, create their arrays, and setup the new playlist for i in range(self.maxChannels): self.updateDialog.update(i * 100 // self.maxChannels, "Updating channel " + str(i + 1), "waiting for file lock") self.channels.append(Channel()) # If the user pressed cancel, stop everything and exit if self.updateDialog.iscanceled(): self.log('Update channels cancelled') self.updateDialog.close() return None # Block until the file is unlocked # This also has the affect of removing any stray locks (given, after 15 seconds). self.fileLock.isFileLocked(CHANNELS_LOC + 'channel_' + str(i + 1) + '.m3u', True) self.setupChannel(i + 1) REAL_SETTINGS.setSetting('ForceChannelReset', 'false') self.updateDialog.update(100, "Update complete") self.updateDialog.close() return self.channels def log(self, msg, level = xbmc.LOGDEBUG): log('ChannelList: ' + msg, level) # Determine the maximum number of channels by opening consecutive # playlists until we don't find one def findMaxChannels(self): self.log('findMaxChannels') self.maxChannels = 0 for i in range(999): chtype = 9999 chsetting1 = '' chsetting2 = '' try: chtype = int(ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_2') except: pass if chtype == 0: if os.path.exists(xbmc.translatePath(chsetting1)): self.maxChannels = i + 1 elif chtype < 7: if len(chsetting1) > 0: self.maxChannels = i + 1 self.log('findMaxChannels return ' + str(self.maxChannels)) def determineWebServer(self): if self.discoveredWebServer: return self.discoveredWebServer = True self.webPort = 8080 self.webUsername = '' self.webPassword = '' fle = xbmc.translatePath("special://profile/guisettings.xml") try: xml = open(fle, "r") except: self.log("determineWebServer Unable to open the settings file", xbmc.LOGERROR) self.httpJSON = False return try: dom = parse(xml) except: self.log('determineWebServer Unable to parse settings file', xbmc.LOGERROR) self.httpJSON = False return xml.close() try: plname = dom.getElementsByTagName('webserver') self.httpJSON = (plname[0].childNodes[0].nodeValue.lower() == 'true') self.log('determineWebServer is ' + str(self.httpJSON)) if self.httpJSON == True: plname = dom.getElementsByTagName('webserverport') self.webPort = int(plname[0].childNodes[0].nodeValue) self.log('determineWebServer port ' + str(self.webPort)) plname = dom.getElementsByTagName('webserverusername') self.webUsername = plname[0].childNodes[0].nodeValue self.log('determineWebServer username ' + self.webUsername) plname = dom.getElementsByTagName('webserverpassword') self.webPassword = plname[0].childNodes[0].nodeValue self.log('determineWebServer password is ' + self.webPassword) except: return # Code for sending JSON through http adapted from code by sffjunkie (forum.xbmc.org/showthread.php?t=92196) def sendJSON(self, command): self.log('sendJSON') data = '' usedhttp = False self.determineWebServer() # If there have been problems using the server, just skip the attempt and use executejsonrpc if self.httpJSON == True: payload = command.encode('utf-8') headers = {'Content-Type': 'application/json-rpc; charset=utf-8'} if self.webUsername != '': userpass = base64.encodestring('%s:%s' % (self.webUsername, self.webPassword))[:-1] headers['Authorization'] = 'Basic %s' % userpass try: conn = httplib.HTTPConnection('127.0.0.1', self.webPort) conn.request('POST', '/jsonrpc', payload, headers) response = conn.getresponse() if response.status == 200: data = response.read() usedhttp = True conn.close() except: pass if usedhttp == False: self.httpJSON = False data = xbmc.executeJSONRPC(command) return data def setupChannel(self, channel): self.log('setupChannel ' + str(channel)) returnval = False createlist = True chtype = 9999 chsetting1 = '' chsetting2 = '' needsreset = False try: chtype = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_2') except: pass try: needsreset = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_changed') == 'True' except: pass if chtype == 9999: self.channels[channel - 1].isValid = False return False # If possible, use an existing playlist if os.path.exists(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u'): try: self.channels[channel - 1].totalTimePlayed = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_time')) if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: self.channels[channel - 1].isValid = True self.channels[channel - 1].fileName = CHANNELS_LOC + 'channel_' + str(channel) + '.m3u' returnval = True # If this channel has been watched for longer than it lasts, reset the channel if self.channelResetSetting == 0 and self.channels[channel - 1].totalTimePlayed < self.channels[channel - 1].getTotalDuration(): createlist = self.forceReset if self.channelResetSetting > 0 and self.channelResetSetting < 4: timedif = time.time() - self.lastResetTime if self.channelResetSetting == 1 and timedif < (60 * 60 * 24): createlist = self.forceReset if self.channelResetSetting == 2 and timedif < (60 * 60 * 24 * 7): createlist = self.forceReset if self.channelResetSetting == 3 and timedif < (60 * 60 * 24 * 30): createlist = self.forceReset if timedif < 0: createlist = self.forceReset if createlist: ADDON_SETTINGS.setSetting('LastResetTime', str(int(time.time()))) if self.channelResetSetting == 4: createlist = self.forceReset except: pass if createlist or needsreset: self.fileLock.lockFile(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', True) self.updateDialog.update((channel - 1) * 100 // self.maxChannels, "Updating channel " + str(channel), "adding videos") if self.makeChannelList(channel, chtype, chsetting1, chsetting2) == True: if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: self.channels[channel - 1].totalTimePlayed = 0 self.channels[channel - 1].isValid = True self.channels[channel - 1].fileName = CHANNELS_LOC + 'channel_' + str(channel) + '.m3u' returnval = True ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_time', '0') ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_changed', 'False') self.fileLock.unlockFile(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') self.updateDialog.update((channel - 1) * 100 // self.maxChannels, "Updating channel " + str(channel), "clearing history") self.clearPlaylistHistory(channel) if chtype == 6: if chsetting2 == str(MODE_SERIAL): self.channels[channel - 1].mode = MODE_SERIAL # if there is no start mode in the channel mode flags, set it to the default if self.channels[channel - 1].mode & MODE_STARTMODES == 0: if self.startMode == 0: self.channels[channel - 1].mode = MODE_RESUME elif self.startMode == 1: self.channels[channel - 1].mode = MODE_REALTIME elif self.startMode == 2: self.channels[channel - 1].mode = MODE_RANDOM if self.channels[channel - 1].mode & MODE_ALWAYSPAUSE > 0: self.channels[channel - 1].isPaused = True if self.channels[channel - 1].mode & MODE_RANDOM > 0: self.channels[channel - 1].showTimeOffset = random.randint(0, self.channels[channel - 1].getTotalDuration()) if self.channels[channel - 1].mode & MODE_REALTIME > 0: chantime = 0 try: chantime = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_time')) except: pass timedif = int(time.time()) - self.lastExitTime self.channels[channel - 1].totalTimePlayed += timedif if self.channels[channel - 1].mode & MODE_RESUME > 0: self.channels[channel - 1].showTimeOffset = self.channels[channel - 1].totalTimePlayed self.channels[channel - 1].totalTimePlayed = 0 while self.channels[channel - 1].showTimeOffset > self.channels[channel - 1].getCurrentDuration(): self.channels[channel - 1].showTimeOffset -= self.channels[channel - 1].getCurrentDuration() self.channels[channel - 1].addShowPosition(1) self.channels[channel - 1].name = self.getChannelName(chtype, chsetting1) return returnval def clearPlaylistHistory(self, channel): self.log("clearPlaylistHistory") if self.channels[channel - 1].isValid == False: self.log("channel not valid, ignoring") return # if we actually need to clear anything if (self.channels[channel - 1].totalTimePlayed > 60 * 60 * 24) and self.fileLock.lockFile(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u'): try: fle = open(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', 'w') except: self.log("clearPlaylistHistory Unable to open the smart playlist", xbmc.LOGERROR) self.fileLock.unlockFile(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') return fle.write("#EXTM3U\n") tottime = 0 timeremoved = 0 for i in range(self.channels[channel - 1].Playlist.size()): tottime += self.channels[channel - 1].getItemDuration(i) if tottime > (self.channels[channel - 1].totalTimePlayed - (60 * 60 * 24)): tmpstr = str(self.channels[channel - 1].getItemDuration(i)) + ',' tmpstr += self.channels[channel - 1].getItemTitle(i) + "//" + self.channels[channel - 1].getItemEpisodeTitle(i) + "//" + self.channels[channel - 1].getItemDescription(i) tmpstr = tmpstr[:600] tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace("\\\"", "\"") tmpstr = tmpstr + '\n' + self.channels[channel - 1].getItemFilename(i) fle.write("#EXTINF:" + tmpstr + "\n") else: timeremoved = tottime fle.close() if timeremoved > 0: self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') self.channels[channel - 1].totalTimePlayed -= timeremoved self.fileLock.unlockFile(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') def getChannelName(self, chtype, setting1): self.log('getChannelName ' + str(chtype)) if len(setting1) == 0: return '' if chtype == 0: return self.getSmartPlaylistName(setting1) elif chtype == 1 or chtype == 2 or chtype == 5 or chtype == 6: return setting1 elif chtype == 3: return setting1 + " TV" elif chtype == 4: return setting1 + " Movies" return '' # Open the smart playlist and read the name out of it...this is the channel name def getSmartPlaylistName(self, fle): self.log('getSmartPlaylistName') fle = xbmc.translatePath(fle) try: xml = open(fle, "r") except: self.log("getSmartPlaylisyName Unable to open the smart playlist " + fle, xbmc.LOGERROR) return '' try: dom = parse(xml) except: self.log('getSmartPlaylistName Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return '' xml.close() try: plname = dom.getElementsByTagName('name') self.log('getSmartPlaylistName return ' + plname[0].childNodes[0].nodeValue) return plname[0].childNodes[0].nodeValue except: self.log("Unable to get the playlist name.", xbmc.LOGERROR) return '' # Based on a smart playlist, create a normal playlist that can actually be used by us def makeChannelList(self, channel, chtype, setting1, setting2, append = False): self.log('makeChannelList ' + str(channel)) if chtype == 0: fle = setting1 else: fle = self.makeTypePlaylist(chtype, setting1, setting2) fle = xbmc.translatePath(fle) if len(fle) == 0: self.log('Unable to locate the playlist for channel ' + str(channel), xbmc.LOGERROR) return False try: xml = open(fle, "r") except: self.log("makeChannelList Unable to open the smart playlist " + fle, xbmc.LOGERROR) return False try: dom = parse(xml) except: self.log('makeChannelList Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return False xml.close() if self.getSmartPlaylistType(dom) == 'mixed': fileList = self.buildMixedFileList(dom) else: fileList = self.buildFileList(fle) try: if append == True: channelplaylist = open(CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "r+") channelplaylist.seek(0, 2) else: channelplaylist = open(CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "w") except: self.log('Unable to open the cache file ' + CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', xbmc.LOGERROR) return False if append == False: channelplaylist.write("#EXTM3U\n") if len(fileList) == 0: self.log("Unable to get information about channel " + str(channel), xbmc.LOGERROR) channelplaylist.close() return False try: order = dom.getElementsByTagName('order') if order[0].childNodes[0].nodeValue.lower() == 'random': random.shuffle(fileList) except: pass fileList = fileList[:250] # Write each entry into the new playlist for string in fileList: channelplaylist.write("#EXTINF:" + string + "\n") channelplaylist.close() self.log('makeChannelList return') return True def appendChannel(self, channel): self.log("appendChannel") chtype = 9999 chsetting1 = '' chsetting2 = '' try: chtype = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_2') except: self.log("appendChannel unable to get channel settings") return False return self.makeChannelList(channel, chtype, chsetting1, chsetting2, True) def makeTypePlaylist(self, chtype, setting1, setting2): if chtype == 1: if len(self.networkList) == 0: self.fillTVInfo() return self.createNetworkPlaylist(setting1) elif chtype == 2: if len(self.studioList) == 0: self.fillMovieInfo() return self.createStudioPlaylist(setting1) elif chtype == 3: if len(self.showGenreList) == 0: self.fillTVInfo() return self.createGenrePlaylist('episodes', chtype, setting1) elif chtype == 4: if len(self.movieGenreList) == 0: self.fillMovieInfo() return self.createGenrePlaylist('movies', chtype, setting1) elif chtype == 5: if len(self.mixedGenreList) == 0: if len(self.showGenreList) == 0: self.fillTVInfo() if len(self.movieGenreList) == 0: self.fillMovieInfo() self.mixedGenreList = self.makeMixedList(self.showGenreList, self.movieGenreList) self.mixedGenreList.sort(key=lambda x: x.lower()) return self.createGenreMixedPlaylist(setting1) elif chtype == 6: if len(self.showList) == 0: self.fillTVInfo() return self.createShowPlaylist(setting1, setting2) self.log('makeTypePlaylists invalid channel type: ' + str(chtype)) return '' def createNetworkPlaylist(self, network): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Network_' + network + '.xsp') try: fle = open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "episodes", self.getChannelName(1, network)) network = network.lower() added = False for i in range(len(self.showList)): if self.threadPause() == False: fle.close() return '' if self.showList[i][1].lower() == network: theshow = self.cleanString(self.showList[i][0]) fle.write(' <rule field="tvshow" operator="is">' + theshow + '</rule>\n') added = True self.writeXSPFooter(fle, 250, "random") fle.close() if added == False: return '' return flename def createShowPlaylist(self, show, setting2): order = 'random' try: setting = int(setting2) if setting & MODE_ORDERAIRDATE > 0: order = 'airdate' except: pass flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Show_' + show + '_' + order + '.xsp') try: fle = open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, 'episodes', self.getChannelName(6, show)) show = self.cleanString(show) fle.write(' <rule field="tvshow" operator="is">' + show + '</rule>\n') self.writeXSPFooter(fle, 250, order) fle.close() return flename def createGenreMixedPlaylist(self, genre): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Mixed_' + genre + '.xsp') try: fle = open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' epname = os.path.basename(self.createGenrePlaylist('episodes', 3, genre)) moname = os.path.basename(self.createGenrePlaylist('movies', 4, genre)) self.writeXSPHeader(fle, 'mixed', self.getChannelName(5, genre)) fle.write(' <rule field="playlist" operator="is">' + epname + '</rule>\n') fle.write(' <rule field="playlist" operator="is">' + moname + '</rule>\n') self.writeXSPFooter(fle, 250, "random") fle.close() return flename def createGenrePlaylist(self, pltype, chtype, genre): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + pltype + '_' + genre + '.xsp') try: fle = open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, pltype, self.getChannelName(chtype, genre)) genre = self.cleanString(genre) fle.write(' <rule field="genre" operator="is">' + genre + '</rule>\n') self.writeXSPFooter(fle, 250, "random") fle.close() return flename def createStudioPlaylist(self, studio): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Studio_' + studio + '.xsp') try: fle = open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "movies", self.getChannelName(2, studio)) studio = self.cleanString(studio) fle.write(' <rule field="studio" operator="is">' + studio + '</rule>\n') self.writeXSPFooter(fle, 250, "random") fle.close() return flename def writeXSPHeader(self, fle, pltype, plname): fle.write('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n') fle.write('<smartplaylist type="' + pltype + '">\n') plname = self.cleanString(plname) fle.write(' <name>' + plname + '</name>\n') fle.write(' <match>one</match>\n') def writeXSPFooter(self, fle, limit, order): fle.write(' <limit>' + str(limit) + '</limit>\n') fle.write(' <order direction="ascending">' + order + '</order>\n') fle.write('</smartplaylist>\n') def cleanString(self, string): newstr = string newstr = newstr.replace('&', '&') newstr = newstr.replace('>', '>') newstr = newstr.replace('<', '<') return newstr def fillTVInfo(self): self.log("fillTVInfo") json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": {"fields":["studio", "genre"]}, "id": 1}' json_folder_detail = self.sendJSON(json_query) # self.log(json_folder_detail) detail = re.compile( "{(.*?)}", re.DOTALL ).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return match = re.search('"studio" *: *"(.*?)",', f) network = '' if match: found = False network = match.group(1).strip() for item in self.networkList: if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return if item.lower() == network.lower(): found = True break if found == False and len(network) > 0: self.networkList.append(network) match = re.search('"label" *: *"(.*?)",', f) if match: show = match.group(1).strip() self.showList.append([show, network]) match = re.search('"genre" *: *"(.*?)",', f) if match: genres = match.group(1).split('/') for genre in genres: found = False curgenre = genre.lower().strip() for g in self.showGenreList: if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return if curgenre == g.lower(): found = True break if found == False: self.showGenreList.append(genre.strip()) self.networkList.sort(key=lambda x: x.lower()) self.showGenreList.sort(key=lambda x: x.lower()) self.log("found shows " + str(self.showList)) self.log("found genres " + str(self.showGenreList)) self.log("fillTVInfo return " + str(self.networkList)) def fillMovieInfo(self): self.log("fillMovieInfo") studioList = [] json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": {"fields":["studio", "genre"]}, "id": 1}' json_folder_detail = self.sendJSON(json_query) # self.log(json_folder_detail) detail = re.compile( "{(.*?)}", re.DOTALL ).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.movieGenreList[:] del self.studioList[:] del studioList[:] break match = re.search('"genre" *: *"(.*?)",', f) if match: genres = match.group(1).split('/') for genre in genres: found = False curgenre = genre.lower().strip() for g in self.movieGenreList: if curgenre == g.lower(): found = True break if found == False: self.movieGenreList.append(genre.strip()) match = re.search('"studio" *: *"(.*?)",', f) if match: studios = match.group(1).split('/') for studio in studios: curstudio = studio.strip() found = False for i in range(len(studioList)): if studioList[i][0].lower() == curstudio.lower(): studioList[i][1] += 1 found = True if found == False and len(curstudio) > 0: studioList.append([curstudio, 1]) maxcount = 0 for i in range(len(studioList)): if studioList[i][1] > maxcount: maxcount = studioList[i][1] bestmatch = 1 lastmatch = 1000 for i in range(maxcount, 0, -1): itemcount = 0 for j in range(len(studioList)): if studioList[j][1] == i: itemcount += 1 if abs(itemcount - 15) < abs(lastmatch - 15): bestmatch = i lastmatch = itemcount for i in range(len(studioList)): if studioList[i][1] == bestmatch: self.studioList.append(studioList[i][0]) self.studioList.sort(key=lambda x: x.lower()) self.movieGenreList.sort(key=lambda x: x.lower()) self.log("found genres " + str(self.movieGenreList)) self.log("fillMovieInfo return " + str(self.studioList)) def makeMixedList(self, list1, list2): self.log("makeMixedList") newlist = [] for item in list1: curitem = item.lower() for a in list2: if curitem == a.lower(): newlist.append(item) break self.log("makeMixedList return " + str(newlist)) return newlist def buildFileList(self, dir_name, media_type="video", recursive="TRUE"): self.log("buildFileList") fileList = [] json_query = '{"jsonrpc": "2.0", "method": "Files.GetDirectory", "params": {"directory": "%s", "media": "%s", "fields":["duration","runtime","tagline","showtitle","album","artist","plot"]}, "id": 1}' % ( self.escapeDirJSON( dir_name ), media_type ) json_folder_detail = self.sendJSON(json_query) self.log(json_folder_detail) file_detail = re.compile( "{(.*?)}", re.DOTALL ).findall(json_folder_detail) for f in file_detail: if self.threadPause() == False: del fileList[:] break match = re.search('"file" *: *"(.*?)",', f) if match: if(match.group(1).endswith("/") or match.group(1).endswith("\\")): if(recursive == "TRUE"): fileList.extend(self.buildFileList(match.group(1), media_type, recursive)) else: duration = re.search('"duration" *: *([0-9]*?),', f) try: dur = int(duration.group(1)) except: dur = 0 if dur == 0: duration = re.search('"runtime" *: *"([0-9]*?)",', f) try: # Runtime is reported in minutes dur = int(duration.group(1)) * 60 except: dur = 0 if dur == 0: dur = self.videoParser.getVideoLength(match.group(1).replace("\\\\", "\\")) try: if dur > 0: title = re.search('"label" *: *"(.*?)"', f) tmpstr = str(dur) + ',' showtitle = re.search('"showtitle" *: *"(.*?)"', f) plot = re.search('"plot" *: *"(.*?)",', f) if plot == None: theplot = "" else: theplot = plot.group(1) # This is a TV show if showtitle != None and len(showtitle.group(1)) > 0: tmpstr += showtitle.group(1) + "//" + title.group(1) + "//" + theplot else: tmpstr += title.group(1) + "//" album = re.search('"album" *: *"(.*?)"', f) # This is a movie if album == None or len(album.group(1)) == 0: tagline = re.search('"tagline" *: *"(.*?)"', f) if tagline != None: tmpstr += tagline.group(1) tmpstr += "//" + theplot else: artist = re.search('"artist" *: *"(.*?)"', f) tmpstr += album.group(1) + "//" + artist.group(1) tmpstr = tmpstr[:600] tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace("\\\"", "\"") tmpstr = tmpstr + '\n' + match.group(1).replace("\\\\", "\\") fileList.append(tmpstr) except: pass else: continue self.videoParser.finish() return fileList def buildMixedFileList(self, dom1): fileList = [] self.log('buildMixedFileList') try: rules = dom1.getElementsByTagName('rule') order = dom1.getElementsByTagName('order') except: self.log('buildMixedFileList Problem parsing playlist ' + filename, xbmc.LOGERROR) xml.close() return fileList for rule in rules: rulename = rule.childNodes[0].nodeValue if os.path.exists(xbmc.translatePath('special://profile/playlists/video/') + rulename): fileList.extend(self.buildFileList(xbmc.translatePath('special://profile/playlists/video/') + rulename)) else: fileList.extend(self.buildFileList(GEN_CHAN_LOC + rulename)) self.log("buildMixedFileList returning") return fileList def threadPause(self): if threading.activeCount() > 1: if self.exitThread == True: return False time.sleep(self.sleepTime) return True def escapeDirJSON(self, dir_name): if (dir_name.find(":")): dir_name = dir_name.replace("\\", "\\\\") return dir_name def getSmartPlaylistType(self, dom): self.log('getSmartPlaylistType') try: pltype = dom.getElementsByTagName('smartplaylist') return pltype[0].attributes['type'].value except: self.log("Unable to get the playlist type.", xbmc.LOGERROR) return ''
class ChannelList: def __init__(self): self.networkList = [] self.studioList = [] self.mixedGenreList = [] self.showGenreList = [] self.movieGenreList = [] self.showList = [] self.channels = [] self.videoParser = VideoParser() self.sleepTime = 0 self.threadPaused = False self.runningActionChannel = 0 self.runningActionId = 0 self.enteredChannelCount = 0 self.background = True random.seed() def readConfig(self): self.channelResetSetting = int(ADDON.getSetting("ChannelResetSetting")) self.log('Channel Reset Setting is ' + str(self.channelResetSetting)) self.forceReset = ADDON.getSetting('ForceChannelReset') == "true" self.log('Force Reset is ' + str(self.forceReset)) self.updateDialog = xbmcgui.DialogProgress() self.startMode = int(ADDON.getSetting("StartMode")) self.log('Start Mode is ' + str(self.startMode)) self.backgroundUpdating = int(ADDON.getSetting("ThreadMode")) self.mediaLimit = MEDIA_LIMIT[int(ADDON.getSetting("MediaLimit"))] self.findMaxChannels() if self.forceReset: ADDON.setSetting('ForceChannelReset', "False") self.forceReset = False try: self.lastResetTime = int(ADDON_SETTINGS.getSetting("LastResetTime")) except: self.lastResetTime = 0 try: self.lastExitTime = int(ADDON_SETTINGS.getSetting("LastExitTime")) except: self.lastExitTime = int(time.time()) def setupList(self): self.readConfig() self.updateDialog.create(ADDON_NAME, "Updating channel list") self.updateDialog.update(0, "Updating channel list") self.updateDialogProgress = 0 foundvalid = False makenewlists = False self.background = False if self.backgroundUpdating > 0 and self.myOverlay.isMaster == True: makenewlists = True # Go through all channels, create their arrays, and setup the new playlist for i in range(self.maxChannels): self.updateDialogProgress = i * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(i + 1), "waiting for file lock") self.channels.append(Channel()) # If the user pressed cancel, stop everything and exit if self.updateDialog.iscanceled(): self.log('Update channels cancelled') self.updateDialog.close() return None self.setupChannel(i + 1, False, makenewlists, False) if self.channels[i].isValid: foundvalid = True if makenewlists == True: ADDON.setSetting('ForceChannelReset', 'false') if foundvalid == False and makenewlists == False: for i in range(self.maxChannels): self.updateDialogProgress = i * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(i + 1), "waiting for file lock", '') self.setupChannel(i + 1, False, True, False) if self.channels[i].isValid: foundvalid = True break self.updateDialog.update(100, "Update complete") self.updateDialog.close() return self.channels def log(self, msg, level = xbmc.LOGDEBUG): log('ChannelList: ' + msg, level) # Determine the maximum number of channels by opening consecutive # playlists until we don't find one def findMaxChannels(self): self.log('findMaxChannels') self.maxChannels = 0 self.enteredChannelCount = 0 for i in range(999): chtype = 9999 chsetting1 = '' chsetting2 = '' try: chtype = int(ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_2') except: pass if chtype == 0: if FileAccess.exists(xbmc.translatePath(chsetting1)): self.maxChannels = i + 1 self.enteredChannelCount += 1 elif chtype < 8: if len(chsetting1) > 0: self.maxChannels = i + 1 self.enteredChannelCount += 1 if self.forceReset and (chtype != 9999): ADDON_SETTINGS.setSetting('Channel_' + str(i + 1) + '_changed', "True") self.log('findMaxChannels return ' + str(self.maxChannels)) def sendJSON(self, command): data = xbmc.executeJSONRPC(command) return unicode(data, 'utf-8', errors='ignore') def setupChannel(self, channel, background = False, makenewlist = False, append = False): self.log('setupChannel ' + str(channel)) returnval = False createlist = makenewlist chtype = 9999 chsetting1 = '' chsetting2 = '' needsreset = False self.background = background self.settingChannel = channel try: chtype = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_2') except: pass while len(self.channels) < channel: self.channels.append(Channel()) if chtype == 9999: self.channels[channel - 1].isValid = False return False self.channels[channel - 1].isSetup = True self.channels[channel - 1].loadRules(channel) self.runActions(RULES_ACTION_START, channel, self.channels[channel - 1]) try: needsreset = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_changed') == 'True' if needsreset: self.channels[channel - 1].isSetup = False except: pass # If possible, use an existing playlist # Don't do this if we're appending an existing channel # Don't load if we need to reset anyway if FileAccess.exists(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') and append == False and needsreset == False: try: self.channels[channel - 1].totalTimePlayed = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_time', True)) createlist = True if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(channel), "reading playlist", '') if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: self.channels[channel - 1].isValid = True self.channels[channel - 1].fileName = CHANNELS_LOC + 'channel_' + str(channel) + '.m3u' returnval = True # If this channel has been watched for longer than it lasts, reset the channel if self.channelResetSetting == 0 and self.channels[channel - 1].totalTimePlayed < self.channels[channel - 1].getTotalDuration(): createlist = False if self.channelResetSetting > 0 and self.channelResetSetting < 4: timedif = time.time() - self.lastResetTime if self.channelResetSetting == 1 and timedif < (60 * 60 * 24): createlist = False if self.channelResetSetting == 2 and timedif < (60 * 60 * 24 * 7): createlist = False if self.channelResetSetting == 3 and timedif < (60 * 60 * 24 * 30): createlist = False if timedif < 0: createlist = False if self.channelResetSetting == 4: createlist = False except: pass if createlist or needsreset: self.channels[channel - 1].isValid = False if makenewlist: try: os.remove(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') except: pass append = False if createlist: ADDON_SETTINGS.setSetting('LastResetTime', str(int(time.time()))) if append == False: if chtype == 6 and chsetting2 == str(MODE_ORDERAIRDATE): self.channels[channel - 1].mode = MODE_ORDERAIRDATE # if there is no start mode in the channel mode flags, set it to the default if self.channels[channel - 1].mode & MODE_STARTMODES == 0: if self.startMode == 0: self.channels[channel - 1].mode |= MODE_RESUME elif self.startMode == 1: self.channels[channel - 1].mode |= MODE_REALTIME elif self.startMode == 2: self.channels[channel - 1].mode |= MODE_RANDOM if ((createlist or needsreset) and makenewlist) or append: if self.background == False: self.updateDialogProgress = (channel - 1) * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(channel), "adding videos", '') if self.makeChannelList(channel, chtype, chsetting1, chsetting2, append) == True: if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: returnval = True self.channels[channel - 1].fileName = CHANNELS_LOC + 'channel_' + str(channel) + '.m3u' self.channels[channel - 1].isValid = True # Don't reset variables on an appending channel if append == False: self.channels[channel - 1].totalTimePlayed = 0 ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_time', '0') if needsreset: ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_changed', 'False') self.channels[channel - 1].isSetup = True self.runActions(RULES_ACTION_BEFORE_CLEAR, channel, self.channels[channel - 1]) # Don't clear history when appending channels if self.background == False and append == False and self.myOverlay.isMaster: self.updateDialogProgress = (channel - 1) * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(channel), "clearing history", '') self.clearPlaylistHistory(channel) if append == False: self.runActions(RULES_ACTION_BEFORE_TIME, channel, self.channels[channel - 1]) if self.channels[channel - 1].mode & MODE_ALWAYSPAUSE > 0: self.channels[channel - 1].isPaused = True if self.channels[channel - 1].mode & MODE_RANDOM > 0: self.channels[channel - 1].showTimeOffset = random.randint(0, self.channels[channel - 1].getTotalDuration()) if self.channels[channel - 1].mode & MODE_REALTIME > 0: timedif = int(self.myOverlay.timeStarted) - self.lastExitTime self.channels[channel - 1].totalTimePlayed += timedif if self.channels[channel - 1].mode & MODE_RESUME > 0: self.channels[channel - 1].showTimeOffset = self.channels[channel - 1].totalTimePlayed self.channels[channel - 1].totalTimePlayed = 0 while self.channels[channel - 1].showTimeOffset > self.channels[channel - 1].getCurrentDuration(): self.channels[channel - 1].showTimeOffset -= self.channels[channel - 1].getCurrentDuration() self.channels[channel - 1].addShowPosition(1) self.channels[channel - 1].name = self.getChannelName(chtype, chsetting1) if ((createlist or needsreset) and makenewlist) and returnval: self.runActions(RULES_ACTION_FINAL_MADE, channel, self.channels[channel - 1]) else: self.runActions(RULES_ACTION_FINAL_LOADED, channel, self.channels[channel - 1]) return returnval def clearPlaylistHistory(self, channel): self.log("clearPlaylistHistory") if self.channels[channel - 1].isValid == False: self.log("channel not valid, ignoring") return # if we actually need to clear anything if self.channels[channel - 1].totalTimePlayed > (60 * 60 * 24 * 2): try: fle = FileAccess.open(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', 'w') except: self.log("clearPlaylistHistory Unable to open the smart playlist", xbmc.LOGERROR) return flewrite = uni("#EXTM3U\n") tottime = 0 timeremoved = 0 for i in range(self.channels[channel - 1].Playlist.size()): tottime += self.channels[channel - 1].getItemDuration(i) if tottime > (self.channels[channel - 1].totalTimePlayed - (60 * 60 * 12)): tmpstr = str(self.channels[channel - 1].getItemDuration(i)) + ',' tmpstr += self.channels[channel - 1].getItemTitle(i) + "//" + self.channels[channel - 1].getItemEpisodeTitle(i) + "//" + self.channels[channel - 1].getItemDescription(i) tmpstr = uni(tmpstr[:2036]) tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace("\\\"", "\"") tmpstr = uni(tmpstr) + uni('\n') + uni(self.channels[channel - 1].getItemFilename(i)) flewrite += uni("#EXTINF:") + uni(tmpstr) + uni("\n") else: timeremoved = tottime fle.write(flewrite) fle.close() if timeremoved > 0: if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == False: self.channels[channel - 1].isValid = False else: self.channels[channel - 1].totalTimePlayed -= timeremoved # Write this now so anything sharing the playlists will get the proper info ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_time', str(self.channels[channel - 1].totalTimePlayed)) def getChannelName(self, chtype, setting1): self.log('getChannelName ' + str(chtype)) if len(setting1) == 0: return '' if chtype == 0: return self.getSmartPlaylistName(setting1) elif chtype == 1 or chtype == 2 or chtype == 5 or chtype == 6: return setting1 elif chtype == 3: return setting1 + " TV" elif chtype == 4: return setting1 + " Movies" elif chtype == 7: if setting1[-1] == '/' or setting1[-1] == '\\': return os.path.split(setting1[:-1])[1] else: return os.path.split(setting1)[1] return '' # Open the smart playlist and read the name out of it...this is the channel name def getSmartPlaylistName(self, fle): self.log('getSmartPlaylistName') fle = xbmc.translatePath(fle) try: xml = FileAccess.open(fle, "r") except: self.log("getSmartPlaylisyName Unable to open the smart playlist " + fle, xbmc.LOGERROR) return '' try: dom = parse(xml) except: self.log('getSmartPlaylistName Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return '' xml.close() try: plname = dom.getElementsByTagName('name') self.log('getSmartPlaylistName return ' + plname[0].childNodes[0].nodeValue) return plname[0].childNodes[0].nodeValue except: self.log("Unable to get the playlist name.", xbmc.LOGERROR) return '' # Based on a smart playlist, create a normal playlist that can actually be used by us def makeChannelList(self, channel, chtype, setting1, setting2, append = False): self.log('makeChannelList ' + str(channel)) israndom = False fileList = [] if chtype == 7: fileList = self.createDirectoryPlaylist(setting1) israndom = True else: if chtype == 0: if FileAccess.copy(setting1, MADE_CHAN_LOC + os.path.split(setting1)[1]) == False: if FileAccess.exists(MADE_CHAN_LOC + os.path.split(setting1)[1]) == False: self.log("Unable to copy or find playlist " + setting1) return False fle = MADE_CHAN_LOC + os.path.split(setting1)[1] else: fle = self.makeTypePlaylist(chtype, setting1, setting2) fle = fle if len(fle) == 0: self.log('Unable to locate the playlist for channel ' + str(channel), xbmc.LOGERROR) return False try: xml = FileAccess.open(fle, "r") except: self.log("makeChannelList Unable to open the smart playlist " + fle, xbmc.LOGERROR) return False try: dom = parse(xml) except: self.log('makeChannelList Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return False xml.close() if self.getSmartPlaylistType(dom) == 'mixed': fileList = self.buildMixedFileList(dom, channel) else: fileList = self.buildFileList(fle, channel) try: order = dom.getElementsByTagName('order') if order[0].childNodes[0].nodeValue.lower() == 'random': israndom = True except: pass try: if append == True: channelplaylist = FileAccess.open(CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "r") channelplaylist.seek(0, 2) channelplaylist.close() else: channelplaylist = FileAccess.open(CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "w") except: self.log('Unable to open the cache file ' + CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', xbmc.LOGERROR) return False if append == False: channelplaylist.write(uni("#EXTM3U\n")) if israndom: random.shuffle(fileList) if len(fileList) > 16384: fileList = fileList[:16384] fileList = self.runActions(RULES_ACTION_LIST, channel, fileList) self.channels[channel - 1].isRandom = israndom if append: if len(fileList) + self.channels[channel - 1].Playlist.size() > 16384: fileList = fileList[:(16384 - self.channels[channel - 1].Playlist.size())] else: if len(fileList) > 16384: fileList = fileList[:16384] # Write each entry into the new playlist for string in fileList: channelplaylist.write(uni("#EXTINF:") + uni(string) + uni("\n")) channelplaylist.close() self.log('makeChannelList return') return True def makeTypePlaylist(self, chtype, setting1, setting2): if chtype == 1: if len(self.networkList) == 0: self.fillTVInfo() return self.createNetworkPlaylist(setting1) elif chtype == 2: if len(self.studioList) == 0: self.fillMovieInfo() return self.createStudioPlaylist(setting1) elif chtype == 3: if len(self.showGenreList) == 0: self.fillTVInfo() return self.createGenrePlaylist('episodes', chtype, setting1) elif chtype == 4: if len(self.movieGenreList) == 0: self.fillMovieInfo() return self.createGenrePlaylist('movies', chtype, setting1) elif chtype == 5: if len(self.mixedGenreList) == 0: if len(self.showGenreList) == 0: self.fillTVInfo() if len(self.movieGenreList) == 0: self.fillMovieInfo() self.mixedGenreList = self.makeMixedList(self.showGenreList, self.movieGenreList) self.mixedGenreList.sort(key=lambda x: x.lower()) return self.createGenreMixedPlaylist(setting1) elif chtype == 6: if len(self.showList) == 0: self.fillTVInfo() return self.createShowPlaylist(setting1, setting2) self.log('makeTypePlaylists invalid channel type: ' + str(chtype)) return '' def createNetworkPlaylist(self, network): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Network_' + network + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "episodes", self.getChannelName(1, network)) network = network.lower() added = False fle.write(' <rule field="tvshow" operator="is">\n') for i in range(len(self.showList)): if self.showList[i][1].lower() == network: theshow = self.cleanString(self.showList[i][0]) fle.write(' <value>' + theshow + '</value>\n') added = True fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() if added == False: return '' return flename def createShowPlaylist(self, show, setting2): order = 'random' try: setting = int(setting2) if setting & MODE_ORDERAIRDATE > 0: order = 'episode' except: pass flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Show_' + show + '_' + order + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, 'episodes', self.getChannelName(6, show)) show = self.cleanString(show) fle.write(' <rule field="tvshow" operator="is">\n') fle.write(' <value>' + show + '</value>\n') fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, order) fle.close() return flename def createGenreMixedPlaylist(self, genre): if isinstance(genre, str): filegenre = genre.decode('ascii', 'ignore').encode('ascii') #note: this removes the character and encodes back to string. elif isinstance(genre, unicode): filegenre = genre.encode('ascii', 'ignore') flename = xbmc.makeLegalFilename(u''.join((GEN_CHAN_LOC, 'Mixed_', filegenre, '.xsp')).encode('utf-8').strip()) try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' epname = os.path.basename(self.createGenrePlaylist('episodes', 3, genre)) moname = os.path.basename(self.createGenrePlaylist('movies', 4, genre)) self.writeXSPHeader(fle, 'mixed', self.getChannelName(5, genre)) fle.write(' <rule field="playlist" operator="is">' + epname + '</rule>\n') fle.write(' <rule field="playlist" operator="is">' + moname + '</rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createGenrePlaylist(self, pltype, chtype, genre): if isinstance(genre, str): filegenre = genre.decode('ascii', 'ignore').encode('ascii') #note: this removes the character and encodes back to string. elif isinstance(genre, unicode): filegenre = genre.encode('ascii', 'ignore') flename = xbmc.makeLegalFilename(u''.join((GEN_CHAN_LOC, pltype, '_', filegenre, '.xsp')).encode('utf-8').strip()) try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, pltype, self.getChannelName(chtype, genre)) genre = self.cleanString(genre) fle.write(' <rule field="genre" operator="is">\n') fle.write(' <value>' + genre + '</value>\n') fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createStudioPlaylist(self, studio): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Studio_' + studio + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.log(LANGUAGE(30034) + ' ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "movies", self.getChannelName(2, studio)) studio = self.cleanString(studio) fle.write(' <rule field="studio" operator="is">\n') fle.write(' <value>' + studio + '</value>\n') fle.write(' </rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createDirectoryPlaylist(self, setting1): self.log("createDirectoryPlaylist " + setting1) fileList = [] filecount = 0 def listdir_fullpath(dir): return [os.path.join(dir, f) for f in xbmcvfs.listdir(dir)[1]] if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "getting file list") file_detail = listdir_fullpath(setting1) for f in file_detail: if self.threadPause() == False: del fileList[:] break duration = self.videoParser.getVideoLength(f) if duration > 0: filecount += 1 if self.background == False: if filecount == 1: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entry") else: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entries") afile = os.path.basename(f) afile, ext = os.path.splitext(afile) tmpstr = str(duration) + ',' tmpstr += afile + "//" + "//" + xbmc.getLocalizedString(21801) + ': ' + setting1 + "\n" tmpstr += setting1 + os.path.basename(f) tmpstr = tmpstr[:2036] fileList.append(tmpstr) if filecount == 0: self.log('Unable to access Videos files in ' + setting1) return fileList def writeXSPHeader(self, fle, pltype, plname): fle.write('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n') fle.write('<smartplaylist type="' + pltype + '">\n') plname = self.cleanString(plname) fle.write(' <name>' + plname + '</name>\n') fle.write(' <match>one</match>\n') def writeXSPFooter(self, fle, limit, order): if self.mediaLimit > 0: fle.write(' <limit>' + str(self.mediaLimit) + '</limit>\n') fle.write(' <order direction="ascending">' + order + '</order>\n') fle.write('</smartplaylist>\n') def cleanString(self, string): newstr = uni(string) newstr = newstr.replace('&', '&') newstr = newstr.replace('>', '>') newstr = newstr.replace('<', '<') return uni(newstr) def fillTVInfo(self, sortbycount = False): self.log("fillTVInfo") json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": {"properties":["studio", "genre"]}, "id": 1}' if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "reading TV data") json_folder_detail = self.sendJSON(json_query) detail = re.compile("{(.*?)}", re.DOTALL).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return match = re.search('"studio" *: *\[(.*?)\]', f) network = '' if match: network = (match.group(1).split(','))[0] network = network.strip('"').strip() found = False for item in range(len(self.networkList)): if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return itm = self.networkList[item] if sortbycount: itm = itm[0] if itm.lower() == network.lower(): found = True if sortbycount: self.networkList[item][1] += 1 break if found == False and len(network) > 0: if sortbycount: self.networkList.append([network, 1]) else: self.networkList.append(network) match = re.search('"label" *: *"(.*?)",', f) if match: show = match.group(1).strip() self.showList.append([show, network]) match = re.search('"genre" *: *\[(.*?)\]', f) if match: genres = match.group(1).split(',') for genre in genres: found = False curgenre = genre.lower().strip('"').strip() for g in range(len(self.showGenreList)): if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return itm = self.showGenreList[g] if sortbycount: itm = itm[0] if curgenre == itm.lower(): found = True if sortbycount: self.showGenreList[g][1] += 1 break if found == False: if sortbycount: self.showGenreList.append([genre.strip('"').strip(), 1]) else: self.showGenreList.append(genre.strip('"').strip()) if sortbycount: self.networkList.sort(key=lambda x: x[1], reverse = True) self.showGenreList.sort(key=lambda x: x[1], reverse = True) else: self.networkList.sort(key=lambda x: x.lower()) self.showGenreList.sort(key=lambda x: x.lower()) if (len(self.showList) == 0) and (len(self.showGenreList) == 0) and (len(self.networkList) == 0): self.log(json_folder_detail) self.log("found shows " + str(self.showList)) self.log("found genres " + str(self.showGenreList)) self.log("fillTVInfo return " + str(self.networkList)) def fillMovieInfo(self, sortbycount = False): self.log("fillMovieInfo") studioList = [] json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": {"properties":["studio", "genre"]}, "id": 1}' if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "reading movie data") json_folder_detail = self.sendJSON(json_query) detail = re.compile("{(.*?)}", re.DOTALL).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.movieGenreList[:] del self.studioList[:] del studioList[:] break match = re.search('"genre" *: *\[(.*?)\]', f) if match: genres = match.group(1).split(',') for genre in genres: found = False curgenre = genre.lower().strip('"').strip() for g in range(len(self.movieGenreList)): itm = self.movieGenreList[g] if sortbycount: itm = itm[0] if curgenre == itm.lower(): found = True if sortbycount: self.movieGenreList[g][1] += 1 break if found == False: if sortbycount: self.movieGenreList.append([genre.strip('"').strip(), 1]) else: self.movieGenreList.append(genre.strip('"').strip()) match = re.search('"studio" *: *\[(.*?)\]', f) if match: studios = match.group(1).split(',') for studio in studios: curstudio = studio.strip('"').strip() found = False for i in range(len(studioList)): if studioList[i][0].lower() == curstudio.lower(): studioList[i][1] += 1 found = True break if found == False and len(curstudio) > 0: studioList.append([curstudio, 1]) maxcount = 0 for i in range(len(studioList)): if studioList[i][1] > maxcount: maxcount = studioList[i][1] bestmatch = 1 lastmatch = 1000 counteditems = 0 for i in range(maxcount, 0, -1): itemcount = 0 for j in range(len(studioList)): if studioList[j][1] == i: itemcount += 1 if abs(itemcount + counteditems - 8) < abs(lastmatch - 8): bestmatch = i lastmatch = itemcount counteditems += itemcount if sortbycount: studioList.sort(key=lambda x: x[1], reverse=True) self.movieGenreList.sort(key=lambda x: x[1], reverse=True) else: studioList.sort(key=lambda x: x[0].lower()) self.movieGenreList.sort(key=lambda x: x.lower()) for i in range(len(studioList)): if studioList[i][1] >= bestmatch: if sortbycount: self.studioList.append([studioList[i][0], studioList[i][1]]) else: self.studioList.append(studioList[i][0]) if (len(self.movieGenreList) == 0) and (len(self.studioList) == 0): self.log(json_folder_detail) self.log("found genres " + str(self.movieGenreList)) self.log("fillMovieInfo return " + str(self.studioList)) def makeMixedList(self, list1, list2): self.log("makeMixedList") newlist = [] for item in list1: curitem = item.lower() for a in list2: if curitem == a.lower(): newlist.append(item) break self.log("makeMixedList return " + str(newlist)) return newlist def buildFileList(self, dir_name, channel): self.log("buildFileList") fileList = [] seasoneplist = [] filecount = 0 json_query = '{"jsonrpc": "2.0", "method": "Files.GetDirectory", "params": {"directory": "%s", "media": "video", "properties":["duration","runtime","showtitle","plot","season","episode","year","playcount"]}, "id": 1}' % (self.escapeDirJSON(dir_name)) if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "querying database") json_folder_detail = self.sendJSON(json_query) file_detail = re.compile("{(.*?)}", re.DOTALL).findall(json_folder_detail) for f in file_detail: if self.threadPause() == False: del fileList[:] break f = uni(f) match = re.search('"file" *: *"(.*?)",', f) if match: if(match.group(1).endswith("/") or match.group(1).endswith("\\")): fileList.extend(self.buildFileList(match.group(1), channel)) else: f = self.runActions(RULES_ACTION_JSON, channel, f) duration = re.search('"duration" *: *([0-9]*?),', f) try: dur = int(duration.group(1)) except: dur = 0 if dur == 0: duration = re.search('"runtime" *: *([0-9]*?),', f) try: dur = int(duration.group(1)) except: dur = 0 if dur == 0: try: dur = self.videoParser.getVideoLength(uni(match.group(1)).replace("\\\\", "\\")) except: dur = 0 try: if dur > 0: filecount += 1 if self.background == False: if filecount == 1: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entry") else: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entries") tmpstr = str(dur) + ',' title = re.search('"label" *: *"(.*?)"', f) showtitle = re.search('"showtitle" *: *"(.*?)"', f) plot = re.search('"plot" *: *"(.*?)",', f) if len(plot.group(1)) > 0: theplot = plot.group(1) else: theplot = xbmc.getLocalizedString(161) # This is a TV show if showtitle != None and len(showtitle.group(1)) > 0: swtitle = title.group(1) season = re.search('"season" *: *(.*?),', f) episode = re.search('"episode" *: *(.*?),', f) seasonval = int(season.group(1)) epval = int(episode.group(1)) if epval != None and len(episode.group(1)) > 0: swtitle = swtitle + ' (' + str(seasonval) + 'x' + str(epval).zfill(2) + ')' tmpstr += showtitle.group(1) + "//" + swtitle + "//" + theplot else: # This is a movie if showtitle == None or len(showtitle.group(1)) == 0: tmpstr += title.group(1) years = re.search('"year" *: *([\d.]*\d+)', f) if len(years.group(1)) > 0: year = '(' + str(years.group(1)) + ')' tmpstr += "//" + year + "//" + theplot else: tmpstr += "//" + "//" + theplot tmpstr = tmpstr[:2036] tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace("\\\"", "\"") tmpstr = tmpstr + '\n' + match.group(1).replace("\\\\", "\\") if self.channels[channel - 1].mode & MODE_ORDERAIRDATE > 0: seasoneplist.append([seasonval, epval, tmpstr]) else: fileList.append(tmpstr) except: pass else: continue if self.channels[channel - 1].mode & MODE_ORDERAIRDATE > 0: seasoneplist.sort(key=lambda seep: seep[1]) seasoneplist.sort(key=lambda seep: seep[0]) for seepitem in seasoneplist: fileList.append(seepitem[2]) if filecount == 0: self.log(json_folder_detail) self.log("buildFileList return") return fileList def buildMixedFileList(self, dom1, channel): fileList = [] self.log('buildMixedFileList') try: rules = dom1.getElementsByTagName('rule') order = dom1.getElementsByTagName('order') except: self.log('buildMixedFileList Problem parsing playlist ' + filename, xbmc.LOGERROR) xml.close() return fileList for rule in rules: rulename = rule.childNodes[0].nodeValue if FileAccess.exists(xbmc.translatePath('special://profile/playlists/video/') + rulename): FileAccess.copy(xbmc.translatePath('special://profile/playlists/video/') + rulename, MADE_CHAN_LOC + rulename) fileList.extend(self.buildFileList(MADE_CHAN_LOC + rulename, channel)) else: fileList.extend(self.buildFileList(GEN_CHAN_LOC + rulename, channel)) self.log("buildMixedFileList returning") return fileList # Run rules for a channel def runActions(self, action, channel, parameter): self.log("runActions " + str(action) + " on channel " + str(channel)) if channel < 1: return self.runningActionChannel = channel index = 0 for rule in self.channels[channel - 1].ruleList: if rule.actions & action > 0: self.runningActionId = index if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "processing rule " + str(index + 1), '') parameter = rule.runAction(action, self, parameter) index += 1 self.runningActionChannel = 0 self.runningActionId = 0 return parameter def threadPause(self): if threading.activeCount() > 1: while self.threadPaused == True and self.myOverlay.isExiting == False: time.sleep(self.sleepTime) # This will fail when using config.py try: if self.myOverlay.isExiting == True: self.log("IsExiting") return False except: pass return True def escapeDirJSON(self, dir_name): mydir = uni(dir_name) if (mydir.find(":")): mydir = mydir.replace("\\", "\\\\") return mydir def getSmartPlaylistType(self, dom): self.log('getSmartPlaylistType') try: pltype = dom.getElementsByTagName('smartplaylist') return pltype[0].attributes['type'].value except: self.log("Unable to get the playlist type.", xbmc.LOGERROR) return ''
class ChannelList: def __init__(self): self.networkList = [] self.studioList = [] self.mixedGenreList = [] self.showGenreList = [] self.movieGenreList = [] self.showList = [] self.channels = [] self.videoParser = VideoParser() self.httpJSON = True self.sleepTime = 0 self.discoveredWebServer = False self.threadPaused = False self.runningActionChannel = 0 self.runningActionId = 0 self.enteredChannelCount = 0 self.background = True random.seed() def readConfig(self): self.channelResetSetting = int(REAL_SETTINGS.getSetting("ChannelResetSetting")) self.log('Channel Reset Setting is ' + str(self.channelResetSetting)) self.forceReset = REAL_SETTINGS.getSetting('ForceChannelReset') == "true" self.log('Force Reset is ' + str(self.forceReset)) self.updateDialog = xbmcgui.DialogProgress() self.startMode = int(REAL_SETTINGS.getSetting("StartMode")) self.log('Start Mode is ' + str(self.startMode)) self.backgroundUpdating = int(REAL_SETTINGS.getSetting("ThreadMode")) self.incIceLibrary = REAL_SETTINGS.getSetting('IncludeIceLib') == "true" self.log("IceLibrary is " + str(self.incIceLibrary)) self.showSeasonEpisode = REAL_SETTINGS.getSetting("ShowSeEp") == "true" self.findMaxChannels() if self.forceReset: REAL_SETTINGS.setSetting('ForceChannelReset', "False") self.forceReset = False try: self.lastResetTime = int(ADDON_SETTINGS.getSetting("LastResetTime")) except: self.lastResetTime = 0 try: self.lastExitTime = int(ADDON_SETTINGS.getSetting("LastExitTime")) except: self.lastExitTime = int(time.time()) def setupList(self): self.readConfig() self.updateDialog.create("PseudoTV", "Updating channel list") self.updateDialog.update(0, "Updating channel list") self.updateDialogProgress = 0 foundvalid = False makenewlists = False self.background = False if self.backgroundUpdating > 0 and self.myOverlay.isMaster == True: makenewlists = True # Go through all channels, create their arrays, and setup the new playlist for i in range(self.maxChannels): self.updateDialogProgress = i * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(i + 1), "waiting for file lock") self.channels.append(Channel()) # If the user pressed cancel, stop everything and exit if self.updateDialog.iscanceled(): self.log('Update channels cancelled') self.updateDialog.close() return None self.setupChannel(i + 1, False, makenewlists, False) if self.channels[i].isValid: foundvalid = True if makenewlists == True: REAL_SETTINGS.setSetting('ForceChannelReset', 'false') if foundvalid == False and makenewlists == False: for i in range(self.maxChannels): self.updateDialogProgress = i * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(i + 1), "waiting for file lock", '') self.setupChannel(i + 1, False, True, False) if self.channels[i].isValid: foundvalid = True break self.updateDialog.update(100, "Update complete") self.updateDialog.close() return self.channels def log(self, msg, level = xbmc.LOGDEBUG): log('ChannelList: ' + msg, level) # Determine the maximum number of channels by opening consecutive # playlists until we don't find one def findMaxChannels(self): self.log('findMaxChannels') self.maxChannels = 0 self.enteredChannelCount = 0 for i in range(999): chtype = 9999 chsetting1 = '' chsetting2 = '' try: chtype = int(ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(i + 1) + '_2') except: pass if chtype == 0: if FileAccess.exists(xbmc.translatePath(chsetting1)): self.maxChannels = i + 1 self.enteredChannelCount += 1 elif chtype < 8: if len(chsetting1) > 0: self.maxChannels = i + 1 self.enteredChannelCount += 1 if self.forceReset and (chtype != 9999): ADDON_SETTINGS.setSetting('Channel_' + str(i + 1) + '_changed', "True") self.log('findMaxChannels return ' + str(self.maxChannels)) def determineWebServer(self): if self.discoveredWebServer: return self.discoveredWebServer = True self.webPort = 8080 self.webUsername = '' self.webPassword = '' fle = xbmc.translatePath("special://profile/guisettings.xml") try: xml = FileAccess.open(fle, "r") except: self.log("determineWebServer Unable to open the settings file", xbmc.LOGERROR) self.httpJSON = False return try: dom = parse(xml) except: self.log('determineWebServer Unable to parse settings file', xbmc.LOGERROR) self.httpJSON = False return xml.close() try: plname = dom.getElementsByTagName('webserver') self.httpJSON = (plname[0].childNodes[0].nodeValue.lower() == 'true') self.log('determineWebServer is ' + str(self.httpJSON)) if self.httpJSON == True: plname = dom.getElementsByTagName('webserverport') self.webPort = int(plname[0].childNodes[0].nodeValue) self.log('determineWebServer port ' + str(self.webPort)) plname = dom.getElementsByTagName('webserverusername') self.webUsername = plname[0].childNodes[0].nodeValue self.log('determineWebServer username ' + self.webUsername) plname = dom.getElementsByTagName('webserverpassword') self.webPassword = plname[0].childNodes[0].nodeValue self.log('determineWebServer password is ' + self.webPassword) except: return # Code for sending JSON through http adapted from code by sffjunkie (forum.xbmc.org/showthread.php?t=92196) def sendJSON(self, command): self.log('sendJSON') data = '' usedhttp = False self.determineWebServer() if USING_EDEN: command = command.replace('fields', 'properties') # If there have been problems using the server, just skip the attempt and use executejsonrpc if self.httpJSON == True: try: payload = command.encode('utf-8') except: return data headers = {'Content-Type': 'application/json-rpc; charset=utf-8'} if self.webUsername != '': userpass = base64.encodestring('%s:%s' % (self.webUsername, self.webPassword))[:-1] headers['Authorization'] = 'Basic %s' % userpass try: conn = httplib.HTTPConnection('127.0.0.1', self.webPort) conn.request('POST', '/jsonrpc', payload, headers) response = conn.getresponse() if response.status == 200: data = response.read() usedhttp = True conn.close() except: self.log("Exception when getting JSON data") if usedhttp == False: self.httpJSON = False data = xbmc.executeJSONRPC(command) return data def setupChannel(self, channel, background = False, makenewlist = False, append = False): self.log('setupChannel ' + str(channel)) returnval = False createlist = makenewlist chtype = 9999 chsetting1 = '' chsetting2 = '' needsreset = False self.background = background self.settingChannel = channel try: chtype = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_type')) chsetting1 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_1') chsetting2 = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_2') except: pass while len(self.channels) < channel: self.channels.append(Channel()) if chtype == 9999: self.channels[channel - 1].isValid = False return False self.channels[channel - 1].isSetup = True self.channels[channel - 1].loadRules(channel) self.runActions(RULES_ACTION_START, channel, self.channels[channel - 1]) GlobalFileLock.lockFile(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', True) try: needsreset = ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_changed') == 'True' except: pass # If possible, use an existing playlist # Don't do this if we're appending an existing channel # Don't load if we need to reset anyway if FileAccess.exists(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') and append == False and needsreset == False: try: self.channels[channel - 1].totalTimePlayed = int(ADDON_SETTINGS.getSetting('Channel_' + str(channel) + '_time', True)) createlist = True if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(channel), "reading playlist", '') if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: self.channels[channel - 1].isValid = True self.channels[channel - 1].fileName = CHANNELS_LOC + 'channel_' + str(channel) + '.m3u' returnval = True # If this channel has been watched for longer than it lasts, reset the channel if self.channelResetSetting == 0 and self.channels[channel - 1].totalTimePlayed < self.channels[channel - 1].getTotalDuration(): createlist = False if self.channelResetSetting > 0 and self.channelResetSetting < 4: timedif = time.time() - self.lastResetTime if self.channelResetSetting == 1 and timedif < (60 * 60 * 24): createlist = False if self.channelResetSetting == 2 and timedif < (60 * 60 * 24 * 7): createlist = False if self.channelResetSetting == 3 and timedif < (60 * 60 * 24 * 30): createlist = False if timedif < 0: createlist = False if self.channelResetSetting == 4: createlist = False except: pass if createlist or needsreset: self.channels[channel - 1].isValid = False if makenewlist: try: os.remove(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') except: pass append = False if createlist: ADDON_SETTINGS.setSetting('LastResetTime', str(int(time.time()))) if append == False: if chtype == 6 and chsetting2 == str(MODE_ORDERAIRDATE): self.channels[channel - 1].mode = MODE_ORDERAIRDATE # if there is no start mode in the channel mode flags, set it to the default if self.channels[channel - 1].mode & MODE_STARTMODES == 0: if self.startMode == 0: self.channels[channel - 1].mode |= MODE_RESUME elif self.startMode == 1: self.channels[channel - 1].mode |= MODE_REALTIME elif self.startMode == 2: self.channels[channel - 1].mode |= MODE_RANDOM if ((createlist or needsreset) and makenewlist) or append: if self.background == False: self.updateDialogProgress = (channel - 1) * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(channel), "adding videos", '') if self.makeChannelList(channel, chtype, chsetting1, chsetting2, append) == True: if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == True: returnval = True self.channels[channel - 1].fileName = CHANNELS_LOC + 'channel_' + str(channel) + '.m3u' self.channels[channel - 1].isValid = True # Don't reset variables on an appending channel if append == False: self.channels[channel - 1].totalTimePlayed = 0 ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_time', '0') if needsreset: ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_changed', 'False') self.runActions(RULES_ACTION_BEFORE_CLEAR, channel, self.channels[channel - 1]) # Don't clear history when appending channels if self.background == False and append == False and self.myOverlay.isMaster: self.updateDialogProgress = (channel - 1) * 100 // self.enteredChannelCount self.updateDialog.update(self.updateDialogProgress, "Loading channel " + str(channel), "clearing history", '') self.clearPlaylistHistory(channel) GlobalFileLock.unlockFile(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') if append == False: self.runActions(RULES_ACTION_BEFORE_TIME, channel, self.channels[channel - 1]) if self.channels[channel - 1].mode & MODE_ALWAYSPAUSE > 0: self.channels[channel - 1].isPaused = True if self.channels[channel - 1].mode & MODE_RANDOM > 0: self.channels[channel - 1].showTimeOffset = random.randint(0, self.channels[channel - 1].getTotalDuration()) if self.channels[channel - 1].mode & MODE_REALTIME > 0: timedif = int(self.myOverlay.timeStarted) - self.lastExitTime self.channels[channel - 1].totalTimePlayed += timedif if self.channels[channel - 1].mode & MODE_RESUME > 0: self.channels[channel - 1].showTimeOffset = self.channels[channel - 1].totalTimePlayed self.channels[channel - 1].totalTimePlayed = 0 while self.channels[channel - 1].showTimeOffset > self.channels[channel - 1].getCurrentDuration(): self.channels[channel - 1].showTimeOffset -= self.channels[channel - 1].getCurrentDuration() self.channels[channel - 1].addShowPosition(1) self.channels[channel - 1].name = self.getChannelName(chtype, chsetting1) if ((createlist or needsreset) and makenewlist) and returnval: self.runActions(RULES_ACTION_FINAL_MADE, channel, self.channels[channel - 1]) else: self.runActions(RULES_ACTION_FINAL_LOADED, channel, self.channels[channel - 1]) return returnval def clearPlaylistHistory(self, channel): self.log("clearPlaylistHistory") if self.channels[channel - 1].isValid == False: self.log("channel not valid, ignoring") return # if we actually need to clear anything if self.channels[channel - 1].totalTimePlayed > (60 * 60 * 24 * 2): try: fle = FileAccess.open(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', 'w') except: self.log("clearPlaylistHistory Unable to open the smart playlist", xbmc.LOGERROR) return flewrite = "#EXTM3U\n" tottime = 0 timeremoved = 0 for i in range(self.channels[channel - 1].Playlist.size()): tottime += self.channels[channel - 1].getItemDuration(i) if tottime > (self.channels[channel - 1].totalTimePlayed - (60 * 60 * 12)): tmpstr = str(self.channels[channel - 1].getItemDuration(i)) + ',' tmpstr += self.channels[channel - 1].getItemTitle(i) + "//" + self.channels[channel - 1].getItemEpisodeTitle(i) + "//" + self.channels[channel - 1].getItemDescription(i) tmpstr = tmpstr[:600] tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace("\\\"", "\"") tmpstr = tmpstr + '\n' + self.channels[channel - 1].getItemFilename(i) flewrite += "#EXTINF:" + tmpstr + "\n" else: timeremoved = tottime fle.write(flewrite) fle.close() if timeremoved > 0: if self.channels[channel - 1].setPlaylist(CHANNELS_LOC + 'channel_' + str(channel) + '.m3u') == False: self.channels[channel - 1].isValid = False else: self.channels[channel - 1].totalTimePlayed -= timeremoved # Write this now so anything sharing the playlists will get the proper info ADDON_SETTINGS.setSetting('Channel_' + str(channel) + '_time', str(self.channels[channel - 1].totalTimePlayed)) def getChannelName(self, chtype, setting1): self.log('getChannelName ' + str(chtype)) if len(setting1) == 0: return '' if chtype == 0: return self.getSmartPlaylistName(setting1) elif chtype == 1 or chtype == 2 or chtype == 5 or chtype == 6: return setting1 elif chtype == 3: return setting1 + " TV" elif chtype == 4: return setting1 + " Movies" elif chtype == 7: if setting1[-1] == '/' or setting1[-1] == '\\': return os.path.split(setting1[:-1])[1] else: return os.path.split(setting1)[1] return '' # Open the smart playlist and read the name out of it...this is the channel name def getSmartPlaylistName(self, fle): self.log('getSmartPlaylistName') fle = xbmc.translatePath(fle) try: xml = FileAccess.open(fle, "r") except: self.log("getSmartPlaylisyName Unable to open the smart playlist " + fle, xbmc.LOGERROR) return '' try: dom = parse(xml) except: self.log('getSmartPlaylistName Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return '' xml.close() try: plname = dom.getElementsByTagName('name') self.log('getSmartPlaylistName return ' + plname[0].childNodes[0].nodeValue) return plname[0].childNodes[0].nodeValue except: self.log("Unable to get the playlist name.", xbmc.LOGERROR) return '' # Based on a smart playlist, create a normal playlist that can actually be used by us def makeChannelList(self, channel, chtype, setting1, setting2, append = False): self.log('makeChannelList ' + str(channel)) israndom = False fileList = [] if chtype == 7: fileList = self.createDirectoryPlaylist(setting1) israndom = True else: if chtype == 0: if FileAccess.copy(setting1, MADE_CHAN_LOC + os.path.split(setting1)[1]) == False: if FileAccess.exists(MADE_CHAN_LOC + os.path.split(setting1)[1]) == False: self.log("Unable to copy or find playlist " + setting1) return False fle = MADE_CHAN_LOC + os.path.split(setting1)[1] else: fle = self.makeTypePlaylist(chtype, setting1, setting2) fle = xbmc.translatePath(fle) if len(fle) == 0: self.log('Unable to locate the playlist for channel ' + str(channel), xbmc.LOGERROR) return False try: xml = FileAccess.open(fle, "r") except: self.log("makeChannelList Unable to open the smart playlist " + fle, xbmc.LOGERROR) return False try: dom = parse(xml) except: self.log('makeChannelList Problem parsing playlist ' + fle, xbmc.LOGERROR) xml.close() return False xml.close() if self.getSmartPlaylistType(dom) == 'mixed': fileList = self.buildMixedFileList(dom, channel) else: fileList = self.buildFileList(fle, channel) try: order = dom.getElementsByTagName('order') if order[0].childNodes[0].nodeValue.lower() == 'random': israndom = True except: pass try: if append == True: channelplaylist = FileAccess.open(CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "r+") channelplaylist.seek(0, 2) else: channelplaylist = FileAccess.open(CHANNELS_LOC + "channel_" + str(channel) + ".m3u", "w") except: self.log('Unable to open the cache file ' + CHANNELS_LOC + 'channel_' + str(channel) + '.m3u', xbmc.LOGERROR) return False if append == False: channelplaylist.write("#EXTM3U\n") if len(fileList) == 0: self.log("Unable to get information about channel " + str(channel), xbmc.LOGERROR) channelplaylist.close() return False if israndom: random.shuffle(fileList) if len(fileList) > 4096: fileList = fileList[:4096] fileList = self.runActions(RULES_ACTION_LIST, channel, fileList) self.channels[channel - 1].isRandom = israndom if append: if len(fileList) + self.channels[channel - 1].Playlist.size() > 4096: fileList = fileList[:(4096 - self.channels[channel - 1].Playlist.size())] else: if len(fileList) > 4096: fileList = fileList[:4096] # Write each entry into the new playlist for string in fileList: channelplaylist.write("#EXTINF:" + string + "\n") channelplaylist.close() self.log('makeChannelList return') return True def makeTypePlaylist(self, chtype, setting1, setting2): if chtype == 1: if len(self.networkList) == 0: self.fillTVInfo() return self.createNetworkPlaylist(setting1) elif chtype == 2: if len(self.studioList) == 0: self.fillMovieInfo() return self.createStudioPlaylist(setting1) elif chtype == 3: if len(self.showGenreList) == 0: self.fillTVInfo() return self.createGenrePlaylist('episodes', chtype, setting1) elif chtype == 4: if len(self.movieGenreList) == 0: self.fillMovieInfo() return self.createGenrePlaylist('movies', chtype, setting1) elif chtype == 5: if len(self.mixedGenreList) == 0: if len(self.showGenreList) == 0: self.fillTVInfo() if len(self.movieGenreList) == 0: self.fillMovieInfo() self.mixedGenreList = self.makeMixedList(self.showGenreList, self.movieGenreList) self.mixedGenreList.sort(key=lambda x: x.lower()) return self.createGenreMixedPlaylist(setting1) elif chtype == 6: if len(self.showList) == 0: self.fillTVInfo() return self.createShowPlaylist(setting1, setting2) self.log('makeTypePlaylists invalid channel type: ' + str(chtype)) return '' def createNetworkPlaylist(self, network): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Network_' + network + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "episodes", self.getChannelName(1, network)) network = network.lower() added = False for i in range(len(self.showList)): if self.threadPause() == False: fle.close() return '' if self.showList[i][1].lower() == network: theshow = self.cleanString(self.showList[i][0]) fle.write(' <rule field="tvshow" operator="is">' + theshow + '</rule>\n') added = True self.writeXSPFooter(fle, 0, "random") fle.close() if added == False: return '' return flename def createShowPlaylist(self, show, setting2): order = 'random' try: setting = int(setting2) if setting & MODE_ORDERAIRDATE > 0: order = 'airdate' except: pass flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Show_' + show + '_' + order + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, 'episodes', self.getChannelName(6, show)) show = self.cleanString(show) fle.write(' <rule field="tvshow" operator="is">' + show + '</rule>\n') self.writeXSPFooter(fle, 0, order) fle.close() return flename def createGenreMixedPlaylist(self, genre): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Mixed_' + genre + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' epname = os.path.basename(self.createGenrePlaylist('episodes', 3, genre)) moname = os.path.basename(self.createGenrePlaylist('movies', 4, genre)) self.writeXSPHeader(fle, 'mixed', self.getChannelName(5, genre)) fle.write(' <rule field="playlist" operator="is">' + epname + '</rule>\n') fle.write(' <rule field="playlist" operator="is">' + moname + '</rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createGenrePlaylist(self, pltype, chtype, genre): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + pltype + '_' + genre + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, pltype, self.getChannelName(chtype, genre)) genre = self.cleanString(genre) fle.write(' <rule field="genre" operator="is">' + genre + '</rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createStudioPlaylist(self, studio): flename = xbmc.makeLegalFilename(GEN_CHAN_LOC + 'Studio_' + studio + '.xsp') try: fle = FileAccess.open(flename, "w") except: self.Error('Unable to open the cache file ' + flename, xbmc.LOGERROR) return '' self.writeXSPHeader(fle, "movies", self.getChannelName(2, studio)) studio = self.cleanString(studio) fle.write(' <rule field="studio" operator="is">' + studio + '</rule>\n') self.writeXSPFooter(fle, 0, "random") fle.close() return flename def createDirectoryPlaylist(self, setting1): self.log("createDirectoryPlaylist " + setting1) fileList = [] filecount = 0 json_query = '{"jsonrpc": "2.0", "method": "Files.GetDirectory", "params": {"directory": "%s", "media": "files"}, "id": 1}' % ( self.escapeDirJSON(setting1),) if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "getting file list") json_folder_detail = self.sendJSON(json_query) # self.log(json_folder_detail) file_detail = re.compile( "{(.*?)}", re.DOTALL ).findall(json_folder_detail) thedir = '' if setting1[-1:1] == '/' or setting1[-1:1] == '\\': thedir = os.path.split(setting1[:-1])[1] else: thedir = os.path.split(setting1)[1] for f in file_detail: if self.threadPause() == False: del fileList[:] break match = re.search('"file" *: *"(.*?)",', f) if match: if(match.group(1).endswith("/") or match.group(1).endswith("\\")): fileList.extend(self.createDirectoryPlaylist(match.group(1).replace("\\\\", "\\"))) else: duration = self.videoParser.getVideoLength(match.group(1).replace("\\\\", "\\")) if duration > 0: filecount += 1 if self.background == False: if filecount == 1: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entry") else: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entries") afile = os.path.split(match.group(1).replace("\\\\", "\\"))[1] afile, ext = os.path.splitext(afile) tmpstr = str(duration) + ',' tmpstr += afile + "//" + thedir + "//" tmpstr = tmpstr[:600] tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace("\\\"", "\"") tmpstr += "\n" + match.group(1).replace("\\\\", "\\") fileList.append(tmpstr) if filecount == 0: self.log(json_folder_detail) return fileList def writeXSPHeader(self, fle, pltype, plname): fle.write('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>\n') fle.write('<smartplaylist type="' + pltype + '">\n') plname = self.cleanString(plname) fle.write(' <name>' + plname + '</name>\n') fle.write(' <match>one</match>\n') def writeXSPFooter(self, fle, limit, order): if limit > 0: fle.write(' <limit>' + str(limit) + '</limit>\n') fle.write(' <order direction="ascending">' + order + '</order>\n') fle.write('</smartplaylist>\n') def cleanString(self, string): newstr = string newstr = newstr.replace('&', '&') newstr = newstr.replace('>', '>') newstr = newstr.replace('<', '<') return newstr def fillTVInfo(self, sortbycount = False): self.log("fillTVInfo") json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetTVShows", "params": {"fields":["studio", "genre"]}, "id": 1}' if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "reading TV data") json_folder_detail = self.sendJSON(json_query) # self.log(json_folder_detail) detail = re.compile( "{(.*?)}", re.DOTALL ).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return match = re.search('"studio" *: *"(.*?)",', f) network = '' if match: found = False network = match.group(1).strip() for item in range(len(self.networkList)): if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return itm = self.networkList[item] if sortbycount: itm = itm[0] if itm.lower() == network.lower(): found = True if sortbycount: self.networkList[item][1] += 1 break if found == False and len(network) > 0: if sortbycount: self.networkList.append([network, 1]) else: self.networkList.append(network) match = re.search('"label" *: *"(.*?)",', f) if match: show = match.group(1).strip() self.showList.append([show, network]) match = re.search('"genre" *: *"(.*?)",', f) if match: genres = match.group(1).split('/') for genre in genres: found = False curgenre = genre.lower().strip() for g in range(len(self.showGenreList)): if self.threadPause() == False: del self.networkList[:] del self.showList[:] del self.showGenreList[:] return itm = self.showGenreList[g] if sortbycount: itm = itm[0] if curgenre == itm.lower(): found = True if sortbycount: self.showGenreList[g][1] += 1 break if found == False: if sortbycount: self.showGenreList.append([genre.strip(), 1]) else: self.showGenreList.append(genre.strip()) if sortbycount: self.networkList.sort(key=lambda x: x[1], reverse = True) self.showGenreList.sort(key=lambda x: x[1], reverse = True) else: self.networkList.sort(key=lambda x: x.lower()) self.showGenreList.sort(key=lambda x: x.lower()) if (len(self.showList) == 0) and (len(self.showGenreList) == 0) and (len(self.networkList) == 0): self.log(json_folder_detail) self.log("found shows " + str(self.showList)) self.log("found genres " + str(self.showGenreList)) self.log("fillTVInfo return " + str(self.networkList)) def fillMovieInfo(self, sortbycount = False): self.log("fillMovieInfo") studioList = [] json_query = '{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": {"fields":["studio", "genre"]}, "id": 1}' if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "reading movie data") json_folder_detail = self.sendJSON(json_query) # self.log(json_folder_detail) detail = re.compile( "{(.*?)}", re.DOTALL ).findall(json_folder_detail) for f in detail: if self.threadPause() == False: del self.movieGenreList[:] del self.studioList[:] del studioList[:] break match = re.search('"genre" *: *"(.*?)",', f) if match: genres = match.group(1).split('/') for genre in genres: found = False curgenre = genre.lower().strip() for g in range(len(self.movieGenreList)): itm = self.movieGenreList[g] if sortbycount: itm = itm[0] if curgenre == itm.lower(): found = True if sortbycount: self.movieGenreList[g][1] += 1 break if found == False: if sortbycount: self.movieGenreList.append([genre.strip(), 1]) else: self.movieGenreList.append(genre.strip()) match = re.search('"studio" *: *"(.*?)",', f) if match: studios = match.group(1).split('/') for studio in studios: curstudio = studio.strip() found = False for i in range(len(studioList)): if studioList[i][0].lower() == curstudio.lower(): studioList[i][1] += 1 found = True break if found == False and len(curstudio) > 0: studioList.append([curstudio, 1]) maxcount = 0 for i in range(len(studioList)): if studioList[i][1] > maxcount: maxcount = studioList[i][1] bestmatch = 1 lastmatch = 1000 counteditems = 0 for i in range(maxcount, 0, -1): itemcount = 0 for j in range(len(studioList)): if studioList[j][1] == i: itemcount += 1 if abs(itemcount + counteditems - 8) < abs(lastmatch - 8): bestmatch = i lastmatch = itemcount counteditems += itemcount if sortbycount: studioList.sort(key=lambda x: x[1], reverse=True) self.movieGenreList.sort(key=lambda x: x[1], reverse=True) else: studioList.sort(key=lambda x: x[0].lower()) self.movieGenreList.sort(key=lambda x: x.lower()) for i in range(len(studioList)): if studioList[i][1] >= bestmatch: if sortbycount: self.studioList.append([studioList[i][0], studioList[i][1]]) else: self.studioList.append(studioList[i][0]) if (len(self.movieGenreList) == 0) and (len(self.studioList) == 0): self.log(json_folder_detail) self.log("found genres " + str(self.movieGenreList)) self.log("fillMovieInfo return " + str(self.studioList)) def makeMixedList(self, list1, list2): self.log("makeMixedList") newlist = [] for item in list1: curitem = item.lower() for a in list2: if curitem == a.lower(): newlist.append(item) break self.log("makeMixedList return " + str(newlist)) return newlist def buildFileList(self, dir_name, channel): self.log("buildFileList") fileList = [] seasoneplist = [] filecount = 0 json_query = '{"jsonrpc": "2.0", "method": "Files.GetDirectory", "params": {"directory": "%s", "media": "video", "fields":["season","episode","playcount","streamdetails","duration","runtime","tagline","showtitle","album","artist","plot"]}, "id": 1}' % (self.escapeDirJSON(dir_name)) if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "querying database") json_folder_detail = self.sendJSON(json_query) # self.log(json_folder_detail) file_detail = re.compile( "{(.*?)}", re.DOTALL ).findall(json_folder_detail) for f in file_detail: if self.threadPause() == False: del fileList[:] break match = re.search('"file" *: *"(.*?)",', f) istvshow = False if match: if(match.group(1).endswith("/") or match.group(1).endswith("\\")): fileList.extend(self.buildFileList(match.group(1), channel)) else: f = self.runActions(RULES_ACTION_JSON, channel, f) duration = re.search('"duration" *: *([0-9]*?),', f) try: dur = int(duration.group(1)) except: dur = 0 # If duration doesn't exist, try to figure it out if dur == 0: dur = self.videoParser.getVideoLength(match.group(1).replace("\\\\", "\\")) # As a last resort (since it's not as accurate), use runtime if dur == 0: duration = re.search('"runtime" *: *"([0-9]*?)",', f) try: # Runtime is reported in minutes dur = int(duration.group(1)) * 60 except: dur = 0 # Remove any file types that we don't want (ex. IceLibrary) if self.incIceLibrary == False: if match.group(1).replace("\\\\", "\\")[-4:].lower() == 'strm': dur = 0 try: if dur > 0: filecount += 1 seasonval = -1 epval = -1 if self.background == False: if filecount == 1: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entry") else: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "adding videos", "added " + str(filecount) + " entries") title = re.search('"label" *: *"(.*?)"', f) tmpstr = str(dur) + ',' showtitle = re.search('"showtitle" *: *"(.*?)"', f) plot = re.search('"plot" *: *"(.*?)",', f) if plot == None: theplot = "" else: theplot = plot.group(1) # This is a TV show if showtitle != None and len(showtitle.group(1)) > 0: season = re.search('"season" *: *(.*?),', f) episode = re.search('"episode" *: *(.*?),', f) swtitle = title.group(1) try: seasonval = int(season.group(1)) epval = int(episode.group(1)) if self.showSeasonEpisode: swtitle = swtitle + '(S' + ('0' if seasonval < 10 else '') + str(seasonval) + ' E' + ('0' if epval < 10 else '') + str(epval) + ')' except: seasonval = -1 epval = -1 tmpstr += showtitle.group(1) + "//" + swtitle + "//" + theplot istvshow = True else: tmpstr += title.group(1) + "//" album = re.search('"album" *: *"(.*?)"', f) # This is a movie if album == None or len(album.group(1)) == 0: tagline = re.search('"tagline" *: *"(.*?)"', f) if tagline != None: tmpstr += tagline.group(1) tmpstr += "//" + theplot else: artist = re.search('"artist" *: *"(.*?)"', f) tmpstr += album.group(1) + "//" + artist.group(1) tmpstr = tmpstr[:600] tmpstr = tmpstr.replace("\\n", " ").replace("\\r", " ").replace("\\\"", "\"") tmpstr = tmpstr + '\n' + match.group(1).replace("\\\\", "\\") if self.channels[channel - 1].mode & MODE_ORDERAIRDATE > 0: seasoneplist.append([seasonval, epval, tmpstr]) else: fileList.append(tmpstr) except: pass else: continue if self.channels[channel - 1].mode & MODE_ORDERAIRDATE > 0: seasoneplist.sort(key=lambda seep: seep[1]) seasoneplist.sort(key=lambda seep: seep[0]) for seepitem in seasoneplist: fileList.append(seepitem[2]) if filecount == 0: self.log(json_folder_detail) self.log("buildFileList return") return fileList def buildMixedFileList(self, dom1, channel): fileList = [] self.log('buildMixedFileList') try: rules = dom1.getElementsByTagName('rule') order = dom1.getElementsByTagName('order') except: self.log('buildMixedFileList Problem parsing playlist ' + filename, xbmc.LOGERROR) xml.close() return fileList for rule in rules: rulename = rule.childNodes[0].nodeValue if FileAccess.exists(xbmc.translatePath('special://profile/playlists/video/') + rulename): FileAccess.copy(xbmc.translatePath('special://profile/playlists/video/') + rulename, MADE_CHAN_LOC + rulename) fileList.extend(self.buildFileList(MADE_CHAN_LOC + rulename, channel)) else: fileList.extend(self.buildFileList(GEN_CHAN_LOC + rulename, channel)) self.log("buildMixedFileList returning") return fileList # Run rules for a channel def runActions(self, action, channel, parameter): self.log("runActions " + str(action) + " on channel " + str(channel)) if channel < 1: return self.runningActionChannel = channel index = 0 for rule in self.channels[channel - 1].ruleList: if rule.actions & action > 0: self.runningActionId = index if self.background == False: self.updateDialog.update(self.updateDialogProgress, "Updating channel " + str(self.settingChannel), "processing rule " + str(index + 1), '') parameter = rule.runAction(action, self, parameter) index += 1 self.runningActionChannel = 0 self.runningActionId = 0 return parameter def threadPause(self): if threading.activeCount() > 1: while self.threadPaused == True and self.myOverlay.isExiting == False: time.sleep(self.sleepTime) # This will fail when using config.py try: if self.myOverlay.isExiting == True: self.log("IsExiting") return False except: pass return True def escapeDirJSON(self, dir_name): if (dir_name.find(":")): dir_name = dir_name.replace("\\", "\\\\") return dir_name def getSmartPlaylistType(self, dom): self.log('getSmartPlaylistType') try: pltype = dom.getElementsByTagName('smartplaylist') return pltype[0].attributes['type'].value except: self.log("Unable to get the playlist type.", xbmc.LOGERROR) return ''