Example #1
0
    def query(self, key, method=None, headers=None, timeout=None, **kwargs):
        """ Main method used to handle HTTPS requests to the Plex server. This method helps
            by encoding the response to utf-8 and parsing the returned XML into and
            ElementTree object. Returns None if no data exists in the response.
        """

        url = self.url(key)
        method = method or self._session.get
        timeout = timeout or TIMEOUT
        log.debug('%s %s', method.__name__.upper(), url)
        headers = self._headers(**headers or {})
        response = method(url, headers=headers, timeout=timeout, **kwargs)
        if response.status_code not in (200, 201, 204):
            codename = codes.get(response.status_code)[0]
            errtext = response.text.replace('\n', ' ')
            message = '(%s) %s; %s %s' % (response.status_code, codename,
                                          response.url, errtext)
            if response.status_code == 401:
                raise Unauthorized(message)
            elif response.status_code == 404:
                raise NotFound(message)
            else:
                raise BadRequest(message)
        data = response.text.encode('utf8')
        return ElementTree.fromstring(data) if data.strip() else None
Example #2
0
    def signin(cls, username=None, password=None, session=None):
        """ Returns a new :class:`~myplex.MyPlexAccount` object by connecting to MyPlex with the
            specified username and password. This is essentially logging into MyPlex and often
            the very first entry point to using this API.

            Parameters:
                username (str): Your MyPlex.tv username. If not specified, it will check the config.ini file.
                password (str): Your MyPlex.tv password. If not specified, it will check the config.ini file.

            Raises:
                :class:`~plexapi.exceptions.Unauthorized`: (401) If the username or password are invalid.
                :class:`~plexapi.exceptions.BadRequest`: If any other errors occured not allowing us to log into MyPlex.tv.
        """
        if 'X-Plex-Token' in plexapi.BASE_HEADERS:
            del plexapi.BASE_HEADERS['X-Plex-Token']
        username = username or CONFIG.get('authentication.username')
        password = password or CONFIG.get('authentication.password')
        auth = (username, password)
        log.info('POST %s', cls.SIGNIN)
        sess = session or requests.Session()
        response = sess.post(cls.SIGNIN,
                             headers=plexapi.BASE_HEADERS,
                             auth=auth,
                             timeout=TIMEOUT)
        if response.status_code != requests.codes.created:
            codename = codes.get(response.status_code)[0]
            if response.status_code == 401:
                raise Unauthorized('(%s) %s' %
                                   (response.status_code, codename))
            raise BadRequest('(%s) %s' % (response.status_code, codename))
        data = ElementTree.fromstring(response.text.encode('utf8'))
        return MyPlexAccount(data, cls.SIGNIN, session=sess)
Example #3
0
    def signin(cls, username, password):
        """Summary

        Args:
            username (str): username
            password (str): password

        Returns:
            class: MyPlexAccount

        Raises:
            BadRequest: (HTTPCODE) http codename
            Unauthorized: (HTTPCODE) http codename
        """
        if 'X-Plex-Token' in plexapi.BASE_HEADERS:
            del plexapi.BASE_HEADERS['X-Plex-Token']
        auth = (username, password)
        log.info('POST %s', cls.SIGNIN)
        response = requests.post(cls.SIGNIN,
                                 headers=plexapi.BASE_HEADERS,
                                 auth=auth,
                                 timeout=TIMEOUT)
        if response.status_code != requests.codes.created:
            codename = codes.get(response.status_code)[0]
            if response.status_code == 401:
                raise Unauthorized('(%s) %s' %
                                   (response.status_code, codename))
            raise BadRequest('(%s) %s' % (response.status_code, codename))
        data = ElementTree.fromstring(response.text.encode('utf8'))
        return cls(data, cls.SIGNIN)
Example #4
0
    def __init__(self, bot, **kwargs):
        """
        Initializes Plex resources

        Connects to Plex library and sets up
        all asyncronous communications.

        Args:
            bot: discord.ext.command.Bot, bind for cogs
            base_url: str url to Plex server
            plex_token: str X-Token of Plex server
            lib_name: str name of Plex library to search through

        Raises:
            plexapi.exceptions.Unauthorized: Invalid Plex token

        Returns:
            None
        """

        self.bot = bot
        self.base_url = kwargs["base_url"]
        self.plex_token = kwargs["plex_token"]
        self.library_name = kwargs["lib_name"]
        self.bot_prefix = bot.command_prefix

        if kwargs["lyrics_token"]:
            self.genius = lyricsgenius.Genius(kwargs["lyrics_token"])
        else:
            plex_log.warning("No lyrics token specified, lyrics disabled")
            self.genius = None

        # Log fatal invalid plex token
        try:
            self.pms = PlexServer(self.base_url, self.plex_token)
        except Unauthorized:
            plex_log.fatal("Invalid Plex token, stopping...")
            raise Unauthorized("Invalid Plex token")

        self.music = self.pms.library.section(self.library_name)
        plex_log.debug("Connected to plex library: %s", self.library_name)

        # Initialize necessary vars
        self.voice_channel = None
        self.current_track = None
        self.np_message_id = None
        self.ctx = None

        # Initialize events
        self.play_queue = asyncio.Queue()
        self.play_next_event = asyncio.Event()

        bot_log.info("Started bot successfully")
        self.bot.loop.create_task(self._audio_player_task())
