Esempio n. 1
0
    def _notify(self, title, body, access_token=None, device_iden=None, **kwargs):
        """
        Sends a pushbullet notification based on the provided info or SG config

        title: The title of the notification to send
        body: The body string to send
        access_token: The access token to grant access
        device_iden: The iden of a specific target, if none provided send to all devices
        """
        access_token = self._choose(access_token, sickbeard.PUSHBULLET_ACCESS_TOKEN)
        device_iden = self._choose(device_iden, sickbeard.PUSHBULLET_DEVICE_IDEN)

        # send the request to Pushbullet
        result = None
        try:
            headers = {'Authorization': 'Basic %s' % b64encodestring('%s:%s' % (access_token, '')),
                       'Content-Type': 'application/json'}
            resp = requests.post(PUSHAPI_ENDPOINT, headers=headers,
                                 data=json.dumps(dict(
                                     type='note', title=title, body=body.strip().encode('utf-8'),
                                     device_iden=device_iden)))
            resp.raise_for_status()
        except (BaseException, Exception):
            try:
                # noinspection PyUnboundLocalVariable
                result = resp.json()['error']['message']
            except (BaseException, Exception):
                result = 'no response'
            self._log_warning(u'%s' % result)

        return self._choose((True, 'Failed to send notification: %s' % result)[bool(result)], not bool(result))
Esempio n. 2
0
    def _send_to_xbmc(self, command, host=None, username=None, password=None):
        """Handles communication to XBMC servers via HTTP API

        Args:
            command: Dictionary of field/data pairs, encoded via urllib and passed to the XBMC API via HTTP
            host: XBMC webserver host:port
            username: XBMC webserver username
            password: XBMC webserver password

        Returns:
            Returns response.result for successful commands or False if there was an error

        """
        if not host:
            self._log_debug(u'No host passed, aborting update')
            return False

        username = self._choose(username, sickbeard.XBMC_USERNAME)
        password = self._choose(password, sickbeard.XBMC_PASSWORD)

        for key in command:
            if not PY2 or type(command[key]) == text_type:
                command[key] = command[key].encode('utf-8')

        enc_command = urlencode(command)
        self._log_debug(u'Encoded API command: ' + enc_command)

        url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
        try:
            req = urllib.request.Request(url)
            # if we have a password, use authentication
            if password:
                req.add_header(
                    'Authorization', 'Basic %s' %
                    b64encodestring('%s:%s' % (username, password)))
                self._log_debug(u'Contacting (with auth header) via url: ' +
                                fixStupidEncodings(url))
            else:
                self._log_debug(u'Contacting via url: ' +
                                fixStupidEncodings(url))

            http_response_obj = urllib.request.urlopen(
                req)  # PY2 http_response_obj has no `with` context manager
            result = decode_str(http_response_obj.read(),
                                sickbeard.SYS_ENCODING)
            http_response_obj.close()

            self._log_debug(u'HTTP response: ' + result.replace('\n', ''))
            return result

        except (urllib.error.URLError, IOError) as e:
            self._log_warning(u'Couldn\'t contact HTTP at %s %s' %
                              (fixStupidEncodings(url), ex(e)))
            return False
Esempio n. 3
0
    def get_devices(access_token=None):
        # fill in omitted parameters
        if not access_token:
            access_token = sickbeard.PUSHBULLET_ACCESS_TOKEN

        # get devices from pushbullet
        try:
            headers = dict(Authorization='Basic %s' % b64encodestring('%s:%s' % (access_token, '')))
            return requests.get(DEVICEAPI_ENDPOINT, headers=headers).text
        except (BaseException, Exception):
            return json.dumps(dict(error=dict(message='Error failed to connect')))
Esempio n. 4
0
 def solve_ddg_challenge(self, resp, **original_kwargs):
     parsed_url = urlparse(resp.url)
     try:
         submit_url = parsed_url.scheme + ':' + re.findall(
             '"frm"[^>]+?action="([^"]+)"', resp.text)[0]
         kwargs = {
             k: v
             for k, v in original_kwargs.items() if k not in ['hooks']
         }
         kwargs.setdefault('headers', {})
         kwargs.setdefault(
             'data',
             dict(h=b64encodestring(
                 '%s://%s' % (parsed_url.scheme, parsed_url.hostname)),
                  u=b64encodestring(parsed_url.path),
                  p=b64encodestring(parsed_url.port or '')))
         self.wait()
         resp = self.request('POST', submit_url, **kwargs)
     except (BaseException, Exception):
         pass
     return resp
