| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 | # SPDX-License-Identifier: AGPL-3.0-or-later# lint: pylint"""Torznab WebAPIA engine that implements the `torznab WebAPI`_... _torznab WebAPI: https://torznab.github.io/spec-1.3-draft/torznab"""from datetime import datetimefrom urllib.parse import quotefrom lxml import etreefrom searx.exceptions import SearxEngineAPIException# aboutabout = {    "website": None,    "wikidata_id": None,    "official_api_documentation": "https://torznab.github.io/spec-1.3-draft",    "use_official_api": True,    "require_api_key": False,    "results": 'XML',}categories = ['files']paging = Falsetime_range_support = False# defined in settings.yml# example (Jackett): "http://localhost:9117/api/v2.0/indexers/all/results/torznab"base_url = ''api_key = ''# https://newznab.readthedocs.io/en/latest/misc/api/#predefined-categoriestorznab_categories = []def init(engine_settings=None): # pylint: disable=unused-argument    if len(base_url) < 1:        raise ValueError('missing torznab base_url')def request(query, params):    search_url = base_url + '?t=search&q={search_query}'    if len(api_key) > 0:        search_url += '&apikey={api_key}'    if len(torznab_categories) > 0:        search_url += '&cat={torznab_categories}'    params['url'] = search_url.format(        search_query = quote(query),        api_key = api_key,        torznab_categories = ",".join([str(x) for x in torznab_categories])    )    return paramsdef response(resp):    results = []    search_results = etree.XML(resp.content)    # handle errors    # https://newznab.readthedocs.io/en/latest/misc/api/#newznab-error-codes    if search_results.tag == "error":        raise SearxEngineAPIException(search_results.get("description"))    for item in search_results[0].iterfind('item'):        result = {'template': 'torrent.html'}        enclosure = item.find('enclosure')        result["filesize"] = int(enclosure.get('length'))        link = get_property(item, 'link')        guid = get_property(item, 'guid')        comments = get_property(item, 'comments')        # define url        result["url"] = enclosure.get('url')        if comments is not None and comments.startswith('http'):            result["url"] = comments        elif guid is not None and guid.startswith('http'):            result["url"] = guid        # define torrent file url        result["torrentfile"] = None        if enclosure.get('url').startswith("http"):            result["torrentfile"] = enclosure.get('url')        elif link is not None and link.startswith('http'):            result["torrentfile"] = link        # define magnet link        result["magnetlink"] = get_torznab_attr(item, 'magneturl')        if result["magnetlink"] is None:            if enclosure.get('url').startswith("magnet"):                result["magnetlink"] = enclosure.get('url')            elif link is not None and link.startswith('magnet'):                result["magnetlink"] = link        result["title"] = get_property(item, 'title')        result["files"] = get_property(item, 'files')        result["publishedDate"] = None        try:            result["publishedDate"] = datetime.strptime(                get_property(item, 'pubDate'), '%a, %d %b %Y %H:%M:%S %z')        except (ValueError, TypeError) as e:            logger.debug("ignore exception (publishedDate): %s", e)        result["seed"] = get_torznab_attr(item, 'seeders')        # define leech        result["leech"] = get_torznab_attr(item, 'leechers')        if result["leech"] is None and result["seed"] is not None:            peers = get_torznab_attr(item, 'peers')            if peers is not None:                result["leech"] = int(peers) - int(result["seed"])        results.append(result)    return resultsdef get_property(item, property_name):    property_element = item.find(property_name)    if property_element is not None:        return property_element.text    return Nonedef get_torznab_attr(item, attr_name):    element = item.find(        './/torznab:attr[@name="{attr_name}"]'.format(attr_name=attr_name),        {            'torznab': 'http://torznab.com/schemas/2015/feed'        }    )    if element is not None:        return element.get("value")    return None
 |