示例#1
0
    def GetPublicUsers(baseUrl, deviceId=None):
        users = []

        usersUrl = Url.append(baseUrl, constants.EMBY_PROTOCOL,
                              constants.URL_USERS, constants.URL_USERS_PUBLIC)
        headers = Request.PrepareApiCallHeaders(deviceId=deviceId)
        resultObj = Request.GetAsJson(usersUrl, headers=headers)
        if not resultObj:
            return users

        for userObj in resultObj:
            # make sure the 'Name' and 'Id' properties are available
            if not constants.PROPERTY_USER_NAME in userObj or not constants.PROPERTY_USER_ID in userObj:
                continue

            # make sure the name and id properties are valid
            user = User(userObj[constants.PROPERTY_USER_NAME],
                        userObj[constants.PROPERTY_USER_ID])
            if not user.name or not user.id:
                continue

            # check if the user is disabled
            if constants.PROPERTY_USER_POLICY in userObj and \
               constants.PROPERTY_USER_IS_DISABLED in userObj[constants.PROPERTY_USER_POLICY] and \
               userObj[constants.PROPERTY_USER_POLICY][constants.PROPERTY_USER_IS_DISABLED]:
                continue

            users.append(user)

        return users
    def Exchange(baseUrl, accessKey, userId, deviceId=None):
        if not baseUrl:
            raise ValueError('invalid baseUrl')
        if not accessKey:
            raise ValueError('invalid accessKey')
        if not userId:
            raise ValueError('invalid userId')

        exchangeUrl = server.Server.BuildConnectExchangeUrl(baseUrl, userId)
        headers = Request.PrepareApiCallHeaders(deviceId=deviceId)
        headers.update({
            constants.EMBY_CONNECT_TOKEN_HEADER: accessKey,
        })

        resultObj = Request.GetAsJson(exchangeUrl, headers=headers)
        if not resultObj or \
           constants.PROPERTY_EMBY_CONNECT_EXCHANGE_LOCAL_USER_ID not in resultObj or \
           constants.PROPERTY_EMBY_CONNECT_EXCHANGE_ACCESS_TOKEN not in resultObj:
            log('invalid response from {}: {}'.format(exchangeUrl, resultObj))
            return None

        return EmbyConnect.AuthenticationResult(
            accessToken=resultObj.get(constants.PROPERTY_EMBY_CONNECT_EXCHANGE_ACCESS_TOKEN),
            userId=resultObj.get(constants.PROPERTY_EMBY_CONNECT_EXCHANGE_LOCAL_USER_ID)
        )
    def Authenticate(baseUrl,
                     authenticationMethod,
                     username=None,
                     userId=None,
                     password=None,
                     deviceId=None):
        if not password:
            raise ValueError('invalid password')

        # prepare the authentication URL
        authUrl = baseUrl
        authUrl = Url.append(authUrl, constants.URL_USERS)

        body = {constants.PROPERTY_USER_AUTHENTICATION_PASSWORD: password}
        if authenticationMethod == Authentication.Method.UserId:
            if not userId:
                raise ValueError('invalid userId')

            authUrl = Url.append(authUrl, userId, constants.URL_AUTHENTICATE)

        elif authenticationMethod == Authentication.Method.Username:
            if not username:
                raise ValueError('invalid username')

            authUrl = Url.append(authUrl, constants.URL_AUTHENTICATE_BY_NAME)

            body[constants.PROPERTY_USER_AUTHENTICATION_USERNAME] = username

        else:
            raise ValueError('invalid authenticationMethod')

        headers = Request.PrepareApiCallHeaders(deviceId=deviceId,
                                                userId=userId)
        headers['Content-Type'] = constants.EMBY_CONTENT_TYPE

        resultObj = Request.PostAsJson(
            authUrl,
            headers=headers,
            json=body,
            timeout=Authentication.REQUEST_TIMEOUT_S)
        if not resultObj:
            return Authentication.Result()

        if constants.PROPERTY_USER_AUTHENTICATION_ACCESS_TOKEN not in resultObj:
            return Authentication.Result()
        accessToken = \
            resultObj[constants.PROPERTY_USER_AUTHENTICATION_ACCESS_TOKEN]

        if constants.PROPERTY_USER_AUTHENTICATION_USER not in resultObj:
            return Authentication.Result()
        userObj = resultObj[constants.PROPERTY_USER_AUTHENTICATION_USER]
        if constants.PROPERTY_USER_AUTHENTICATION_USER_ID not in userObj:
            return Authentication.Result()

        userId = userObj[constants.PROPERTY_USER_AUTHENTICATION_USER_ID]

        return Authentication.Result(result=True,
                                     accessToken=accessToken,
                                     userId=userId)
    def Authenticate(username, password):
        if not username:
            raise ValueError('invalid username')
        if not password:
            raise ValueError('invalid password')

        url = Url.append(constants.URL_EMBY_CONNECT_BASE, constants.URL_EMBY_CONNECT_AUTHENTICATE)
        headers = EmbyConnect._getApplicationHeader()

        body = {
            constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_NAME_OR_EMAIL: username,
            constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_PASSWORD: hashlib.md5(password),  # nosec
        }

        resultObj = Request.PostAsJson(url, headers=headers, json=body)
        if not resultObj or \
           constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_ACCESS_TOKEN not in resultObj or \
           constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_USER not in resultObj:
            log('invalid response from {}: {}'.format(url, resultObj))
            return None

        userObj = resultObj.get(constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_USER)
        if constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_USER_ID not in userObj:
            log('invalid response from {}: {}'.format(url, resultObj))
            return None

        return EmbyConnect.AuthenticationResult(
            accessToken=resultObj.get(constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_ACCESS_TOKEN),
            userId=userObj.get(constants.PROPERTY_EMBY_CONNECT_AUTHENTICATION_USER_ID)
        )
