Example #1
0
def _process_data_album_dict(hm,
                             lastfm,
                             alb_name,
                             a_name,
                             do_whichone,
                             mi=None,
                             process_to_end=True):
    assert (
        do_whichone in ('GRACENOTE', 'LASTFM', 'MUSICBRAINZ')
    ), "error, metadata service = %s should be one of GRACENOTE, LASTFM, or MUSICBRAINZ" % do_whichone
    if do_whichone == 'GRACENOTE':
        album_data_dict, status = hm.get_music_metadatas_album(
            a_name, alb_name)
        if status == 'SUCCESS': return album_data_dict, status
        if not_process_to_end:
            return return_error_raw(status)

        #
        ## now lastfm
        album_data_dict, status = lastfm.get_music_metadatas_album(
            a_name, alb_name)
        if status == 'SUCCESS': return album_data_dict, status

        #
        ## now musicbrainz
        if mi is not None:
            assert (mi.artist_name == a_name)
        else:
            mi = music.MusicInfo(a_name)
        album_data_dict, status = mi.get_music_metadatas_album(alb_name)
        if status == 'SUCCESS': return album_data_dict, status
        return return_error_raw(status)
    elif do_whichone == 'LASTFM':
        album_data_dict, status = lastfm.get_music_metadatas_album(
            a_name, alb_name)
        if status == 'SUCCESS': return album_data_dict, status
        if not process_to_end: return return_error_raw(status)

        #
        ## now musicbrainz
        if mi is not None:
            assert (mi.artist_name == a_name)
        else:
            mi = music.MusicInfo(a_name)
        album_data_dict, status = mi.get_music_metadatas_album(alb_name)
        if status == 'SUCCESS': return album_data_dict, status
        return return_error_raw(status)
    else:
        if mi is not None:
            assert (mi.artist_name == a_name)
        else:
            mi = music.MusicInfo(a_name)
        album_data_dict, status = mi.get_music_metadatas_album(alb_name)
        if status == 'SUCCESS': return album_data_dict, status
        return return_error_raw(status)
Example #2
0
def get_default_tvlibrary(fullURL, token):
    try:
        all_libraries = core.get_libraries(token=token,
                                           fullURL=fullURL,
                                           do_full=True)
    except:
        return return_error_raw("Error, bad token or URL may have been given.")
    key_found = list(
        filter(lambda key: all_libraries[key][1] == 'show', all_libraries))
    if key_found is None:
        return_error_raw("Error, could not find any TV libraries.")
    key_found = min(key_found)
    return all_libraries[key_found][0], 'SUCCESS'
Example #3
0
def set_excluded_shows(tvdata, shows_to_exclude):
    shows_act = set(shows_to_exclude) & set(tvdata)
    if len(shows_act) == 0:
        return return_error_raw(
            "Error, the set of TV shows, %s, is not in any of %d TV shows in Plex library."
            % (sorted(shows_act), len(set(tvdata))))
    #
    ##
    tv.push_shows_to_exclude(tvdata, shows_act)
Example #4
0
def get_tvdata(tvlibraryname, fullURL, token, doCheck=True):
    if doCheck:
        try:
            all_libraries = core.get_libraries(token=token,
                                               fullURL=fullURL,
                                               do_full=True)
        except:
            return return_error_raw(
                "Error, bad token or URL may have been given.")
        key_found = list(
            filter(lambda key: all_libraries[key][0] == tvlibraryname,
                   all_libraries))
        if key_found is None:
            return return_error_raw("Error, %s library does not exist." %
                                    tvlibraryname)
        key_found = min(key_found)
        if all_libraries[key_found][1] != 'show':
            return return_error_raw("Error, %s library is not a TV library." %
                                    tvlibraryname)
    #
    ## now get data
    tvdata = core.get_library_data(tvlibraryname, token=token, fullURL=fullURL)
    return tvdata, 'SUCCESS'
