Compare commits

...

4 Commits

Author SHA1 Message Date
Matthias@Dell
b1d3d76755 add xdg-open 2023-10-23 00:36:10 +02:00
Matthias@Dell
abdf937968 fix window resize issue 2023-10-23 00:18:04 +02:00
Matthias@Dell
17d9d1df43 use argparse, add xdg-open 2023-10-23 00:17:58 +02:00
Matthias@Dell
be6dc9f224 improve error handling 2023-10-23 00:06:56 +02:00
3 changed files with 105 additions and 47 deletions

View File

@ -1,6 +1,8 @@
# imgsort - Image Sorter # imgsort - Image Sorter
This is a python program that lets you easily sort images from one directory into other directories. This is a python program for Linux that lets you easily sort images from one directory into other directories.
For example, you could go through your phone's camera folder and sort the images into different folders, like *Family*, *Landscapes*, *Friends* etc. It lets you go through a folder of images and simply move them using a single key press, which you define at program startup.
This is very useful when you want to sort your phone's camera folder or messenger media folders.
For example, you could quickly go through your WhatsApp media (after copying it to your pc) and sort the images into different directories like *Selfies*, *Landscapes*, *Friends* etc.
## Usage ## Usage
1. Navigate to the folder containing the images and run "imgsort". 1. Navigate to the folder containing the images and run "imgsort".
@ -20,16 +22,31 @@ imgsort
## Installation ## Installation
Clone this repository and install it using python-pip. Clone this repository and install it using python-pip.
pip should also install https://github.com/seebye/ueberzug, which lets you view images in a terminal. This project depends on ueberzug to display the images in the terminal.
The original ueberzug is no longer maintained, but there is [a continuation](https://github.com/ueber-devel/ueberzug/) as well as a [new C++ alternative](https://github.com/jstkdng/ueberzugpp) available.
For the version supporting the original **ueberzug**:
```shell ```shell
cd ~/Downloads
git clone https://github.com/MatthiasQuintern/imgsort.git git clone https://github.com/MatthiasQuintern/imgsort.git
cd imgsort cd imgsort
python3 -m pip install . python3 -m pip install .
``` ```
You can also install it system-wide using `sudo python3 -m pip install.` For the version supporting the new **ueberzug++**:
```shell
git clone --branch ueberzugpp https://github.com/MatthiasQuintern/imgsort.git
cd imgsort
python3 -m pip install .
```
## Changelog ## Changelog
<<<<<<< HEAD
=======
### 1.2
- Support ueberzugpp
- Added option to open file with `xdg-open`
- Use pyproject.toml for installation
>>>>>>> d511c4b (add xdg-open)
### 1.1 ### 1.1
- Terminal does not break anymore when program exits - Terminal does not break anymore when program exits
- Todo-Images are now sorted by filename - Todo-Images are now sorted by filename

View File

@ -2,7 +2,8 @@ from os import path, getcwd, listdir, mkdir, makedirs, rename
import re import re
def read_config(filepath): def read_config(filepath):
if not path.isfile(filepath): return False if not path.isfile(filepath):
raise FileNotFoundError(f"read_config: Invalid filepath {filepath}")
file = open(filepath, 'r') file = open(filepath, 'r')
keys = {} keys = {}
@ -26,13 +27,12 @@ def create_config():
""" """
=================================================================================================== ===================================================================================================
Creating a new config Creating a new config
Please enter at least one key and one directory.
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 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).
=================================================================================================== ===================================================================================================
Please enter at least one key and one directory.
The key must be one single letter, a single digit number or some other keyboard key like .-#+&/ ...
The key can not be 'q', 's' or 'u'.
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 '/') or a relative path (from here).
Do not use '~'!
""" """
) )
@ -93,9 +93,9 @@ def select_config():
print(f"{n}: {conf}") print(f"{n}: {conf}")
choice = input("Please select a config: ") choice = input("Please select a config: ")
if choice == "0": return False if choice == "0": return None
elif choice in configs: elif choice in configs:
return path.normpath(config_path + "/" + configs[choice]) return path.normpath(config_path + "/" + configs[choice])
else: else:
print("Invalid choice - creating new config") print("Invalid choice - creating new config")
return False return None

View File