示例#5
0
    def _request(self, url, function, *args):
        headers = Request.PrepareApiCallHeaders(authToken=self.AccessToken(), userId=self.UserId(),
                                                deviceId=self._devideId)
        try:
            return function(url, headers, *args)
        except NotAuthenticatedError:
            # try to authenticate
            if not self._authenticate(force=True):
                return False

            # retrieve the headers again because the access token has changed
            headers = Request.PrepareApiCallHeaders(authToken=self.AccessToken(), userId=self.UserId(),
                                                    deviceId=self._devideId)

            # execute the actual request again
            return function(url, headers, *args)
        def checkLogin(self):
            if self.finished:
                return not self.expired

            url = Url.append(constants.URL_EMBY_CONNECT_BASE, constants.URL_EMBY_CONNECT_PIN)
            url = Url.addOptions(url, {
                constants.URL_QUERY_DEVICE_ID: self.deviceId,
                constants.URL_QUERY_PIN: self.pin,
            })

            resultObj = Request.GetAsJson(url)
            if not resultObj or \
               constants.PROPERTY_EMBY_CONNECT_PIN_IS_CONFIRMED not in resultObj or \
               constants.PROPERTY_EMBY_CONNECT_PIN_IS_EXPIRED not in resultObj:
                log('failed to check status of PIN {} at {}: {}'.format(self.pin, url, resultObj), xbmc.LOGWARNING)
                self.finished = True
                self.expired = True
                return False

            self.finished = resultObj.get(constants.PROPERTY_EMBY_CONNECT_PIN_IS_CONFIRMED)
            self.expired = resultObj.get(constants.PROPERTY_EMBY_CONNECT_PIN_IS_EXPIRED)
            if self.expired:
                self.finished = True

            return self.finished
        def exchange(self):
            if not self.pin:
                return None

            if not self.finished or self.expired:
                return None

            if self._authenticationResult:
                return self._authenticationResult

            url = Url.append(constants.URL_EMBY_CONNECT_BASE, constants.URL_EMBY_CONNECT_PIN,
                             constants.URL_EMBY_CONNECT_PIN_AUTHENTICATE)
            body = {
                constants.URL_QUERY_DEVICE_ID: self.deviceId,
                constants.URL_QUERY_PIN: self.pin,
            }

            resultObj = Request.PostAsJson(url, json=body)
            if not resultObj or \
               constants.PROPERTY_EMBY_CONNECT_PIN_USER_ID not in resultObj or \
               constants.PROPERTY_EMBY_CONNECT_PIN_ACCESS_TOKEN not in resultObj:
                log('failed to authenticate with PIN {} at {}: {}'.format(self.pin, url, resultObj))
                return None

            self._authenticationResult = EmbyConnect.AuthenticationResult(
                accessToken=resultObj.get(constants.PROPERTY_EMBY_CONNECT_PIN_ACCESS_TOKEN),
                userId=resultObj.get(constants.PROPERTY_EMBY_CONNECT_PIN_USER_ID))
            return self._authenticationResult