Esempio n. 5
0
    def _send_to_plex(self, command, host, username=None, password=None):
        """Handles communication to Plex hosts via HTTP API

        Args:
            command: Dictionary of field/data pairs, encoded via urllib and passed to the legacy xbmcCmds HTTP API
            host: Plex host:port
            username: Plex API username
            password: Plex API password

        Returns:
            Returns True for successful commands or False if there was an error

        """
        if not host:
            self._log_error(u'No host specified, check your settings')
            return False

        for key in command:
            if not PY2 or type(command[key]) == text_type:
                command[key] = command[key].encode('utf-8')

        enc_command = urlencode(command)
        self._log_debug(u'Encoded API command: ' + enc_command)

        url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
        try:
            req = urllib.request.Request(url)
            if password:
                req.add_header(
                    'Authorization', 'Basic %s' %
                    b64encodestring('%s:%s' % (username, password)))
                self._log_debug(u'Contacting (with auth header) via url: ' +
                                url)
            else:
                self._log_debug(u'Contacting via url: ' + url)

            http_response_obj = urllib.request.urlopen(
                req)  # PY2 http_response_obj has no `with` context manager
            result = decode_str(http_response_obj.read(),
                                sickbeard.SYS_ENCODING)
            http_response_obj.close()

            self._log_debug(u'HTTP response: ' + result.replace('\n', ''))
            return True

        except (urllib.error.URLError, IOError) as e:
            self._log_warning(u'Couldn\'t contact Plex at ' +
                              fixStupidEncodings(url) + ' ' + ex(e))
            return False
Esempio n. 6
0
    def _add_torrent_file(self, result):

        result.hash = self._request_json(
            {
                'method':
                'core.add_torrent_file',
                'params': [
                    '%s.torrent' % result.name,
                    b64encodestring(result.content), {
                        'move_completed': 'true',
                        'move_completed_path': sickbeard.TV_DOWNLOAD_DIR
                    }
                ],
                'id':
                2
            }, True)

        return result.hash
Esempio n. 7
0
 def send_auth(self, h):
     if self.username and self.password:
         h.putheader(
             'Authorization', 'Basic %s' %
             b64encodestring('%s:%s' % (self.username, self.password)))
Esempio n. 8
0
def send_nzb(search_result):
    """

    :param search_result: search result
    :type search_result: NZBSearchResult or NZBDataSearchResult
    :return:
    """
    result = False
    add_to_top = False
    nzbget_prio = 0

    authed, auth_msg, rpc_client = test_nzbget(sickbeard.NZBGET_HOST,
                                               sickbeard.NZBGET_USE_HTTPS,
                                               sickbeard.NZBGET_USERNAME,
                                               sickbeard.NZBGET_PASSWORD)

    if not authed:
        return authed

    dupekey = ''
    dupescore = 0
    # if it aired recently make it high priority and generate DupeKey/Score
    for cur_ep_obj in search_result.ep_obj_list:
        if '' == dupekey:
            dupekey = 'SickGear-%s%s' % (sickbeard.TVInfoAPI(
                cur_ep_obj.show_obj.tvid).config.get(
                    'dupekey', ''), cur_ep_obj.show_obj.prodid)
        dupekey += '-%s.%s' % (cur_ep_obj.season, cur_ep_obj.episode)

    if 1 == search_result.priority:
        add_to_top = True
        nzbget_prio = sickbeard.NZBGET_PRIORITY

    if Quality.UNKNOWN != search_result.quality:
        dupescore = search_result.quality * 100

    dupescore += (0,
                  9 + search_result.properlevel)[0 < search_result.properlevel]

    nzbcontent64 = None
    if 'nzbdata' == search_result.resultType:
        data = search_result.get_data()
        if not data:
            return False
        nzbcontent64 = b64encodestring(data, keep_eol=True)
    elif 'Anizb' == search_result.provider.name and 'nzb' == search_result.resultType:
        gen_provider = GenericProvider('')
        data = gen_provider.get_url(search_result.url)
        if None is data:
            return result
        nzbcontent64 = b64encodestring(data, keep_eol=True)

    logger.log(u'Sending NZB to NZBGet: %s' % search_result.name)

    try:
        # Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old cmd
        nzbget_version_str = rpc_client.version()
        nzbget_version = try_int(
            nzbget_version_str[:nzbget_version_str.find('.')])

        # v13+ has a combined append method that accepts both (url and content)
        # also the return value has changed from boolean to integer
        # (Positive number representing NZBID of the queue item. 0 and negative numbers represent error codes.)
        if 13 <= nzbget_version:
            nzbget_result = 0 < rpc_client.append(
                '%s.nzb' % search_result.name,
                (nzbcontent64, search_result.url)[None is nzbcontent64],
                sickbeard.NZBGET_CATEGORY, nzbget_prio, False, False, dupekey,
                dupescore, 'score')

        elif 12 == nzbget_version:
            if None is not nzbcontent64:
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, nzbcontent64, False, dupekey,
                    dupescore, 'score')
            else:
                nzbget_result = rpc_client.appendurl(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, search_result.url, False, dupekey,
                    dupescore, 'score')
        elif 0 == nzbget_version:
            if None is not nzbcontent64:
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    add_to_top, nzbcontent64)
            else:
                if 'nzb' == search_result.resultType:
                    gen_provider = GenericProvider('')
                    data = gen_provider.get_url(search_result.url)
                    if None is data:
                        return result

                    nzbcontent64 = b64encodestring(data, keep_eol=True)
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    add_to_top, nzbcontent64)
        else:
            if None is not nzbcontent64:
                nzbget_result = rpc_client.append(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, nzbcontent64)
            else:
                nzbget_result = rpc_client.appendurl(
                    '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY,
                    nzbget_prio, False, search_result.url)

        if nzbget_result:
            logger.log(u'NZB sent to NZBGet successfully', logger.DEBUG)
            result = True
        else:
            logger.log(
                u'NZBGet could not add %s.nzb to the queue' %
                search_result.name, logger.ERROR)
    except (BaseException, Exception):
        logger.log(
            u'Connect Error to NZBGet: could not add %s.nzb to the queue' %
            search_result.name, logger.ERROR)

    return result