Example #5
0
 def signin(cls, username, password):
     if 'X-Plex-Token' in plexapi.BASE_HEADERS:
         del plexapi.BASE_HEADERS['X-Plex-Token']
     auth = (username, password)
     log.info('POST %s', cls.SIGNIN)
     response = requests.post(cls.SIGNIN, headers=plexapi.BASE_HEADERS, auth=auth, timeout=TIMEOUT)
     if response.status_code != requests.codes.created:
         codename = codes.get(response.status_code)[0]
         if response.status_code == 401:
             raise Unauthorized('(%s) %s' % (response.status_code, codename))
         raise BadRequest('(%s) %s' % (response.status_code, codename))
     data = ElementTree.fromstring(response.text.encode('utf8'))
     return cls(data, cls.SIGNIN)
Example #6
0
    def connect(self):
        """Connect to a Plex server directly, obtaining direct URL if necessary."""
        config_entry_update_needed = False

        def _connect_with_token():
            available_servers = [(x.name, x.clientIdentifier)
                                 for x in self.account.resources()
                                 if "server" in x.provides and x.presence]

            if not available_servers:
                raise NoServersFound
            if not self._server_name and len(available_servers) > 1:
                raise ServerNotSpecified(available_servers)

            self.server_choice = (self._server_name if self._server_name else
                                  available_servers[0][0])
            self._plex_server = self.account.resource(
                self.server_choice).connect(timeout=10)

        def _connect_with_url():
            session = None
            if self._url.startswith("https") and not self._verify_ssl:
                session = Session()
                session.verify = False
            self._plex_server = plexapi.server.PlexServer(
                self._url, self._token, session)

        def _update_plexdirect_hostname():
            matching_servers = [
                x.name for x in self.account.resources()
                if x.clientIdentifier == self._server_id
            ]
            if matching_servers:
                self._plex_server = self.account.resource(
                    matching_servers[0]).connect(timeout=10)
                return True
            _LOGGER.error("Attempt to update plex.direct hostname failed")
            return False

        if self._url:
            try:
                _connect_with_url()
            except requests.exceptions.SSLError as error:
                while error and not isinstance(error,
                                               ssl.SSLCertVerificationError):
                    error = error.__context__
                if isinstance(error, ssl.SSLCertVerificationError):
                    domain = urlparse(self._url).netloc.split(":")[0]
                    if domain.endswith(
                            "plex.direct") and error.args[0].startswith(
                                f"hostname '{domain}' doesn't match"):
                        _LOGGER.warning(
                            "Plex SSL certificate's hostname changed, updating"
                        )
                        if _update_plexdirect_hostname():
                            config_entry_update_needed = True
                        else:
                            raise Unauthorized(  # pylint: disable=raise-missing-from
                                "New certificate cannot be validated with provided token"
                            )
                    else:
                        raise
                else:
                    raise
        else:
            _connect_with_token()

        try:
            system_accounts = self._plex_server.systemAccounts()
            shared_users = self.account.users() if self.account else []
        except Unauthorized:
            _LOGGER.warning(
                "Plex account has limited permissions, shared account filtering will not be available"
            )
        else:
            self._accounts = []
            for user in shared_users:
                for shared_server in user.servers:
                    if shared_server.machineIdentifier == self.machine_identifier:
                        self._accounts.append(user.title)

            _LOGGER.debug("Linked accounts: %s", self.accounts)

            owner_account = next(
                (account.name
                 for account in system_accounts if account.accountID == 1),
                None,
            )
            if owner_account:
                self._owner_username = owner_account
                self._accounts.append(owner_account)
                _LOGGER.debug("Server owner found: '%s'", self._owner_username)

        self._version = self._plex_server.version

        if config_entry_update_needed:
            raise ShouldUpdateConfigEntry