Example #5
0
def get_movie_torrent(name, verify=True):
    """
    Returns a :py:class:`tuple` of candidate movie found using `Torrent files <torrent file_>`_ using the `YTS API`_ torrent service and the string ``"SUCCESS"``, if successful.
    
    :param str name: the movie string on which to search.
    :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``.
    :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of `Torrent files <torrent file_>`_ that match the searched movie, ordered from *earliest* to *latest* year of release. The second element is the string ``"SUCCESS"``. If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`.
    :rtype: tuple
    
    .. _`YTS API`: https://yts.ag/api
    .. _`torrent file`: https://en.wikipedia.org/wiki/Torrent_file
    """
    mainURL = 'https://yts.ag/api/v2/list_movies.json'
    params = {'query_term': name, 'order_by': 'year'}
    response = requests.get(mainURL, params=params, verify=verify)
    if response.status_code != 200:
        return return_error_raw(
            'Error, could not use the movie service. Exiting...')
    data = response.json()['data']
    if 'movies' not in data or len(data['movies']) == 0:
        return return_error_raw("Could not find %s, exiting..." % name)
    movies = data['movies']
    return movies, 'SUCCESS'
Example #6
0
def get_book_torrent_jackett(name, maxnum=10, keywords=[], verify=True):
    """
    Returns a :py:class:`tuple` of candidate book Magnet links found using the main Jackett_ torrent searching service and the string ``"SUCCESS"``, if successful.

    :param str name: the book to search for.
    :param int maxnum: optional argumeent, the maximum number of magnet links to return. Default is 10. Must be :math:`\ge 5`.
    :param list keywords: optional argument. If not empty, the title of the candidate element must have at least one of the keywords in ``keywords``.
    :param bool verify:  optional argument, whether to verify SSL connections. Default is ``True``.
    
    :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched episode, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are,
       
      * ``title`` is the name of the candidate book to download, and in parentheses the size of the candidate in MB or GB.
      * ``rawtitle`` also the name of the candidate episode to download.
      * ``seeders`` is the number of seeds for this Magnet link.
      * ``leechers`` is the number of leeches for this Magnet link.
      * ``link`` is the Magnet URI link.
      * ``torrent_size`` is the size of this torrent in Megabytes.
    
    If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`.
    
    :rtype: tuple

    .. _Jackett: https://github.com/Jackett/Jackett
    """
    import validators
    assert (maxnum >= 5)
    data = core.get_jackett_credentials()
    if data is None:
        return return_error_raw(
            'FAILURE, COULD NOT GET JACKETT SERVER CREDENTIALS')
    url, apikey = data
    if not url.endswith('/'): url = '%s/' % url
    endpoint = 'api/v2.0/indexers/all/results/torznab/api'
    name_split = name.split()
    last_tok = name_split[-1].lower()
    status = re.match('^s[0-9]{2}e[0-9]{2}', last_tok)

    def _return_params(name):
        params = {'apikey': apikey, 'q': name, 'cat': '7020'}
        return params

    logging.info('URL ENDPOINT: %s, PARAMS = %s.' % (urljoin(url, endpoint), {
        'apikey': apikey,
        'q': name,
        'cat': '7020'
    }))
    response = requests.get(urljoin(url, endpoint),
                            params={
                                'apikey': apikey,
                                'q': name,
                                'cat': '7020'
                            },
                            verify=verify)  # tv shows
    if response.status_code != 200:
        return return_error_raw(
            'FAILURE, PROBLEM WITH JACKETT SERVER ACCESSIBLE AT %s.' % url)
    html = BeautifulSoup(response.content, 'lxml')
    if len(html.find_all('item')) == 0:
        return return_error_raw(
            'FAILURE, NO BOOKS SATISFYING CRITERIA FOR GETTING %s' % name)
    items = []

    def _get_magnet_url(item):
        magnet_url = item.find('torznab:attr', {'name': 'magneturl'})
        if magnet_url is not None and 'magnet' in magnet_url['value']:
            return magnet_url['value']
        #
        ## not found it here, must go into URL
        url2 = item.find('guid')
        if url2 is None: return None
        url2 = url2.text
        if not validators.url(url2): return None
        resp2 = requests.get(url2, verify=verify)
        if resp2.status_code != 200: return None
        h2 = BeautifulSoup(resp2.content, 'lxml')
        valid_magnet_links = set(
            map(
                lambda elem: elem['href'],
                filter(
                    lambda elem: 'href' in elem.attrs and 'magnet' in elem[
                        'href'], h2.find_all('a'))))
        if len(valid_magnet_links) == 0: return None
        return max(valid_magnet_links)

    if status is None: last_tok = None
    for item in html('item'):
        title = item.find('title')
        if title is None: continue
        title = title.text
        #
        ## now check if the sXXeYY in name
        if last_tok is not None:
            if last_tok not in title.lower(): continue
        torrent_size = item.find('size')
        if torrent_size is not None:
            torrent_size = float(torrent_size.text) / 1024**2
        seeders = item.find('torznab:attr', {'name': 'seeders'})
        if seeders is None: seeders = -1
        else: seeders = int(seeders['value'])
        leechers = item.find('torznab:attr', {'name': 'peers'})
        if leechers is None: leechers = -1
        else: leechers = int(leechers['value'])
        #
        ## now do one of two things to get the magnet URL
        magnet_url = _get_magnet_url(item)
        if magnet_url is None: continue
        myitem = {
            'title': title,
            'rawtitle': title,
            'seeders': seeders,
            'leechers': leechers,
            'link': magnet_url
        }
        if torrent_size is not None:
            myitem['title'] = '%s (%0.1f MiB)' % (title, torrent_size)
            myitem['torrent_size'] = torrent_size
        pubdate_elem = item.find('pubdate')
        if pubdate_elem is not None:
            try:
                pubdate_s = pubdate_elem.get_text().split(',')[-1].strip()
                pubdate_s = ' '.join(pubdate_s.split()[:3])
                pubdate = datetime.datetime.strptime(pubdate_s,
                                                     '%d %B %Y').date()
                myitem['pubdate'] = pubdate
            except:
                pass
        items.append(myitem)

    items = sorted(items,
                   key=lambda elem: elem['seeders'] + elem['leechers'])[::-1]
    if len(keywords) != 0:
        items = list(
            filter(
                lambda item: any(
                    map(lambda tok: tok.lower() in item['rawtitle'].lower(
                    ), keywords)) and not any(
                        map(
                            lambda tok: tok.lower() in item['rawtitle'].lower(
                            ), keywords_exc)) and all(
                                map(
                                    lambda tok: tok.lower() in item['rawtitle']
                                    .lower(), must_have)), items))
    if len(items) == 0:
        return return_error_raw(
            'FAILURE, NO BOOKS SATISFYING CRITERIA FOR GETTING %s' % name)

    return items[:maxnum], 'SUCCESS'
