2
2
3
3
# Author: Trizen
4
4
# Date: 30 September 2024
5
+ # Edit: 24 September 2025
5
6
# https://github.com/trizen
6
7
7
- # Add the EXIF "DateTimeOriginal" to images, based on the filename of the image, along with custom GPS tags.
8
+ # Add the EXIF "DateTimeOriginal" to images, based on the filename of the image, with support for GPS tags.
8
9
9
10
use 5.036;
10
11
use Image::ExifTool qw( ) ;
12
+ use File::Find qw( find) ;
11
13
use Getopt::Long qw( GetOptions) ;
14
+ use Time::Piece qw( ) ;
15
+
16
+ my $latitude = 45.84692326942804;
17
+ my $longitude = 22.796479967835673;
12
18
13
- my $latitude = 45.84692326942804;
14
- my $longitude = 22.796479967835673;
15
19
my $coordinates = undef ;
20
+ my $set_gps = 0;
21
+ my $utc_offset = 0;
22
+
23
+ my $img_formats = ' ' ;
24
+
25
+ my @img_formats = qw(
26
+ jpeg
27
+ jpg
28
+ ) ;
16
29
17
30
sub usage ($exit_code = 0) {
18
31
19
32
print <<"EOT" ;
20
33
usage: $0 [options] [images]
21
34
22
35
options:
23
-
36
+ --gps! : set the GPS coordinates
24
37
--latitude=float : value for GPSLatitude
25
38
--longitude=float : value for GPSLongitude
26
39
--coordinates=str : GPS coordinates as "latitude,longitude"
40
+ --UTC-offset=i : offset date by this many hours (default: $utc_offset )
41
+ -f --formats=s,s : specify more image formats (default: @img_formats )
27
42
--help : print this message and exit
28
43
EOT
29
44
30
45
exit $exit_code ;
31
46
}
32
47
33
48
GetOptions(
49
+ " gps!" => \$set_gps ,
50
+ " f|formats=s" => \$img_formats ,
51
+ " utc-offset=i" => \$utc_offset ,
34
52
" latitude=f" => \$latitude ,
35
53
" longitude=f" => \$longitude ,
36
54
" coordinates=s" => \$coordinates ,
37
55
' help' => sub { usage(0) }
38
56
)
39
57
or die (" Error in command line arguments\n " );
40
58
41
- @ARGV or usage(2);
42
-
43
59
if (defined ($coordinates )) {
44
60
($latitude , $longitude ) = split (/ \s *,\s */ , $coordinates );
45
61
}
46
62
47
- foreach my $file (@ARGV ) {
48
-
49
- say " Processing: $file " ;
63
+ sub process_image ($file ) {
50
64
51
65
my $exifTool = Image::ExifTool-> new;
66
+
52
67
$exifTool -> ExtractInfo($file );
53
68
54
- if ($file =~ m { .*IMG_ ((?:20|19)[0-9]{2})([0-9]{2})([0-9]{2})_([0-9]{2})([0-9]{2})([0-9]{2})} ) {
69
+ if ($file =~ m { .*(?:/| \D _| \b ) ((?:20|19)[0-9]{2})([0-9]{2})([0-9]{2})_([0-9]{2})([0-9]{2})([0-9]{2})} ) {
55
70
my ($year , $month , $day , $hour , $min , $sec ) = ($1 , $2 , $3 , $4 , $5 , $6 );
56
71
57
72
my $date = " $year :$month :$day $hour :$min :$sec " ;
@@ -66,14 +81,57 @@ ($exit_code = 0)
66
81
}
67
82
68
83
# Set GPSLatitude and GPSLongitude tags
69
- $exifTool -> SetNewValue(' GPSLatitude' , $latitude );
70
- $exifTool -> SetNewValue(' GPSLatitudeRef' , $latitude >= 0 ? ' N' : ' S' );
71
- $exifTool -> SetNewValue(' GPSLongitude' , $longitude );
72
- $exifTool -> SetNewValue(' GPSLongitudeRef' , $longitude >= 0 ? ' E' : ' W' );
84
+ if ($set_gps ) {
85
+ $exifTool -> SetNewValue(' GPSLatitude' , $latitude );
86
+ $exifTool -> SetNewValue(' GPSLatitudeRef' , $latitude >= 0 ? ' N' : ' S' );
87
+ $exifTool -> SetNewValue(' GPSLongitude' , $longitude );
88
+ $exifTool -> SetNewValue(' GPSLongitudeRef' , $longitude >= 0 ? ' E' : ' W' );
89
+ }
90
+
91
+ my $time_obj = Time::Piece-> strptime($date , " %Y :%m :%d %H :%M :%S " );
92
+
93
+ if ($utc_offset ) {
94
+ $time_obj += $utc_offset * 3600;
95
+ }
96
+
97
+ my ($dev , $ino , $mode , $nlink , $uid , $gid , $rdev , $size , $atime , $mtime , $ctime , $blksize , $blocks ) = stat ($file );
73
98
74
99
$exifTool -> WriteInfo($file );
100
+
101
+ $mtime = $time_obj -> epoch;
102
+ $atime = $mtime ;
103
+
104
+ # Set the original ownership of the image
105
+ chown ($uid , $gid , $file );
106
+
107
+ # Set the modification time
108
+ utime ($atime , $mtime , $file )
109
+ or warn " Can't change timestamp: $! \n " ;
110
+
111
+ # Set original permissions
112
+ chmod ($mode & 07777, $file )
113
+ or warn " Can't change permissions: $! \n " ;
75
114
}
76
115
else {
77
116
warn " Unable to determine the image creation date. Skipping...\n " ;
78
117
}
118
+
79
119
}
120
+
121
+ @ARGV || usage(1);
122
+
123
+ push @img_formats , map { quotemeta } split (/ \s *,\s */ , $img_formats );
124
+
125
+ my $img_formats_re = do {
126
+ local $" = ' |' ;
127
+ qr /\. (@img_formats )\z / i ;
128
+ };
129
+
130
+ find {
131
+ no_chdir => 1,
132
+ wanted => sub {
133
+ (/ $img_formats_re /o && -f ) || return ;
134
+ say " :: Processing: $_ " ;
135
+ process_image($_ );
136
+ }
137
+ } => @ARGV ;
0 commit comments