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 # imgsort - Image Sorter
This is a python program for Linux that lets you easily sort images from one directory into other directories. This is a python program 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. 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 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".
@ -22,31 +20,16 @@ imgsort
## Installation ## Installation
Clone this repository and install it using python-pip. Clone this repository and install it using python-pip.
This project depends on ueberzug to display the images in the terminal. pip should also install https://github.com/seebye/ueberzug, which lets you view images in a 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 .
``` ```
For the version supporting the new **ueberzug++**: You can also install it system-wide using `sudo python3 -m pip install.`
```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,9 +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): if not path.isfile(filepath): return False
raise FileNotFoundError(f"read_config: Invalid filepath {filepath}")
file = open(filepath, 'r') file = open(filepath, 'r')
keys = {} keys = {}
for line in file.readlines(): for line in file.readlines():
@ -27,12 +26,13 @@ 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 '~'!
""" """
) )
@ -77,7 +77,7 @@ def select_config():
config_path = path.expanduser("~") + "/.config/imgsort" config_path = path.expanduser("~") + "/.config/imgsort"
if not path.isdir(config_path) or len(listdir(config_path)) == 0: if not path.isdir(config_path) or len(listdir(config_path)) == 0:
return False return False
configs = {} configs = {}
i = 1 i = 1
@ -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 None if choice == "0": return False
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 None return False

View File

@ -3,25 +3,16 @@
import curses as c import curses as c
import ueberzug.lib.v0 as uz 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 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 from sys import argv
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
@ -35,8 +26,6 @@ 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
@ -77,6 +66,9 @@ 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.
@ -125,7 +117,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, undo or open # check for quit, skip or undo
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")
@ -143,13 +135,6 @@ 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:
@ -158,26 +143,24 @@ 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 '{self.keys[self.pressed_key]}'." self.message = f"ERROR: Failed to move '{self.image}' to '{keys[self.pressed_key]}'."
self.image_iter += 1 self.image_iter += 1
self.quit("All done!") self.quit("All done!")
def print_window(self): def print_window(self):
""" """
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
version_str = f"imgsort {version}" x = self.win_x - len(self.version) - 1
x = self.win_x - len(version_str) - 1 self.window.addstr(self.win_y - 1, x, self.version)
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."
@ -226,74 +209,50 @@ class Sorter:
i += 1 i += 1
self.window.move(CURSOR_Y, CURSOR_X) self.window.move(CURSOR_Y, CURSOR_X)
def move_file(self, file, dest): def move_file(self, file, dest):
# if not path.isdir(dest): # if not path.isdir(dest):
# makedirs(dest) # makedirs(dest)
if not path.isfile(file): return False if not path.isfile(file): return False
if not path.isdir(dest): return False if not path.isdir(dest): return False
new_path = path.normpath(dest + '/' + path.split(file)[1]) new_path = path.normpath(dest + '/' + path.split(file)[1])
rename(file, new_path) rename(file, new_path)
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(f"""=================================================================================================== print("""
imgsort {version} - Image Sorter ===================================================================================================
===================================================================================================""") 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 len(argv) > 1:
if 'IMGSOSRT_CONFIG_DIR' in os.environ: config_dir = os.environ['IMGSORT_CONFIG_DIR'] wd = path.abspath(argv[1])
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: else:
wd = getcwd() wd = getcwd();
# configuration config_name = select_config()
if args.config: if type(config_name) == str:
config_path = args.config_file config = read_config(config_name)
# 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_path) print(" Config Name:", config_name)
print(" Config:", config) print(" Config:", config)
exit(1) exit(1)