Esempio n. 9
0
    def _search_provider(self, search_params, **kwargs):

        results = []
        if not self.url:
            return results

        items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []}

        quote_fx = (lambda t: quote(t, safe='~()*!.\''))
        for mode in search_params:
            for search_string in search_params[mode]:
                search_url = self.url
                cnt = len(items[mode])
                try:
                    for token in self._get_tokens():
                        if self.should_skip():
                            return results
                        if not token:
                            continue

                        params = dict(token=token[0], ent=token[1])
                        if 'Cache' != mode:
                            params.update(
                                {'ss': quote_fx(unidecode(search_string))})

                        data_json = None
                        vals = [i for i in range(3, 8)]
                        random.SystemRandom().shuffle(vals)
                        for x in vals[0], vals[2], vals[4]:
                            time.sleep(x)
                            params.update(dict(ts=self.ts()))
                            search_url = self.urls[
                                ('search', 'browse')['Cache' == mode]] % params
                            # decode json below as get resp will false -ve to 'nodata' when no search results
                            html_json = self.get_url(search_url)
                            if None is not html_json:
                                data_json = json.loads(html_json)
                                if data_json or 'Cache' != mode:
                                    break
                            if self.should_skip():
                                return results

                        for item in filter_iter(
                                lambda di: re.match(
                                    '(?i).*?(tv|television)',
                                    di.get('type', '') or di.get(
                                        'category', '')) and
                            (not self.confirmed or di.get('trusted') or di.get(
                                'verified')), data_json or {}):
                            seeders, leechers, size = map_list(
                                lambda arg: try_int(*([
                                    item.get(arg[0]) if None is not item.get(
                                        arg[0]) else item.get(arg[1])
                                ]) * 2),
                                (('seeder', 'seed'), ('leecher', 'leech'),
                                 ('size', 'size')))
                            if self._reject_item(seeders, leechers):
                                continue
                            title = item.get('name') or item.get('title')
                            download_url = item.get('magnet') or item.get(
                                'magnetLink')
                            if not download_url:
                                source = item.get('site') or item.get('source')
                                link = self._link(
                                    item.get('url') or item.get('pageLink'))
                                if not source or not link:
                                    continue
                                download_url = self.urls['get'] % dict(
                                    token=token[0],
                                    src=quote_fx(source),
                                    url=b64encodestring(quote_fx(link)),
                                    ts='%(ts)s')
                            if title and download_url:
                                items[mode].append(
                                    (title, download_url, seeders, size))

                except generic.HaltParseException:
                    pass
                except (BaseException, Exception):
                    logger.log(
                        u'Failed to parse. Traceback: %s' %
                        traceback.format_exc(), logger.ERROR)

                self._log_search(mode, len(items[mode]) - cnt, search_url)

            results = self._sort_seeding(mode, results + items[mode])

        return results