Example #7
0
def get_movie_torrent_zooqle(name, maxnum=10, verify=True):
    """
    Returns a :py:class:`tuple` of candidate movie Magnet links found using the Zooqle_ torrent service and the string ``"SUCCESS"``, if successful.

    :param str name: the movie string on which to search.
    :param int maxnum: optional argument, the maximum number of magnet links to return. Default is 100. Must be :math:`\ge 5`.
    :param bool verify:  optional argument, whether to verify SSL connections. Default is ``True``.
    
    :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched movie, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are,

      * ``title`` is the name of the candidate movie to download, and in parentheses is the size of the download in MB or GB.
      * ``rawtitle`` is *only* the name of the candidate movie to download.
      * ``seeders`` is the number of seeds for this Magnet link.
      * ``leechers`` is the number of leeches for this Magnet link.
      * ``link`` is the Magnet URI link.
      * ``torrent_size`` is the size of this torrent in MB.
    
    If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`.
    
    :rtype: tuple
    
    .. _Zooqle: https://zooqle.com
    """
    assert (maxnum >= 5)
    names_of_trackers = map(
        lambda tracker: tracker.replace(':', '%3A').replace('/', '%2F'), [
            'udp://tracker.opentrackr.org:1337/announce',
            'udp://open.demonii.com:1337', 'udp://tracker.pomf.se:80/announce',
            'udp://torrent.gresille.org:80/announce',
            'udp://11.rarbg.com/announce', 'udp://11.rarbg.com:80/announce',
            'udp://open.demonii.com:1337/announce',
            'udp://tracker.openbittorrent.com:80',
            'http://tracker.ex.ua:80/announce',
            'http://tracker.ex.ua/announce',
            'http://bt.careland.com.cn:6969/announce',
            'udp://glotorrents.pw:6969/announce'
        ])
    tracklist = ''.join(
        map(lambda tracker: '&tr=%s' % tracker, names_of_trackers))

    def _get_magnet_link(info_hash, title):
        download_url = "magnet:?xt=urn:btih:" + info_hash + "&dn=" + '+'.join(
            title.split()) + tracklist
        return download_url

    candname = re.sub("'", '', name)
    url = 'https://zooqle.com/search'
    params = {
        'q': '+'.join(candname.split() + [
            'category%3AMovie',
        ]),
        'fmt': 'rss'
    }
    paramurl = '?' + '&'.join(
        map(lambda tok: '%s=%s' % (tok, params[tok]), params))
    fullurl = urljoin(url, paramurl)
    response = requests.get(fullurl, verify=verify)
    if response.status_code != 200:
        return return_error_raw(
            'ERROR, COULD NOT FIND ZOOQLE TORRENTS FOR %s' % candname)
    myxml = BeautifulSoup(response.content, 'lxml')

    def is_valid_elem(elem):
        names = set(map(lambda elm: elm.name, elem.find_all()))
        return len(names & set([
            'torrent:infohash', 'torrent:seeds', 'torrent:peers',
            'torrent:contentlength'
        ])) == 4

    cand_items = list(
        filter(
            lambda elem: len(elem.find_all('title')) >= 1 and is_valid_elem(
                elem) and get_maximum_matchval(
                    max(elem.find_all('title')).get_text(), candname) >= 80,
            myxml.find_all('item')))

    def get_num_forelem(elem, name):
        valid_elm = list(
            filter(lambda elm: elm.name == 'torrent:%s' % name, elem))
        if len(valid_elm) == 0: return None
        valid_elm = valid_elm[0]
        return int(valid_elm.get_text())

    def get_infohash(elem):
        valid_elm = list(
            filter(lambda elm: elm.name == 'torrent:infohash', elem))
        if len(valid_elm) == 0: return None
        valid_elm = valid_elm[0]
        return valid_elm.get_text().lower()

    items_toshow = list(
        map(
            lambda elem: {
                'title':
                '%s (%s)' %
                (max(elem.find_all('title')).get_text(),
                 get_formatted_size(get_num_forelem(elem, 'contentlength'))),
                'raw_title':
                max(elem.find_all('title')).get_text(),
                'seeders':
                get_num_forelem(elem, 'seeds'),
                'leechers':
                get_num_forelem(elem, 'peers'),
                'link':
                _get_magnet_link(get_infohash(elem),
                                 max(elem.find_all('title')).get_text()),
                'torrent_size':
                float(get_num_forelem(elem, 'contentlength') * 1.0 / 1024**2)
            }, cand_items))
    if len(items_toshow) == 0:
        return return_error_raw(
            'ERROR, COULD NOT FIND ZOOQLE TORRENTS FOR %s' % candname)
    return sorted(items_toshow,
                  key=lambda item: -item['seeders'] - item['leechers']
                  )[:maxnum], 'SUCCESS'
