Skip to content

Commit 3af8571

Browse files
committed
Adding sort feature
1 parent 4a25ecc commit 3af8571

File tree

5 files changed

+125
-115
lines changed

5 files changed

+125
-115
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## [v2.2] - 2023-10-22
4+
* Adding sort feature
5+
36
## [v2.1] - 2023-03-12
47

58
### Features

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ It also deals with duplicates, so you don't have to.
1414

1515
## Disclaimer
1616

17-
Per design this command line interface tool deletes only duplicate files to potentially avoid any risk of losing data.
17+
Per design, this command line interface tool **only deletes duplicate files** to avoid any risk of losing data.
1818

1919

2020
## Features
2121

22-
* Move, Copy or delete duplicates operations
22+
* Move, copy, sort or delete duplicates operations
2323
* User-friendly console output
2424
* Obscure creation date detection
2525
* Custom folder structure definition
@@ -70,7 +70,7 @@ We are going to start creating a new directory `media`:
7070
7171
mkdir media
7272
73-
We are going to create a new catalogue named `local_photos` which is going to get store on the `media` directory.
73+
We are going to create a new catalogue named `local_media` which is going to get store on the newly created `media` directory.
7474
We specify our format pattern so photos are group by `year` and a subgroup of `month`:
7575
7676
cataloguer create-catalogue local_media ./media --format-pattern %Y/%m/{file}
@@ -107,11 +107,11 @@ To get a summary of our catalogue we run:
107107
* `%B`: Month name
108108
* other format codes specified [here](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes)
109109
* `/` Specifies a new folder
110-
* `{media_type}`: File type (`image`, `video`)
111-
* `{media_format}`: File format (`jpeg`, `tiff`, `png`, `gif`, `mp4` ...)
110+
* `{media_type}`: Detected file type (`image`, `video`)
111+
* `{media_format}`: Detected file format (`jpeg`, `tiff`, `png`, `gif`, `mp4` ...)
112112
* `{file}` Original filename (`photo.jpg`)
113-
* `{file_extension}` filename extension (`photo`)
114-
* `{file_name}` filename without the extension (`jpg`)
113+
* `{file_name}` Filename without the extension (`photo`)
114+
* `{file_extension}` Filename extension (`jpg`)
115115
* `{relative_path}` Relative path to the source directory
116116
117117
@@ -124,14 +124,14 @@ Variables can also be specified as environment variables, using a `CATALOGUER_`
124124
125125
export CATALOGUER_FORMAT_PATTERN=%Y/%m/{file}
126126
127-
`CATALOGUER_STORAGE_LOCATION` Accepts any path. That location will store cataloguer metadata.
127+
`CATALOGUER_STORAGE_LOCATION` Accepts any path. That location will store metadata.
128128
By default, it will create a `.catalogues` in the user's home directory.
129129
130130
#### Examples:
131131
132132
Pattern to fix file extensions keeping the folder structure:
133133
134-
cataloguer --format-pattern {relative_path}/{basename}.{media_format} move ./input ./output
134+
cataloguer --format-pattern {relative_path}/{file_name}.{media_format} sort ./target/
135135
136136
137137
## TODO list

cataloguer/cli.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Context(BaseModel):
3232

3333
class Operation(Enum):
3434
MOVE = "move"
35+
SORT = "sort"
3536
COPY = "copy"
3637
DELETE = "delete"
3738

@@ -231,6 +232,18 @@ def move(ctx: Context, src, dst, dry_run):
231232
return operate(ctx, src, dst, operation_mode, dry_run)
232233

233234

235+
@cli.command()
236+
@click.argument("src")
237+
@click.option("--dry-run", is_flag=True)
238+
@click.pass_obj
239+
def sort(ctx: Context, src, dry_run):
240+
"""
241+
Move files. In case of duplicates will take the shortest name.
242+
"""
243+
operation_mode = Operation.SORT
244+
return operate(ctx, src, None, operation_mode, dry_run)
245+
246+
234247
@cli.command()
235248
@click.argument("src")
236249
@click.argument("dst", required=False)
@@ -346,7 +359,7 @@ def operate(ctx, src, dst, operation_mode, dry_run=False):
346359
f'Error "{dst}" cannot be a subdirectory of {src}'
347360
)
348361

349-
if operation_mode in (Operation.MOVE, Operation.COPY):
362+
if operation_mode in (Operation.MOVE, Operation.COPY, Operation.SORT):
350363
format_pattern = ctx.global_settings.format_pattern
351364
if dst_data and isinstance(dst_data, Catalogue):
352365
format_pattern = format_pattern or dst_data.format_pattern
@@ -368,7 +381,7 @@ def operate(ctx, src, dst, operation_mode, dry_run=False):
368381
duplicated_discarded_files,
369382
files_to_operate,
370383
) = extract_files(src_data)
371-
384+
372385
if operation_mode == Operation.DELETE:
373386
if dst_data:
374387
duplicate_files_across_directories = [
@@ -400,6 +413,9 @@ def operate(ctx, src, dst, operation_mode, dry_run=False):
400413
duplicated_files=duplicated_list_of_different_filenames
401414
)
402415

416+
elif operation_mode == Operation.SORT:
417+
files_to_process = files_to_operate
418+
dst_data = src_data
403419
else: # dst_data can only be Directory or Catalogue
404420
duplicate_files_across_directories = [
405421
file
@@ -477,6 +493,10 @@ def process_files(
477493
if isinstance(dst_data, Catalogue):
478494
path_format = path_format or dst_data.format_pattern
479495
unknown_format_pattern = unknown_format_pattern or dst_data.unknown_format_pattern
496+
if operation_mode.SORT and isinstance(src_data, Catalogue):
497+
path_format = path_format or src_data.format_pattern
498+
unknown_format_pattern = unknown_format_pattern or src_data.unknown_format_pattern
499+
480500

481501
tree = DirectoryTree()
482502
skipped_tree = DirectoryTree()
@@ -527,6 +547,9 @@ def process_file(file, dst_file_path, operation, dst_directory, dry_run):
527547

528548
path_available = dst_directory.is_path_available(dst_file_path)
529549
if not path_available:
550+
if operation.SORT:
551+
# TODO: check if it is same file, for now just avoid moving it
552+
return file
530553
logging.debug(f"Path {dst_file_path} not available, renaming file")
531554
dst_file_path = dst_directory.find_new_path(dst_file_path)
532555

@@ -543,6 +566,10 @@ def process_file(file, dst_file_path, operation, dst_directory, dry_run):
543566
dst_directory.add_file(file)
544567
return file
545568

569+
if operation == Operation.SORT:
570+
file.move_file(dst_file_path)
571+
return file
572+
546573
if operation == Operation.COPY:
547574
new_file = file.clone_file(dst_file_path)
548575
dst_directory.add_file(new_file)

0 commit comments

Comments
 (0)