Esempio n. 10
0
    def _add_torrent_file(self, result):

        return self._add_torrent({'metainfo': b64encodestring(result.content)})
Esempio n. 11
0
    def update_library(self,
                       ep_obj=None,
                       host=None,
                       username=None,
                       password=None,
                       location=None,
                       **kwargs):
        """Handles updating the Plex Media Server host via HTTP API

        Plex Media Server currently only supports updating the whole video library and not a specific path.

        Returns:
            Returns None for no issue, else a string of host with connection issues

        """
        host = self._choose(host, sickbeard.PLEX_SERVER_HOST)
        if not host:
            msg = u'No Plex Media Server host specified, check your settings'
            self._log_debug(msg)
            return '%sFail: %s' % (('', '<br>')[self._testing], msg)

        username = self._choose(username, sickbeard.PLEX_USERNAME)
        password = self._choose(password, sickbeard.PLEX_PASSWORD)

        # if username and password were provided, fetch the auth token from plex.tv
        token_arg = None
        if username and password:

            self._log_debug(u'Fetching plex.tv credentials for user: '******'https://plex.tv/users/sign_in.xml',
                                         data=b'')
            req.add_header(
                'Authorization',
                'Basic %s' % b64encodestring('%s:%s' % (username, password)))
            req.add_header('X-Plex-Device-Name', 'SickGear')
            req.add_header('X-Plex-Product', 'SickGear Notifier')
            req.add_header('X-Plex-Client-Identifier',
                           '5f48c063eaf379a565ff56c9bb2b401e')
            req.add_header('X-Plex-Version', '1.0')
            token_arg = False

            try:
                http_response_obj = urllib.request.urlopen(
                    req)  # PY2 http_response_obj has no `with` context manager
                auth_tree = XmlEtree.parse(http_response_obj)
                http_response_obj.close()
                token = auth_tree.findall('.//authentication-token')[0].text
                token_arg = '?X-Plex-Token=' + token

            except urllib.error.URLError as e:
                self._log(
                    u'Error fetching credentials from plex.tv for user %s: %s'
                    % (username, ex(e)))

            except (ValueError, IndexError) as e:
                self._log(u'Error parsing plex.tv response: ' + ex(e))

        file_location = location if None is not location else '' if None is ep_obj else ep_obj.location
        host_validate = self._get_host_list(host, all([token_arg]))
        hosts_all = {}
        hosts_match = {}
        hosts_failed = []
        for cur_host in host_validate:
            response = sickbeard.helpers.get_url('%s/library/sections%s' %
                                                 (cur_host, token_arg or ''),
                                                 timeout=10,
                                                 mute_connect_err=True,
                                                 mute_read_timeout=True,
                                                 mute_connect_timeout=True)
            if response:
                response = sickbeard.helpers.parse_xml(response)
            if None is response or not len(response):
                hosts_failed.append(cur_host)
                continue

            sections = response.findall('.//Directory')
            if not sections:
                self._log(u'Plex Media Server not running on: ' + cur_host)
                hosts_failed.append(cur_host)
                continue

            for section in filter_iter(lambda x: 'show' == x.attrib['type'],
                                       sections):
                if str(section.attrib['key']) in hosts_all:
                    continue
                keyed_host = [(str(section.attrib['key']), cur_host)]
                hosts_all.update(keyed_host)
                if not file_location:
                    continue

                for section_location in section.findall('.//Location'):
                    section_path = re.sub(
                        r'[/\\]+', '/',
                        section_location.attrib['path'].lower())
                    section_path = re.sub(r'^(.{,2})[/\\]', '', section_path)
                    location_path = re.sub(r'[/\\]+', '/',
                                           file_location.lower())
                    location_path = re.sub(r'^(.{,2})[/\\]', '', location_path)

                    if section_path in location_path:
                        hosts_match.update(keyed_host)
                        break

        if not self._testing:
            hosts_try = (hosts_all.copy(),
                         hosts_match.copy())[any(hosts_match)]
            host_list = []
            for section_key, cur_host in iteritems(hosts_try):
                refresh_result = None
                if not self._testing:
                    refresh_result = sickbeard.helpers.get_url(
                        '%s/library/sections/%s/refresh%s' %
                        (cur_host, section_key, token_arg or ''))
                if (not self._testing
                        and '' == refresh_result) or self._testing:
                    host_list.append(cur_host)
                else:
                    hosts_failed.append(cur_host)
                    self._log_error(
                        u'Error updating library section for Plex Media Server: %s'
                        % cur_host)

            if len(hosts_failed) == len(host_validate):
                self._log(u'No successful Plex host updated')
                return 'Fail no successful Plex host updated: %s' % ', '.join(
                    [host for host in hosts_failed])
            else:
                hosts = ', '.join(set(host_list))
                if len(hosts_match):
                    self._log(
                        u'Hosts updating where TV section paths match the downloaded show: %s'
                        % hosts)
                else:
                    self._log(u'Updating all hosts with TV sections: %s' %
                              hosts)
                return ''

        hosts = [
            host.replace('http://', '') for host in filter_iter(
                lambda x: x.startswith('http:'), list_values(hosts_all))
        ]
        secured = [
            host.replace('https://', '') for host in filter_iter(
                lambda x: x.startswith('https:'), list_values(hosts_all))
        ]
        failed = ', '.join([
            host.replace('http://', '') for host in filter_iter(
                lambda x: x.startswith('http:'), hosts_failed)
        ])
        failed_secured = ', '.join(
            filter_iter(lambda x: x not in hosts, [
                host.replace('https://', '') for host in filter_iter(
                    lambda x: x.startswith('https:'), hosts_failed)
            ]))

        return '<br>' + '<br>'.join([
            result for result in
            [('',
              'Fail: username/password when fetching credentials from plex.tv'
              )[False is token_arg],
             ('',
              'OK (secure connect): %s' % ', '.join(secured))[any(secured)],
             ('', 'OK%s: %s' %
              ((' (legacy connect)', '')[None is token_arg], ', '.join(hosts))
              )[any(hosts)], ('', 'Fail (secure connect): %s' %
                              failed_secured)[any(failed_secured)],
             ('', 'Fail%s: %s' % ((' (legacy connect)', '')[None is token_arg],
                                  failed))[bool(failed)]] if result
        ])
