5
5
import shutil
6
6
import datetime
7
7
import logging
8
+ import filecmp
8
9
9
10
10
11
def list_files (source , recursive = False , file_endings = None ):
@@ -79,8 +80,26 @@ def ensure_directory_exists(folder_path):
79
80
logging .debug (f"Directory already exists: { folder_path } " )
80
81
81
82
82
- def main ():
83
- # Set up the argument parser
83
+ def configure_logging (verbose ):
84
+ """
85
+ Configure logging settings.
86
+
87
+ Parameters:
88
+ verbose (bool): If True, enable verbose logging.
89
+ """
90
+ logging .basicConfig (
91
+ level = logging .DEBUG if verbose else logging .INFO ,
92
+ format = "%(asctime)s - %(levelname)s - %(message)s" ,
93
+ )
94
+
95
+
96
+ def parse_arguments ():
97
+ """
98
+ Parse command line arguments.
99
+
100
+ Returns:
101
+ Namespace: Parsed command line arguments.
102
+ """
84
103
parser = argparse .ArgumentParser (
85
104
description = "Sort photos from source to target directory."
86
105
)
@@ -115,29 +134,17 @@ def main():
115
134
help = "Do not place month folders inside a year folder" ,
116
135
)
117
136
118
- # Parse the arguments
119
- args = parser .parse_args ()
137
+ return parser .parse_args ()
120
138
121
- # Configure logging
122
- logging .basicConfig (
123
- level = logging .DEBUG if args .verbose else logging .INFO ,
124
- format = "%(asctime)s - %(levelname)s - %(message)s" ,
125
- )
126
-
127
- logging .info ("Starting file sorting process" )
128
139
129
- # Ensure the source directory exists
130
- if not os .path .exists (args .source ):
131
- logging .error (f"Source directory '{ args .source } ' does not exist." )
132
- return
133
-
134
- # Ensure the target directory exists
135
- ensure_directory_exists (args .target )
136
-
137
- # List all files in the source directory
138
- files = list_files (args .source , args .recursive , args .endings )
140
+ def organize_files (args , files ):
141
+ """
142
+ Organize files by moving or copying them to the target directory.
139
143
140
- # Move or copy files to the target directory organized by year/month (and optionally day)
144
+ Parameters:
145
+ args (Namespace): Parsed command line arguments.
146
+ files (list): List of file paths to organize.
147
+ """
141
148
for file_path in files :
142
149
year , month , day = get_creation_date (file_path )
143
150
if args .no_year :
@@ -158,11 +165,51 @@ def main():
158
165
ensure_directory_exists (target_folder )
159
166
target_path = os .path .join (target_folder , os .path .basename (file_path ))
160
167
161
- if args .copy :
162
- # Copy the file
163
- shutil .copy2 (file_path , target_path )
164
- logging .info (f"Copied '{ file_path } ' to '{ target_path } '" )
165
- else :
166
- # Move the file
167
- shutil .move (file_path , target_path )
168
- logging .info (f"Moved '{ file_path } ' to '{ target_path } '" )
168
+ if os .path .exists (target_path ):
169
+ if filecmp .cmp (file_path , target_path , shallow = False ):
170
+ logging .warning (
171
+ f"File '{ target_path } ' already exists and is identical. Skipping."
172
+ )
173
+ continue
174
+ else :
175
+ logging .error (
176
+ f"File '{ target_path } ' already exists and is different. Aborting."
177
+ )
178
+ return
179
+
180
+ try :
181
+ if args .copy :
182
+ shutil .copy2 (file_path , target_path )
183
+ logging .info (f"Copied '{ file_path } ' to '{ target_path } '" )
184
+ else :
185
+ shutil .move (file_path , target_path )
186
+ logging .info (f"Moved '{ file_path } ' to '{ target_path } '" )
187
+ except Exception as e :
188
+ logging .error (
189
+ f"Failed to { 'copy' if args .copy else 'move' } '{ file_path } ' to '{ target_path } ': { e } "
190
+ )
191
+ return
192
+
193
+
194
+ def main ():
195
+ # Parse the arguments
196
+ args = parse_arguments ()
197
+
198
+ # Configure logging
199
+ configure_logging (args .verbose )
200
+
201
+ logging .info ("Starting file sorting process" )
202
+
203
+ # Ensure the source directory exists
204
+ if not os .path .exists (args .source ):
205
+ logging .error (f"Source directory '{ args .source } ' does not exist." )
206
+ return
207
+
208
+ # Ensure the target directory exists
209
+ 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
+
214
+ # Organize files by moving or copying them to the target directory
215
+ organize_files (args , files )
0 commit comments