Compare commits

..

No commits in common. "b1d3d7675513540ca2576a62a64aedd7ac997e7a" and "89f6eadc30bd23b4df0006e0588e6d30adea72c9" have entirely different histories.

3 changed files with 46 additions and 104 deletions

View File

@ -1,8 +1,6 @@
# imgsort - Image Sorter
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.
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.
## Usage
1. Navigate to the folder containing the images and run "imgsort".
@ -22,31 +20,16 @@ imgsort
## Installation
Clone this repository and install it using python-pip.
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**:
pip should also install https://github.com/seebye/ueberzug, which lets you view images in a terminal.
```shell
cd ~/Downloads
git clone https://github.com/MatthiasQuintern/imgsort.git
cd imgsort
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 .
```
You can also install it system-wide using `sudo 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,9 +2,8 @@ from os import path, getcwd, listdir, mkdir, makedirs, rename
import re
def read_config(filepath):
if not path.isfile(filepath):
raise FileNotFoundError(f"read_config: Invalid filepath {filepath}")
if not path.isfile(filepath): return False
file = open(filepath, 'r')
keys = {}
for line in file.readlines():
@ -27,12 +26,13 @@ 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', '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 '~'!
"""
)
@ -77,7 +77,7 @@ def select_config():
config_path = path.expanduser("~") + "/.config/imgsort"
if not path.isdir(config_path) or len(listdir(config_path)) == 0:
return False
configs = {}
i = 1
@ -93,9 +93,9 @@ def select_config():
print(f"{n}: {conf}")
choice = input("Please select a config: ")
if choice == "0": return None
if choice == "0": return False
elif choice in configs:
return path.normpath(config_path + "/" + configs[choice])
else:
print("Invalid choice - creating new config")
return None
return False

View File

@ -3,25 +3,16 @@
import curses as c
import ueberzug.lib.v0 as uz
<<<<<<< HEAD
from imgsort.configs import read_config, write_config, select_config, create_config
from os import path, getcwd, listdir, mkdir, makedirs, rename
import subprocess
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
from sys import argv
settings = {
"q": "quit",
"s": "skip",
"u": "undo",
"o": "open"
}
# Size settings
@ -35,8 +26,6 @@ CURSOR_Y = 2
KEYS_BEGIN = 5
version = "1.2-legacy"
class Sorter:
def __init__(self, wdir, canvas, config):
self.wd = wdir
@ -77,6 +66,9 @@ 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.
@ -125,7 +117,7 @@ class Sorter:
self.pressed_key = self.window.getkey() # wait until user presses something
# check for quit, skip, undo or open
# check for quit, skip or undo
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")
@ -143,13 +135,6 @@ 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:
@ -158,26 +143,24 @@ 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 '{self.keys[self.pressed_key]}'."
self.message = f"ERROR: Failed to move '{self.image}' to '{keys[self.pressed_key]}'."
self.image_iter += 1
self.quit("All done!")
def print_window(self):
"""
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
version_str = f"imgsort {version}"
x = self.win_x - len(version_str) - 1
self.window.addstr(self.win_y - 1, x, version_str)
x = self.win_x - len(self.version) - 1
self.window.addstr(self.win_y - 1, x, self.version)
# wd
wdstring = f"Sorting {self.wd} - {len(self.images)} files - {len(self.images) - self.image_iter} remaining."
@ -226,74 +209,50 @@ class Sorter:
i += 1
self.window.move(CURSOR_Y, CURSOR_X)
def move_file(self, file, dest):
# if not path.isdir(dest):
# makedirs(dest)
if not path.isfile(file): return False
if not path.isdir(dest): return False
new_path = path.normpath(dest + '/' + path.split(file)[1])
rename(file, new_path)
return new_path
def quit(self, message = ""):
print(message)
print(f"Quitting imgsort {version}")
exit(0)
def __del__(self):
def quit(self, message = ""):
self.window.clear()
self.window.refresh()
c.endwin()
print(message)
print("Quitting " + self.version)
exit(0)
def main():
# set working directory
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']
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)
print("""
===================================================================================================
Image Sorter
===================================================================================================
""")
if len(argv) > 1:
wd = path.abspath(argv[1])
else:
wd = getcwd()
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)
config_name = select_config()
if type(config_name) == str:
config = read_config(config_name)
else:
config = create_config()
if not config:
print("Error reading the config:")
print(" Config Name:", config_path)
print(" Config Name:", config_name)
print(" Config:", config)
exit(1)