Compare commits

...

2 Commits

Author SHA1 Message Date
f5344f606e refactor with configmanager 2023-12-15 23:50:34 +01:00
da43eae362 move shared variables 2023-12-15 23:50:24 +01:00
3 changed files with 195 additions and 128 deletions

View File

@ -1,100 +1,165 @@
from os import path, getcwd, listdir, mkdir, makedirs, rename import enum
from os import EX_CANTCREAT, path, getcwd, listdir, rename
from sys import exit
import re import re
def read_config(filepath, root_directory="."): from .globals import version, settings_map
if not path.isfile(filepath): return False from .globals import warning, error, user_error, info, create_dir
file = open(filepath, 'r') class ConfigManager():
keys = {} """Manage config files for imgsort"""
for line in file.readlines(): def __init__(self, config_dir: str):
line = line.replace("\n", "") """TODO: to be defined.
match = re.match(r". = /?([a-z-A-ZöÖäÄüÜ0-9/: _-]+/)*[a-zA-ZöÖäÄüÜ0-9/: _-]+/?", line)
if match:
key, value = line.split(" = ")
keys[key] = root_directory + "/" + value
return keys
def write_config(filepath, keys): @param config_path TODO
file = open(filepath, 'w')
file.write("Config written by imgsort.\n")
for k, v in keys.items() :
file.write(f"{k} = {v}\n")
def create_config(): """
keys = {} self._config_dir = config_dir
print( if not path.isdir(self._config_dir):
""" if path.exists(self._config_dir):
error(f"Config '{self._config_dir}' exists but is not a directory.")
info(f"Creating config dir '{self._config_dir}' since it does not exist")
try:
create_dir(self._config_dir)
except PermissionError as e:
error(f"Could not create '{self._config_dir}': PermissionError: {e}")
except Exception as e:
error(f"Could not create '{self._config_dir}': {e}")
self._configs = [ e for e in listdir(self._config_dir) if path.isfile(path.normpath(self._config_dir + "/" + e)) and e.endswith(".conf") ]
self._configs.sort()
def present_config_selection(self):
"""
Returns to path to an existing config or False if a new config should be created
"""
# get configs
if len(self._configs) == 0:
info(f"No config valid file found in '{self._config_dir}'")
return False
print("0: create new configuration")
for i, c in enumerate(self._configs):
print(f"{i+1:2}: {c}")
while True:
choice = input("Please select a config: ")
try:
choice = int(choice)
except ValueError:
user_error(f"Invalid choice: '{choice}'. Choice must be a number between 0 and {len(self._configs)}")
continue
if not 0 <= choice <= len(self._configs):
user_error(f"Invalid choice: '{choice}'. Choice must be a number between 0 and {len(self._configs)}")
continue
if choice == 0:
return False
return self._configs[choice-1]
def _make_name(self, config_name: str):
return path.normpath(self._config_dir + "/" + config_name.removesuffix(".conf") + ".conf")
def write_config(self, config_name: str, keys: dict[str,str]):
file = open(path.normpath(self._config_dir + "/" + config_name), 'w')
file.write(f"# Config written by imgsort {version}.\n")
for k, v in keys.items() :
file.write(f"{k} = {v}\n")
def read_config(self, config_name: str, root_directory="."):
"""
@param root_directory Make all relative paths relative to this one
"""
if type(config_name) != str:
error(f"load config got wrong type: '{type(config_name)}'")
config_file = self._make_name(config_name)
if not path.isfile(config_file):
error(f"File '{config_file}' does not exist")
try:
file = open(config_file, 'r')
except Exception as e:
error(f"Could not open file '{config_file}': {e}")
keys: dict[str, str] = {}
for i, line in enumerate(file.readlines()):
line = line.replace("\n", "")
match = re.fullmatch(r". = [^*?<>|]+", line)
if match:
key, value = line.split(" = ")
keys[key] = path.normpath(root_directory + "/" + value)
elif not line[0] == "#":
warning(f"In config file '{config_file}': Invalid line ({i+1}): '{line}'")
self.validate_dirs(keys)
return keys
def create_config(self):
keys: dict[str, str] = {}
print(
f"""
=================================================================================================== ===================================================================================================
Creating a new config Creating a new config
Please enter at least one key and one directory. You can now map keys to directories.
The key must be one single letter, a single digit number or some other keyboard key like .-#+&/ ... The key must be one single letter, a single digit number or some other keyboard key like .-#+&/ ...
The key can not be 'q', 's', 'o' or 'u'. The key can not be one of {' '.join(settings_map.keys())}.
The directory must be a valid path to a directory, but is does not have to exist. The directory must be a valid path to a directory, but is does not have to exist.
You can use an absolute path (starting with '/', not '~') or a relative path (from here). You can use an absolute path (starting with '/', not '~') or a relative path (from here).
=================================================================================================== ===================================================================================================
""" """
) )
done = False while True:
while not done: # ask for key
key = input("Please enter a key or 'q' when you are done: ")
if (len(key) != 1):
user_error(f"Invalid key: '{key}' has a length other than 1")
continue
# if done
elif key == 'q':
if len(keys) == 0:
warning(f"No keys were mapped - exiting")
exit(0)
save = input(f"\nDo you want to save the config to {self._config_dir}/<name>.conf?\nType a name to save the config or type 'q' to not save the config: ")
if save != 'q':
self.write_config(save + ".conf", keys)
break
elif key in settings_map.keys():
user_error(f"Invalid key: '{key}' is reserved and can not be mapped")
continue
# ask for key # ask for directory
key = input("Please enter a key or 'q' when you are done: ") directory = input("Please enter the directory/path: ")
if (len(key) != 1): # match = re.match(r"/?([a-z-A-ZöÖäÄüÜ0-9/: _\-]+/)*[a-z-A-ZöÖäÄüÜ0-9/: _\-]+/?", directory)
print("Invalid key: " + key) INVALID_PATH_CHARS = r":*?<>|"
continue if any(c in INVALID_PATH_CHARS for c in directory):
# if done user_error(f"Invalid directory path: '{directory}' contains at least one if '{INVALID_PATH_CHARS}'")
elif key == 'q': continue
save = input("\nDo you want to save the config to ~/.config/imgsort/<name>.conf?\nType a name to save the config or type 'q' to not save the config: ") keys[key] = directory
if not save == 'q': print(f"Added: {key}: '{directory}'\n")
config_path = path.expanduser("~") + "/.config/imgsort"
if not path.isdir(config_path):
mkdir(config_path)
write_config(path.normpath(config_path + "/" + save + ".conf"), keys) self.validate_dirs(keys)
done = True
continue
# ask for directory return keys
directory = input("Please enter the directory path: ")
match = re.match(r"/?([a-z-A-ZöÖäÄüÜ0-9/: _\-]+/)*[a-z-A-ZöÖäÄüÜ0-9/: _\-]+/?", directory)
if not match:
print("Invalid directory path: " + directory)
continue
keys[key] = directory
print(f"Added: ({key}: {directory})\n")
return keys
def select_config(): def validate_dirs(self, keys):
""" """
Returns to path to an existing config or False if a new config should be created Create the directories that dont exist.
""" """
# get configs missing = []
config_path = path.expanduser("~") + "/.config/imgsort" for d in keys.values():
if not path.isdir(config_path) or len(listdir(config_path)) == 0: if not path.isdir(d):
return False missing.append(d)
if len(missing) == 0: return
print(f"The following directories do not exist:")
for d in missing: print(f"\t{d}")
decision = input(f"Create the ({len(missing)}) missing directories? y/*: ")
if (decision == "y"):
for d in missing:
create_dir(d)
else:
error("Exiting - can not use non-existing directories.")
configs = {}
i = 1
for file in listdir(config_path):
if not re.match(r"[a-zA-ZöÖäÄüÜ0-9_\- ]+\.conf", file): continue
configs[str(i)] = file
i += 1
# print configs
print("0: Create new config")
for n, conf in configs.items():
print(f"{n}: {conf}")
choice = input("Please select a config: ")
if choice == "0": return None
elif choice in configs:
return path.normpath(config_path + "/" + configs[choice])
else:
print("Invalid choice - creating new config")
return None

