diff --git a/.vscode/launch.json b/.vscode/launch.json index bbedf91..ca7f355 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,7 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { "name": "Python: Current File", "type": "python", diff --git a/PATCHNOTES.md b/PATCHNOTES.md index 65f7913..629e11e 100644 --- a/PATCHNOTES.md +++ b/PATCHNOTES.md @@ -1,5 +1,25 @@ -# Patch Notes (Updated June 22nd 2022) -- 1.6 +# Patch Notes +- 1.7 (July 8th 2022) + - Added level 4 slot support (#35) + - Fixed restarting the app after setting a different game language (#34) + - You can now ignore an update + - You can now add invalid skills (or skills that are not in the list, don't come to me if the builder doesn't work) + - You can record in windowed mode (See next point) + - You can now let the app remove black bars from videos + - It is experimental. You should fix your recordings first if it doesn't work + - Black bars can be caused by not setting your TV size to 100% in the switch settings. + +# Older patch notes +- 1.6.2 (July 5th 2022) + - Fixed an issue with the creation of the config file + - `--reset` commandline option now takes into account `-a` and `-l` +- 1.6.1 (July 5th 2022) + - Tesseract location now goes through the entire list of OS-specific locations + - You can now set a custom tesseract location in the config (it has a higher priority) + - Added a few extra error messages + - New English Sunbreak skills and corrections are now bundled. + - The other languages use the english names as placeholders, sorry about that, feel free to submit the names. +- 1.6 (June 22nd 2022) - Added an app language dropdown - Version Checker automatically updates language files - Other updates (main app, skills and corrections) are still "on demand" when there is an update @@ -8,10 +28,6 @@ - Languages in language dropdowns should show up in the actual language - NOTE: They are probably wrong - Added a "Go to Set Searcher" button - - -# Older patch notes - - 1.5.3 (FEB 3rd 2022) - Updated where language packs are downloaded from, - Github changed where "raw" data was fetched from and broke "fresh" installs diff --git a/README.md b/README.md index 756e45a..7aa41e9 100644 --- a/README.md +++ b/README.md @@ -12,27 +12,16 @@ This repo contains code that will allow you to extract all of your charms in Mon It's called Utsushi's charm because I thought it would be funny to make a complementary "Utsushi's Armor Search System", but [this armor set searcher](https://mhrise.wiki-db.com/sim/?hl=en) exists. I might still try to port Athena's ASS for MHW to MHR, but for now this works for me. -# Patch Notes (Updated July 5th 2022) -The next non-bugfix version will have the option to ignore an update for those who don't care or don't have issues. Sorry for the double update in one day - -- 1.6.2 (July 5th 2022) - - Fixed an issue with the creation of the config file - - `--reset` commandline option now takes into account `-a` and `-l` -- 1.6.1 (July 5th 2022) - - Tesseract location now goes through the entire list of OS-specific locations - - You can now set a custom tesseract location in the config (it has a higher priority) - - Added a few extra error messages - - New English Sunbreak skills and corrections are now bundled. - - The other languages use the english names as placeholders, sorry about that, feel free to submit the names. -- 1.6 (June 22nd 2022) - - Added an app language dropdown - - Version Checker automatically updates language files - - Other updates (main app, skills and corrections) are still "on demand" when there is an update - - App and game language are now stored inside a config file - - Added a --reset cmd option to clear the config file - - Languages in language dropdowns should show up in the actual language - - NOTE: They are probably wrong - - Added a "Go to Set Searcher" button +# Patch Notes (Updated July 8th 2022) +- 1.7 (July 8th 2022) + - Added level 4 slot support (#35) + - Fixed restarting the app after setting a different game language (#34) + - You can now ignore an update + - You can now add invalid skills (or skills that are not in the list, don't come to me if the builder doesn't work) + - You can record in windowed mode (See next point) + - You can now let the app remove black bars from videos + - It is experimental. You should fix your recordings first if it doesn't work + - Black bars can be caused by not setting your TV size to 100% in the switch settings. The rest of the patch notes can be found [here](PATCHNOTES.md) @@ -124,6 +113,10 @@ Having translations for the instructions for other languages might be useful, bu - I see no reason other capture cards should not work - 1080p adds extra processing due to a downscaling step to 720p (20-30% slower) - I also tested using an Mclassic without any issues. +- Q: I record with a capture card but there are black bars + - 99% of the time this will be caused by not having the screen size set to 100% in the switch settings + - In the remaining cases, you should check your recorder settings. + - You can try using the "Remove black bars" feature. - Q: Does this Work for the PC version - Yes. As long as your resolution is 720p or above, and has a 16:9 ratio, it should - Basically, 1080p, 1440p, 4k and above should work without issues, albeit, the higher the resolution, the slower. @@ -131,6 +124,9 @@ Having translations for the instructions for other languages might be useful, bu - This might be because the PC version has a different name for the skill, add it to the corrections.lang.csv file, or tell me to do it. - An example of this would be `Quick Sheath`, which is named `Quick Sheathe` on PC - Make sure you give me both versions of the skill name if you think the PC version messed up again. +- Q: Can I record in windowed mode + - Yes. + - Yes, but if you have the "top bar" in your recording you will need the "Remove Black Bars" feature. You should try not recording the full window and only the game. # Notes - Version 1.5 is the last version that will officially support console mode, I may fix it from time to time if I need it for dev reasons, but I don't want to maintain a secondary workflow that will likely be used by nobody. diff --git a/data/translations/eng.json b/data/translations/eng.json index 76ddcc6..f8d412e 100644 --- a/data/translations/eng.json +++ b/data/translations/eng.json @@ -7,7 +7,7 @@ "input-dir": "Input video location: ", "frame-dir": "Frame location: ", "skipping-frames": "Skipping frame extraction: ", - "skip-frames": "Skips frames extraction\n (Recover from previous run)", + "skip-frames": "Skip frames extraction\n (Recover from previous run)", "skip-charms": "Skip charm extraction (Why??)", "delete-frames": "Delete old frames", "deleting-frames": "Deleting existing frames: ", @@ -68,5 +68,9 @@ "tess-wait-5-retry": "Waiting 5 seconds before trying to download the language pack again...", "tess-url-error": "A URL error occured, this might be a DNS issue... retrying", "tess-cannot-download": "Unable to download Tesseract language pack.", - "tess-not-found": "Tesseract was not found. Please install it, and if it is installed, pass it as a command line option at least once. If you have done both and it still isn't found, please open an issue on Github." + "tess-not-found": "Tesseract was not found. Please install it, and if it is installed, pass it as a command line option at least once. If you have done both and it still isn't found, please open an issue on Github.", + "remove-black-bars": "Remove black bars\nMay work with windowed capture", + "remove-black-bars-info": "The app will attempt to remove black bars.\nIt should work if you are recording in windowed mode.\nThis should be useful if your switch isn't properly scaled (TV Size setting).\nThis is 100% experimental (and somewhat random). Fix your videos before you complain :)", + "upd-ignore": "Ignore", + "add-as-is": "Add skill as-is" } \ No newline at end of file diff --git a/data/translations/fra.json b/data/translations/fra.json index 01ed334..1265ca9 100644 --- a/data/translations/fra.json +++ b/data/translations/fra.json @@ -7,7 +7,7 @@ "input-dir": "Dossier des vidéos: ", "frame-dir": "Dossier des frames: ", "skipping-frames": "Sauter l'extraction des frames: ", - "skip-frames": "Ignore l'extraction des frames\n (Récupération d'une exécution échouée)", + "skip-frames": "Sauter l'extraction des frames\n(Récupération d'une exécution échouée)", "skip-charms": "Sauter l'extraction des charmes (Pourquoi??)", "delete-frames": "Supprimer les anciens frames", "deleting-frames": "Suppression des frames: ", @@ -65,8 +65,12 @@ "wiki-button": "Visiter le MHR Set Searcher", "wiki-clipboard": "Copier l'URL", "wiki-attempt": "Tentative d'ouverture du site web...", - "tess-wait-5-retry":"Pause de 5 secondes avant de ré-essayer le téléchargement du language pack...", - "tess-url-error":"Une erreur d'URL s'est produite, potentiellement causé par une erreur DNS...", - "tess-cannot-download":"Impossible de télécharger le language pack Tesseract.", - "tess-not-found": "Tesseract n'a pas été trouv. Veuillez l'installer, s'il est installé, passez le à travers le command line au moins une fois. Si cette erreur continue d'apparaître, veuillez créer une issue sur Github.." + "tess-wait-5-retry": "Pause de 5 secondes avant de ré-essayer le téléchargement du language pack...", + "tess-url-error": "Une erreur d'URL s'est produite, potentiellement causé par une erreur DNS...", + "tess-cannot-download": "Impossible de télécharger le language pack Tesseract.", + "tess-not-found": "Tesseract n'a pas été trouv. Veuillez l'installer, s'il est installé, passez le à travers le command line au moins une fois. Si cette erreur continue d'apparaître, veuillez créer une issue sur Github..", + "remove-black-bars": "Retirer les barres noires\nDevrait marcher avec le mode fenêtre", + "remove-black-bars-info": "L'application va essayer d'enlever les barres noires autour du vidéo.\n¨Ça devrait aussi fonctionner si vous enregistrer le jeu en mode fenêtre.\nC'est utile lorsque vous n'avez pas configuré la taille de la TV pour la switch.\nC'est 100% expérimental et un peu aléatoire.\nEnregistrez les vidéos comme il faut avant de vous plaindre :)", + "upd-ignore": "Ignorer", + "add-as-is": "Ajouter tel quel" } \ No newline at end of file diff --git a/data/versions.json b/data/versions.json index 45b811d..b37a57d 100644 --- a/data/versions.json +++ b/data/versions.json @@ -1,10 +1,10 @@ { - "app": "1.6.2", + "app": "1.7", "skills": "3.1.1", "languages": { - "eng": "1.2", + "eng": "1.2.1", "jpn": 0, - "fra": "1.2", + "fra": "1.2.1", "ita": 0, "deu": 0, "spa": 0, diff --git a/images/slots/slot4.png b/images/slots/slot4.png new file mode 100644 index 0000000..1dfccc4 Binary files /dev/null and b/images/slots/slot4.png differ diff --git a/src/__main__.py b/src/__main__.py index c63925e..0e1ab01 100644 --- a/src/__main__.py +++ b/src/__main__.py @@ -29,7 +29,6 @@ ) from .updater.updater_utils import ( ask_main_update, - ask_language_update, ask_skill_update, ask_corrections_update, ) @@ -143,7 +142,7 @@ def run_in_console(args): charm_json = args.charm_json charm_encoded = args.charm_encoded - lang = get_language_code(args.language) + lang = get_game_language() os.makedirs(input_dir, exist_ok=True) os.makedirs(frame_dir, exist_ok=True) diff --git a/src/frame_extraction.py b/src/frame_extraction.py index e1e2c5a..202b20c 100644 --- a/src/frame_extraction.py +++ b/src/frame_extraction.py @@ -1,8 +1,11 @@ import os +import random import cv2 + from .utils import ( apply_pre_crop_mask, get_frame_change_observation_section, + compare_pixel, ) from tqdm import tqdm from math import floor @@ -28,23 +31,111 @@ def resize_frame(frame): return frame -def crop_frames(capture_device): - results = [] - for i, f in read_frames(capture_device): +def crop_frames(capture_device, remove_black_bars): + for i, f in read_frames(capture_device, remove_black_bars): yield i, crop_frame(f) -def read_frames(capture_device): +def _jitter_pos(pos: int, jitter_range: int = 10): + return pos + (random.randint(-jitter_range, jitter_range)) + + +# top, left, right, bottom +def detect_black_bars(frame): + height, width = frame.shape[:2] + + x_pos_base = floor(width / 2) + y_pos_base = floor(height / 2) + prev_pixel = None + + i = 0 + top = 5 + left = 5 + right = 5 + bottom = 5 + while True: + pixel = frame[top, _jitter_pos(x_pos_base)] + if prev_pixel is not None: + if compare_pixel(pixel, prev_pixel): + top += 1 + else: + prev_pixel = None + break + else: + prev_pixel = pixel + + while True: + pixel = frame[height - bottom - 1, _jitter_pos(x_pos_base)] + if prev_pixel is not None: + if compare_pixel(pixel, prev_pixel): + bottom += 1 + else: + prev_pixel = None + break + else: + prev_pixel = pixel + + while True: + pixel = frame[_jitter_pos(y_pos_base), left] + if prev_pixel is not None: + if compare_pixel(pixel, prev_pixel): + left += 1 + else: + prev_pixel = None + break + else: + prev_pixel = pixel + + while True: + pixel = frame[_jitter_pos(y_pos_base), width - right - 1] + if prev_pixel is not None: + if compare_pixel(pixel, prev_pixel): + right += 1 + else: + prev_pixel = None + break + else: + prev_pixel = pixel + + if top == 5: + top = 0 + if left == 5: + left = 0 + if right == 5: + right = 0 + if bottom == 5: + bottom = 0 + + return (top, left, right, bottom) + + +def crop_black_bars(frame, black_bars): + (top, left, right, bottom) = black_bars + height, width = frame.shape[:2] + + y = top + y2 = height - bottom + x = left + x2 = width - right + return frame[y:y2, x:x2] + + +def read_frames(capture_device, remove_black_bars=False): i = 0 fps = capture_device.get(cv2.CAP_PROP_FPS) + black_bars = None while True: ret, frame = capture_device.read() + if i == 0: + black_bars = detect_black_bars(frame) if not ret: break if fps == 60 and (i % 2): pass else: + if remove_black_bars: + frame = crop_black_bars(frame, black_bars) yield i, resize_frame(frame) i += 1 @@ -68,6 +159,7 @@ def is_validated_video_format(video_name): def extract_unique_frames( input_dir, frame_dir, + remove_black_bars=False, _=lambda x: x, iter_wrapper=None, frame_callback=lambda x: None, @@ -102,7 +194,7 @@ def extract_unique_frames( previous_charm_marker = None with iter_wrapper( - crop_frames(cap), + crop_frames(cap, remove_black_bars), total=frame_count, desc=_("fn-total-charm").format(f_name, frame_count), ) as frame_pbar: diff --git a/src/resources.py b/src/resources.py index b5bfd7c..1aaa343 100644 --- a/src/resources.py +++ b/src/resources.py @@ -1,3 +1,4 @@ +from distutils.command.config import config import os import sys import shutil @@ -7,6 +8,8 @@ import logging import json +from .updater.SimpleSemVer import SimpleSemVer + from .exceptions.MissingTranslationError import MissingTranslationError logger = logging.getLogger(__name__) @@ -99,7 +102,9 @@ def load_corrections(language_code, known_corrections=None): def get_spell_checker(language_code): spell = SymSpell(max_dictionary_edit_distance=4) - spell.load_dictionary(get_word_freqs_location(language_code), 0, 1) + spell.load_dictionary( + get_word_freqs_location(language_code), 0, 1, encoding="utf-8" + ) return spell @@ -221,6 +226,7 @@ def init_config(app_language_code, skill_language_code): { "app-language": app_language_code, "game-language": skill_language_code, + "black-bar-threshold": 10, }, config_f, ) @@ -228,7 +234,11 @@ def init_config(app_language_code, skill_language_code): def get_app_language(): config = _load_config() - return config["app-language"] + return ( + config["app-language"] + if "app-language" in config + else get_language_code(default_lang()) + ) def save_app_language(app_language_code): @@ -247,7 +257,16 @@ def untranslate_lang(lang): def get_game_language(): config = _load_config() - return config["game-language"] + return ( + config["game-language"] + if "game-language" in config + else get_language_code(default_lang()) + ) + + +def get_black_bar_threshold(): + config = _load_config() + return config["black-bar-threshold"] if "black-bar-threshold" in config else 10 def save_game_language(app_language_code): @@ -256,6 +275,25 @@ def save_game_language(app_language_code): _write_config(config) +def save_ignored_update(version: SimpleSemVer): + config = _load_config() + config["skipped-version"] = str(version) if version is not None else version + _write_config(config) + + +def get_ignored_update() -> SimpleSemVer: + config = _load_config() + try: + return ( + SimpleSemVer(config["skipped-version"]) + if "skipped-version" in config + else None + ) + except: + save_ignored_update(None) + return None + + def get_tesseract_location(): config = _load_config() if "tesseract-directory" in config: @@ -296,6 +334,7 @@ def default_lang(): "slot1": _alter_resource_path(os.path.join("images", "slots", "slot1.png")), "slot2": _alter_resource_path(os.path.join("images", "slots", "slot2.png")), "slot3": _alter_resource_path(os.path.join("images", "slots", "slot3.png")), + "slot4": _alter_resource_path(os.path.join("images", "slots", "slot4.png")), "mask": _alter_resource_path(os.path.join("images", "mask.png")), "charm_only": _alter_resource_path(os.path.join("images", "charm_only.png")), "skill_mask": _alter_resource_path(os.path.join("images", "skill_mask.png")), diff --git a/src/tesseract/tesseract_utils.py b/src/tesseract/tesseract_utils.py index 44c5a51..b4b0469 100644 --- a/src/tesseract/tesseract_utils.py +++ b/src/tesseract/tesseract_utils.py @@ -143,7 +143,7 @@ def find_tesseract(silent=False): ) -def override_tessdata(): +def use_localappdata_tess(): base_path = HOME if WINDOWS: base_path = os.getenv("LOCALAPPDATA") or HOME @@ -154,17 +154,13 @@ def override_tessdata(): def set_tessdata(): if _is_pyinstaller(): - override_tessdata() + use_localappdata_tess() return if "TESSDATA_PREFIX" in os.environ: return - path = find_tesseract(silent=True) - path = os.path.dirname(path) - TESSDATA_PREFIX = os.path.join(path, "tessdata") - os.environ["TESSDATA_PREFIX"] = TESSDATA_PREFIX - logger.debug(f"Set 'TESSDATA_PREFIX' to {TESSDATA_PREFIX}") + use_localappdata_tess() def get_datapath(): @@ -172,7 +168,7 @@ def get_datapath(): set_tessdata() if os.environ["TESSDATA_PREFIX"] == "tessdata": - override_tessdata() + use_localappdata_tess() return os.environ["TESSDATA_PREFIX"] @@ -204,7 +200,7 @@ def download_language_data(lang="eng", _=lambda x: x, retry=False): except PermissionError as e: print(_("tess-permission-denied")) if not retry: - override_tessdata() + use_localappdata_tess() download_language_data(lang, _, retry=True) except URLError as e: print(_("tess-url-error")) diff --git a/src/ui/AskUpdate.py b/src/ui/AskUpdate.py index f92dc8e..ca4dc67 100644 --- a/src/ui/AskUpdate.py +++ b/src/ui/AskUpdate.py @@ -12,6 +12,12 @@ class UpdateType(Enum): SkillCorrections = 3 +class UpdateAction(Enum): + Nothing = 0 + Update = 1 + Ignore = 2 + + class AskUpdate(tk.Toplevel): def __init__( self, @@ -20,6 +26,7 @@ def __init__( update_type: UpdateType, local: SimpleSemVer, remote: SimpleSemVer, + show_ignore=False, ): super().__init__(parent) self._ = _ @@ -40,6 +47,9 @@ def __init__( self.lang_lbl.grid(row=0, columnspan=2) self.yes_btn.grid(row=1, column=0, sticky="e") self.no_btn.grid(row=1, column=1, sticky="w") + if show_ignore: + self.ignore_btn = tk.Button(self, text=_("upd-ignore"), command=self.ignore) + self.ignore_btn.grid(row=2, column=0, columnspan=2) def build_message(self, update_type, local, remote, _): return _( @@ -52,9 +62,13 @@ def build_message(self, update_type, local, remote, _): ).format(local, remote) def yes(self): - self.answer = True + self.answer = UpdateAction.Update self.destroy() def no(self): - self.answer = False + self.answer = UpdateAction.Nothing + self.destroy() + + def ignore(self): + self.answer = UpdateAction.Ignore self.destroy() diff --git a/src/ui/MainWindow.py b/src/ui/MainWindow.py index cc5d1f0..1d3ccd3 100644 --- a/src/ui/MainWindow.py +++ b/src/ui/MainWindow.py @@ -48,7 +48,9 @@ def __init__(self, _: Translator, args, skill_language_code, app_langs): self.input_dir = tk.StringVar(value=args.input_dir) self.frame_dir = tk.StringVar(value=args.frame_dir) - self.lang = tk.StringVar(value=get_language_from_code(skill_language_code)) + self.lang = tk.StringVar( + value=translate_lang(get_language_from_code(skill_language_code)) + ) self.app_lang = tk.StringVar(value=translate_lang(_.language)) self.charm_json = args.charm_json @@ -56,6 +58,7 @@ def __init__(self, _: Translator, args, skill_language_code, app_langs): self.skip_frames = tk.IntVar(value=args.skip_frames) self.skip_charms = tk.IntVar(value=args.skip_charms) + self.remove_black_bars = tk.IntVar(value=False) self.autosave = tk.IntVar(value=1) self.delete_frames_val = tk.IntVar() @@ -122,9 +125,18 @@ def _runtime_opts(parent=self): runtime_frame, text=_("skip-charms"), variable=self.skip_charms ) + self.remove_black_bars_box = tk.Checkbutton( + runtime_frame, + text=_("remove-black-bars"), + variable=self.remove_black_bars, + command=self._black_bar_activated, + ) + self.autosave_box.grid(column=1, row=1, sticky="w") self.del_frames_box.grid(column=1, row=2, sticky="w") self.skip_frames_box.grid(column=1, row=3, sticky="w") + self.remove_black_bars_box.grid(column=1, row=4, sticky="w") + # self.skip_charms_box.grid(column=0, row=4, sticky="w") # Hidden for now return runtime_frame @@ -311,6 +323,7 @@ def run(self, _: Translator = None): extract_unique_frames( self.input_dir.get(), self.frame_dir.get(), + self.remove_black_bars.get(), _, self.pbar, self.progress_callback, @@ -419,6 +432,12 @@ def _regen_paths(self): os.makedirs(self.input_dir.get(), exist_ok=True) os.makedirs(self.frame_dir.get(), exist_ok=True) + def _black_bar_activated(self, _=None): + if _ is None: + _ = self._ + if self.remove_black_bars.get(): + print(_("remove-black-bars-info")) + def write(self, *message, end="\n", sep=" "): text = "" for item in message: diff --git a/src/ui/ParseRepairWindow.py b/src/ui/ParseRepairWindow.py index 7314591..5541149 100644 --- a/src/ui/ParseRepairWindow.py +++ b/src/ui/ParseRepairWindow.py @@ -79,6 +79,11 @@ def _bottom_buttons(parent=self): btn_cancel.grid(row=2, column=0, sticky="w") btn_empty.grid(row=2, column=1, sticky="w") self.btn_ok.grid(row=2, column=2, sticky="w") + + self.btn_add_anyway = tk.Button( + frame, text=_("add-as-is"), command=self.select_as_is + ) + self.btn_add_anyway.grid(row=3, column=0, columnspan=3) return frame def _lbl(parent=self): @@ -119,9 +124,11 @@ def _lbl(parent=self): def check_valid_skill(self, *args): new_name = self.selected.get() if not is_skill(self.all_skills, new_name): + self.btn_add_anyway["state"] = "normal" self.btn_ok["state"] = "disabled" self.unbind("") else: + self.btn_add_anyway["state"] = "disabled" self.btn_ok["state"] = "normal" self.bind("", self.select_skill) @@ -140,13 +147,15 @@ def feed_charm(self, charm): def feed_error(self, error): self.current_error = error skill_img, parsed, level, error_type = error + if "chi" in self.language or self.language == "kor" or self.language == "jpn": + parsed = parsed.replace(" ", "") b, g, r = cv2.split(skill_img) im = Image.fromarray(cv2.merge((r, g, b))) imgtk = ImageTk.PhotoImage(image=im) self.img_value_lbl.configure(image=imgtk) self.img_value_lbl.image = imgtk self.parsed.set(parsed) - self.selected.set("") + self.selected.set(parsed) self.lvl.set(level) self.update() @@ -164,6 +173,9 @@ def select_empty(self): def select_skill(self, *args): self.select("skill") + def select_as_is(self, *args): + self.select("as-is") + def try_next_charm(self): try: self.feed_charm(next(self.charm_iter)) @@ -188,4 +200,7 @@ def select(self, action): self.fixed_skills[ fix_skill_name(self.all_skills, self.selected.get()) ] = self.current_error[2] + elif action == "as-is": + self.fixed_skills[self.selected.get()] = self.current_error[2] + self.try_next_error() diff --git a/src/updater/updater_utils.py b/src/updater/updater_utils.py index 70131ef..45f1760 100644 --- a/src/updater/updater_utils.py +++ b/src/updater/updater_utils.py @@ -1,50 +1,43 @@ +from ..resources import get_ignored_update, save_ignored_update from .VersionChecker import VersionChecker from .Updater import Updater -from ..ui.AskUpdate import AskUpdate, UpdateType +from ..ui.AskUpdate import UpdateAction, AskUpdate, UpdateType def ask_main_update(version_checker: VersionChecker, main_window, _): new_update, cur_ver, online_ver = version_checker.check_app_version() - answer = False + answer = UpdateAction.Nothing if new_update: - answer = _spawn_window(main_window, _, UpdateType.App, cur_ver, online_ver) - if answer: + skipped = get_ignored_update() + if skipped is not None and online_ver == skipped: + return answer + + answer = _spawn_window( + main_window, _, UpdateType.App, cur_ver, online_ver, True + ) + if answer == UpdateAction.Update: updater = Updater(_, version_checker) updater.update_main_app() + + elif answer == UpdateAction.Ignore: + save_ignored_update(online_ver) + return answer def ask_skill_update(version_checker: VersionChecker, main_window, _): new_update, cur_ver, online_ver = version_checker.check_skill_version() - answer = False + answer = UpdateAction.Nothing if new_update: answer = _spawn_window(main_window, _, UpdateType.Skills, cur_ver, online_ver) - if answer: + if answer == UpdateAction.Update: updater = Updater(_, version_checker) updater.update_all_skills(online_ver) return answer -def ask_language_update( - version_checker: VersionChecker, main_window, app_language_code, _ -): - new_update, cur_ver, online_ver = version_checker.check_language_version( - app_language_code - ) - - answer = False - if new_update: - answer = _spawn_window( - main_window, _, UpdateType.AppLanguage, cur_ver, online_ver - ) - if answer: - updater = Updater(_, version_checker) - updater.update_language(app_language_code, online_ver) - return answer - - def ask_corrections_update( version_checker: VersionChecker, main_window, skill_language_code, _ ): @@ -52,18 +45,18 @@ def ask_corrections_update( skill_language_code ) - answer = False + answer = UpdateAction.Nothing if new_update: answer = _spawn_window( main_window, _, UpdateType.SkillCorrections, cur_ver, online_ver ) - if answer: + if answer == UpdateAction.Update: updater = Updater(_, version_checker) updater.update_skill_corrections(skill_language_code, online_ver) return answer -def _spawn_window(main_window, _, update_type, cur_ver, online_ver): - askup = AskUpdate(main_window, _, update_type, cur_ver, online_ver) +def _spawn_window(main_window, _, update_type, cur_ver, online_ver, show_ignore=False): + askup = AskUpdate(main_window, _, update_type, cur_ver, online_ver, show_ignore) main_window.wait_window(askup) return askup.answer diff --git a/src/utils.py b/src/utils.py index 4ccda33..15376f2 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,10 +1,11 @@ import os import cv2 +from cv2 import norm import numpy as np from skimage.metrics import structural_similarity from math import floor from .tesseract.tesseract_utils import process_image_with_tesseract -from .resources import get_resource_path +from .resources import get_black_bar_threshold, get_resource_path def is_skill(skill_dict, skill_name): @@ -98,22 +99,23 @@ def get_slots(img): slot1 = cv2.imread(get_resource_path("slot1")) slot2 = cv2.imread(get_resource_path("slot2")) slot3 = cv2.imread(get_resource_path("slot3")) + slot4 = cv2.imread(get_resource_path("slot4")) spot1 = img[y : y + h, x1 : x1 + w] spot2 = img[y : y + h, x2 : x2 + w] spot3 = img[y : y + h, x3 : x3 + w] slots = [] - most_similar = None j = 1 for spot in [spot1, spot2, spot3]: - score0 = structural_similarity(spot, slot0, multichannel=True) - score1 = structural_similarity(spot, slot1, multichannel=True) - score2 = structural_similarity(spot, slot2, multichannel=True) - score3 = structural_similarity(spot, slot3, multichannel=True) + score0 = structural_similarity(spot, slot0, channel_axis=-1) + score1 = structural_similarity(spot, slot1, channel_axis=-1) + score2 = structural_similarity(spot, slot2, channel_axis=-1) + score3 = structural_similarity(spot, slot3, channel_axis=-1) + score4 = structural_similarity(spot, slot4, channel_axis=-1) j += 1 - scores = [score0, score1, score2, score3] + scores = [score0, score1, score2, score3, score4] best = max(scores) for i, s in enumerate(scores): if s == best: @@ -215,3 +217,10 @@ def batchify_lazy(lst, batch_size): batch.append(item) i += 1 yield batch + + +def compare_pixel(pixel1, pixel2, threshold=None): + if threshold is None: + threshold = get_black_bar_threshold() + distance = norm(pixel1, pixel2) + return distance <= threshold