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
This is a python program 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.
This is a python program for Linux that lets you easily sort images from one directory into other directories.
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
1. Navigate to the folder containing the images and run "imgsort".
@ -20,16 +22,31 @@ imgsort
## Installation
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
cd ~/Downloads
git clone https://github.com/MatthiasQuintern/imgsort.git
cd imgsort
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
<<<<<<< HEAD
=======
### 1.2
- Support ueberzugpp
- Added option to open file with `xdg-open`
- Use pyproject.toml for installation
>>>>>>> d511c4b (add xdg-open)
### 1.1
- Terminal does not break anymore when program exits
- Todo-Images are now sorted by filename

View File

@ -2,7 +2,8 @@ from os import path, getcwd, listdir, mkdir, makedirs, rename
import re
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')
keys = {}
@ -26,13 +27,12 @@ def create_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' or 'u'.
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 '/') or a relative path (from here).
Do not use '~'!
You can use an absolute path (starting with '/', not '~') or a relative path (from here).
===================================================================================================
"""
)
@ -93,9 +93,9 @@ def select_config():
print(f"{n}: {conf}")
choice = input("Please select a config: ")
if choice == "0": return False
if choice == "0": return None
elif choice in configs:
return path.normpath(config_path + "/" + configs[choice])
else:
print("Invalid choice - creating new config")
return False
return None

View File

@ -3,16 +3,25 @@
import curses as c
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
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 = {
"q": "quit",
"s": "skip",
"u": "undo",
"o": "open"
}
# Size settings
@ -26,6 +35,8 @@ CURSOR_Y = 2
KEYS_BEGIN = 5
version = "1.2-legacy"
class Sorter:
def __init__(self, wdir, canvas, config):
self.wd = wdir
@ -66,9 +77,6 @@ class Sorter:
self.placement.width = self.win_x - SIDEBAR_WIDTH - 1
self.placement.height = self.win_y - FOOTER_HEIGHT - 2
# version
self.version = "Image Sorter 1.1"
def validate_dirs(self):
"""
Create the directories that dont exist.
@ -117,7 +125,7 @@ class Sorter:
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.settings[self.pressed_key] == "quit":
self.quit(f"Key '{self.pressed_key}' pressed. Canceling image sorting")
@ -135,6 +143,13 @@ class Sorter:
else:
self.message = "Nothing to undo!"
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
elif self.pressed_key in self.keys:
@ -143,7 +158,7 @@ class Sorter:
self.images_new[self.image_iter] = new_filepath
self.message = f"Moved image to {self.keys[self.pressed_key]}"
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.quit("All done!")
@ -153,14 +168,16 @@ class Sorter:
Draw lines and text
"""
self.window.erase()
self.win_y, self.win_x = self.window.getmaxyx()
# lines
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)
# version
x = self.win_x - len(self.version) - 1
self.window.addstr(self.win_y - 1, x, self.version)
version_str = f"imgsort {version}"
x = self.win_x - len(version_str) - 1
self.window.addstr(self.win_y - 1, x, version_str)
# wd
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
def quit(self, message = ""):
print(message)
print(f"Quitting imgsort {version}")
exit(0)
def __del__(self):
self.window.clear()
self.window.refresh()
c.endwin()
print(message)
print("Quitting " + self.version)
exit(0)
def main():
# set working directory
print("""
===================================================================================================
Image Sorter
===================================================================================================
""")
if len(argv) > 1:
wd = path.abspath(argv[1])
else:
wd = getcwd();
print(f"""===================================================================================================
imgsort {version} - Image Sorter
===================================================================================================""")
# set directories
config_dir = os.path.join(os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), "regina")
# check if environment variables are set and use them if they are
if 'IMGSOSRT_CONFIG_DIR' in os.environ: config_dir = os.environ['IMGSORT_CONFIG_DIR']
config_name = select_config()
if type(config_name) == str:
config = read_config(config_name)
parser = argparse.ArgumentParser(prog="imgsort")
parser.add_argument("--config", "-c", action="store", help="name or path of config file", metavar="config file")
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:
config = create_config()
if not config:
print("Error reading the config:")
print(" Config Name:", config_name)
print(" Config Name:", config_path)
print(" Config:", config)
exit(1)