示例#1
0
    def setup(self, task, config):
        """Set up a session and log in"""
        self._session = task.requests
        base_url = config.get('base_url', "").rstrip("/")
        if base_url:
            if self.base_url and self.base_url != base_url:
                log.warning(
                    "Using plugin designed for %s on %s - things may break",
                    self.base_url,
                    base_url,
                )
            self.base_url = base_url

        if not self.base_url:
            raise PluginError("No 'base_url' configured")

        # Any more than 5 pages is probably way too loose of a search
        self.max_pages = config.get('max_pages', 5)

        # The consistent request limiting rule seems to be:
        # "Refrain from making more than five (5) requests every ten (10) seconds"
        self._session.add_domain_limiter(
            TokenBucketLimiter(self.base_url, 2, '2 seconds'))

        self.username = config['username']
        self.password = config['password']

        # Login
        self.authenticate()

        # Logged in successfully, it's ok if nothing matches now
        task.no_entries_ok = True
示例#2
0
    def on_task_input(self, task, config):
        """Search on What.cd"""

        self.session = Session()

        # From the API docs: "Refrain from making more than five (5) requests every ten (10) seconds"
        self.session.add_domain_limiter(
            TokenBucketLimiter('ssl.what.cd', 2, '2 seconds'))

        # Custom user agent
        user_agent = config.pop('user_agent', None)
        if user_agent:
            self.session.headers.update({"User-Agent": user_agent})

        # Login
        self._login(config.pop('username'), config.pop('password'))

        # Logged in successfully, it's ok if nothing matches
        task.no_entries_ok = True

        # NOTE: Any values still in config at this point MUST be valid search parameters

        # Perform the search and parse the needed information out of the response
        results = self._search_results(config)
        return list(self._get_entries(results))
示例#3
0
class SearchBTN(object):
    schema = {'type': 'string'}
    # Advertised limit is 150/hour (24s/request average). This may need some tweaking.
    request_limiter = TokenBucketLimiter('api.btnapps.net', 100, '25 seconds')

    def search(self, task, entry, config):
        task.requests.add_domain_limiter(self.request_limiter)
        api_key = config

        searches = entry.get('search_strings', [entry['title']])

        if 'series_name' in entry:
            search = {'category': 'Episode'}
            if 'tvdb_id' in entry:
                search['tvdb'] = entry['tvdb_id']
            elif 'tvrage_id' in entry:
                search['tvrage'] = entry['tvrage_id']
            else:
                search['series'] = entry['series_name']
            if 'series_id' in entry:
                # BTN wants an ep style identifier even for sequence shows
                if entry.get('series_id_type') == 'sequence':
                    search['name'] = 'S01E%02d' % entry['series_id']
                else:
                    search['name'] = entry[
                        'series_id'] + '%'  # added wildcard search for better results.
            searches = [search]
            # If searching by series name ending in a parenthetical, try again without it if there are no results.
            if search.get('series') and search['series'].endswith(')'):
                match = re.match('(.+)\([^\(\)]+\)$', search['series'])
                if match:
                    searches.append(dict(search,
                                         series=match.group(1).strip()))

        results = set()
        for search in searches:
            data = json.dumps({
                'method': 'getTorrents',
                'params': [api_key, search],
                'id': 1
            })
            try:
                r = task.requests.post(
                    'http://api.btnapps.net/',
                    data=data,
                    headers={'Content-type': 'application/json'})
            except requests.RequestException as e:
                log.error('Error searching btn: %s' % e)
                continue
            content = r.json()
            if not content or not content['result']:
                log.debug('No results from btn')
                if content and content.get('error'):
                    if content['error'].get('code') == -32002:
                        log.error(
                            'btn api call limit exceeded, throttling connection rate'
                        )
                        self.request_limiter.tokens = -1
                    else:
                        log.error(
                            'Error searching btn: %s' %
                            content['error'].get('message', content['error']))
                continue
            if 'torrents' in content['result']:
                for item in content['result']['torrents'].values():
                    entry = Entry()
                    entry['title'] = item['ReleaseName']
                    entry['title'] += ' '.join([
                        '', item['Resolution'], item['Source'], item['Codec']
                    ])
                    entry['url'] = item['DownloadURL']
                    entry['torrent_seeds'] = int(item['Seeders'])
                    entry['torrent_leeches'] = int(item['Leechers'])
                    entry['torrent_info_hash'] = item['InfoHash']
                    entry['search_sort'] = torrent_availability(
                        entry['torrent_seeds'], entry['torrent_leeches'])
                    if item['TvdbID'] and int(item['TvdbID']):
                        entry['tvdb_id'] = int(item['TvdbID'])
                    if item['TvrageID'] and int(item['TvrageID']):
                        entry['tvrage_id'] = int(item['TvrageID'])
                    results.add(entry)
                # Don't continue searching if this search yielded results
                break
        return results
