diff --git a/CB/Core.py b/CB/Core.py index ed9ecda..7ec1b98 100644 --- a/CB/Core.py +++ b/CB/Core.py @@ -1,12 +1,15 @@ import os import sys import json +import html import shutil import zipfile import datetime +import requests +from bs4 import BeautifulSoup from tqdm import tqdm from checksumdir import dirhash -from . import __version__ +from . import retry, __version__ from .ElvUI import ElvUIAddon from .CurseForge import CurseForgeAddon from .WoWInterface import WoWInterfaceAddon @@ -194,6 +197,15 @@ def find_orphans(self): orphaneconfig.append(os.path.join(root, f)[4:]) return orphanedaddon, orphaneconfig + @retry(custom_error='Failed to execute the search.') + def search(self, query): + results = [] + soup = BeautifulSoup(requests.get(f'https://www.curseforge.com/wow/addons/search?search=' + f'{html.escape(query.strip())}').content, 'html.parser') + for row in soup.find_all('h2', attrs={'class': 'list-item__title strong mg-b-05'}): + results.append(f'https://www.curseforge.com{row.parent["href"]}') + return results + def create_reg(self): with open('CurseBreaker.reg', 'w') as outfile: outfile.write('Windows Registry Editor Version 5.00\n[HKEY_CURRENT_USER\Software\Classes\curse]\n"URL Proto' diff --git a/CB/CurseForge.py b/CB/CurseForge.py index fc0c891..e1ba44d 100644 --- a/CB/CurseForge.py +++ b/CB/CurseForge.py @@ -7,7 +7,7 @@ class CurseForgeAddon: - @retry + @retry() def __init__(self, url): if url.startswith('https://www.curseforge.com/wow/addons/'): soup = BeautifulSoup(requests.get(url).content, 'html.parser') @@ -33,7 +33,7 @@ def version_search(self, tag): break return version - @retry + @retry() def get_current_version(self): self.currentVersion = self.version_search('Release') if self.currentVersion is None: @@ -50,7 +50,7 @@ def get_current_version(self): if self.currentVersion is None: raise RuntimeError - @retry + @retry() def get_addon(self): self.archive = zipfile.ZipFile(io.BytesIO(requests.get(self.downloadUrl).content)) for file in self.archive.namelist(): diff --git a/CB/ElvUI.py b/CB/ElvUI.py index 963a51e..7cb75da 100644 --- a/CB/ElvUI.py +++ b/CB/ElvUI.py @@ -8,7 +8,7 @@ class ElvUIAddon: - @retry + @retry() def __init__(self, branch): self.soup = BeautifulSoup(requests.get(f'https://git.tukui.org/elvui/elvui/tree/{branch}').content, 'html.parser') @@ -25,7 +25,7 @@ def get_current_version(self): except Exception: raise RuntimeError('Failed to parse addon page. URL is wrong or your source has some issues.') - @retry + @retry() def get_addon(self): self.archive = zipfile.ZipFile(io.BytesIO(requests.get(self.downloadUrl).content)) for file in self.archive.namelist(): diff --git a/CB/TukUI.py b/CB/TukUI.py index 849f554..0aa37e7 100644 --- a/CB/TukUI.py +++ b/CB/TukUI.py @@ -8,7 +8,7 @@ class TukUIAddon: - @retry + @retry() def __init__(self): self.soup = BeautifulSoup(requests.get('https://git.tukui.org/Tukz/Tukui/tree/master').content, 'html.parser') self.name = 'TukUI' @@ -23,7 +23,7 @@ def get_current_version(self): except Exception: raise RuntimeError('Failed to parse addon page. URL is wrong or your source has some issues.') - @retry + @retry() def get_addon(self): self.archive = zipfile.ZipFile(io.BytesIO(requests.get(self.downloadUrl).content)) for file in self.archive.namelist(): diff --git a/CB/WoWInterface.py b/CB/WoWInterface.py index 597e747..d9b0028 100644 --- a/CB/WoWInterface.py +++ b/CB/WoWInterface.py @@ -7,7 +7,7 @@ class WoWInterfaceAddon: - @retry + @retry() def __init__(self, url): self.soup = BeautifulSoup(requests.get(url).content, 'html.parser') self.name = self.soup.find('meta', attrs={'property': 'og:title'})['content'] @@ -22,7 +22,7 @@ def get_current_version(self): except Exception: raise RuntimeError('Failed to parse addon page. URL is wrong or your source has some issues.') - @retry + @retry() def get_addon(self): dsoup = BeautifulSoup(requests.get(self.downloadUrl).content, 'html.parser') self.archive = zipfile.ZipFile(io.BytesIO(requests.get(dsoup.find('div', attrs={'class': 'manuallink'}). diff --git a/CB/__init__.py b/CB/__init__.py index fb34f46..c88d165 100644 --- a/CB/__init__.py +++ b/CB/__init__.py @@ -4,18 +4,24 @@ __docformat__ = 'restructuredtext en' -def retry(func): - def inner(*args, **kwargs): - for i in range(3): - # noinspection PyBroadException - try: - result = func(*args, **kwargs) - except KeyboardInterrupt: - raise - except Exception: - continue +def retry(custom_error=False): + def wraps(func): + def inner(*args, **kwargs): + for i in range(3): + # noinspection PyBroadException + try: + result = func(*args, **kwargs) + except KeyboardInterrupt: + raise + except Exception: + continue + else: + return result else: - return result - else: - raise RuntimeError('Failed to parse addon page. URL is wrong or your source has some issues.') - return inner + if custom_error: + raise RuntimeError(custom_error) + else: + raise RuntimeError('Failed to parse addon page. URL is wrong or your source has some issues.') + return inner + return wraps + diff --git a/CurseBreaker.py b/CurseBreaker.py index e8b8541..19c0e5d 100644 --- a/CurseBreaker.py +++ b/CurseBreaker.py @@ -143,7 +143,7 @@ def print_header(self): f'{__version__} ~~~\n')) def setup_completer(self): - commands = ['install', 'uninstall', 'update', 'force_update', 'status', 'orphans', 'toggle_backup', + commands = ['install', 'uninstall', 'update', 'force_update', 'status', 'orphans', 'search', 'toggle_backup', 'uri_integration', 'help', 'exit'] addons = sorted(self.core.config['Addons'], key=lambda k: k['Name'].lower()) for addon in addons: @@ -254,6 +254,15 @@ def c_toggle_backup(self, _): printft('Backup of WTF directory is now:', HTML('ENABLED') if status else HTML('DISABLED')) + def c_search(self, args): + if args: + results = self.core.search(args) + printft(HTML('Top results of your search:')) + for url in results: + printft(url) + else: + printft(HTML('Usage:\n\tThis command accepts a search query as an argument.')) + def c_help(self, _): printft(HTML('install [URL]\n\tCommand accepts a comma-separated list of links.')) printft(HTML('uninstall [URL/Name]\n\tCommand accepts a comma-separated list of links or' @@ -265,6 +274,7 @@ def c_help(self, _): 'ate.')) printft(HTML('status\n\tPrints the current state of all installed addons.')) printft(HTML('orphans\n\tPrints list of orphaned directories and files.')) + printft(HTML('search [Keyword]\n\tExecute addon search on CurseForge.')) printft(HTML('toggle_backup\n\tEnable/disable automatic daily backup of WTF directory.')) printft(HTML('uri_integration\n\tEnable integration with CurseForge page. "Install" butt' 'on will now start this application.'))