Example #8
0
def get_movie_torrent_eztv_io(name, maxnum=10, verify=True, tmdb_id=None):
    """
    Returns a :py:class:`tuple` of candidate movie Magnet links found using the `EZTV.IO`_ torrent service and the string ``"SUCCESS"``, if successful.

    :param str name: the movie on which to search.
    :param int maxnum: optional argument, the maximum number of magnet links to return. Default is 10. Must be :math:`\ge 5`.
    :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``.
    :param str tmdb_id: optional argument. The TMDB_ ID of the movie.
    
    :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched movie, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are,
       
      * ``title`` is the name of the candidate movie to download, and in parentheses is the size of the download in MB or GB.
      * ``rawtitle`` is *only* the name of the candidate movie to download.
      * ``seeders`` is the number of seeds for this Magnet link.
      * ``leechers`` is the number of leeches for this Magnet link.
      * ``link`` is the Magnet URI link.
      * ``torrent_size`` is the size of this torrent in MB.
    
    If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`.
    
    :rtype: tuple

    .. warning:: As of |date|, cannot get it to work when giving it valid movie searches, such as ``"Star Trek Beyond"``. See :numref:`table_working_movietorrents`.
    
    .. _`EZTV.IO`: https://eztv.io
    .. _`Star Trek Beyond`: https://en.wikipedia.org/wiki/Star_Trek_Beyond
    """
    assert (maxnum >= 5)
    if tmdb_id is None:
        tmdb_id = movie.get_movie_tmdbids(name, verify=verify)
    if tmdb_id is None:
        return return_error_raw('FAILURE, COULD NOT FIND IMDB ID FOR %s.' %
                                name)
    #
    ## check that the name matches
    movie_name = movie.get_movie_info(tmdb_id,
                                      verify=verify)['title'].lower().strip()
    if movie_name != name.lower().strip():
        return return_error_raw('FAILURE, COULD NOT FIND IMDB ID FOR %s.' %
                                name)
    imdb_id = movie.get_imdbid_from_id(tmdb_id, verify=verify)
    if imdb_id is None:
        return return_error_raw('FAILURE, COULD NOT FIND IMDB ID FOR %s.' %
                                name)
    response = requests.get('https://eztv.io/api/get-torrents',
                            params={
                                'imdb_id': int(imdb_id.replace('t', '')),
                                'limit': 100,
                                'page': 0
                            },
                            verify=verify)
    if response.status_code != 200:
        return return_error_raw(
            'ERROR, COULD NOT FIND ANY TORRENTS FOR %s IN EZTV.IO' % name)
    alldat = response.json()
    if alldat['torrents_count'] == 0:
        return return_error_raw(
            'ERROR, COULD NOT FIND ANY TORRENTS FOR %s IN EZTV.IO' % name)
    all_torrents = alldat['torrents']
    for pageno in range(1, 101):
        if alldat['torrents_count'] < 100: break
        response = requests.get('https://eztv.io/api/get-torrents',
                                params={
                                    'imdb_id': int(imdb_id.replace('t', '')),
                                    'limit': 100,
                                    'page': pageno
                                },
                                verify=verify)
        if response.status_code != 200: break
        alldat = response.json()
        if alldat['torrents_count'] == 0: break
        all_torrents += alldat['torrents']
    all_torrents_mine = all_torrents[:maxnum]
    if len(all_torrents_mine) == 0:
        return return_error_raw('ERROR, COULD NOT FIND %s IN EZTV.IO' % name)
    return list(
        map(
            lambda tor: {
                'raw_title':
                tor['title'],
                'title':
                '%s (%s)' %
                (tor['title'], get_formatted_size(tor['size_bytes'])),
                'seeders':
                int(tor['seeds']),
                'leechers':
                int(tor['peers']),
                'link':
                tor['magnet_url'],
                'torrent_size':
                float(tor['size_bytes']) / 1024**2
            }, all_torrents_mine)), 'SUCCESS'
