def __init__(self): client_info = clientinfo.ClientInfo() self.version = client_info.get_version() self.device_id = client_info.get_device_id() # user agent string, used for OS and Kodi version identification kodi_ver = xbmc.getInfoLabel("System.BuildVersion") if(not kodi_ver): kodi_ver = "na" kodi_ver = kodi_ver.strip() if(kodi_ver.find(" ") > 0): kodi_ver = kodi_ver[0:kodi_ver.find(" ")] self.userAgent = "Kodi/" + kodi_ver + " (" + self.getUserAgentOS() + ")" # Use set user name self.user_name = settings('username') or settings('connectUsername') or 'None' # use md5 for client and user for analytics self.device_id = hashlib.md5(self.device_id).hexdigest() self.user_name = hashlib.md5(self.user_name).hexdigest() # resolution self.screen_mode = xbmc.getInfoLabel("System.ScreenMode") self.screen_height = xbmc.getInfoLabel("System.ScreenHeight") self.screen_width = xbmc.getInfoLabel("System.ScreenWidth") self.lang = xbmc.getInfoLabel("System.Language")
def _startup(self): serverId = settings('serverId') if(serverId != None): serverId = hashlib.md5(serverId).hexdigest() ga = GoogleAnalytics() ga.sendEventData("Application", "Startup", serverId) try: ga.sendEventData("Version", "OS", platform.platform()) ga.sendEventData("Version", "Python", platform.python_version()) except Exception: pass # Start up events self.warn_auth = True username = self.userclient_thread.get_username() if settings('connectMsg') == "true" and username: # Get additional users add_users = settings('additionalUsers') if add_users: add_users = ", "+", ".join(add_users.split(',')) dialog(type_="notification", heading="{emby}", message=("%s %s%s" % (lang(33000), username.decode('utf-8'), add_users.decode('utf-8'))), icon="{emby}", time=2000, sound=False) return True
def saveLastSync(self): # Save last sync time overlap = 2 try: # datetime fails when used more than once, TypeError if self.isFastSync: result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json") server_time = result['ServerDateTime'] server_time = utils.convertDate(server_time) else: raise Exception("Fast sync server plugin is not enabled.") except Exception as e: # If the server plugin is not installed or an error happened. log.debug("An exception occurred: %s" % e) time_now = datetime.utcnow()-timedelta(minutes=overlap) lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ') log.info("New sync time: client time -%s min: %s" % (overlap, lastSync)) else: lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ') log.info("New sync time: server time -%s min: %s" % (overlap, lastSync)) finally: settings('LastIncrementalSync', value=lastSync)
def isDirectStream(self): item = self.item if (utils.settings('transcodeH265') == "true" and item['MediaSources'][0]['Name'].startswith(("1080P/HEVC","1080P/H265"))): # Avoid H265 1080p self.logMsg("Option to transcode 1080P/H265 enabled.", 1) return False elif (utils.settings('transcode720H265') == "true" and item['MediaSources'][0]['Name'].startswith(("720P/HEVC","720P/H265"))): # Avoid H265 720p self.logMsg("Option to transcode 720P/H265 enabled.", 1) return False # Requirement: BitRate, supported encoding canDirectStream = item['MediaSources'][0]['SupportsDirectStream'] # Make sure the server supports it if not canDirectStream: return False # Verify the bitrate if not self.isNetworkSufficient(): self.logMsg("The network speed is insufficient to direct stream file.", 1) return False return True
def onSettingsChanged(self): """ Monitor the PKC settings for changes made by the user """ # settings: window-variable items = { 'logLevel': 'plex_logLevel', 'enableContext': 'plex_context', 'plex_restricteduser': '******', 'dbSyncIndicator': 'dbSyncIndicator', 'remapSMB': 'remapSMB', 'replaceSMB': 'replaceSMB', 'force_transcode_pix': 'plex_force_transcode_pix', 'fetch_pms_item_number': 'fetch_pms_item_number' } # Path replacement for typus in REMAP_TYPE_FROM_PLEXTYPE.values(): for arg in ('Org', 'New'): key = 'remapSMB%s%s' % (typus, arg) items[key] = key # Reset the window variables from the settings variables for settings_value, window_value in items.iteritems(): if window(window_value) != settings(settings_value): log.debug('PKC settings changed: %s is now %s' % (settings_value, settings(settings_value))) window(window_value, value=settings(settings_value)) if settings_value == 'fetch_pms_item_number': log.info('Requesting playlist/nodes refresh') window('plex_runLibScan', value="views")
def CheckPMS(self): """ Check the PMS that was set in file settings. Will return False if we need to reconnect, because: PMS could not be reached (no matter the authorization) machineIdentifier did not match Will also set the PMS machineIdentifier in the file settings if it was not set before """ answer = True chk = self.plx.CheckConnection(self.server, verifySSL=False) if chk is False: self.logMsg('Could not reach PMS %s' % self.server, -1) answer = False if answer is True and not self.serverid: self.logMsg('No PMS machineIdentifier found for %s. Trying to ' 'get the PMS unique ID' % self.server, 1) self.serverid = GetMachineIdentifier(self.server) if self.serverid is None: self.logMsg('Could not retrieve machineIdentifier', -1) answer = False else: utils.settings('plex_machineIdentifier', value=self.serverid) elif answer is True: tempServerid = GetMachineIdentifier(self.server) if tempServerid != self.serverid: self.logMsg('The current PMS %s was expected to have a ' 'unique machineIdentifier of %s. But we got ' '%s. Pick a new server to be sure' % (self.server, self.serverid, tempServerid), 1) answer = False return answer
def getDeviceId(reset=False): """ Returns a unique Plex client id "X-Plex-Client-Identifier" from Kodi settings file. Also loads Kodi window property 'plex_client_Id' If id does not exist, create one and save in Kodi settings file. """ if reset is True: window('plex_client_Id', clear=True) settings('plex_client_Id', value="") clientId = window('plex_client_Id') if clientId: return clientId clientId = settings('plex_client_Id') # Because Kodi appears to cache file settings!! if clientId != "" and reset is False: window('plex_client_Id', value=clientId) log.warn("Unique device Id plex_client_Id loaded: %s" % clientId) return clientId log.warn("Generating a new deviceid.") from uuid import uuid4 clientId = str(uuid4()) settings('plex_client_Id', value=clientId) window('plex_client_Id', value=clientId) log.warn("Unique device Id plex_client_Id loaded: %s" % clientId) return clientId
def mustTranscode(self): """ Returns True if we need to transcode because - codec is in h265 - 10bit video codec - HEVC codec if the corresponding file settings are set to 'true' """ videoCodec = self.API.getVideoCodec() log.info("videoCodec: %s" % videoCodec) if (settings('transcodeHi10P') == 'true' and videoCodec['bitDepth'] == '10'): log.info('Option to transcode 10bit video content enabled.') return True codec = videoCodec['videocodec'] if (settings('transcodeHEVC') == 'true' and codec == 'hevc'): log.info('Option to transcode HEVC video codec enabled.') return True if codec is None: # e.g. trailers. Avoids TypeError with "'h265' in codec" log.info('No codec from PMS, not transcoding.') return False try: resolution = int(videoCodec['resolution']) except (TypeError, ValueError): log.info('No video resolution from PMS, not transcoding.') return False if 'h265' in codec: if resolution >= self.getH265(): log.info("Option to transcode h265 enabled. Resolution of " "the media: %s, transcoding limit resolution: %s" % (str(resolution), str(self.getH265()))) return True return False
def startSync(self): # Run at start up - optional to use the server plugin if utils.settings('SyncInstallRunDone') == "true": # Validate views self.refreshViews() completed = False # Verify if server plugin is installed. if utils.settings('serverSync') == "true": # Try to use fast start up url = "{server}/emby/Plugins?format=json" result = self.doUtils.downloadUrl(url) for plugin in result: if plugin['Name'] == "Emby.Kodi Sync Queue": self.logMsg("Found server plugin.", 2) completed = self.fastSync() if not completed: # Fast sync failed or server plugin is not found completed = self.fullSync(manualrun=True) else: # Install sync is not completed completed = self.fullSync() return completed
def getRecentEpisodes(viewid, mediatype, tagname, limit): count = 0 # if the addon is called with recentepisodes parameter, # we return the recentepisodes list of the given tagname xbmcplugin.setContent(HANDLE, 'episodes') appendShowTitle = settings('RecentTvAppendShow') == 'true' appendSxxExx = settings('RecentTvAppendSeason') == 'true' # First we get a list of all the TV shows - filtered by tag params = { 'sort': {'order': "descending", 'method': "dateadded"}, 'filter': {'operator': "is", 'field': "tag", 'value': "%s" % tagname}, } result = JSONRPC('VideoLibrary.GetTVShows').execute(params) # If we found any, find the oldest unwatched show for each one. try: items = result['result'][mediatype] except (KeyError, TypeError): # No items, empty folder xbmcplugin.endOfDirectory(handle=HANDLE) return allshowsIds = set() for item in items: allshowsIds.add(item['tvshowid']) params = { 'sort': {'order': "descending", 'method': "dateadded"}, 'properties': ["title", "playcount", "season", "episode", "showtitle", "plot", "file", "rating", "resume", "tvshowid", "art", "streamdetails", "firstaired", "runtime", "cast", "writer", "dateadded", "lastplayed"], "limits": {"end": limit} } if settings('TVShowWatched') == 'false': params['filter'] = { 'operator': "lessthan", 'field': "playcount", 'value': "1" } result = JSONRPC('VideoLibrary.GetEpisodes').execute(params) try: episodes = result['result']['episodes'] except (KeyError, TypeError): pass else: for episode in episodes: if episode['tvshowid'] in allshowsIds: li = createListItem(episode, appendShowTitle=appendShowTitle, appendSxxExx=appendSxxExx) xbmcplugin.addDirectoryItem( handle=HANDLE, url=episode['file'], listitem=li) count += 1 if count == limit: break xbmcplugin.endOfDirectory(handle=HANDLE)
def get_token(cls): ###$ Begin migration $### if settings('token') == "": settings('token', value=settings('accessToken')) log.info("token migration completed") ###$ End migration $### return settings('token') or None
def setUp(self): with utils.settings(server_ip): if firewall.has_chain(): with utils.settings(server_ip): iptables(firewall.flush_chain) else: with utils.settings(server_ip): iptables(firewall.make_chain) iptables(firewall.jump_to_chain())
def getDeviceName(self): if settings('deviceNameOpt') == "false": # Use Kodi's deviceName deviceName = tryDecode(xbmc.getInfoLabel('System.FriendlyName')) else: deviceName = settings('deviceName') deviceName = deviceName.replace("\"", "_") deviceName = deviceName.replace("/", "_") return deviceName
def loadCurrUser(self, username, userId, usertoken, authenticated=False): log.debug('Loading current user') doUtils = self.doUtils self.currUserId = userId self.currToken = usertoken self.currServer = self.getServer() self.ssl = self.getSSLverify() self.sslcert = self.getSSL() if authenticated is False: log.debug('Testing validity of current token') res = PlexAPI.PlexAPI().CheckConnection(self.currServer, token=self.currToken, verifySSL=self.ssl) if res is False: # PMS probably offline return False elif res == 401: log.error('Token is no longer valid') return 401 elif res >= 400: log.error('Answer from PMS is not as expected. Retrying') return False # Set to windows property window('currUserId', value=userId) window('plex_username', value=username) # This is the token for the current PMS (might also be '') window('pms_token', value=self.currToken) # This is the token for plex.tv for the current user # Is only '' if user is not signed in to plex.tv window('plex_token', value=settings('plexToken')) window('plex_restricteduser', value=settings('plex_restricteduser')) window('pms_server', value=self.currServer) window('plex_machineIdentifier', value=self.machineIdentifier) window('plex_servername', value=self.servername) window('plex_authenticated', value='true') window('useDirectPaths', value='true' if settings('useDirectPaths') == "1" else 'false') window('plex_force_transcode_pix', value='true' if settings('force_transcode_pix') == "1" else 'false') # Start DownloadUtils session doUtils.startSession(reset=True) # self.getAdditionalUsers() # Set user preferences in settings self.currUser = username self.setUserPref() # Writing values to settings file settings('username', value=username) settings('userid', value=userId) settings('accessToken', value=usertoken) return True
def getSSLverify(self): # Verify host certificate s_sslverify = settings('sslverify') if settings('altip') == "true": s_sslverify = settings('secondsslverify') if s_sslverify == "true": return True else: return False
def getSSL(self): # Client side certificate s_cert = settings('sslcert') if settings('altip') == "true": s_cert = settings('secondsslcert') if s_cert == "None": return None else: return s_cert
def getSSLverify(self): # Verify host certificate s_sslverify = utils.settings("sslverify") if utils.settings("altip") == "true": s_sslverify = utils.settings("secondsslverify") if s_sslverify == "true": return True else: return False
def __init__(self): self.artwork = artwork.Artwork() self.emby = embyserver.Read_EmbyServer() self.do_url = downloadutils.DownloadUtils().downloadUrl self.should_stop = should_stop self.kodi_version = int(xbmc.getInfoLabel('System.BuildVersion')[:2]) self.direct_path = settings('useDirectPaths') == "1" self.content_msg = settings('newContent') == "true"
def getSSL(self): # Client side certificate s_cert = utils.settings("sslcert") if utils.settings("altip") == "true": s_cert = utils.settings("secondsslcert") if s_cert == "None": return None else: return s_cert
def isDirectPlay(self): item = self.item # Requirement: Filesystem, Accessible path if utils.settings('playFromStream') == "true": # User forcing to play via HTTP self.logMsg("Can't direct play, play from HTTP enabled.", 1) return False if (utils.settings('transcodeH265') == "true" and result['MediaSources'][0]['Name'].startswith("1080P/H265")): # Avoid H265 1080p self.logMsg("Option to transcode 1080P/H265 enabled.", 1) return False canDirectPlay = item['MediaSources'][0]['SupportsDirectPlay'] # Make sure direct play is supported by the server if not canDirectPlay: self.logMsg("Can't direct play, server doesn't allow/support it.", 1) return False location = item['LocationType'] if location == "FileSystem": # Verify the path if not self.fileExists(): self.logMsg("Unable to direct play.") try: count = int(utils.settings('failCount')) except ValueError: count = 0 self.logMsg("Direct play failed: %s times." % count, 1) if count < 2: # Let the user know that direct play failed utils.settings('failCount', value=str(count+1)) xbmcgui.Dialog().notification( heading="Emby server", message="Unable to direct play.", icon="special://home/addons/plugin.video.emby/icon.png", sound=False) elif utils.settings('playFromStream') != "true": # Permanently set direct stream as true utils.settings('playFromStream', value="true") utils.settings('failCount', value="0") xbmcgui.Dialog().notification( heading="Emby server", message=("Direct play failed 3 times. Enabled play " "from HTTP in the add-on settings."), icon="special://home/addons/plugin.video.emby/icon.png", sound=False) return False return True
def __init__(self): self.enable_texture_cache = settings('enableTextureCache') == "true" self.image_cache_limit = int(settings('imageCacheLimit')) * 5 log.debug("image cache thread count: %s", self.image_cache_limit) if not self.xbmc_port and self.enable_texture_cache: self._set_webserver_details() self.user_id = window('emby_currUser') self.server = window('emby_server%s' % self.user_id)
def get_ssl(cls): """ Returns boolean value or path to certificate True: Verify ssl False: Don't verify connection """ certificate = settings('sslcert') if certificate != "None": return certificate return True if settings('sslverify') == "true" else False
def getDeviceName(self): if utils.settings('deviceNameOpt') == "false": # Use Kodi's deviceName deviceName = xbmc.getInfoLabel('System.FriendlyName').decode('utf-8') else: deviceName = utils.settings('deviceName') deviceName = deviceName.replace("\"", "_") deviceName = deviceName.replace("/", "_") return deviceName
def __init__(self): self.enableTextureCache = utils.settings('enableTextureCache') == "true" self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit")) self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5); utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1) if not self.xbmc_port and self.enableTextureCache: self.setKodiWebServerDetails() self.userId = utils.window('currUserId') self.server = utils.window('pms_server')
def getUsername(self): """ Returns username as unicode """ username = settings('username') if not username: log.debug("No username saved, trying to get Plex username") username = settings('plexLogin') if not username: log.debug("Also no Plex username found") return "" return username
def onSettingsChanged(self): # Monitor emby settings current_log_level = settings('logLevel') if window('emby_logLevel') != current_log_level: # The log level changed, set new prop log.info("New log level: %s", current_log_level) window('emby_logLevel', value=current_log_level) current_context = "true" if settings('enableContext') == "true" else "" if window('emby_context') != current_context: log.info("New context setting: %s", current_context) window('emby_context', value=current_context)
def _set_user_server(self): user = self.doutils.downloadUrl("{server}/emby/Users/{UserId}?format=json") settings('username', value=user['Name']) self._user = user if "PrimaryImageTag" in self._user: window('EmbyUserImage', value=artwork.Artwork().get_user_artwork(self._user['Id'], 'Primary')) self._server = self.doutils.downloadUrl("{server}/emby/System/Configuration?format=json") settings('markPlayed', value=str(self._server['MaxResumePct']))
def resetClient(self): log("Reset UserClient authentication.", 1) if self.currToken is not None: # In case of 401, removed saved token settings('accessToken', value="") window('emby_accessToken%s' % self.getUserId(), clear=True) self.currToken = None log("User token has been removed.", 1) self.auth = True self.currUser = None
def setUserPref(self): doUtils = self.doUtils.downloadUrl result = doUtils("{server}/emby/Users/{UserId}?format=json") self.userSettings = result # Set user image for skin display if result.get('PrimaryImageTag'): window('EmbyUserImage', value=artwork.Artwork().getUserArtwork(result['Id'], 'Primary')) # Set resume point max result = doUtils("{server}/emby/System/Configuration?format=json") settings('markPlayed', value=str(result['MaxResumePct']))
def fastSync(self): lastSync = settings('LastIncrementalSync') if not lastSync: lastSync = "2010-01-01T00:00:00Z" lastSyncTime = utils.convertDate(lastSync) log.info("Last sync run: %s" % lastSyncTime) # get server RetentionDateTime try: result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json") retention_time = result['RetentionDateTime'] except Exception as error: log.error(error) retention_time = "2010-01-01T00:00:00Z" retention_time = utils.convertDate(retention_time) log.info("RetentionDateTime: %s" % retention_time) # if last sync before retention time do a full sync if retention_time > lastSyncTime: log.info("Fast sync server retention insufficient, fall back to full sync") return False params = {'LastUpdateDT': lastSync} if settings('enableMusic') != "true": params['filter'] = "music" url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json" try: result = self.doUtils(url, parameters=params) processlist = { 'added': result['ItemsAdded'], 'update': result['ItemsUpdated'], 'userdata': result['UserDataChanged'], 'remove': result['ItemsRemoved'] } except Exception as error: # To be reviewed to only catch specific errors. log.error(error) log.error("Failed to retrieve latest updates using fast sync.") xbmcgui.Dialog().ok(lang(29999), lang(33095)) return False else: log.info("Fast sync changes: %s" % result) for action in processlist: self.triage_items(action, processlist[action]) return True
def isDirectPlay(self): """ Returns the path/playurl if successful, False otherwise """ # True for e.g. plex.tv watch later if self.API.shouldStream() is True: self.logMsg("Plex item optimized for direct streaming", 1) return False # set to either 'Direct Stream=1' or 'Transcode=2' if utils.settings('playType') != "0": # User forcing to play via HTTP self.logMsg("User chose to not direct play", 1) return False if self.h265enabled(): return False path = self.API.validatePlayurl(self.API.getFilePath(), self.API.getType(), forceCheck=True) if path is None: self.logMsg('Kodi cannot access file %s - no direct play' % path, 1) return False else: self.logMsg('Kodi can access file %s - direct playing' % path, 1) return path
def watchlater(): """ Listing for plex.tv Watch Later section (if signed in to plex.tv) """ if window('plex_token') == '': log.error('No watch later - not signed in to plex.tv') return xbmcplugin.endOfDirectory(HANDLE, False) if window('plex_restricteduser') == 'true': log.error('No watch later - restricted user') return xbmcplugin.endOfDirectory(HANDLE, False) xml = downloadutils.DownloadUtils().downloadUrl( 'https://plex.tv/pms/playlists/queue/all', authenticate=False, headerOptions={'X-Plex-Token': window('plex_token')}) if xml in (None, 401): log.error('Could not download watch later list from plex.tv') return xbmcplugin.endOfDirectory(HANDLE, False) log.info('Displaying watch later plex.tv items') xbmcplugin.setContent(HANDLE, 'movies') for item in xml: __build_item(item) xbmcplugin.endOfDirectory( handle=HANDLE, cacheToDisc=settings('enableTextureCache') == 'true')
def load_connect_servers(self): # Set connect servers if not settings('connectUsername'): return servers = self.connectmanager.get_connect_servers() added_servers = [] for server in servers: if server['Id'] != settings('serverId'): # TODO: SSL setup self.doutils.add_server(server, False) added_servers.append(server['Id']) # Set properties log.info(added_servers) window('emby_servers.json', value=added_servers)
def getPlayUrl(self): """ Returns the playurl for the part (movie might consist of several files) playurl is in unicode! """ self.api.mediastream_number() playurl = self.isDirectPlay() if playurl is not None: LOG.info("File is direct playing.") self.item.playmethod = 'DirectPlay' elif self.isDirectStream(): LOG.info("File is direct streaming.") playurl = self.api.transcode_video_path('DirectStream') self.item.playmethod = 'DirectStream' else: LOG.info("File is transcoding.") playurl = self.api.transcode_video_path( 'Transcode', quality={ 'maxVideoBitrate': self.get_bitrate(), 'videoResolution': self.get_resolution(), 'videoQuality': '100', 'mediaBufferSize': int(settings('kodi_video_cache'))/1024, }) self.item.playmethod = 'Transcode' LOG.info("The playurl is: %s", playurl) self.item.file = playurl return playurl
def __init__(self, embycursor, kodicursor): self.embycursor = embycursor self.kodicursor = kodicursor self.emby = embyserver.Read_EmbyServer() self.music_enabled = settings('enableMusic') == "true"
def isDirectStream(self): log = self.logMsg item = self.item videotrack = item['MediaSources'][0]['Name'] transcodeH265 = utils.settings('transcodeH265') if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack): # Avoid H265/HEVC depending on the resolution resolution = int(videotrack.split("P", 1)[0]) res = {'1': 480, '2': 720, '3': 1080} log( "Resolution is: %sP, transcode for resolution: %sP+" % (resolution, res[transcodeH265]), 1) if res[transcodeH265] <= resolution: return False # Requirement: BitRate, supported encoding canDirectStream = item['MediaSources'][0]['SupportsDirectStream'] # Make sure the server supports it if not canDirectStream: return False # Verify the bitrate if not self.isNetworkSufficient(): log("The network speed is insufficient to direct stream file.", 1) return False return True
def init_plex_playqueue(itemid, librarySectionUUID, mediatype='movie', trailers=False): """ Returns raw API metadata XML dump for a playlist with e.g. trailers. """ url = "{server}/playQueues" args = { 'type': mediatype, 'uri': ('library://' + librarySectionUUID + '/item/%2Flibrary%2Fmetadata%2F' + itemid), 'includeChapters': '1', 'shuffle': '0', 'repeat': '0' } if trailers is True: args['extrasPrefixCount'] = settings('trailerNumber') xml = downloadutils.DownloadUtils().downloadUrl(url + '?' + urlencode(args), action_type="POST") try: xml[0].tag except (IndexError, TypeError, AttributeError): log.error("Error retrieving metadata for %s" % url) return None return xml
def __init__(self, data, labels, info): "Initialize a handler for making statistics on ML data" super().__init__(**settings(data=data, labels=labels, info=info)) self.kneg, self.kpos = None, None self.neg, self.pos = None, None self.features = list() self.scores = list()
def _rate_song(self): with DatabaseConn('music') as cursor_music: query = "SELECT rating FROM song WHERE idSong = ?" cursor_music.execute(query, (self.kodi_id, )) try: value = cursor_music.fetchone()[0] current_value = int(round(float(value), 0)) except TypeError: pass else: new_value = dialog("numeric", 0, lang(30411), str(current_value)) if new_value > -1: new_value = int(new_value) if new_value > 5: new_value = 5 if settings('enableUpdateSongRating') == "true": musicutils.updateRatingToFile(new_value, self.api.get_file_path()) query = "UPDATE song SET rating = ? WHERE idSong = ?" cursor_music.execute(query, ( new_value, self.kodi_id, ))
def check_migration(): log.info('Checking whether we need to migrate something') last_migration = settings('last_migrated_PKC_version') if last_migration == v.ADDON_VERSION: log.info('Already migrated to PKC version %s' % v.ADDON_VERSION) return if not last_migration: log.info('Never migrated, so checking everything') last_migration = '1.0.0' if not compare_version(v.ADDON_VERSION, '1.8.2'): log.info('Migrating to version 1.8.1') # Set the new PKC theMovieDB key settings('themoviedbAPIKey', value='19c90103adb9e98f2172c6a6a3d85dc4') settings('last_migrated_PKC_version', value=v.ADDON_VERSION)
def isDirectPlay(self): """ Returns the path/playurl if we can direct play, None otherwise """ # True for e.g. plex.tv watch later if self.api.should_stream() is True: LOG.info("Plex item optimized for direct streaming") return # Check whether we have a strm file that we need to throw at Kodi 1:1 path = self.api.file_path() if path is not None and path.endswith('.strm'): LOG.info('.strm file detected') playurl = self.api.validate_playurl(path, self.api.plex_type(), force_check=True) return playurl # set to either 'Direct Stream=1' or 'Transcode=2' # and NOT to 'Direct Play=0' if settings('playType') != "0": # User forcing to play via HTTP LOG.info("User chose to not direct play") return if self.mustTranscode(): return return self.api.validate_playurl(path, self.api.plex_type(), force_check=True)
def getBitrate(self): # get the addon video quality bitrate = { '0': 664, '1': 996, '2': 1320, '3': 2000, '4': 3200, '5': 4700, '6': 6200, '7': 7700, '8': 9200, '9': 10700, '10': 12200, '11': 13700, '12': 15200, '13': 16700, '14': 18200, '15': 20000, '16': 40000, '17': 100000, '18': 1000000 } # max bit rate supported by server (max signed 32bit integer) return bitrate.get(settings('videoBitrate'), 2147483)
def GetPlexPlaylist(itemid, librarySectionUUID, mediatype='movie'): """ Returns raw API metadata XML dump for a playlist with e.g. trailers. """ trailerNumber = settings('trailerNumber') if not trailerNumber: trailerNumber = '3' url = "{server}/playQueues" args = { 'type': mediatype, 'uri': 'library://' + librarySectionUUID + '/item/%2Flibrary%2Fmetadata%2F' + itemid, 'includeChapters': '1', 'extrasPrefixCount': trailerNumber, 'shuffle': '0', 'repeat': '0' } xml = downloadutils.DownloadUtils().downloadUrl(url + '?' + urlencode(args), type="POST") try: xml[0].tag except (IndexError, TypeError, AttributeError): logMsg(title, "Error retrieving metadata for %s" % url, -1) return None return xml
def setProperties(self, playurl, listitem): # Set all properties necessary for plugin path playback itemid = self.item['Id'] itemtype = self.item['Type'] embyitem = "emby_%s" % playurl window('%s.runtime' % embyitem, value=str(self.item.get('RunTimeTicks'))) window('%s.type' % embyitem, value=itemtype) window('%s.itemid' % embyitem, value=itemid) if itemtype == "Episode": window('%s.refreshid' % embyitem, value=self.item.get('SeriesId')) else: window('%s.refreshid' % embyitem, value=itemid) # Append external subtitles to stream playmethod = window('%s.playmethod' % embyitem) # Only for direct stream if playmethod in ( "DirectStream") and settings('enableExternalSubs') == "true": # Direct play automatically appends external subtitles = self.externalSubs(playurl) listitem.setSubtitles(subtitles) self.setArtwork(listitem)
def _on_play_(self, data): # Set up report progress for emby playback try: if KODI >= 17: item = xbmc.Player().getVideoInfoTag() kodi_id = item.getDbId() item_type = item.getMediaType() log.info("kodi_id: %s item_type: %s", kodi_id, item_type) else: item = data['item'] kodi_id = item['id'] item_type = item['type'] log.info("kodi_id: %s item_type: %s", kodi_id, item_type) except (KeyError, TypeError): log.info("Item is invalid for playstate update") else: if ((settings('useDirectPaths') == "1" and not item_type == "song") or (item_type == "song" and settings('enableMusic') == "true")): # Set up properties for player item_id = self._get_item_id(kodi_id, item_type) if item_id: url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % item_id result = self.download(url) log.debug("Item: %s", result) playurl = None count = 0 while not playurl and count < 2: try: playurl = xbmc.Player().getPlayingFile() except RuntimeError: count += 1 xbmc.sleep(200) else: listitem = xbmcgui.ListItem() playback = pbutils.PlaybackUtils(result) if item_type == "song" and settings( 'streamMusic') == "true": window('emby_%s.playmethod' % playurl, value="DirectStream") else: window('emby_%s.playmethod' % playurl, value="DirectPlay") # Set properties for player.py playback.setProperties(playurl, listitem)
def isDirectPlay(self): # Requirement: Filesystem, Accessible path if settings('playFromStream') == "true": # User forcing to play via HTTP log.info("Can't direct play, play from HTTP enabled.") return False videotrack = self.item['MediaSources'][0]['Name'] transcodeH265 = settings('transcodeH265') videoprofiles = [ x['Profile'] for x in self.item['MediaSources'][0]['MediaStreams'] if 'Profile' in x ] transcodeHi10P = settings('transcodeHi10P') if transcodeHi10P == "true" and "H264" in videotrack and "High 10" in videoprofiles: return False if transcodeH265 in ("1", "2", "3") and ("HEVC" in videotrack or "H265" in videotrack): # Avoid H265/HEVC depending on the resolution resolution = int(videotrack.split("P", 1)[0]) res = {'1': 480, '2': 720, '3': 1080} log.info("Resolution is: %sP, transcode for resolution: %sP+" % (resolution, res[transcodeH265])) if res[transcodeH265] <= resolution: return False canDirectPlay = self.item['MediaSources'][0]['SupportsDirectPlay'] # Make sure direct play is supported by the server if not canDirectPlay: log.info("Can't direct play, server doesn't allow/support it.") return False location = self.item['LocationType'] if location == "FileSystem": # Verify the path if not self.fileExists(): log.info("Unable to direct play.") log.info(self.directPlay()) xbmcgui.Dialog().ok(heading=lang(29999), line1=lang(33011), line2=(self.directPlay())) sys.exit() return True
def run(self): log = self.logMsg # Currently not working due to missing SSL environment sslopt = {} if utils.settings('sslverify') == "false": sslopt["cert_reqs"] = ssl.CERT_NONE log("----===## Starting WebSocketClient ##===----", 0) threadStopped = self.threadStopped threadSuspended = self.threadSuspended while not threadStopped(): # In the event the server goes offline while threadSuspended(): # Set in service.py if self.ws is not None: try: self.ws.shutdown() except: pass self.ws = None if threadStopped(): # Abort was requested while waiting. We should exit log("##===---- WebSocketClient Stopped ----===##", 0) return xbmc.sleep(1000) try: self.process(*self.receive(self.ws)) except websocket.WebSocketTimeoutException: # No worries if read timed out pass except websocket.WebSocketConnectionClosedException: log("Connection closed, (re)connecting", 0) uri = self.getUri() try: # Low timeout - let's us shut this thread down! self.ws = websocket.create_connection( uri, timeout=1, sslopt=sslopt, enable_multithread=True) except IOError: log("Error connecting", 0) self.ws = None xbmc.sleep(1000) except websocket.WebSocketTimeoutException: log("timeout while connecting, trying again", 0) self.ws = None xbmc.sleep(1000) except Exception as e: log("Unknown exception encountered in connecting: %s" % e) self.ws = None xbmc.sleep(1000) except Exception as e: log("Unknown exception encountered: %s" % e) try: self.ws.shutdown() except: pass self.ws = None log("##===---- WebSocketClient Stopped ----===##", 0)
def getSettings(): client = clientinfo.ClientInfo() options = {} title = 'PlexCompanion Settings' options['gdm_debug'] = settings('companionGDMDebugging') options['gdm_debug'] = True if options['gdm_debug'] == 'true' else False options['client_name'] = settings('deviceName') # XBMC web server options options['webserver_enabled'] = (getGUI('webserver') == "true") logMsg(title, 'Webserver is set to %s' % options['webserver_enabled'], 0) webserverport = getGUI('webserverport') try: webserverport = int(webserverport) logMsg(title, 'Using webserver port %s' % str(webserverport), 0) except: logMsg( title, 'No setting for webserver port found in guisettings.xml.' 'Using default fallback port 8080', 0) webserverport = 8080 options['port'] = webserverport options['user'] = getGUI('webserverusername') options['passwd'] = getGUI('webserverpassword') logMsg( title, 'Webserver username: %s, password: %s' % (options['user'], options['passwd']), 1) options['addonName'] = client.getAddonName() options['uuid'] = settings('plex_client_Id') options['platform'] = client.getPlatform() options['version'] = client.getVersion() options['plexbmc_version'] = options['version'] options['myplex_user'] = settings('username') try: options['myport'] = int(settings('companionPort')) logMsg(title, 'Using Plex Companion Port %s' % str(options['myport']), 0) except: logMsg( title, 'Error getting Plex Companion Port from file settings. ' 'Using fallback port 39005', -1) options['myport'] = 39005 return options
def getLogLevel(self): try: logLevel = int(utils.settings('logLevel')) except ValueError: logLevel = 0 return logLevel
def setUserPref(self): doUtils = self.doUtils.downloadUrl art = artwork.Artwork() url = "{server}/emby/Users/{UserId}?format=json" result = doUtils(url) self.userSettings = result # Set user image for skin display if result.get('PrimaryImageTag'): utils.window('EmbyUserImage', value=art.getUserArtwork(result['Id'], 'Primary')) # Set resume point max url = "{server}/emby/System/Configuration?format=json" result = doUtils(url) utils.settings('markPlayed', value=str(result['MaxResumePct']))
def build_global_catalog_item_url(): """ Build a URL to hit the global catalog service and do stuff with catalog items. """ path = "/v1/catalog_item" base_url = "{0}".format(settings('global_catalog_url')) return "{0}{1}".format(base_url, path)
def __init__(self): logLevel = self.getLogLevel() self.monitor = Monitor() window('plex_logLevel', value=str(logLevel)) window('plex_kodiProfile', value=tryDecode(translatePath("special://profile"))) window('plex_context', value='true' if settings('enableContext') == "true" else "") window('fetch_pms_item_number', value=settings('fetch_pms_item_number')) # Initial logging log.warn("======== START %s ========" % v.ADDON_NAME) log.warn("Platform: %s" % v.PLATFORM) log.warn("KODI Version: %s" % v.KODILONGVERSION) log.warn("%s Version: %s" % (v.ADDON_NAME, v.ADDON_VERSION)) log.warn("Using plugin paths: %s" % (settings('useDirectPaths') != "true")) log.warn("Number of sync threads: %s" % settings('syncThreadNumber')) log.warn("Log Level: %s" % logLevel) log.warn("Full sys.argv received: %s" % argv) # Reset window props for profile switch properties = [ "plex_online", "plex_serverStatus", "plex_onWake", "plex_dbCheck", "plex_kodiScan", "plex_shouldStop", "plex_dbScan", "plex_initialScan", "plex_customplayqueue", "plex_playbackProps", "plex_runLibScan", "pms_token", "plex_token", "pms_server", "plex_machineIdentifier", "plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths", "kodiplextimeoffset", "countError", "countUnauthorized", "plex_restricteduser", "plex_allows_mediaDeletion", "plex_command", "plex_result", "plex_force_transcode_pix" ] for prop in properties: window(prop, clear=True) # Clear video nodes properties videonodes.VideoNodes().clearProperties() # Set the minimum database version window('plex_minDBVersion', value="1.5.10")
def __init__(self): self.clientinfo = clientinfo.ClientInfo() self.addonName = self.clientinfo.getAddonName() self.enableTextureCache = utils.settings( 'enableTextureCache') == "true" self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit")) self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5) utils.logMsg( "Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1) if not self.xbmc_port and self.enableTextureCache: self.setKodiWebServerDetails() self.userId = utils.window('emby_currUser') self.server = utils.window('emby_server%s' % self.userId)
def _write_PMS_settings(self, url, token): """ Sets certain settings for server by asking for the PMS' settings Call with url: scheme://ip:port """ xml = get_PMS_settings(url, token) try: xml.attrib except AttributeError: log.error('Could not get PMS settings for %s' % url) return for entry in xml: if entry.attrib.get('id', '') == 'allowMediaDeletion': settings('plex_allows_mediaDeletion', value=entry.attrib.get('value', 'true')) window('plex_allows_mediaDeletion', value=entry.attrib.get('value', 'true'))
def addSets(self, movieid, collections, kodicursor, API): for setname in collections: setid = self.createBoxset(setname) # Process artwork if settings('setFanartTV') == 'true': self.artwork.addArtwork(API.getSetArtwork(), setid, "set", kodicursor) self.assignBoxset(setid, movieid)
def __init__(self): LOG.debug('Entering initialsetup class') self.plx = PlexAPI() self.dialog = xbmcgui.Dialog() self.server = UserClient().getServer() self.serverid = settings('plex_machineIdentifier') # Get Plex credentials from settings file, if they exist plexdict = self.plx.GetPlexLoginFromSettings() self.myplexlogin = plexdict['myplexlogin'] == 'true' self.plexLogin = plexdict['plexLogin'] self.plexToken = plexdict['plexToken'] self.plexid = plexdict['plexid'] # Token for the PMS, not plex.tv self.pms_token = settings('accessToken') if self.plexToken: LOG.debug('Found a plex.tv token in the settings')
def _server_restarting(cls): if settings('supressRestartMsg') == "true": dialog(type_="notification", heading="{emby}", message=lang(33006), icon="{emby}") window('emby_online', value="false")
def _auto_pick_pms(self): """ Will try to pick PMS based on machineIdentifier saved in file settings but only once Returns server or None if unsuccessful """ https_updated = False checked_plex_tv = False server = None while True: if https_updated is False: serverlist = PF.discover_pms(self.plex_token) for item in serverlist: if item.get('machineIdentifier') == self.serverid: server = item if server is None: name = settings('plex_servername') LOG.warn( 'The PMS you have used before with a unique ' 'machineIdentifier of %s and name %s is ' 'offline', self.serverid, name) return chk = self._check_pms_connectivity(server) if chk == 504 and https_updated is False: # switch HTTPS to HTTP or vice-versa if server['scheme'] == 'https': server['scheme'] = 'http' else: server['scheme'] = 'https' https_updated = True continue if chk == 401: LOG.warn('Not yet authorized for Plex server %s', server['name']) if self.check_plex_tv_sign_in() is True: if checked_plex_tv is False: # Try again checked_plex_tv = True https_updated = False continue else: LOG.warn('Not authorized even though we are signed ' ' in to plex.tv correctly') dialog( 'ok', lang(29999), '%s %s' % (lang(39214), try_encode(server['name']))) return else: return # Problems connecting elif chk >= 400 or chk is False: LOG.warn('Problems connecting to server %s. chk is %s', server['name'], chk) return LOG.info('We found a server to automatically connect to: %s', server['name']) return server
def _AutoPickPMS(self): """ Will try to pick PMS based on machineIdentifier saved in file settings but only once Returns server or None if unsuccessful """ httpsUpdated = False checkedPlexTV = False server = None while True: if httpsUpdated is False: serverlist = self._getServerList() for item in serverlist: if item.get('machineIdentifier') == self.serverid: server = item if server is None: name = settings('plex_servername') log.warn('The PMS you have used before with a unique ' 'machineIdentifier of %s and name %s is ' 'offline' % (self.serverid, name)) # "PMS xyz offline" self.dialog.notification(addonName, '%s %s' % (name, lang(39213)), xbmcgui.NOTIFICATION_ERROR, 7000, False) return chk = self._checkServerCon(server) if chk == 504 and httpsUpdated is False: # Not able to use HTTP, try HTTPs for now server['scheme'] = 'https' httpsUpdated = True continue if chk == 401: log.warn('Not yet authorized for Plex server %s' % server['name']) if self.CheckPlexTVSignIn() is True: if checkedPlexTV is False: # Try again checkedPlexTV = True httpsUpdated = False continue else: log.warn('Not authorized even though we are signed ' ' in to plex.tv correctly') self.dialog.ok(addonName, '%s %s' % lang(39214) + server['name']) return else: return # Problems connecting elif chk >= 400 or chk is False: log.warn('Problems connecting to server %s. chk is %s' % (server['name'], chk)) return log.info('We found a server to automatically connect to: %s' % server['name']) return server
def getServer(self, prefix=True): # Original host self.servername = settings('plex_servername') HTTPS = settings('https') == "true" host = settings('ipaddress') port = settings('port') self.machineIdentifier = settings('plex_machineIdentifier') server = host + ":" + port if not host: LOG.debug("No server information saved.") return False # If https is true if prefix and HTTPS: server = "https://%s" % server # If https is false elif prefix and not HTTPS: server = "http://%s" % server # User entered IP; we need to get the machineIdentifier if self.machineIdentifier == '' and prefix is True: self.machineIdentifier = PF.GetMachineIdentifier(server) if self.machineIdentifier is None: self.machineIdentifier = '' settings('plex_machineIdentifier', value=self.machineIdentifier) LOG.debug('Returning active server: %s', server) return server