Browse Source

rebase to pathlib

master
imDMG 3 years ago
parent
commit
657edba195
  1. 58
      kinozal.py
  2. 4
      modules/helpers.py
  3. 61
      nnmclub.py
  4. 55
      rutor.py
  5. 64
      rutracker.py

58
kinozal.py

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
# VERSION: 2.4
# VERSION: 2.5
# AUTHORS: imDMG [imdmgg@gmail.com]
# Kinozal.tv search engine plugin for qBittorrent
@ -7,16 +7,15 @@ import base64 @@ -7,16 +7,15 @@ import base64
import gzip
import json
import logging
import os
import re
import socket
import tempfile
import time
from concurrent.futures.thread import ThreadPoolExecutor
from functools import partial
from html import unescape
from http.cookiejar import MozillaCookieJar
from pathlib import Path
from tempfile import NamedTemporaryFile
from urllib.error import URLError, HTTPError
from urllib.parse import urlencode, unquote
from urllib.request import build_opener, HTTPCookieProcessor, ProxyHandler
@ -25,7 +24,6 @@ from novaprinter import prettyPrinter @@ -25,7 +24,6 @@ from novaprinter import prettyPrinter
# default config
config = {
"version": 3,
"torrentDate": True,
"username": "USERNAME",
"password": "PASSWORD",
@ -38,24 +36,26 @@ config = { @@ -38,24 +36,26 @@ config = {
"ua": "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 "
}
FILE = Path(__file__)
BASEDIR = FILE.parent.absolute()
FILENAME = FILE.name[:-3]
FILE_J, FILE_C = [BASEDIR / (FILENAME + fl) for fl in ['.json', '.cookie']]
def path_to(*file):
return os.path.abspath(os.path.join(os.path.dirname(__file__), *file))
PAGES = 50
def rng(t):
return range(1, -(-t // 50))
return range(1, -(-t // PAGES))
RE_TORRENTS = re.compile(
r'nam"><a\s+?href="/(.+?)"\s+?class="r\d">(.+?)</a>.+?s\'>.+?s\'>(.+?)<.+?'
r'sl_s\'>(\d+?)<.+?sl_p\'>(\d+?)<.+?s\'>(.+?)</td>', re.S)
r'sl_s\'>(\d+?)<.+?sl_p\'>(\d+?)<.+?s\'>(.+?)</td>', re.S
)
RE_RESULTS = re.compile(r'</span>Найдено\s+?(\d+?)\s+?раздач', re.S)
PATTERNS = ('%sbrowse.php?s=%s&c=%s', "%s&page=%s")
FILENAME = os.path.basename(__file__)[:-3]
FILE_J, FILE_C = [path_to(FILENAME + fe) for fe in ['.json', '.cookie']]
# base64 encoded image
ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAA"
"AAAAAAAAAAAACARztMgEc7/4BHO/+ARztMAAAAAIBHO0yhd2n/gEc7/6F3af+ARztMAAAA"
@ -84,33 +84,24 @@ ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAA" @@ -84,33 +84,24 @@ ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAA"
logging.basicConfig(
format="%(asctime)s %(name)-12s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M",
level=logging.DEBUG)
level=logging.DEBUG
)
logger = logging.getLogger(__name__)
try:
# try to load user data from file
with open(FILE_J, 'r+') as f:
cfg = json.load(f)
if "version" not in cfg.keys():
cfg.update({"version": 2, "torrentDate": True})
f.seek(0)
f.write(json.dumps(cfg, indent=4, sort_keys=False))
f.truncate()
config = cfg
config = json.loads(FILE_J.read_text())
logger.debug("Config is loaded.")
except OSError as e:
logger.error(e)
# if file doesn't exist, we'll create it
with open(FILE_J, 'w') as f:
f.write(json.dumps(config, indent=4, sort_keys=False))
FILE_J.write_text(json.dumps(config, indent=4, sort_keys=False))
# also write/rewrite ico file
with open(path_to(FILENAME + '.ico'), 'wb') as f:
f.write(base64.b64decode(ICON))
(BASEDIR / (FILENAME + '.ico')).write_bytes(base64.b64decode(ICON))
logger.debug("Write files.")
class kinozal:
class Kinozal:
name = 'Kinozal'
url = 'http://kinozal.tv/'
url_dl = url.replace("//", "//dl.")
@ -170,7 +161,7 @@ class kinozal: @@ -170,7 +161,7 @@ class kinozal:
self.pretty_error(what)
return None
# do async requests
if total > 50:
if total > PAGES:
qrs = [PATTERNS[1] % (query, x) for x in rng(total)]
with ThreadPoolExecutor(len(qrs)) as executor:
executor.map(self.searching, qrs, timeout=30)
@ -195,10 +186,9 @@ class kinozal: @@ -195,10 +186,9 @@ class kinozal:
path = 'magnet:?xt=urn:btih:' + response.decode()[18:58]
else:
# Create a torrent file
file, path = tempfile.mkstemp('.torrent')
with os.fdopen(file, "wb") as fd:
# Write it to a file
with NamedTemporaryFile(suffix='.torrent', delete=False) as fd:
fd.write(response)
path = fd.name
# return magnet link / file path
logger.debug(path + " " + url)
@ -214,7 +204,8 @@ class kinozal: @@ -214,7 +204,8 @@ class kinozal:
logger.debug(f"Login. Data before: {form_data}")
# so we first encode vals to cp1251 then do default decode whole string
data_encoded = urlencode(
{k: v.encode('cp1251') for k, v in form_data.items()}).encode()
{k: v.encode('cp1251') for k, v in form_data.items()}
).encode()
logger.debug(f"Login. Data after: {data_encoded}")
self._catch_error_request(self.url_login, data_encoded)
@ -311,6 +302,9 @@ class kinozal: @@ -311,6 +302,9 @@ class kinozal:
self.error = None
# pep8
kinozal = Kinozal
if __name__ == "__main__":
engine = kinozal()
engine.search('doctor')

4
modules/helpers.py

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
#VERSION: 1.42
#VERSION: 1.43
# Author:
# Christophe DUMEZ (chris@qbittorrent.org)
@ -40,7 +40,7 @@ import urllib.parse @@ -40,7 +40,7 @@ import urllib.parse
import urllib.request
# Some sites blocks default python User-agent
user_agent = 'Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0'
user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'
headers = {'User-Agent': user_agent}
# SOCKS5 Proxy support
if "sock_proxy" in os.environ and len(os.environ["sock_proxy"].strip()) > 0:

61
nnmclub.py

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
# VERSION: 2.6
# VERSION: 2.7
# AUTHORS: imDMG [imdmgg@gmail.com]
# NoNaMe-Club search engine plugin for qBittorrent
@ -6,14 +6,14 @@ @@ -6,14 +6,14 @@
import base64
import json
import logging
import os
import re
import socket
import tempfile
import time
from concurrent.futures import ThreadPoolExecutor
from html import unescape
from http.cookiejar import Cookie, MozillaCookieJar
from pathlib import Path
from tempfile import NamedTemporaryFile
from urllib.error import URLError, HTTPError
from urllib.parse import urlencode, unquote
from urllib.request import build_opener, HTTPCookieProcessor, ProxyHandler
@ -35,25 +35,27 @@ config = { @@ -35,25 +35,27 @@ config = {
"ua": "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 "
}
FILE = Path(__file__)
BASEDIR = FILE.parent.absolute()
def path_to(*file):
return os.path.abspath(os.path.join(os.path.dirname(__file__), *file))
FILENAME = FILE.name[:-3]
FILE_J, FILE_C = [BASEDIR / (FILENAME + fl) for fl in ['.json', '.cookie']]
PAGES = 50
def rng(t):
return range(50, -(-t // 50) * 50, 50)
return range(PAGES, -(-t // PAGES) * PAGES, PAGES)
RE_TORRENTS = re.compile(
r'topictitle"\shref="(.+?)"><b>(.+?)</b>.+?href="(d.+?)".+?<u>(\d+?)</u>.+?'
r'<b>(\d+)</b>.+?<b>(\d+)</b>.+?<u>(\d+)</u>', re.S)
r'<b>(\d+)</b>.+?<b>(\d+)</b>.+?<u>(\d+)</u>', re.S
)
RE_RESULTS = re.compile(r'TP_VER">(?:Результатов\sпоиска:\s(\d{1,3}))?\s', re.S)
RE_CODE = re.compile(r'name="code"\svalue="(.+?)"', re.S)
PATTERNS = ('%stracker.php?nm=%s&%s', "%s&start=%s")
FILENAME = os.path.basename(__file__)[:-3]
FILE_J, FILE_C = [path_to(FILENAME + fe) for fe in ['.json', '.cookie']]
# base64 encoded image
ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaQicAXRQFADICAQAHAAAA"
@ -82,33 +84,24 @@ ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAA" @@ -82,33 +84,24 @@ ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAA"
logging.basicConfig(
format="%(asctime)s %(name)-12s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M",
level=logging.DEBUG)
level=logging.DEBUG
)
logger = logging.getLogger(__name__)
try:
# try to load user data from file
with open(FILE_J, 'r+') as f:
cfg = json.load(f)
if "version" not in cfg.keys():
cfg.update({"version": 2, "torrentDate": True})
f.seek(0)
f.write(json.dumps(cfg, indent=4, sort_keys=False))
f.truncate()
config = cfg
config = json.loads(FILE_J.read_text())
logger.debug("Config is loaded.")
except OSError as e:
logger.error(e)
# if file doesn't exist, we'll create it
with open(FILE_J, 'w') as f:
f.write(json.dumps(config, indent=4, sort_keys=False))
FILE_J.write_text(json.dumps(config, indent=4, sort_keys=False))
# also write/rewrite ico file
with open(path_to(FILENAME + '.ico'), 'wb') as f:
f.write(base64.b64decode(ICON))
(BASEDIR / (FILENAME + '.ico')).write_bytes(base64.b64decode(ICON))
logger.debug("Write files.")
class nnmclub:
class NNMClub:
name = 'NoNaMe-Club'
url = 'https://nnmclub.to/forum/'
url_dl = 'https://nnm-club.ws/'
@ -169,7 +162,7 @@ class nnmclub: @@ -169,7 +162,7 @@ class nnmclub:
self.pretty_error(what)
return None
# do async requests
if total > 50:
if total > PAGES:
qrs = [PATTERNS[1] % (query, x) for x in rng(total)]
with ThreadPoolExecutor(len(qrs)) as executor:
executor.map(self.searching, qrs, timeout=30)
@ -185,14 +178,12 @@ class nnmclub: @@ -185,14 +178,12 @@ class nnmclub:
return None
# Create a torrent file
file, path = tempfile.mkstemp('.torrent')
with os.fdopen(file, "wb") as fd:
# Write it to a file
with NamedTemporaryFile(suffix='.torrent', delete=False) as fd:
fd.write(response)
# return file path
logger.debug(path + " " + url)
print(path + " " + url)
# return file path
logger.debug(fd.name + " " + url)
print(fd.name + " " + url)
def login(self, mcj):
if self.error:
@ -214,7 +205,8 @@ class nnmclub: @@ -214,7 +205,8 @@ class nnmclub:
"login": "Вход"}
# so we first encode vals to cp1251 then do default decode whole string
data_encoded = urlencode(
{k: v.encode('cp1251') for k, v in form_data.items()}).encode()
{k: v.encode('cp1251') for k, v in form_data.items()}
).encode()
self._catch_error_request(self.url_login, data_encoded)
if self.error:
@ -303,6 +295,9 @@ class nnmclub: @@ -303,6 +295,9 @@ class nnmclub:
self.error = None
# pep8
nnmclub = NNMClub
if __name__ == "__main__":
engine = nnmclub()
engine.search('doctor')

55
rutor.py

@ -1,20 +1,18 @@ @@ -1,20 +1,18 @@
# VERSION: 1.1
# VERSION: 1.2
# AUTHORS: imDMG [imdmgg@gmail.com]
# Rutor.org search engine plugin for qBittorrent
import base64
import json
import logging
import os
import re
import socket
import tempfile
import time
from concurrent.futures.thread import ThreadPoolExecutor
from html import unescape
from pathlib import Path
from tempfile import NamedTemporaryFile
from urllib.error import URLError, HTTPError
from urllib.parse import unquote
from urllib.request import build_opener, ProxyHandler
@ -23,7 +21,6 @@ from novaprinter import prettyPrinter @@ -23,7 +21,6 @@ from novaprinter import prettyPrinter
# default config
config = {
"version": 2,
"torrentDate": True,
"username": "USERNAME",
"password": "PASSWORD",
@ -35,24 +32,26 @@ config = { @@ -35,24 +32,26 @@ config = {
"ua": "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 "
}
FILE = Path(__file__)
BASEDIR = FILE.parent.absolute()
FILENAME = FILE.name[:-3]
FILE_J, FILE_C = [BASEDIR / (FILENAME + fl) for fl in ['.json', '.cookie']]
def path_to(*file):
return os.path.abspath(os.path.join(os.path.dirname(__file__), *file))
PAGES = 100
def rng(t):
return range(1, -(-t // 100))
return range(1, -(-t // PAGES))
RE_TORRENTS = re.compile(
r'(?:gai|tum)"><td>(.+?)</td.+?href="/(torrent/(\d+).+?)">(.+?)</a.+?right"'
r'>([.\d]+&nbsp;\w+)</td.+?alt="S"\s/>(.+?)</s.+?red">(.+?)</s', re.S)
r'>([.\d]+&nbsp;\w+)</td.+?alt="S"\s/>(.+?)</s.+?red">(.+?)</s', re.S
)
RE_RESULTS = re.compile(r'</b>\sРезультатов\sпоиска\s(\d{1,4})\s', re.S)
PATTERNS = ('%ssearch/%i/%i/000/0/%s',)
FILENAME = os.path.basename(__file__)[:-3]
FILE_J, FILE_C = [path_to(FILENAME + fe) for fe in ['.json', '.cookie']]
# base64 encoded image
ICON = ("AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAc4AAMwHNdcQ4vsN3fYS2fUY3fUe3fMj4fkk4fco4PYo5fgk7f5gp8Zu"
@ -77,27 +76,24 @@ ICON = ("AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAA" @@ -77,27 +76,24 @@ ICON = ("AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAA"
logging.basicConfig(
format="%(asctime)s %(name)-12s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M",
level=logging.DEBUG)
level=logging.DEBUG
)
logger = logging.getLogger(__name__)
try:
# try to load user data from file
with open(FILE_J, 'r+') as f:
config = json.load(f)
config = json.loads(FILE_J.read_text())
logger.debug("Config is loaded.")
except OSError as e:
logger.error(e)
# if file doesn't exist, we'll create it
with open(FILE_J, 'w') as f:
f.write(json.dumps(config, indent=4, sort_keys=False))
FILE_J.write_text(json.dumps(config, indent=4, sort_keys=False))
# also write/rewrite ico file
with open(path_to(FILENAME + '.ico'), 'wb') as f:
f.write(base64.b64decode(ICON))
(BASEDIR / (FILENAME + '.ico')).write_bytes(base64.b64decode(ICON))
logger.debug("Write files.")
class rutor:
class Rutor:
name = 'Rutor'
url = 'http://rutor.info/'
url_dl = url.replace("//", "//d.") + "download/"
@ -142,7 +138,7 @@ class rutor: @@ -142,7 +138,7 @@ class rutor:
self.pretty_error(what)
return None
# do async requests
if total > 100:
if total > PAGES:
query = query.replace('h/0', 'h/%i')
qrs = [query % x for x in rng(total)]
with ThreadPoolExecutor(len(qrs)) as executor:
@ -159,14 +155,12 @@ class rutor: @@ -159,14 +155,12 @@ class rutor:
return None
# Create a torrent file
file, path = tempfile.mkstemp('.torrent')
with os.fdopen(file, "wb") as fd:
# Write it to a file
with NamedTemporaryFile(suffix='.torrent', delete=False) as fd:
fd.write(response)
# return file path
logger.debug(path + " " + url)
print(path + " " + url)
# return file path
logger.debug(fd.name + " " + url)
print(fd.name + " " + url)
def searching(self, query, first=False):
response = self._catch_error_request(query)
@ -242,6 +236,9 @@ class rutor: @@ -242,6 +236,9 @@ class rutor:
self.error = None
# pep8
rutor = Rutor
if __name__ == "__main__":
engine = rutor()
engine.search('doctor')

64
rutracker.py

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
# VERSION: 1.3
# VERSION: 1.4
# AUTHORS: imDMG [imdmgg@gmail.com]
# rutracker.org search engine plugin for qBittorrent
@ -6,15 +6,14 @@ @@ -6,15 +6,14 @@
import base64
import json
import logging
import os
import re
import socket
import tempfile
import time
from concurrent.futures import ThreadPoolExecutor
from html import unescape
from http.cookiejar import Cookie, MozillaCookieJar
from pathlib import Path
from tempfile import NamedTemporaryFile
from urllib.error import URLError, HTTPError
from urllib.parse import urlencode, unquote
from urllib.request import build_opener, HTTPCookieProcessor, ProxyHandler
@ -23,7 +22,6 @@ from novaprinter import prettyPrinter @@ -23,7 +22,6 @@ from novaprinter import prettyPrinter
# default config
config = {
"version": 2,
"torrentDate": True,
"username": "USERNAME",
"password": "PASSWORD",
@ -35,25 +33,27 @@ config = { @@ -35,25 +33,27 @@ config = {
"ua": "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 "
}
FILE = Path(__file__)
BASEDIR = FILE.parent.absolute()
FILENAME = FILE.name[:-3]
FILE_J, FILE_C = [BASEDIR / (FILENAME + fl) for fl in ['.json', '.cookie']]
def path_to(*file):
return os.path.abspath(os.path.join(os.path.dirname(__file__), *file))
PAGES = 50
def rng(t):
return range(50, -(-t // 50) * 50, 50)
return range(PAGES, -(-t // PAGES) * PAGES, PAGES)
RE_TORRENTS = re.compile(
r'data-topic_id="(\d+?)".+?">(.+?)</a.+?tor-size"\sdata-ts_text="(\d+?)">'
r'.+?data-ts_text="([-0-9]+?)">.+?Личи">(\d+?)</.+?data-ts_text="(\d+?)">',
re.S)
re.S
)
RE_RESULTS = re.compile(r'Результатов\sпоиска:\s(\d{1,3})\s<span', re.S)
PATTERNS = ('%s/tracker.php?nm=%s&c=%s', "%s&start=%s")
FILENAME = os.path.basename(__file__)[:-3]
FILE_J, FILE_C = [path_to(FILENAME + fl) for fl in ['.json', '.cookie']]
# base64 encoded image
ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCw"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAABs3wUAY8wFBGPMBQN2sw8A9kA6AOdOOl/nTjo/5046"
@ -82,40 +82,30 @@ ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCw" @@ -82,40 +82,30 @@ ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCw"
logging.basicConfig(
format="%(asctime)s %(name)-12s %(levelname)-8s %(message)s",
datefmt="%m-%d %H:%M",
level=logging.DEBUG)
level=logging.DEBUG
)
logger = logging.getLogger(__name__)
try:
# try to load user data from file
with open(FILE_J, 'r+') as f:
config = json.load(f)
config = json.loads(FILE_J.read_text())
logger.debug("Config is loaded.")
except OSError as e:
logger.error(e)
# if file doesn't exist, we'll create it
with open(FILE_J, 'w') as f:
f.write(json.dumps(config, indent=4, sort_keys=False))
FILE_J.write_text(json.dumps(config, indent=4, sort_keys=False))
# also write/rewrite ico file
with open(path_to(FILENAME + '.ico'), 'wb') as f:
f.write(base64.b64decode(ICON))
(BASEDIR / (FILENAME + '.ico')).write_bytes(base64.b64decode(ICON))
logger.debug("Write files.")
class rutracker:
class Rutracker:
name = 'Rutracker'
url = 'https://rutracker.org/forum/'
url_dl = url + 'dl.php?t='
url_login = url + 'login.php'
supported_categories = {'all': '-1'}
# 'movies': '2',
# 'tv': '3',
# 'music': '4',
# 'games': '5',
# 'anime': '6',
# 'software': '7'}
def __init__(self):
# error message
self.error = None
@ -163,7 +153,7 @@ class rutracker: @@ -163,7 +153,7 @@ class rutracker:
self.pretty_error(what)
return None
# do async requests
if total > 50:
if total > PAGES:
qrs = [PATTERNS[1] % (query, x) for x in rng(total)]
with ThreadPoolExecutor(len(qrs)) as executor:
executor.map(self.searching, qrs, timeout=30)
@ -179,14 +169,12 @@ class rutracker: @@ -179,14 +169,12 @@ class rutracker:
return None
# Create a torrent file
file, path = tempfile.mkstemp('.torrent')
with os.fdopen(file, "wb") as fd:
# Write it to a file
with NamedTemporaryFile(suffix='.torrent', delete=False) as fd:
fd.write(response)
# return file path
logger.debug(path + " " + url)
print(path + " " + url)
# return file path
logger.debug(fd.name + " " + url)
print(fd.name + " " + url)
def login(self, mcj):
if self.error:
@ -203,7 +191,8 @@ class rutracker: @@ -203,7 +191,8 @@ class rutracker:
logger.debug(f"Login. Data before: {form_data}")
# so we first encode vals to cp1251 then do default decode whole string
data_encoded = urlencode(
{k: v.encode('cp1251') for k, v in form_data.items()}).encode()
{k: v.encode('cp1251') for k, v in form_data.items()}
).encode()
logger.debug(f"Login. Data after: {data_encoded}")
self._catch_error_request(self.url_login, data_encoded)
if self.error:
@ -293,6 +282,9 @@ class rutracker: @@ -293,6 +282,9 @@ class rutracker:
self.error = None
# pep8
rutracker = Rutracker
if __name__ == "__main__":
engine = rutracker()
engine.search('doctor')

Loading…
Cancel
Save