示例#8
0
def loadProviderSettings(handle, _):
    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    settings = mediaProvider.getSettings()
    if not settings:
        log('cannot retrieve media provider settings', xbmc.LOGERROR)
        return

    # make sure we have a device identifier
    if not settings.getString(emby.constants.SETTING_PROVIDER_DEVICEID):
        settings.setString(emby.constants.SETTING_PROVIDER_DEVICEID, Request.GenerateDeviceId())

    settings.registerActionCallback(emby.constants.SETTING_PROVIDER_LINK_EMBY_CONNECT, 'linkembyconnect')
    settings.registerActionCallback(emby.constants.SETTING_PROVIDER_TEST_AUTHENTICATION, 'testauthentication')
    settings.registerActionCallback(emby.constants.SETTING_PROVIDER_ADVANCED_RESET_DEVICE_ID, 'resetdeviceid')
    settings.registerActionCallback(emby.constants.SETTING_PROVIDER_ADVANCED_CHANGE_URL, 'changeurl')

    # register a setting options filler for the list of users
    settings.registerOptionsFillerCallback(emby.constants.SETTING_PROVIDER_USER, 'settingoptionsfillerusers')

    settings.setLoaded()
示例#9
0
def linkEmbyConnect(handle, _):
    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # get the media provider settings
    providerSettings = mediaProvider.prepareSettings()
    if not providerSettings:
        return

    # make sure we have a valid device ID
    deviceId = providerSettings.getString(emby.constants.SETTING_PROVIDER_DEVICEID)
    if not deviceId:
        deviceId = Request.GenerateDeviceId()
        providerSettings.setString(emby.constants.SETTING_PROVIDER_DEVICEID, deviceId)

    embyConnect = linkToEmbyConnect(deviceId)
    if not embyConnect:
        return

    # make sure the configured Emby server is still accessible
    serverUrl = ProviderSettings.GetUrl(providerSettings)
    matchingServer = None
    serverId = Server.GetServerId(mediaProvider.getIdentifier())

    # get all connected servers
    servers = EmbyConnect.GetServers(embyConnect.accessToken, embyConnect.userId)
    if not servers:
        log('no servers available for Emby Connect user id {}'.format(embyConnect.userId), xbmc.LOGWARNING)
        return

    for server in servers:
        if server.systemId == serverId:
            matchingServer = server
            break

    if not matchingServer:
        log('no Emby server matching {} found'.format(serverUrl), xbmc.LOGWARNING)
        xbmcgui.Dialog().ok(localise(32038), localise(32061))
        return

    # change the settings
    providerSettings.setString(emby.constants.SETTING_PROVIDER_EMBY_CONNECT_USER_ID, embyConnect.userId)
    providerSettings.setString(emby.constants.SETTING_PROVIDER_EMBY_CONNECT_ACCESS_KEY, matchingServer.accessKey)

    success = False
    try:
        success = Server(mediaProvider).Authenticate(force=True)
    except:
        pass

    if success:
        xbmcgui.Dialog().ok(localise(32038), localise(32062))
        log('successfully linked to Emby Connect server {} ({}) {}'.format(matchingServer.name, serverId, serverUrl))
    else:
        xbmcgui.Dialog().ok(localise(32038), localise(32061))
        log('failed to link to Emby Connect server {} ({}) {}'.format(matchingServer.name, serverId, serverUrl),
            xbmc.LOGWARNING)