示例#4
0
class SearchBTN:
    schema = {
        'oneOf': [
            {'type': 'string'},
            {
                'type': 'object',
                'properties': {
                    'api_key': {'type': 'string'},
                    'append_quality': {'type': 'boolean'},
                    'origin': one_or_more({'type': 'string', 'enum': ORIGINS}),
                },
                'required': ['api_key'],
                'additionalProperties': False,
            },
        ]
    }
    # Advertised limit is 150/hour (24s/request average). This may need some tweaking.
    request_limiter = TokenBucketLimiter('api.broadcasthe.net/', 100, '25 seconds')

    def prepare_config(self, config):
        if not isinstance(config, dict):
            config = {'api_key': config}

        config.setdefault('append_quality', True)
        config.setdefault('origin', [])
        if not isinstance(config['origin'], list):
            config['origin'] = [config['origin']]
        return config

    def search(self, task, entry, config):
        task.requests.add_domain_limiter(self.request_limiter)
        config = self.prepare_config(config)
        api_key = config['api_key']

        searches = entry.get('search_strings', [entry['title']])

        if 'series_name' in entry:
            if entry.get('season_pack_lookup', False):
                search = {'category': 'Season'}
            else:
                search = {'category': 'Episode'}
            if config.get('origin'):
                search['origin'] = config['origin']
            if 'tvdb_id' in entry:
                search['tvdb'] = entry['tvdb_id']
            elif 'tvrage_id' in entry:
                search['tvrage'] = entry['tvrage_id']
            else:
                search['series'] = entry['series_name']
            if entry.get('season_pack_lookup', False) and 'series_season' in entry:
                search['name'] = 'Season %s' % entry['series_season']
            elif 'series_id' in entry:
                # BTN wants an ep style identifier even for sequence shows
                if entry.get('series_id_type') == 'sequence':
                    search['name'] = 'S01E%02d' % entry['series_id']
                else:
                    search['name'] = (
                        entry['series_id'] + '%'
                    )  # added wildcard search for better results.
            searches = [search]
            # If searching by series name ending in a parenthetical, try again without it if there are no results.
            if search.get('series') and search['series'].endswith(')'):
                match = re.match(r'(.+)\([^\(\)]+\)$', search['series'])
                if match:
                    searches.append(dict(search, series=match.group(1).strip()))

        results = set()
        for search in searches:
            data = json.dumps({'method': 'getTorrents', 'params': [api_key, search], 'id': 1})
            try:
                r = task.requests.post(
                    'https://api.broadcasthe.net/',
                    data=data,
                    headers={'Content-type': 'application/json'},
                )
            except requests.RequestException as e:
                log.error('Error searching btn: %s' % e)
                continue
            try:
                content = r.json()
            except ValueError as e:
                raise plugin.PluginError('Error searching btn. Maybe it\'s down?. %s' % str(e))
            if not content or not content['result']:
                log.debug('No results from btn')
                if content and content.get('error'):
                    if content['error'].get('code') == -32002:
                        log.error('btn api call limit exceeded, throttling connection rate')
                        self.request_limiter.tokens = -1
                    else:
                        log.error(
                            'Error searching btn: %s'
                            % content['error'].get('message', content['error'])
                        )
                continue
            if 'torrents' in content['result']:
                for item in content['result']['torrents'].values():
                    entry = Entry()
                    entry['title'] = item['ReleaseName']
                    if config['append_quality']:
                        entry['title'] += ' '.join(
                            ['', item['Resolution'], item['Source'], item['Codec']]
                        )
                    entry['url'] = item['DownloadURL']
                    entry['torrent_seeds'] = int(item['Seeders'])
                    entry['torrent_leeches'] = int(item['Leechers'])
                    entry['torrent_info_hash'] = item['InfoHash']
                    entry['torrent_availability'] = torrent_availability(
                        entry['torrent_seeds'], entry['torrent_leeches']
                    )
                    entry['btn_origin'] = item['Origin']
                    if item['TvdbID'] and int(item['TvdbID']):
                        entry['tvdb_id'] = int(item['TvdbID'])
                    if item['TvrageID'] and int(item['TvrageID']):
                        entry['tvrage_id'] = int(item['TvrageID'])
                    results.add(entry)
                # Don't continue searching if this search yielded results
                break
        return results