Esempio n. 12
0
    def _send_to_xbmc_json(self,
                           command,
                           host=None,
                           username=None,
                           password=None):
        # type: (...) -> Union[bool, Dict]
        """Handles communication to XBMC servers via JSONRPC

        Args:
            command: Dictionary of field/data pairs, encoded via urllib and passed to the XBMC JSON-RPC via HTTP
            host: XBMC webserver host:port
            username: XBMC webserver username
            password: XBMC webserver password

        Returns:
            Returns response.result for successful commands or False if there was an error

        """
        if not host:
            self._log_debug(u'No host passed, aborting update')
            return False

        username = self._choose(username, sickbeard.XBMC_USERNAME)
        password = self._choose(password, sickbeard.XBMC_PASSWORD)

        command = command.encode('utf-8')
        self._log_debug(u'JSON command: ' + command)

        url = 'http://%s/jsonrpc' % host
        try:
            req = urllib.request.Request(url, command)
            req.add_header('Content-type', 'application/json')
            # if we have a password, use authentication
            if password:
                req.add_header(
                    'Authorization', 'Basic %s' %
                    b64encodestring('%s:%s' % (username, password)))
                self._log_debug(u'Contacting (with auth header) via url: ' +
                                fixStupidEncodings(url))
            else:
                self._log_debug(u'Contacting via url: ' +
                                fixStupidEncodings(url))

            try:
                http_response_obj = urllib.request.urlopen(
                    req)  # PY2 http_response_obj has no `with` context manager
            except urllib.error.URLError as e:
                self._log_warning(
                    u'Error while trying to retrieve API version for "%s": %s'
                    % (host, ex(e)))
                return False

            # parse the json result
            try:
                result = json.load(http_response_obj)
                http_response_obj.close()
                self._log_debug(u'JSON response: ' + str(result))
                return result  # need to return response for parsing
            except ValueError:
                self._log_warning(u'Unable to decode JSON: ' +
                                  http_response_obj)
                return False

        except IOError as e:
            self._log_warning(u'Couldn\'t contact JSON API at ' +
                              fixStupidEncodings(url) + ' ' + ex(e))
            return False