32
imgsort/globals.py Normal file
View File

@ -0,0 +1,32 @@
version = "1.2"
settings_map = {
"q": "quit",
"s": "skip",
"u": "undo",
"o": "open"
}
from os import makedirs
def error(*args, exitcode=1, **kwargs):
print("\033[31mError: \033[0m", *args, **kwargs)
exit(exitcode)
def user_error(*args, exitcode=1, **kwargs):
print("\033[31mError: \033[0m", *args, **kwargs)
exit(exitcode)
def warning(*args, **kwargs):
print("\033[33mWarning: \033[0m", *args, **kwargs)
def info(*args, **kwargs):
print("\033[34mInfo: \033[0m", *args, **kwargs)
def create_dir(d):
try:
makedirs(d)
except PermissionError as e:
error(f"Could not create '{d}': PermissionError: {e}")
except Exception as e:
error(f"Could not create '{d}': {e}")

View File

@ -13,16 +13,10 @@ if __name__ == "__main__": # make relative imports work as described here: http
filepath = path.realpath(path.abspath(__file__)) filepath = path.realpath(path.abspath(__file__))
sys.path.insert(0, path.dirname(path.dirname(filepath))) sys.path.insert(0, path.dirname(path.dirname(filepath)))
from .configs import read_config, select_config from .configs import ConfigManager
from .ueberzug import UeberzugLayer from .ueberzug import UeberzugLayer
from .globals import version, settings_map
from .globals import warning, error, user_error, info, create_dir
settings = {
"q": "quit",
"s": "skip",
"u": "undo",
"o": "open"
}
# Size settings # Size settings
FOOTER_LEFT = 0 FOOTER_LEFT = 0
@ -35,8 +29,6 @@ CURSOR_Y = 2
KEYS_BEGIN = 5 KEYS_BEGIN = 5
version = "1.2"
class Sorter: class Sorter:
def __init__(self, wdir, config): def __init__(self, wdir, config):
self.wd = wdir self.wd = wdir
@ -48,9 +40,7 @@ class Sorter:
self.keys = config self.keys = config
self.settings = settings self.settings = settings_map
self.validate_dirs()
# info about last action # info about last action
self.last_dir = "" self.last_dir = ""
@ -77,21 +67,6 @@ class Sorter:
def validate_dirs(self):
"""
Create the directories that dont exist.
"""
for d in self.keys.values():
if not path.isdir(d):
print(f"Directory '{d}' does not exist.")
decision = input(f"Create directory '{path.abspath(d)}'? y/n: ")
if (decision == "y"):
makedirs(d)
else:
print("Exiting - can not use non-existing directory.")
exit(1)
def get_images(self): def get_images(self):
""" """
Put all image-paths from wd in images dictionary. Put all image-paths from wd in images dictionary.
@ -133,7 +108,7 @@ class Sorter:
self.image_iter += 1 self.image_iter += 1
self.message = "Skipped image" self.message = "Skipped image"
continue continue
elif settings[self.pressed_key] == "undo": elif settings_map[self.pressed_key] == "undo":
if self.image_iter > 0: if self.image_iter > 0:
self.image_iter -= 1 # using last image self.image_iter -= 1 # using last image
rename(self.images_new[self.image_iter], self.images[self.image_iter]) rename(self.images_new[self.image_iter], self.images[self.image_iter])
@ -143,7 +118,7 @@ class Sorter:
else: else:
self.message = "Nothing to undo!" self.message = "Nothing to undo!"
continue continue
elif settings[self.pressed_key] == "open": elif settings_map[self.pressed_key] == "open":
try: try:
subprocess.run(['xdg-open', self.image], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) subprocess.run(['xdg-open', self.image], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
self.message = "Opening with xdg-open" self.message = "Opening with xdg-open"
@ -263,7 +238,7 @@ Image Sorter
if 'IMGSOSRT_CONFIG_DIR' in os.environ: config_dir = os.environ['IMGSORT_CONFIG_DIR'] if 'IMGSOSRT_CONFIG_DIR' in os.environ: config_dir = os.environ['IMGSORT_CONFIG_DIR']
parser = argparse.ArgumentParser("imgsort") parser = argparse.ArgumentParser("imgsort")
parser.add_argument("-c", "--config", action="store", help="name of the config file in ($IMGSORT_CONFIG_DIR > $XDG_CONFIG_HOME/imgsort > ~/.config/imgsort)") parser.add_argument("-c", "--config", action="store", help="name of the config file in ($IMGSORT_CONFIG_DIR > $XDG_CONFIG_HOME/imgsort > ~/.config/imgsort)", default=None)
parser.add_argument("-i", "--sort-dir", action="store", help="the directory where the folders from the config will be created") parser.add_argument("-i", "--sort-dir", action="store", help="the directory where the folders from the config will be created")
args = parser.parse_args() args = parser.parse_args()
@ -274,23 +249,18 @@ Image Sorter
else: else:
args.sort_dir = getcwd() args.sort_dir = getcwd()
# configuration confman = ConfigManager(config_dir)
if args.config:
config_path = args.config_file
# search locally
if not path.isfile(config_path):
config_path = path.join(config_dir, args.config)
if not path.isfile(config_path):
parser.error(f"invalid configuration path/name:'{config_path}'/'{args.config}'")
else:
args.config = select_config()
config = read_config(args.config, root_directory=args.sort_dir) # configuration
if not config: if not args.config:
print("Error reading the config:") args.config = confman.present_config_selection()
print(" Config Name:", args.config) # if create config was selected
print(" Config:", config) if args.config is False:
exit(1) config = confman.create_config()
else:
if type(args.config) != str:
error(f"Could not determine condig file to load ('{args.config}' is of type '{type(args.config)}' not ")
config = confman.read_config(args.config, args.sort_dir)
sorter = Sorter(wd, config) sorter = Sorter(wd, config)
sorter.get_images() sorter.get_images()