Last active
February 16, 2020 13:37
-
-
Save mkpoli/79a95c399c47049e19ad6dc1d780e49b to your computer and use it in GitHub Desktop.
Minecraft Mod Jar Filename Renamer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from collections import defaultdict | |
| # accept(1) -> A[c]cept | |
| def format_option(option): | |
| x = option.text.capitalize() | |
| return "{}[{}]{}".format(x[0:option.key_pos], x[option.key_pos], x[option.key_pos + 1:]) | |
| class Option: | |
| def __init__(self, text: str, key_pos: int=0): | |
| self.text = text | |
| self.key_pos = key_pos | |
| def key_letter(self): | |
| return self.text[self.key_pos] | |
| def to_next(self): | |
| self.key_pos += 1 | |
| def formatted(self): | |
| return format_option(self) | |
| def available_inputs(self): | |
| return [self.key_letter().upper(), self.key_letter().lower(), self.text.upper(), self.text.lower()] | |
| def __len__(self): | |
| return len(self.text) | |
| def ask(question, *options): | |
| return_bool = False | |
| if len(options) == 0: | |
| options = ["Yes", "No"] | |
| return_bool = True | |
| if len(options) < 2: | |
| raise IndexError | |
| if not all(isinstance(x, str) for x in options): | |
| raise TypeError | |
| if len(options) != len(set(options)): | |
| raise ValueError("duplicated options") | |
| # Find key | |
| option_objs = [Option(option) for option in options] | |
| checked_keys = [] | |
| for option in option_objs: | |
| while True: | |
| key_letter = option.key_letter() | |
| if key_letter.upper() not in checked_keys: | |
| checked_keys.append(key_letter.upper()) | |
| break | |
| elif option.key_pos + 1 >= len(option): | |
| raise IndexError("Cannot find enough characters to fallback") | |
| else: | |
| option.to_next() | |
| while True: | |
| formatted_options = [ x.formatted() for x in option_objs ] | |
| answer = input("{} ({} or {}) ".format(question, ", ".join(formatted_options[:-1]), formatted_options[-1])) | |
| for option in option_objs: | |
| if answer in option.available_inputs(): | |
| if return_bool: | |
| return option.text == "Yes" | |
| else: | |
| return option.text |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Python 3.7.4+ | |
| # Format all mod jars to [<mcver>]<mod_name>-<mod_version+>.jar | |
| import os | |
| import sys | |
| from pathlib import Path | |
| import regex | |
| from ask import ask | |
| from os import pipe | |
| SUBPATTERN_NAME = r"(?<name>.*)" | |
| SUBPATTERN_NAME_N = r"(?<name>.*?)" | |
| SUBPATTERN_MCV = r"(?:for-)?(?:mc)?(?<mcv>1.\d\d+(\.[x\d]+)?)" | |
| SUBPATTERN_V = r"(?:v)?(?<v>.*?)(?:-universal|-dist)?" | |
| PATTERN_FINAL = regex.compile(r"\[" + SUBPATTERN_MCV + r"\]" + SUBPATTERN_NAME_N + r"-" + SUBPATTERN_V + r"\.jar", regex.IGNORECASE) | |
| PATTERN_NAME_MCV_V = regex.compile(SUBPATTERN_NAME + "-" + SUBPATTERN_MCV + r"-" + SUBPATTERN_V + r"\.jar", regex.IGNORECASE) | |
| PATTERN_NAME_V_MCV = regex.compile(SUBPATTERN_NAME + "-" + SUBPATTERN_V + r"-" + SUBPATTERN_MCV + r"\.jar", regex.IGNORECASE) | |
| PATTERN_NAME_V = regex.compile(SUBPATTERN_NAME + "-" + SUBPATTERN_V + r"\.jar") | |
| def input_default(question: str = None, default: str = None): | |
| if default: | |
| try: | |
| from pyautogui import typewrite | |
| print(question, end='') | |
| typewrite(default) | |
| return input() | |
| except ModuleNotFoundError: | |
| pass | |
| return input(question) if question else input() | |
| def capitalize(string): | |
| return string[0].upper() + string[1:] | |
| def camelcase(string): | |
| return "".join(capitalize(x) if x else "-" for x in string.split("-")) | |
| def minecraft_default_dir(): | |
| if sys.platform.startswith("darwin"): | |
| return Path.home() / "Library/Application Support/minecraft" | |
| if sys.platform.startswith("linux"): | |
| return Path.home() / ".minecraft" | |
| elif sys.platform.startswith("win32"): | |
| return Path(os.environ["APPDATA"]) / ".minecraft" | |
| def normalize(original_filename: str): | |
| original_filename = original_filename.replace("_", "-") | |
| gd = {} | |
| matched = PATTERN_FINAL.match(original_filename) | |
| if matched: | |
| gd = matched.groupdict() | |
| else: | |
| matched = PATTERN_NAME_V.match(original_filename) | |
| if matched: | |
| gd = matched.groupdict() | |
| matched = PATTERN_NAME_MCV_V.match(original_filename) | |
| if matched: | |
| gd = matched.groupdict() | |
| else: | |
| matched = PATTERN_NAME_V_MCV.match(original_filename) | |
| if matched: | |
| gd = matched.groupdict() | |
| else: | |
| return original_filename | |
| gd["name"] = camelcase(capitalize(gd['name'])) | |
| if "mcv" in gd: | |
| return f"[{ gd['mcv'] }]{ capitalize(gd['name']) }-{ gd['v'] }.jar" | |
| else: | |
| return f"{ camelcase(capitalize(gd['name'])) }-{ gd['v'] }.jar" | |
| def print_sep_line(): | |
| import shutil | |
| print("-" * (shutil.get_terminal_size((80, 20))[0] - 1)) | |
| def main(): | |
| minecraft_folder = minecraft_default_dir() | |
| if minecraft_folder.exists() and not ask(f"Minecraft in \"{ minecraft_folder }\"?"): | |
| minecraft_folder = Path(input("Please input path to \".minecraft\": ")) | |
| if not minecraft_folder.exists(): | |
| minecraft_folder = Path(minecraft_default_dir().parent / minecraft_folder) | |
| if minecraft_folder.exists() and minecraft_folder.name == "mods": | |
| minecraft_folder = Path(minecraft_folder.parent) | |
| if not minecraft_folder.exists(): | |
| print("Error: Cannot find Minecraft folder.") | |
| return | |
| print_sep_line() | |
| mod_folder = minecraft_folder / "mods" | |
| print("Minecraft Mods Directory: ", mod_folder) | |
| print_sep_line() | |
| all_mod_jars = sorted(mod_folder.glob("*.jar")) | |
| max_index_width = len(str(len(all_mod_jars))) | |
| normalized_jars = {} | |
| change_count = 0 | |
| print("[n] will be changed, (n) will not be changed.") | |
| print() | |
| for index, mod_jar in enumerate(all_mod_jars): | |
| normalized_jars[mod_jar] = mod_folder / normalize(mod_jar.name) | |
| if normalized_jars[mod_jar] != mod_jar: | |
| change_count += 1 | |
| header = f"[{ index + 1 }]".rjust(max_index_width + 2) | |
| print(f"{ header } { mod_jar.name }") | |
| else: | |
| header = f"({ index + 1 })".rjust(max_index_width + 2) | |
| print(f"{ header } { mod_jar.name } )") | |
| if change_count == 0: | |
| print() | |
| print(f"None of { len(all_mod_jars) } jar files will be changed.") | |
| return | |
| print_sep_line() | |
| print("The following WILL BE changed as:") | |
| print() | |
| for index, mod_jar in enumerate(all_mod_jars): | |
| if normalized_jars[mod_jar] == mod_jar: | |
| continue | |
| header = f"[{ index + 1 }]".rjust(max_index_width + 2) | |
| print(f"{ header } { mod_jar.name }\n{ ' ' * max_index_width }-> { normalized_jars[mod_jar].name }") | |
| print() | |
| if not ask(f"Are you sure you want to rename jar files above as such ({ change_count } of { len(all_mod_jars) } files will be changed) ?"): | |
| answer = ask("Do you want to change or exit?", "Change", "Exit") | |
| answer == "Change" | |
| if answer == "change": | |
| change_indices = input("- Which ones do you want to change? (seperate with \", \") ").split(", ") | |
| for index_str in change_indices: | |
| current_mod_jar_name = all_mod_jars[int(index_str)].name | |
| changed = input_default(current_mod_jar_name) | |
| if ask("Are you sure? "): | |
| normalized_jars[current_mod_jar_name] = mod_folder / changed | |
| else: | |
| return | |
| print_sep_line() | |
| print("Renaming...") | |
| succeeded_count = 0 | |
| changed_count = 0 | |
| for original, normalized in normalized_jars.items(): | |
| if original == normalized: | |
| continue | |
| changed_count += 1 | |
| os.rename(original, normalized) | |
| if normalized.exists(): | |
| succeeded_count += 1 | |
| print_sep_line() | |
| print(f"Finished ({ succeeded_count } succeeded / { changed_count } changes). ") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment