|
2 | 2 |
|
3 | 3 | import argparse
|
4 | 4 | import os
|
| 5 | +import re |
5 | 6 | import shutil
|
6 | 7 | import datetime
|
7 | 8 | import logging
|
8 | 9 | import filecmp
|
9 | 10 |
|
10 | 11 |
|
11 |
| -def list_files(source, recursive=False, file_endings=None): |
| 12 | +def list_files(source, recursive=False, file_endings=None, exclude_pattern=None): |
12 | 13 | """
|
13 |
| - List all files in the source directory. |
| 14 | + List all files in the source directory, optionally filtering by file extension and excluding files by regex. |
14 | 15 |
|
15 | 16 | Parameters:
|
16 | 17 | source (str): The source directory path.
|
17 | 18 | recursive (bool): If True, list files recursively. Default is False.
|
18 |
| - file_endings (list): List of file endings/extensions to include. Default is None. |
| 19 | + file_endings (list): List of file extensions to include (e.g., ['.jpg', '.png']). Default is None. |
| 20 | + exclude_pattern (str): A regex pattern to exclude matching files. Default is None. |
19 | 21 |
|
20 | 22 | Returns:
|
21 |
| - list: A list of file paths. |
| 23 | + list: A list of file paths that meet the criteria. |
22 | 24 | """
|
23 | 25 | file_list = []
|
| 26 | + pattern = re.compile(exclude_pattern) if exclude_pattern else None |
| 27 | + |
24 | 28 | if recursive:
|
25 | 29 | for root, dirs, files in os.walk(source):
|
26 | 30 | for file in files:
|
27 |
| - if not file_endings or file.lower().endswith(tuple(file_endings)): |
| 31 | + if (not file_endings or file.lower().endswith(tuple(file_endings))) and (not pattern or not pattern.search(file)): |
28 | 32 | file_list.append(os.path.join(root, file))
|
29 | 33 | else:
|
30 | 34 | with os.scandir(source) as entries:
|
31 | 35 | for entry in entries:
|
32 |
| - if entry.is_file() and ( |
33 |
| - not file_endings or entry.name.lower().endswith(tuple(file_endings)) |
34 |
| - ): |
| 36 | + if entry.is_file() and (not file_endings or entry.name.lower().endswith(tuple(file_endings))) and (not pattern or not pattern.search(entry.name)): |
35 | 37 | file_list.append(entry.path)
|
36 |
| - logging.debug(f"Listed {len(file_list)} files from {source}") |
| 38 | + |
| 39 | + logging.debug(f"Listed {len(file_list)} files from {source}, excluding pattern: {exclude_pattern}") |
37 | 40 | return file_list
|
38 | 41 |
|
39 | 42 |
|
@@ -95,45 +98,24 @@ def configure_logging(verbose):
|
95 | 98 |
|
96 | 99 | def parse_arguments():
|
97 | 100 | """
|
98 |
| - Parse command line arguments. |
| 101 | + Parse command-line arguments for the photo organizer. |
99 | 102 |
|
100 | 103 | Returns:
|
101 |
| - Namespace: Parsed command line arguments. |
| 104 | + argparse.Namespace: Parsed command-line arguments. |
102 | 105 | """
|
103 | 106 | parser = argparse.ArgumentParser(
|
104 | 107 | description="Sort photos from source to target directory."
|
105 | 108 | )
|
106 | 109 | parser.add_argument("source", type=str, help="The source directory")
|
107 | 110 | parser.add_argument("target", type=str, help="The target directory")
|
108 |
| - parser.add_argument( |
109 |
| - "-r", "--recursive", action="store_true", help="Sort photos recursively" |
110 |
| - ) |
111 |
| - parser.add_argument( |
112 |
| - "-d", |
113 |
| - "--daily", |
114 |
| - action="store_true", |
115 |
| - default=False, |
116 |
| - help="Folder structure with daily folders", |
117 |
| - ) |
118 |
| - parser.add_argument( |
119 |
| - "-e", |
120 |
| - "--endings", |
121 |
| - type=str, |
122 |
| - nargs="*", |
123 |
| - help="File endings/extensions to copy (e.g., .jpg .png)", |
124 |
| - ) |
125 |
| - parser.add_argument( |
126 |
| - "-v", "--verbose", action="store_true", help="Enable verbose logging" |
127 |
| - ) |
128 |
| - parser.add_argument( |
129 |
| - "-c", "--copy", action="store_true", help="Copy files instead of moving them" |
130 |
| - ) |
131 |
| - parser.add_argument( |
132 |
| - "--no-year", |
133 |
| - action="store_true", |
134 |
| - help="Do not place month folders inside a year folder", |
135 |
| - ) |
136 |
| - |
| 111 | + parser.add_argument("-r", "--recursive", action="store_true", help="Sort photos recursively") |
| 112 | + parser.add_argument("-d", "--daily", action="store_true", default=False, help="Folder structure with daily folders") |
| 113 | + parser.add_argument("-e", "--endings", type=str, nargs="*", help="File endings/extensions to copy (e.g., .jpg .png)") |
| 114 | + parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose logging") |
| 115 | + parser.add_argument("-c", "--copy", action="store_true", help="Copy files instead of moving them") |
| 116 | + parser.add_argument("--no-year", action="store_true", help="Do not place month folders inside a year folder") |
| 117 | + parser.add_argument("--exclude", type=str, help="Regex pattern to exclude files from processing") |
| 118 | + |
137 | 119 | return parser.parse_args()
|
138 | 120 |
|
139 | 121 |
|
@@ -192,24 +174,27 @@ def organize_files(args, files):
|
192 | 174 |
|
193 | 175 |
|
194 | 176 | def main():
|
| 177 | + """ |
| 178 | + Main entry point for the photo organizer script. Parses arguments, configures logging, and processes files. |
| 179 | + """ |
195 | 180 | # Parse the arguments
|
196 | 181 | args = parse_arguments()
|
197 |
| - |
| 182 | + |
198 | 183 | # Configure logging
|
199 | 184 | configure_logging(args.verbose)
|
200 |
| - |
| 185 | + |
201 | 186 | logging.info("Starting file sorting process")
|
202 |
| - |
| 187 | + |
203 | 188 | # Ensure the source directory exists
|
204 | 189 | if not os.path.exists(args.source):
|
205 | 190 | logging.error(f"Source directory '{args.source}' does not exist.")
|
206 | 191 | return
|
207 |
| - |
| 192 | + |
208 | 193 | # Ensure the target directory exists
|
209 | 194 | ensure_directory_exists(args.target)
|
210 |
| - |
211 |
| - # List all files in the source directory |
212 |
| - files = list_files(args.source, args.recursive, args.endings) |
213 |
| - |
| 195 | + |
| 196 | + # List all files in the source directory, applying the exclusion filter |
| 197 | + files = list_files(args.source, args.recursive, args.endings, args.exclude) |
| 198 | + |
214 | 199 | # Organize files by moving or copying them to the target directory
|
215 | 200 | organize_files(args, files)
|
0 commit comments