def __init__(self, sagexHost, useLock=True, log=None): '''Creates a SageX object @param sagexHost url such as http://usr:pwd@host:port/ @param useLock whether to synchronize sagex API calls @param log plexlog obj from Agent, or None ''' # setup log if log: # called from agent self.log = log self.isAgent = True else: # called from scanner, use default (console) self.log = plexlog.PlexLog() self.isAgent = False # set property if sagexHost: if not sagexHost.endswith('/'): sagexHost += '/' self.SAGEX_HOST = sagexHost else: self.SAGEX_HOST = '' self.log.error('SageX: sagexHost not specified!!') # used to synchronize sagex http call, otherwise may get # fanart download problem as PLEX calls Agent.search() and # Agent.update() from multiple threads and there seems to be # some issue with downloading fanart there. self.useLock = useLock # self.lock = Thread.Lock('sagexLock') self.lock = threading.Lock()
def __init__(self, platform, env_var=DEFAULT_CFG_ENV, log=None): '''Create a SagePlexConfig object Creates a configuration object by either using default or read configuration file pointed by SAGEPLEX_CFG environment variable @param platform current platform, either python sys.platform or Platform.OS under PMS @param env environment variable to lookup config file @param log PlexLog obj from agent, or None ''' self.data = {} self.SAGEX_HOST = None self.PLEX_HOST = None if log: # called from agent self.log = log else: # called from scanner, use default (console) self.log = plexlog.PlexLog() # look up environment variable if specified cfg = None if env_var: # if env_var is passed in to constructor cfg = os.environ.get(env_var) if cfg: # if env variable exist # expand env variables in value such as # %LOCALAPPDATA%\Plex Media Server\sageplex_cfg.json cfg = os.path.expandvars(cfg) self.log.info('Config: %s=%s', env_var, cfg) if os.path.isdir(cfg): cfg = os.path.join(cfg, CFG_FILE) self.log.info( 'Config: %s points to directory, adding %s to path', env_var, CFG_FILE) else: self.log.info('Config: no env var %s', env_var) # lookup location if no env var used if not cfg: cfg = os.path.join(self.plexDataDir(platform), CFG_FILE) if not os.path.isfile(cfg): self.log.error('Config: file not found: %s', cfg) return # now try to open the JSON file and read it self.log.info('Config: %s', cfg) try: if log: # if in agent, use readFile() data_file = self.readFile(cfg) if not data_file: self.log.error("Config: readfile() failed to return data") return self.data = JSON.ObjectFromString(data_file) else: # use python open() with open(cfg) as data_file: self.data = json.load(data_file) except (IOError, ValueError, os.error), e: self.log.error("Config: %s: %s", cfg, e) return
def __init__(self, plexHost, log=None, token=None): '''Creates a PlexApi object @param plexHost url such as http://host:port/ @param log plexlog obj from Agent, or None ''' self.appid = 'com.plexapp.plugins.library' # setup log if log: # called from agent self.log = log self.isAgent = True else: # called from scanner, use default (console) self.log = plexlog.PlexLog() self.isAgent = False # set PLEX_HOST, remove ending / if any if plexHost: if plexHost[-1] == '/': # if ends with / plexHost = plexHost[:-1] # remove ending / self.PLEX_HOST = plexHost else: self.PLEX_HOST = '' self.log.error('PlexApi: plexHost not specified!!') # store token value self.PLEX_TOKEN = token
def Start(): '''This function is called when the plug-in first starts. It can be used to perform extra initialisation tasks such as configuring the environment and setting default attributes. ''' global mylog, myconfig, mysagex, myplex mylog = plexlog.PlexLog(isAgent=True) # use Log instead of logging mylog.info('***** Initializing "SageTV BMT Agent (TV Shows)" *****') HTTP.CacheTime = CACHE_1HOUR * 24 # PMS supports a DefaultPrefs.json file for plug-ins, and settings # can be accessed via simple Prefs['id']. # # The problem is, after the 1st run, settings are stored per plugin # in an XML file in the "plug-in support folder": # plug-in support\preferences\com.plexapp.agents.bmtagenttvshows.xml # This makes updating the setting by the user a little confusing, # as there are 2 places to change. # # using a standalone json file for both scanner/agent is easier # for the user to understand and manage. myconfig = config.Config(Platform.OS, log=mylog) sagexHost = myconfig.getSagexHost() mylog.info('SAGE_HOST: %s', sagexHost) plexHost = myconfig.getPlexHost() mylog.info('PLEX_HOST: %s', plexHost) # create the SageX object from sagex module mysagex = sagex.SageX(sagexHost, useLock=myconfig.getAgentLocking(), log=mylog) # create plex api object myplex = plexapi.PlexApi(plexHost, log=mylog, token=myconfig.getPlexToken())
def update(self, metadata, media, lang): '''Adding metadata to media Once an item has been successfully matched, it is added to the update queue. As the framework processes queued items, it calls the update method of the relevant agents. This is called once for each Show. The function should craw through all the seasons/episodes and set the appropriate attribute on each one. @param metadata A pre-initialized metadata object if this is the first time the item is being updated, or the existing metadata object if the item is being refreshed. @param media An object containing information about the media hierarchy in the database. @param lang A string identifying the user's currently selected language. ''' # use a local mylog object as this function is called from # different threads and we want the log header (that is stored # inside the mylog object) to be unique for each call. mylog = plexlog.PlexLog(isAgent=True) mylog.info("***** entering BMTAgent.update(%s) ***** ", media.title) if media and metadata.title is None: metadata.title = media.title # Show/Series info is set when we encounter 1st episode below seriesInfo = False # now set information for each episode in each season for s in media.seasons: season = metadata.seasons[s] season.index = int(s) for e in media.seasons[s].episodes: episode = metadata.seasons[s].episodes[e] episodeMedia = media.seasons[s].episodes[e].items[0] # the file for episode is available from the media # object, no need to use mysagex.getMediaFilesForShow epFile = os.path.basename(episodeMedia.parts[0].file) # set a log prefix such as: "showname/s5/e11: " mylog.setPrefix('%s/s%s/e%s: ' % (metadata.title, s, e)) mylog.info('%s', epFile) # get the MediaFile object from sage mf = mysagex.getMediaFileForName(epFile) if not mf: # this should not happen mylog.error("no media info from SageTV") continue mediaFileID = mf.get('MediaFileID') mylog.info('mfid: %s', mediaFileID) # retrieving the airing/show field that should always exist airing = mf.get('Airing') if not airing: mylog.error('no Airing field, skipping file') continue show = airing.get('Show') if not show: mylog.error('no [Airing][Show] field, skipping file') continue # set series info if we haven't done so already if not seriesInfo: if self.setShowSeriesInfo(metadata, media, mf, mylog): seriesInfo = True # TODO: move the following episode setter to own function? mylog.info('setting episode level metadata') if (airing.get('AiringStartTime')): startTime = airing.get('AiringStartTime') // 1000 airDate = Datetime.FromTimestamp(startTime) mylog.debug('airdate: %s', airDate) episode.title = show.get('ShowEpisode') if not episode.title: episode.title = show.get('ShowTitle') episode.summary = show.get('ShowDescription') episode.originally_available_at = airDate episode.duration = airing.get('AiringDuration') episode.season = int(s) stars = show.get('PeopleListInShow') episode.guest_stars.clear() if stars: # See issue #13 for star in stars: episode.guest_stars.add(star) else: mylog.debug('no PeopleListInShow data.') # set rating rSource = airing.get('ParentalRating') rTarget = rSource[:2] + '-' + rSource[2:] mylog.debug('rating: %s -> %s', rSource, rTarget) episode.content_rating = rTarget # set watch flag and resume position self.setWatchStatus(mf, airing, media, s, e, mylog) # misc stuff mfprops = mf.get('MediaFileMetadataProperties') episode.guest_stars.add(show.get('PeopleInShow')) episode.writers.add(mfprops.get('Writer')) episode.directors.add(mfprops.get('Director')) episode.producers.add(mfprops.get('ExecutiveProducer')) # set the fanart for show and episode self.setFanArt(metadata, season, episode, mediaFileID, mylog) mylog.debug( "COMPLETED SETTING EPISODE-LEVEL METADATA: " "episode.title=%s;episode.summary=%s;" "episode.originally_available_at=%s;episode.duration=%s;" "episode.season=%s;metadata.content_rating=%s;" % (episode.title, episode.summary, episode.originally_available_at, episode.duration, episode.season, metadata.content_rating))