mirror of
https://github.com/searxng/searxng.git
synced 2024-11-22 20:17:45 +01:00
[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
|
require_api_key: false
|
||||||
results: HTML
|
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:
|
# Doku engine lets you access to any Doku wiki instance:
|
||||||
# A public one or a privete/corporate one.
|
# A public one or a privete/corporate one.
|
||||||
# - name: ubuntuwiki
|
# - name: ubuntuwiki
|
||||||
|
Loading…
Reference in New Issue
Block a user