]>
code.delx.au - monosys/blob - hacks/rename-by-date
10 def read_directories(src_directories
):
12 for src_directory
in src_directories
:
13 for root
, dirnames
, filenames
in os
.walk(src_directory
):
14 for filename
in filenames
:
15 filename
= os
.path
.join(root
, filename
)
16 result
.append(filename
)
20 def get_timestamp(filename
):
21 ext
= os
.path
.splitext(filename
.lower())[1]
24 return get_jpg_timestamp(filename
)
27 return get_mp4_timestamp(filename
)
29 raise NotImplementedError("Unsupported extension: " + ext
)
31 def get_mp4_timestamp(filename
):
32 output
= subprocess
.check_output([
33 "ffprobe", filename
, "-show_format", "-v", "quiet",
35 line
= [line
for line
in output
.split("\n") if line
.startswith("TAG:creation_time=")][0]
36 value
= line
.split("=")[1].split(".")[0]
37 return datetime
.datetime
.strptime(value
, "%Y-%m-%dT%H:%M:%S")
39 def get_jpg_timestamp(filename
):
40 output
= subprocess
.check_output([
41 "exiv2", "-pt", "-g", "Exif.Photo.DateTimeOriginal", filename
43 first_line
= output
.split("\n")[0]
44 timestamp
= " ".join(first_line
.split()[-2:])
45 return datetime
.datetime
.strptime(timestamp
, "%Y:%m:%d %H:%M:%S")
47 class FilesByDate(object):
48 def __init__(self
, dest
, src_directories
):
50 self
.src_directories
= src_directories
53 sorted_filenames
= self
.get_sorted_filenames()
55 for i
, filename
in enumerate(sorted_filenames
, 1):
56 yield filename
, self
.get_new_filename(filename
, i
)
58 def get_sorted_filenames(self
):
59 src_filenames
= read_directories(self
.src_directories
)
62 for filename
in src_filenames
:
63 timestamp
= get_timestamp(filename
)
64 ts_filenames
.append((timestamp
, filename
))
67 return [filename
for _
, filename
in sorted(ts_filenames
)]
69 def get_new_filename(self
, orig_filename
, i
):
70 prefix
= "S" + str(i
).zfill(3) + "_"
71 orig_filename
= os
.path
.basename(orig_filename
)
72 return os
.path
.join(self
.dest
, prefix
+ orig_filename
)
74 class DirectoryFanout(object):
75 def __init__(self
, dest
, src_directories
):
77 self
.src_directories
= src_directories
80 src_filenames
= read_directories(self
.src_directories
)
82 for filename
in src_filenames
:
83 timestamp
= get_timestamp(filename
)
84 yield filename
, self
.get_new_filename(filename
, timestamp
)
86 def get_new_filename(self
, orig_filename
, timestamp
):
87 prefix
= timestamp
.strftime("%Y-%m-%d") + "/"
88 orig_filename
= os
.path
.basename(orig_filename
)
89 return os
.path
.join(self
.dest
, prefix
+ orig_filename
)
92 for orig_filename
, new_filename
in plan
:
93 print(" ", orig_filename
, "->", new_filename
)
95 def execute_plan(plan
):
96 for orig_filename
, new_filename
in plan
:
97 os
.makedirs(os
.path
.dirname(new_filename
), exist_ok
=True)
98 os
.link(orig_filename
, new_filename
)
101 parser
= argparse
.ArgumentParser(description
="Relink photos based on EXIF dates")
103 parser
.add_argument("--dry-run", action
="store_true")
105 parser
.add_argument("dest", nargs
=1)
106 parser
.add_argument("src", nargs
="+")
108 action_group
= parser
.add_mutually_exclusive_group(required
=True)
110 action_group
.add_argument("--directory-fanout", action
="store_true",
111 help="Create directories with names like 2015-01-01, place files into them")
113 action_group
.add_argument("--rename-files", action
="store_true",
114 help="Rename files from different cameras based on timestamps")
116 args
= parser
.parse_args()
117 args
.dest
= args
.dest
[0]
124 raise NotImplementedError()
126 if args
.directory_fanout
:
127 planner
= DirectoryFanout(args
.dest
, args
.src
).plan
128 elif args
.rename_files
:
129 planner
= FilesByDate(args
.dest
, args
.src
).plan
137 if __name__
== "__main__":