forked from zaclys/searxng
[mod] engines - add torznab WebAPI
This commit is contained in:
parent
7246d62770
commit
d803df8d89
144
searx/engines/torznab.py
Normal file
144
searx/engines/torznab.py
Normal file
@ -0,0 +1,144 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Torznab WebAPI
|
||||
|
||||
A engine that implements the `torznab WebAPI`_.
|
||||
|
||||
.. _torznab WebAPI: https://torznab.github.io/spec-1.3-draft/torznab
|
||||
|
||||
"""
|
||||
|
||||
from urllib.parse import quote
|
||||
from lxml import etree
|
||||
from datetime import datetime
|
||||
|
||||
from searx.exceptions import SearxEngineAPIException
|
||||
|
||||
# about
|
||||
about = {
|
||||
"website": None,
|
||||
"wikidata_id": None,
|
||||
"official_api_documentation": "https://torznab.github.io/spec-1.3-draft/torznab/Specification-v1.3.html#torznab-api-specification",
|
||||
"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 request(query, params):
|
||||
if len(base_url) < 1:
|
||||
raise SearxEngineAPIException('missing torznab base_url')
|
||||
|
||||
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(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:
|
||||
pass
|
||||
|
||||
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
|
@ -1654,6 +1654,19 @@ engines:
|
||||
require_api_key: false
|
||||
results: HTML
|
||||
|
||||
# torznab engine lets you query any torznab compatible indexer.
|
||||
# Using this engine in combination with Jackett (https://github.com/Jackett/Jackett)
|
||||
# opens the possibility to query a lot of public and private indexers directly from searXNG.
|
||||
# - name: torznab
|
||||
# engine: torznab
|
||||
# shortcut: trz
|
||||
# base_url: http://localhost:9117/api/v2.0/indexers/all/results/torznab
|
||||
# enable_http: true # if using localhost
|
||||
# api_key: xxxxxxxxxxxxxxx
|
||||
# torznab_categories: # optional
|
||||
# - 2000
|
||||
# - 5000
|
||||
|
||||
# Doku engine lets you access to any Doku wiki instance:
|
||||
# A public one or a privete/corporate one.
|
||||
# - name: ubuntuwiki
|
||||
|
Loading…
Reference in New Issue
Block a user