@ -3,16 +3,25 @@
import curses as c import curses as c
import ueberzug.lib.v0 as uz import ueberzug.lib.v0 as uz
from imgsort.configs import read_config, write_config, select_config, create_config <<<<<<< HEAD
from os import path, getcwd, listdir, mkdir, makedirs, rename from os import path, getcwd, listdir, mkdir, makedirs, rename
import subprocess
from sys import argv if __name__ == "__main__": # make relative imports work as described here: https://peps.python.org/pep-0366/#proposed-change
if __package__ is None:
__package__ = "imgsort"
import sys
filepath = path.realpath(path.abspath(__file__))
sys.path.insert(0, path.dirname(path.dirname(filepath)))
from .configs import read_config, write_config, select_config, create_config
settings = { settings = {
"q": "quit", "q": "quit",
"s": "skip", "s": "skip",
"u": "undo", "u": "undo",
"o": "open"
} }
# Size settings # Size settings
@ -26,6 +35,8 @@ CURSOR_Y = 2
KEYS_BEGIN = 5 KEYS_BEGIN = 5
version = "1.2-legacy"
class Sorter: class Sorter:
def __init__(self, wdir, canvas, config): def __init__(self, wdir, canvas, config):
self.wd = wdir self.wd = wdir
@ -66,9 +77,6 @@ class Sorter:
self.placement.width = self.win_x - SIDEBAR_WIDTH - 1 self.placement.width = self.win_x - SIDEBAR_WIDTH - 1
self.placement.height = self.win_y - FOOTER_HEIGHT - 2 self.placement.height = self.win_y - FOOTER_HEIGHT - 2
# version
self.version = "Image Sorter 1.1"
def validate_dirs(self): def validate_dirs(self):
""" """
Create the directories that dont exist. Create the directories that dont exist.
@ -117,7 +125,7 @@ class Sorter:
self.pressed_key = self.window.getkey() # wait until user presses something self.pressed_key = self.window.getkey() # wait until user presses something
# check for quit, skip or undo # check for quit, skip, undo or open
if self.pressed_key in self.settings: if self.pressed_key in self.settings:
if self.settings[self.pressed_key] == "quit": if self.settings[self.pressed_key] == "quit":
self.quit(f"Key '{self.pressed_key}' pressed. Canceling image sorting") self.quit(f"Key '{self.pressed_key}' pressed. Canceling image sorting")
@ -135,6 +143,13 @@ class Sorter:
else: else:
self.message = "Nothing to undo!" self.message = "Nothing to undo!"
continue continue
elif settings[self.pressed_key] == "open":
try:
subprocess.run(['xdg-open', self.image], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
self.message = "Opening with xdg-open"
except Exception as e:
print(f"open: Error: {e}")
continue
# move to folder # move to folder
elif self.pressed_key in self.keys: elif self.pressed_key in self.keys:
@ -143,7 +158,7 @@ class Sorter:
self.images_new[self.image_iter] = new_filepath self.images_new[self.image_iter] = new_filepath
self.message = f"Moved image to {self.keys[self.pressed_key]}" self.message = f"Moved image to {self.keys[self.pressed_key]}"
else: else:
self.message = f"ERROR: Failed to move '{self.image}' to '{keys[self.pressed_key]}'." self.message = f"ERROR: Failed to move '{self.image}' to '{self.keys[self.pressed_key]}'."
self.image_iter += 1 self.image_iter += 1
self.quit("All done!") self.quit("All done!")
@ -153,14 +168,16 @@ class Sorter:
Draw lines and text Draw lines and text
""" """
self.window.erase() self.window.erase()
self.win_y, self.win_x = self.window.getmaxyx()
# lines # lines
self.window.hline(self.win_y - FOOTER_HEIGHT, FOOTER_LEFT, '=', self.win_x) self.window.hline(self.win_y - FOOTER_HEIGHT, FOOTER_LEFT, '=', self.win_x)
self.window.vline(0, SIDEBAR_WIDTH, '|', self.win_y - FOOTER_HEIGHT + 1) self.window.vline(0, SIDEBAR_WIDTH, '|', self.win_y - FOOTER_HEIGHT + 1)
# version # version
x = self.win_x - len(self.version) - 1 version_str = f"imgsort {version}"
self.window.addstr(self.win_y - 1, x, self.version) x = self.win_x - len(version_str) - 1
self.window.addstr(self.win_y - 1, x, version_str)
# wd # wd
wdstring = f"Sorting {self.wd} - {len(self.images)} files - {len(self.images) - self.image_iter} remaining." wdstring = f"Sorting {self.wd} - {len(self.images)} files - {len(self.images) - self.image_iter} remaining."
@ -222,37 +239,61 @@ class Sorter:
return new_path return new_path
def quit(self, message = ""): def quit(self, message = ""):
print(message)
print(f"Quitting imgsort {version}")
exit(0)
def __del__(self):
self.window.clear() self.window.clear()
self.window.refresh() self.window.refresh()
c.endwin() c.endwin()
print(message)
print("Quitting " + self.version)
exit(0)
def main(): def main():
# set working directory # set working directory
print(""" print(f"""===================================================================================================
=================================================================================================== imgsort {version} - Image Sorter
Image Sorter ===================================================================================================""")
=================================================================================================== # set directories
""") config_dir = os.path.join(os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), "regina")
if len(argv) > 1: # check if environment variables are set and use them if they are
wd = path.abspath(argv[1]) if 'IMGSOSRT_CONFIG_DIR' in os.environ: config_dir = os.environ['IMGSORT_CONFIG_DIR']
else:
wd = getcwd();
config_name = select_config() parser = argparse.ArgumentParser(prog="imgsort")
if type(config_name) == str: parser.add_argument("--config", "-c", action="store", help="name or path of config file", metavar="config file")
config = read_config(config_name) parser.add_argument("--dir", "-d", action="store", help="working directory", metavar="working directory")
args = parser.parse_args()
# working directory
if args.dir:
if not path.isdir(args.dir):
parser.error(f"invalid working directory: {args.wdir}")
wd = path.abspath(args.dir)
os.chdir(wd)
else:
wd = getcwd()
# configuration
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:
config_path = select_config()
print(config_path)
if config_path is not None:
config = read_config(config_path)
else: else:
config = create_config() config = create_config()
if not config: if not config:
print("Error reading the config:") print("Error reading the config:")
print(" Config Name:", config_name) print(" Config Name:", config_path)
print(" Config:", config) print(" Config:", config)
exit(1) exit(1)