Compare commits
No commits in common. "b1d3d7675513540ca2576a62a64aedd7ac997e7a" and "89f6eadc30bd23b4df0006e0588e6d30adea72c9" have entirely different histories.
b1d3d76755
...
89f6eadc30
27
README.md
27
README.md
@ -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
|
||||||
|
@ -2,8 +2,7 @@ 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 = {}
|
||||||
@ -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 '~'!
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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
|
||||||
|
@ -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,7 +143,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 '{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!")
|
||||||
@ -168,16 +153,14 @@ 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
|
||||||
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."
|
||||||
@ -239,61 +222,37 @@ 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(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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user