示例#10
0
    def _request(self, url, function, *args):
        if not self._authenticate():
            return False

        headers = Request.PrepareApiCallHeaders(authToken=self.AccessToken(),
                                                userId=self.UserId(),
                                                deviceId=self._devideId)
        return function(url, headers, *args)
示例#11
0
    def GetServers(accessToken, userId):
        if not accessToken:
            raise ValueError('invalid accessToken')
        if not userId:
            raise ValueError('invalid userId')

        url = Url.append(constants.URL_EMBY_CONNECT_BASE,
                         constants.URL_EMBY_CONNECT_SERVERS)
        url = Url.addOptions(url, {
            constants.URL_QUERY_USER_ID: userId,
        })
        headers = EmbyConnect._getApplicationHeader()
        headers.update({
            constants.EMBY_CONNECT_USER_TOKEN_HEADER: accessToken,
        })

        resultObj = Request.GetAsJson(url, headers=headers)
        if not resultObj:
            log('invalid response from {}: {}'.format(url, resultObj))
            return None

        servers = []
        for server in resultObj:
            id = server.get(constants.PROPERTY_EMBY_CONNECT_SERVER_ID, None)
            systemId = server.get(
                constants.PROPERTY_EMBY_CONNECT_SERVER_SYSTEM_ID, None)
            accessKey = server.get(
                constants.PROPERTY_EMBY_CONNECT_SERVER_ACCESS_KEY, None)
            name = server.get(constants.PROPERTY_EMBY_CONNECT_SERVER_NAME,
                              None)
            remoteUrl = server.get(
                constants.PROPERTY_EMBY_CONNECT_SERVER_REMOTE_URL, None)
            localUrl = server.get(
                constants.PROPERTY_EMBY_CONNECT_SERVER_LOCAL_URL, None)

            if None in (id, accessKey, name, remoteUrl, localUrl):
                log('invalid Emby server received from {}: {}'.format(
                    url, server))
                continue

            servers.append(
                EmbyConnect.Server(id=id,
                                   systemId=systemId,
                                   accessKey=accessKey,
                                   name=name,
                                   remoteUrl=remoteUrl,
                                   localUrl=localUrl))

        return servers
示例#12
0
        def _getPin(self):
            if self.pin:
                return self.pin

            url = Url.append(constants.URL_EMBY_CONNECT_BASE,
                             constants.URL_EMBY_CONNECT_PIN)
            body = {constants.URL_QUERY_DEVICE_ID: self.deviceId}

            resultObj = Request.PostAsJson(url, json=body)
            if not resultObj or \
               not constants.PROPERTY_EMBY_CONNECT_PIN in resultObj:
                log('failed to get a PIN from {}: {}'.format(url, resultObj))
                return None

            self.pin = resultObj.get(constants.PROPERTY_EMBY_CONNECT_PIN)

            return self.pin
示例#13
0
def resetDeviceId(handle, _):
    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # get the media provider settings
    providerSettings = mediaProvider.prepareSettings()
    if not providerSettings:
        return

    deviceId = Request.GenerateDeviceId()
    log('created a new device identifier for {}: {}'.format(mediaProvider2str(mediaProvider), deviceId))

    providerSettings.setString(emby.constants.SETTING_PROVIDER_DEVICEID, deviceId)

    xbmcgui.Dialog().ok(mediaProvider.getFriendlyName(), localise(32063))