Example #9
0
def get_movie_torrent_jackett(name,
                              maxnum=10,
                              verify=True,
                              doRaw=False,
                              tmdb_id=None):
    """
    Returns a :py:class:`tuple` of candidate movie Magnet links found using the main Jackett_ torrent searching service and the string ``"SUCCESS"``, if successful.

    :param str name: the movie string on which to search.
    :param int maxnum: optional argumeent, the maximum number of magnet links to return. Default is 10. Must be :math:`\ge 5`.
    :param bool verify: optional argument, whether to verify SSL connections. Default is ``True``.
    :param bool doRaw: optional argument. If ``True``, uses the IMDb_ information to search for the movie. Otherwise, uses the full string in ``name`` to search for the movie.
    :param int tmdb_id: optional argument. If defined, use this TMDB_ movie ID to search for magnet links.
    
    :returns: if successful, then returns a two member :py:class:`tuple` the first member is a :py:class:`list` of elements that match the searched movie, ordered from *most* seeds and leechers to least. The second element is the string ``"SUCCESS"``. The keys in each element of the list are,
       
      * ``title`` is the name of the candidate movie to download, and in parentheses the size of the candidate in MB or GB.
      * ``rawtitle`` is *only* the name of the candidate movie to download.
      * ``seeders`` is the number of seeds for this Magnet link.
      * ``leechers`` is the number of leeches for this Magnet link.
      * ``link`` is the Magnet URI link.
      * ``torrent_size`` is the size of this torrent in GB.
    
    If this is unsuccessful, then returns an error :py:class:`tuple` of the form returned by :py:meth:`return_error_raw <howdy.core.return_error_raw>`.
    
    :rtype: tuple
    
    .. _Jackett: https://github.com/Jackett/Jackett
    """
    time0 = time.time()
    data = core.get_jackett_credentials()
    if data is None:
        return return_error_raw(
            'failure, could not get jackett server credentials')
    url, apikey = data
    endpoint = 'api/v2.0/indexers/all/results/torznab/api'
    popName = False
    if tmdb_id is not None: popName = True

    def _return_params(name, popName, tmdb_id):
        params = {'apikey': apikey, 'cat': 2000}
        if tmdb_id is not None:
            imdb_id = movie.get_imdbid_from_id(tmdb_id, verify=verify)
            params['imdbid'] = imdb_id
            return params
        elif doRaw:
            params['q'] = name
            return params

        tmdb_id = movie.get_movie_tmdbids(name, verify=verify)
        #if tmdb_id is None or doRaw and not popName:
        #    params['q'] = name
        #    return params
        #
        ## check that the name matches
        movie_name = movie.get_movie_info(
            tmdb_id, verify=verify)['title'].lower().strip()
        if movie_name != name.lower().strip():
            params['q'] = name
            return params
        imdb_id = movie.get_imdbid_from_id(tmdb_id, verify=verify)
        if imdb_id is None:
            params['q'] = name
            return params
        params['imdbid'] = imdb_id
        return params

    params = _return_params(name, popName, tmdb_id)
    if popName and 'q' in params: params.pop('q')
    logging.info('params: %s, mainURL = %s' % (params, urljoin(url, endpoint)))
    response = requests.get(urljoin(url, endpoint),
                            verify=verify,
                            params=params)
    if response.status_code != 200:
        return return_error_raw(' '.join([
            'failure, problem with jackett server accessible at %s.' % url,
            'Error code = %d. Error data = %s.' %
            (response.status_code, response.content)
        ]))
    logging.info('processed jackett torrents for %s in %0.3f seconds.' %
                 (name, time.time() - time0))
    html = BeautifulSoup(response.content, 'lxml')
    if len(html.find_all('item')) == 0:
        return return_error_raw(
            'failure, could not find movie %s with jackett.' % name)
    items = []

    def _get_magnet_url(item):
        magnet_url = item.find('torznab:attr', {'name': 'magneturl'})
        if magnet_url is not None and 'magnet' in magnet_url['value']:
            return magnet_url['value']
        #
        ## not found it here, must go into URL
        url2 = item.find('guid')
        if url2 is None: return None
        url2 = url2.text
        if not validators.url(url2): return None
        resp2 = requests.get(url2, verify=verify)
        if resp2.status_code != 200: return None
        h2 = BeautifulSoup(resp2.content, 'lxml')
        valid_magnet_links = set(
            map(
                lambda elem: elem['href'],
                filter(
                    lambda elem: 'href' in elem.attrs and 'magnet' in elem[
                        'href'], h2.find_all('a'))))
        if len(valid_magnet_links) == 0: return None
        return max(valid_magnet_links)

    for item in html.find_all('item'):
        title = item.find('title')
        if title is None: continue
        title = title.text
        torrent_size = item.find('size')
        if torrent_size is not None:
            torrent_size = float(torrent_size.text) / 1024**2
        seeders = item.find('torznab:attr', {'name': 'seeders'})
        if seeders is None: seeders = -1
        else: seeders = int(seeders['value'])
        leechers = item.find('torznab:attr', {'name': 'peers'})
        if leechers is None: leechers = -1
        else: leechers = int(leechers['value'])
        #
        ## now do one of two things to get the magnet URL
        magnet_url = _get_magnet_url(item)
        if magnet_url is None: continue
        myitem = {
            'raw_title': title,
            'title': title,
            'seeders': seeders,
            'leechers': leechers,
            'link': magnet_url
        }
        if torrent_size is not None:
            myitem['title'] = '%s (%s)' % (
                title, get_formatted_size(torrent_size * 1024**2))
            myitem['torrent_size'] = torrent_size
        items.append(myitem)
    if len(items) == 0:
        return return_error_raw('FAILURE, JACKETT CANNOT FIND %s' % name)
    return items[:maxnum], 'SUCCESS'