From cf46fb349e4caf64767c7cb13c3026e7cbc55a39 Mon Sep 17 00:00:00 2001 From: imDMG Date: Thu, 17 Mar 2022 00:16:56 +0500 Subject: [PATCH] kinozal v2.7, repo update --- .gitignore | 29 ++- README.md | 2 +- kinozal.png => engines/kinozal.png | Bin kinozal.py => engines/kinozal.py | 257 +++++++++++++++------------ nnmclub.png => engines/nnmclub.png | Bin nnmclub.py => engines/nnmclub.py | 0 rutor.png => engines/rutor.png | Bin rutor.py => engines/rutor.py | 8 +- engines/rutracker.png | Bin 0 -> 788 bytes rutracker.py => engines/rutracker.py | 0 proxychecker.py | 45 ++--- settings_gui.py | 83 ++++----- 12 files changed, 224 insertions(+), 200 deletions(-) rename kinozal.png => engines/kinozal.png (100%) rename kinozal.py => engines/kinozal.py (59%) rename nnmclub.png => engines/nnmclub.png (100%) rename nnmclub.py => engines/nnmclub.py (100%) rename rutor.png => engines/rutor.png (100%) rename rutor.py => engines/rutor.py (98%) create mode 100644 engines/rutracker.png rename rutracker.py => engines/rutracker.py (100%) diff --git a/.gitignore b/.gitignore index f9270ef..51dd1aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,12 @@ +/engines/kinozal.cookie +/engines/kinozal.json +/engines/nnmclub.cookie +/engines/nnmclub.ico +/engines/nnmclub.json +/engines/rutor.ico +/engines/rutor.json +/engines/rutracker.cookie +/engines/rutracker.json +/modules /tests -/kinozal.cookie -/kinozal.cookie.bak -/kinozal.ico -/kinozal.json -/kinozal.json.bak -/nnmclub.cookie -/nnmclub.cookie.bak -/nnmclub.ico -/nnmclub.json -/nnmclub.json.bak -/proxylist.txt -/rutor.ico -/rutor.json -/rutracker.cookie -/rutracker.cookie.bak -/rutracker.ico -/rutracker.json -/rutracker.json.bak +/proxylist.txt \ No newline at end of file diff --git a/README.md b/README.md index 2319ec9..249fcde 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ According of the name of search plugin: * Then follow [official tutorial](https://github.com/qbittorrent/search-plugins/wiki/Install-search-plugins). * _After installation you can change your settings with `*.json` file which will be created automatically in:_ * Windows: `%localappdata%\qBittorrent\nova3\engines` - * Linux: `~/.local/share/data/qBittorrent/nova3/engines` + * Linux: `~/.local/share/qBittorrent/nova3/engines` * OS X: `~/Library/Application Support/qBittorrent/nova3/engines` **For update just reinstall `*.py` file.** diff --git a/kinozal.png b/engines/kinozal.png similarity index 100% rename from kinozal.png rename to engines/kinozal.png diff --git a/kinozal.py b/engines/kinozal.py similarity index 59% rename from kinozal.py rename to engines/kinozal.py index 2d19c0b..580fbbb 100644 --- a/kinozal.py +++ b/engines/kinozal.py @@ -1,4 +1,4 @@ -# VERSION: 2.6 +# VERSION: 2.7 # AUTHORS: imDMG [imdmgg@gmail.com] # Kinozal.tv search engine plugin for qBittorrent @@ -8,15 +8,16 @@ import gzip import json import logging import re -import socket import sys import time from concurrent.futures.thread import ThreadPoolExecutor +from dataclasses import dataclass, field from functools import partial from html import unescape from http.cookiejar import MozillaCookieJar from pathlib import Path from tempfile import NamedTemporaryFile +from typing import Union, Optional from urllib.error import URLError, HTTPError from urllib.parse import urlencode, unquote from urllib.request import build_opener, HTTPCookieProcessor, ProxyHandler @@ -27,39 +28,29 @@ except ImportError: sys.path.insert(0, str(Path(__file__).parent.parent.absolute())) from novaprinter import prettyPrinter -# default config -config = { - "torrentDate": True, - "username": "USERNAME", - "password": "PASSWORD", - "proxy": False, - "proxies": { - "http": "", - "https": "" - }, - "magnet": True, - "ua": "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 " -} +# setup logging +logging.basicConfig( + format="%(asctime)s %(name)-12s %(levelname)-8s %(message)s", + datefmt="%m-%d %H:%M", + level=logging.DEBUG +) + +logger = logging.getLogger(__name__) FILE = Path(__file__) BASEDIR = FILE.parent.absolute() FILENAME = FILE.name[:-3] -FILE_J, FILE_C = [BASEDIR / (FILENAME + fl) for fl in ['.json', '.cookie']] +FILE_J, FILE_C = [BASEDIR / (FILENAME + fl) for fl in (".json", ".cookie")] PAGES = 50 - -def rng(t): - return range(1, -(-t // PAGES)) - - RE_TORRENTS = re.compile( r'nam">(.+?).+?s\'>.+?s\'>(.+?)<.+?' r'sl_s\'>(\d+?)<.+?sl_p\'>(\d+?)<.+?s\'>(.+?)', re.S ) -RE_RESULTS = re.compile(r'Найдено\s+?(\d+?)\s+?раздач', re.S) -PATTERNS = ('%sbrowse.php?s=%s&c=%s', "%s&page=%s") +RE_RESULTS = re.compile(r"Найдено\s+?(\d+?)\s+?раздач", re.S) +PATTERNS = ("%sbrowse.php?s=%s&c=%s", "%s&page=%s") # base64 encoded image ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAA" @@ -85,75 +76,104 @@ ICON = ("AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAA" "gEc7/4BHO/+ARztMAAAAAIBHO0yARzv/gEc7/4BHO0wAAAAACCEAAAABAAAAAQAAAAEAAI" "ADAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAACAAwAAAAEAAAABAAAAAQAACCEAAA== ") -# setup logging -logging.basicConfig( - format="%(asctime)s %(name)-12s %(levelname)-8s %(message)s", - datefmt="%m-%d %H:%M", - level=logging.DEBUG -) -logger = logging.getLogger(__name__) +def rng(t: int) -> range: + return range(1, -(-t // PAGES)) + + +@dataclass +class Config: + username: str = "USERNAME" + password: str = "PASSWORD" + torrent_date: bool = True + magnet: bool = False + proxy: bool = False + # dynamic_proxy: bool = True + proxies: dict = field(default_factory=lambda: {"http": "", "https": ""}) + ua: str = ("Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 " + "Firefox/38.0 ") + + def __post_init__(self): + try: + if not self._validate_json(json.loads(FILE_J.read_text())): + raise ValueError("Incorrect json scheme.") + except Exception as e: + logger.error(e) + FILE_J.write_text(self.to_str()) + (BASEDIR / f"{FILENAME}.ico").write_bytes(base64.b64decode(ICON)) + + def to_str(self) -> str: + return json.dumps(self.to_dict(), indent=4, sort_keys=False) + + def to_dict(self) -> dict: + return {self._to_camel(k): v for k, v in self.__dict__.items()} + + def _validate_json(self, obj: dict) -> bool: + is_valid = True + for k, v in self.__dict__.items(): + _val = obj.get(self._to_camel(k)) + if type(_val) is not type(v): + is_valid = False + continue + setattr(self, k, _val) + return is_valid + + @staticmethod + def _to_camel(s: str) -> str: + return "".join(x.title() if i else x + for i, x in enumerate(s.split("_"))) -try: - 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 - FILE_J.write_text(json.dumps(config, indent=4, sort_keys=False)) - # also write/rewrite ico file - (BASEDIR / (FILENAME + '.ico')).write_bytes(base64.b64decode(ICON)) - logger.debug("Write files.") + +config = Config() class Kinozal: - name = 'Kinozal' - url = 'http://kinozal.tv/' + name = "Kinozal" + url = "http://kinozal.tv/" url_dl = url.replace("//", "//dl.") - url_login = url + 'takelogin.php' - supported_categories = {'all': '0', - 'movies': '1002', - 'tv': '1001', - 'music': '1004', - 'games': '23', - 'anime': '20', - 'software': '32'} + url_login = url + "takelogin.php" + supported_categories = {"all": "0", + "movies": "1002", + "tv": "1001", + "music": "1004", + "games": "23", + "anime": "20", + "software": "32"} + + # error message + error: Optional[str] = None + # cookies + mcj = MozillaCookieJar() + # establish connection + session = build_opener(HTTPCookieProcessor(mcj)) def __init__(self): - # error message - self.error = None - - # establish connection - self.session = build_opener() - # add proxy handler if needed - if config['proxy']: - if any(config['proxies'].values()): - self.session.add_handler(ProxyHandler(config['proxies'])) + if config.proxy: + if any(config.proxies.values()): + self.session.add_handler(ProxyHandler(config.proxies)) logger.debug("Proxy is set!") else: self.error = "Proxy enabled, but not set!" # change user-agent - self.session.addheaders = [('User-Agent', config['ua'])] + self.session.addheaders = [("User-Agent", config.ua)] # load local cookies - mcj = MozillaCookieJar() try: - mcj.load(FILE_C, ignore_discard=True) - if 'uid' in [cookie.name for cookie in mcj]: + self.mcj.load(FILE_C, ignore_discard=True) + if "uid" in [cookie.name for cookie in self.mcj]: # if cookie.expires < int(time.time()) logger.info("Local cookies is loaded") - self.session.add_handler(HTTPCookieProcessor(mcj)) else: logger.info("Local cookies expired or bad") - logger.debug(f"That we have: {[cookie for cookie in mcj]}") - mcj.clear() - self.login(mcj) + logger.debug(f"That we have: {[cookie for cookie in self.mcj]}") + self.mcj.clear() + self.login() except FileNotFoundError: - self.login(mcj) + self.login() - def search(self, what, cat='all'): + def search(self, what: str, cat: str = "all") -> None: if self.error: self.pretty_error(what) return None @@ -174,24 +194,24 @@ class Kinozal: logger.debug(f"--- {time.time() - t0} seconds ---") logger.info(f"Found torrents: {total}") - def download_torrent(self, url: str): + def download_torrent(self, url: str) -> None: # choose download method - if config.get("magnet"): + if config.magnet: url = "%sget_srv_details.php?action=2&id=%s" % (self.url, - url.split('=')[1]) + url.split("=")[1]) - response = self._catch_error_request(url) + response = self._request(url) if self.error: self.pretty_error(url) return None - if config.get("magnet"): + if config.magnet: if response.startswith(b"\x1f\x8b\x08"): response = gzip.decompress(response) - path = 'magnet:?xt=urn:btih:' + response.decode()[18:58] + path = "magnet:?xt=urn:btih:" + response.decode()[18:58] else: # Create a torrent file - with NamedTemporaryFile(suffix='.torrent', delete=False) as fd: + with NamedTemporaryFile(suffix=".torrent", delete=False) as fd: fd.write(response) path = fd.name @@ -199,55 +219,62 @@ class Kinozal: logger.debug(path + " " + url) print(path + " " + url) - def login(self, mcj): + def login(self) -> None: if self.error: return None - self.session.add_handler(HTTPCookieProcessor(mcj)) - form_data = {"username": config['username'], - "password": config['password']} + form_data = {"username": config.username, "password": config.password} 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() + # encoding to cp1251 then do default decode whole string + data_encoded = urlencode(form_data, encoding="cp1251").encode() logger.debug(f"Login. Data after: {data_encoded}") - self._catch_error_request(self.url_login, data_encoded) + self._request(self.url_login, data_encoded) if self.error: return None - logger.debug(f"That we have: {[cookie for cookie in mcj]}") - if 'uid' in [cookie.name for cookie in mcj]: - mcj.save(FILE_C, ignore_discard=True, ignore_expires=True) - logger.info('We successfully authorized') + logger.debug(f"That we have: {[cookie for cookie in self.mcj]}") + if "uid" in [cookie.name for cookie in self.mcj]: + self.mcj.save(FILE_C, ignore_discard=True, ignore_expires=True) + logger.info("We successfully authorized") else: self.error = "We not authorized, please check your credentials!" logger.warning(self.error) - def searching(self, query, first=False): - response = self._catch_error_request(query) - if not response: + def searching(self, query: str, first: bool = False) -> Union[None, int]: + response = self._request(query) + if self.error: return None if response.startswith(b"\x1f\x8b\x08"): response = gzip.decompress(response) - page, torrents_found = response.decode('cp1251'), -1 + page, torrents_found = response.decode("cp1251"), -1 if first: + # check login status + if "Гость! ( Зарегистрируйтесь )" in page: + logger.debug("Looks like we lost session id, lets login") + self.mcj.clear() + self.login() + if self.error: + return None # firstly we check if there is a result - torrents_found = int(RE_RESULTS.search(page)[1]) + result = RE_RESULTS.search(page) + if not result: + self.error = "Unexpected page content" + return None + torrents_found = int(result[1]) if not torrents_found: return 0 self.draw(page) return torrents_found - def draw(self, html: str): + def draw(self, html: str) -> None: torrents = RE_TORRENTS.findall(html) _part = partial(time.strftime, "%y.%m.%d") # yeah this is yesterday yesterday = _part(time.localtime(time.time() - 86400)) for tor in torrents: torrent_date = "" - if config['torrentDate']: + if config.torrent_date: ct = tor[5].split()[0] if "сегодня" in ct: torrent_date = _part() @@ -255,10 +282,10 @@ class Kinozal: torrent_date = yesterday else: torrent_date = _part(time.strptime(ct, "%d.%m.%Y")) - torrent_date = f'[{torrent_date}] ' + torrent_date = f"[{torrent_date}] " # replace size units - table = {'Т': 'T', 'Г': 'G', 'М': 'M', 'К': 'K', 'Б': 'B'} + table = {"Т": "T", "Г": "G", "М": "M", "К": "K", "Б": "B"} prettyPrinter({ "engine_url": self.url, @@ -271,31 +298,31 @@ class Kinozal: }) del torrents - def _catch_error_request(self, url=None, data=None, repeated=False): - url = url or self.url - + def _request( + self, url: str, data: Optional[bytes] = None, repeated: bool = False + ) -> Union[bytes, None]: try: with self.session.open(url, data, 5) as r: # checking that tracker isn't blocked - if r.url.startswith((self.url, self.url_dl)): + if r.geturl().startswith((self.url, self.url_dl)): return r.read() - raise URLError(f"{self.url} is blocked. Try another proxy.") - except (socket.error, socket.timeout) as err: - if not repeated: - return self._catch_error_request(url, data, True) - logger.error(err) - self.error = f"{self.url} is not response! Maybe it is blocked." - if "no host given" in err.args: - self.error = "Proxy is bad, try another!" + self.error = f"{url} is blocked. Try another proxy." except (URLError, HTTPError) as err: logger.error(err.reason) - self.error = err.reason - if hasattr(err, 'code'): + error = str(err.reason) + if "timed out" in error and not repeated: + logger.debug("Repeating request...") + return self._request(url, data, True) + if "no host given" in error: + self.error = "Proxy is bad, try another!" + elif hasattr(err, "code"): self.error = f"Request to {url} failed with status: {err.code}" + else: + self.error = f"{url} is not response! Maybe it is blocked." return None - def pretty_error(self, what): + def pretty_error(self, what: str) -> None: prettyPrinter({"engine_url": self.url, "desc_link": "https://github.com/imDMG/qBt_SE", "name": f"[{unquote(what)}][Error]: {self.error}", @@ -311,9 +338,9 @@ class Kinozal: kinozal = Kinozal if __name__ == "__main__": - if BASEDIR.parent.joinpath('settings_gui.py').exists(): + if BASEDIR.parent.joinpath("settings_gui.py").exists(): from settings_gui import EngineSettingsGUI EngineSettingsGUI(FILENAME) engine = kinozal() - engine.search('doctor') + engine.search("doctor") diff --git a/nnmclub.png b/engines/nnmclub.png similarity index 100% rename from nnmclub.png rename to engines/nnmclub.png diff --git a/nnmclub.py b/engines/nnmclub.py similarity index 100% rename from nnmclub.py rename to engines/nnmclub.py diff --git a/rutor.png b/engines/rutor.png similarity index 100% rename from rutor.png rename to engines/rutor.png diff --git a/rutor.py b/engines/rutor.py similarity index 98% rename from rutor.py rename to engines/rutor.py index 5d92ca3..a44945f 100644 --- a/rutor.py +++ b/engines/rutor.py @@ -245,9 +245,9 @@ class Rutor: rutor = Rutor if __name__ == "__main__": - if BASEDIR.parent.joinpath('settings_gui.py').exists(): - from settings_gui import EngineSettingsGUI - - EngineSettingsGUI(FILENAME) + # if BASEDIR.parent.joinpath('settings_gui.py').exists(): + # from settings_gui import EngineSettingsGUI + # + # EngineSettingsGUI(FILENAME) engine = rutor() engine.search('doctor') diff --git a/engines/rutracker.png b/engines/rutracker.png new file mode 100644 index 0000000000000000000000000000000000000000..83730b192539cbdd3da4d5d10053b033f2a2b4df GIT binary patch literal 788 zcmV+v1MB>WP)ELvn}RRgN;gamo#@q7hy>EZ8{b?(QYs}M z5bERw&$j-N5_z6J3TE~AxwTIDOkrW^tut-^2~aTcg7T0`K>I>?c;4#< z*(%6b;NymlGuxuz9z&jS5G$P-HeO}}=opZI2rnsQ0fKVFI9Xc!YtB!?*Zv@67;tS_ zn6#G_-E#G}NPF>GkWYY&p&{w@(fbC9Kz|30Nt)(6vA-`r0ob-kjZro_r=P!nxaY#U z6TOY7%s}yPXB6&lEPT7`gU+2*o~Eaxnh^LVArICL16o(i6KlT}Q8m7t|6xsMyrA3F zf%NPuTD|u|-pr*7;wKxI{MA$=s{8Kwsr}PpE!`s=tXwQA2L%acCiU!<`c9XXU`|Tx z*>@-G=<73~8iU>}>u&wxZZPNQk9JHVlsnZ80d6kO_ximCQQlC*f9>aoJHFa*XL>x> z#5=JBaJ*J)rNG#}K0_=;{#c6F^6 zuGfViP-0S4T9pqmj6)xt*D2B?s`n9kU%Xn@RC>DVBal3lgoE1F=J2WE97LnW&WlwU zV-EpYihf6O)TYS|_2pjw=qYw!bcyx7S%6hYDkjo;yztAXlX&sXY%jB*SseK(nE&c~ zYyJp^i+5vKA&>&Or>GUkQ_PW`jQBfG1ELsPy|NCmz5Xlvj#X5uO=RERBS?jc`;?@d zmZ0?%zg*OfBkndo`;GdRg_jkHI%u+G)w33=x{fO+!|foCpGYby&fAuj%Je@vh$pkx Sfk4Lq0000 bool: +async def is_good_proxy_aio(proxy: str) -> bool: try: + await asyncio.sleep(.1) opener = build_opener(ProxyHandler({f"{SCHEME}": proxy})) opener.addheaders = [("User-agent", "Mozilla/5.0")] _part = functools.partial(opener.open, HOST, timeout=3) - res = await loop.run_in_executor(None, _part) - if not res.geturl().startswith(HOST): + # res = await loop.run_in_executor(None, _part) + if not _part().geturl().startswith(HOST): raise Exception() print(proxy) return True - except Exception as e: - # print(e) + except OSError: return False async def run_aio(proxies): - loop = asyncio.get_event_loop() - for proxy in proxies: - await is_good_proxy_aio(loop, proxy) + await asyncio.gather(*[is_good_proxy(proxy) for proxy in proxies]) -def run_thread(proxies): +def run_thread(proxies: list) -> None: tasks = [] for proxy in proxies: task = threading.Thread(target=is_good_proxy, args=(proxy,)) @@ -41,20 +39,19 @@ def run_thread(proxies): t.join() -def run_pool(proxies): +def run_pool(proxies: list) -> None: with ThreadPoolExecutor(len(proxies)) as executor: - executor.map(is_good_proxy, proxies, timeout=30) + executor.map(is_good_proxy, proxies, timeout=3) -def is_good_proxy(proxy): +def is_good_proxy(proxy: str) -> bool: try: opener = build_opener(ProxyHandler({f"{SCHEME}": proxy})) opener.addheaders = [("User-agent", "Mozilla/5.0")] - req = opener.open(HOST, timeout=3) - if not req.geturl().startswith(HOST): - raise Exception() - except Exception as e: - # print(e) + with opener.open(HOST, timeout=3) as r: + if not r.geturl().startswith(HOST): + raise Exception() + except OSError: return False else: print(proxy) @@ -63,8 +60,14 @@ def is_good_proxy(proxy): def main(): t0 = time.time() - with open(PROXY_FILE) as f: - proxy_list = [x.rstrip() for x in f] + + if PROXY_FILE.startswith("http"): + opener = build_opener() + with opener.open(PROXY_FILE) as r: + proxy_list = [x.rstrip().decode("utf-8") for x in r] + else: + with open(PROXY_FILE) as f: + proxy_list = [x.rstrip() for x in f] print("Working proxies:") # run_thread(proxy_list) diff --git a/settings_gui.py b/settings_gui.py index 64ed641..f8873c9 100644 --- a/settings_gui.py +++ b/settings_gui.py @@ -1,6 +1,6 @@ import json +import tkinter as tk from pathlib import Path -from tkinter import * from tkinter import ttk, messagebox @@ -11,64 +11,65 @@ class EngineSettingsGUI: if self.cfg_file.exists(): self.config = json.loads(self.cfg_file.read_text()) - self.window = Tk() - self.window.title(engine_name + " Settings") + self.window = tk.Tk() + self.window.title(engine_name.capitalize() + " Settings") mainframe = ttk.Frame(self.window, padding="10") - mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) + mainframe.grid(column=0, row=0, sticky=tk.N) self.window.columnconfigure(0, weight=1) self.window.rowconfigure(0, weight=1) - self.username = StringVar(value=self.config.get('username', '')) - self.password = StringVar(value=self.config.get('password', '')) - self.proxy_http = StringVar( - value=self.config.get('proxies').get('http', '') + self.username = tk.StringVar(value=self.config.get("username", "")) + self.password = tk.StringVar(value=self.config.get("password", "")) + self.proxy_http = tk.StringVar( + value=self.config.get("proxies").get("http", "") ) - self.proxy_https = StringVar( - value=self.config.get('proxies').get('https', '') + self.proxy_https = tk.StringVar( + value=self.config.get("proxies").get("https", "") ) - self.date = BooleanVar(value=self.config.get('torrentDate', True)) - self.magnet = BooleanVar(value=self.config.get('magnet', False)) - self.proxy = BooleanVar(value=self.config.get('proxy', False)) + self.date = tk.BooleanVar(value=self.config.get("torrentDate", True)) + self.magnet = tk.BooleanVar(value=self.config.get("magnet", False)) + self.proxy = tk.BooleanVar(value=self.config.get("proxy", False)) - ttk.Label(mainframe, text="Username:").grid(column=0, row=0, sticky=W) - ttk.Label(mainframe, text="Password:").grid(column=0, row=1, sticky=W, - rowspan=2) + ttk.Label(mainframe, text="Username:").grid( + column=0, row=0, sticky=tk.W) + ttk.Label(mainframe, text="Password:").grid( + column=0, row=1, sticky=tk.W, rowspan=2) ttk.Entry(mainframe, width=25, textvariable=self.username).grid( - column=1, row=0, sticky=(W, E), padx=(0, 5) + column=1, row=0, sticky=tk.EW, padx=(0, 5) ) ttk.Entry(mainframe, width=25, textvariable=self.password).grid( - column=1, row=1, rowspan=2, sticky=(W, E), padx=(0, 5) + column=1, row=1, rowspan=2, sticky=tk.EW, padx=(0, 5) ) ttk.Checkbutton( mainframe, text="Date before torrent", variable=self.date, onvalue=True - ).grid(column=2, row=0, sticky=W) + ).grid(column=2, row=0, sticky=tk.W) ttk.Checkbutton( mainframe, text="Use magnet link", variable=self.magnet, onvalue=True - ).grid(column=2, row=1, sticky=W) + ).grid(column=2, row=1, sticky=tk.W) ttk.Checkbutton( mainframe, text="Proxy", variable=self.proxy, onvalue=True, command=self.proxy_action - ).grid(column=2, row=2, sticky=W) + ).grid(column=2, row=2, sticky=tk.W) - ttk.Label(mainframe, text="HTTP:").grid(column=0, row=3, sticky=W) - ttk.Label(mainframe, text="HTTPS:").grid(column=0, row=4, sticky=W) + ttk.Label(mainframe, text="HTTP:").grid(column=0, row=3, sticky=tk.W) + ttk.Label(mainframe, text="HTTPS:").grid(column=0, row=4, sticky=tk.W) - proxy_state = NORMAL if self.proxy.get() else DISABLED + proxy_state = tk.NORMAL if self.proxy.get() else tk.DISABLED self.http_entry = ttk.Entry( mainframe, textvariable=self.proxy_http, state=proxy_state ) - self.http_entry.grid(column=1, row=3, sticky=(W, E), + self.http_entry.grid(column=1, row=3, sticky=tk.EW, padx=(0, 5), pady=(0, 5)) self.https_entry = ttk.Entry( mainframe, textvariable=self.proxy_https, state=proxy_state ) - self.https_entry.grid(column=1, row=4, sticky=(W, E), padx=(0, 5)) + self.https_entry.grid(column=1, row=4, sticky=tk.EW, padx=(0, 5)) ttk.Button( mainframe, text="Save", command=self.close @@ -76,37 +77,37 @@ class EngineSettingsGUI: self.window.mainloop() - def proxy_action(self): - state = ("!" if self.proxy.get() else "") + DISABLED + def proxy_action(self) -> None: + state = ("!" if self.proxy.get() else "") + tk.DISABLED self.http_entry.state([state]) self.https_entry.state([state]) - def close(self): + def close(self) -> None: if not (self.username.get() or self.password.get()): - messagebox.showinfo('Error', "Some fields is empty!") + messagebox.showinfo("Error", "Some fields is empty!") return None if self.proxy.get() and not (self.http_entry.get() or self.https_entry.get()): - messagebox.showinfo('Error', "Some fields is empty!") + messagebox.showinfo("Error", "Some fields is empty!") return None - self.config['username'] = self.username.get() - self.config['password'] = self.password.get() - self.config['proxy'] = self.proxy.get() - if self.config['proxy']: - self.config['proxies'] = { - 'http': self.http_entry.get(), - 'https': self.https_entry.get() + self.config["username"] = self.username.get() + self.config["password"] = self.password.get() + self.config["proxy"] = self.proxy.get() + if self.config["proxy"]: + self.config["proxies"] = { + "http": self.http_entry.get(), + "https": self.https_entry.get() } - self.config['torrentDate'] = self.date.get() - self.config['magnet'] = self.magnet.get() + self.config["torrentDate"] = self.date.get() + self.config["magnet"] = self.magnet.get() self.cfg_file.write_text( json.dumps(self.config, indent=4, sort_keys=False) ) self.window.destroy() -if __name__ == '__main__': +if __name__ == "__main__": settings = EngineSettingsGUI("kinozal") print(settings.config)