123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- # SPDX-License-Identifier: AGPL-3.0-or-later
- # lint: pylint
- """Torznab WebAPI
- A engine that implements the `torznab WebAPI`_.
- .. _torznab WebAPI: https://torznab.github.io/spec-1.3-draft/torznab
- """
- from datetime import datetime
- from urllib.parse import quote
- from lxml import etree
- from searx.exceptions import SearxEngineAPIException
- # about
- about = {
- "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 = False
- time_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-categories
- torznab_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 params
- def 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 results
- def get_property(item, property_name):
- property_element = item.find(property_name)
- if property_element is not None:
- return property_element.text
- return None
- def 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
|