示例#14
0
 def ApiDelete(self, url):
     return self._request(url, lambda url, headers: Request.Delete(url, headers=headers))
示例#15
0
 def ApiPost(self, url, data=None, json=None):
     return self._request(url, lambda url, headers, data, json:
                          Request.PostAsJson(url, headers=headers, body=data, json=json),
                          data, json)
示例#16
0
 def ApiGet(self, url):
     return self._request(url, lambda url, headers: Request.GetAsJson(url, headers=headers))
示例#17
0
    def GetInfo(baseUrl):
        publicInfoUrl = server.Server.BuildPublicInfoUrl(baseUrl)
        headers = Request.PrepareApiCallHeaders()
        resultObj = Request.GetAsJson(publicInfoUrl, headers=headers)

        return Server.Info.fromPublicInfo(resultObj)
示例#18
0
def discoverProviderWithEmbyConnect(handle, options):
    deviceId = Request.GenerateDeviceId()

    embyConnect = linkToEmbyConnect(deviceId)
    if not embyConnect:
        return None

    dialog = xbmcgui.Dialog()

    # get all connected servers
    servers = EmbyConnect.GetServers(embyConnect.accessToken,
                                     embyConnect.userId)
    if not servers:
        log(
            'no servers available for Emby Connect user id {}'.format(
                embyConnect.userId), xbmc.LOGWARNING)
        return None

    if len(servers) == 1:
        server = servers[0]
    else:
        # ask the user which server to use
        serverChoices = [server.name for server in servers]
        serverChoice = dialog.select(localise(32057), serverChoices)
        if serverChoice < 0 or serverChoice >= len(serverChoices):
            return None

        server = server[serverChoice]

    if not server:
        return None

    urls = []
    if server.localUrl:
        # ask the user whether to use a local or remote connection
        isLocal = dialog.yesno(localise(32058),
                               localise(32059).format(server.name))
        if isLocal:
            urls.append(server.localUrl)

    if server.remoteUrl:
        urls.append(server.remoteUrl)

    baseUrl = None
    # find a working connection / base URL
    for url in urls:
        try:
            _ = emby.api.server.Server.GetInfo(url)
        except:
            log('failed to connect to "{}" at {}'.format(server.name, url),
                xbmc.LOGDEBUG)
            continue

        baseUrl = url
        break

    if not baseUrl:
        dialog.ok(localise(32058), localise(32060).format(server.name))
        log(
            'failed to connect to Emby server "{}" with Emby Connect user ID {}'
            .format(server.name, embyConnect.userId), xbmc.LOGWARNING)
        return None

    providerId = Server.BuildProviderId(server.systemId)
    providerIconUrl = Server.BuildIconUrl(baseUrl)
    provider = xbmcmediaimport.MediaProvider(
        providerId, baseUrl, server.name, providerIconUrl,
        emby.constants.SUPPORTED_MEDIA_TYPES)
    provider.setIconUrl(kodi.Api.downloadIcon(provider))

    # store Emby connect authentication in settings
    providerSettings = provider.prepareSettings()
    if not providerSettings:
        return None

    providerSettings.setString(
        emby.constants.SETTING_PROVIDER_AUTHENTICATION,
        emby.constants.SETTING_PROVIDER_AUTHENTICATION_OPTION_EMBY_CONNECT)
    providerSettings.setString(
        emby.constants.SETTING_PROVIDER_EMBY_CONNECT_USER_ID,
        embyConnect.userId)
    providerSettings.setString(
        emby.constants.SETTING_PROVIDER_EMBY_CONNECT_ACCESS_KEY,
        server.accessKey)
    providerSettings.setString(emby.constants.SETTING_PROVIDER_DEVICEID,
                               deviceId)
    providerSettings.save()

    log('Emby Connect server {} successfully discovered at {}'.format(
        mediaProvider2str(provider), baseUrl))

    return provider