-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGeoNamePhoto.sh
executable file
·355 lines (314 loc) · 13.6 KB
/
GeoNamePhoto.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#!/bin/bash
# https://github.com/TippyTurtle/GeoNamePhotos/
# This script should rename (move) all images or vidoe files, recursively, to rename them something similar to: ~/Pictures/2013-12/United States/2013-12-17 13h27m02s(Silverwood Theme Park-Athol)(p500).jpg
# Before running, I highly recommend running rdfind to remove duplicats. By default it won't remove duplicates, you need rdfind -deleteduplicates true
# rdfind prioritizes the order the the paths you include, deleting the lowest priority, like rdfind -deleteduplicates true /Most/Important/Path /Less/Important /Least .
# rdfind looks for duplicates across all file types...not just jpg.
email='[email protected]'
# Text Colors
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
BOLD=$(tput bold)
NORM=$(tput sgr0)
# Initialize IsError variable
IsError=0
# You need to have the following installed:
# exiftool curl jq
# Check if exiftool is installed
if ! command -v exiftool &> /dev/null; then
echo "exiftool is not installed. On Debian, run:"
echo "sudo apt install libimage-exiftool-perl"
IsError=1
fi
# Check if curl is installed
if ! command -v curl &> /dev/null; then
echo "curl is not installed. On Debian, run:"
echo "sudo apt install curl"
IsError=1
fi
# Check if jq is installed
if ! command -v jq &> /dev/null; then
echo "jq is not installed. On Debian, run:"
echo "sudo apt install jq"
IsError=1
fi
# Check if both arguments are present
if [ "$#" -lt 2 ]; then
echo "Usage: GeoNamePhoto.sh /source/directory/ /output/directory/"
IsError=1
fi
# Assign the arguments to variables
InDir="$1"
OutDir="$2"
FileTypes="$3"
GeoNamePhotoErrors=$InDir\GeoNameCityError.txt
# Check if directories exist
if [ ! -d "$InDir" ]; then
echo "Error: $InDir does not exist."
IsError=1
fi
if [ ! -d "$OutDir" ]; then
echo "Error: $OutDir does not exist."
IsError=1
fi
if [ -z "$FileTypes" ]; then
echo "Photo's [p], Movie's [m] or Both [b]?"
read -n1 FileTypes
echo
fi
case $FileTypes in
p)
FilesCollection=$(find $InDir -type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.cr2' \))
;;
m)
FilesCollection=$(find $InDir -type f \( -iname '*.mpg' -o -iname '*.mpeg' -o -iname '*.mov' -o -iname '*.mp4' -o -iname '*.wmv' -o -iname '*.avi' -o -iname '*.3gp' \))
;;
b)
FilesCollection=$(find $InDir -type f \( -iname '*.mpg' -o -iname '*.mpeg' -o -iname '*.mov' -o -iname '*.mp4' -o -iname '*.wmv' -o -iname '*.avi' -o -iname '*.3gp' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.cr2' \))
;;
*)
echo "Invalid choice for file types."
IsError=1
esac
# Exit with non-zero status code if any tool is missing
if [ "$IsError" -eq 1 ]; then
exit 1
fi
FileTotal=$(echo "$FilesCollection" | wc -l)
FileCount=0
StartTime=$(date +%s)
echo "File count: $FileTotal"
#Use this log to find $Landmark fields that should have been used. You will need to process the list more with a "Group-By" tool to be more efficient.
echo $(date) > FirstAddressField.txt
#Most are false-positives, some sometimes you will find a legitmate field that should be used for or $city that was missed.
echo $(date) > "$GeoNamePhotoErrors"
IFS=$'\n'
for CurrentFile in $FilesCollection ; do
echo -e "--------------------${BOLD}$CurrentFile${NORM}--------------------"
ExifData=$(exiftool -fast2 -m -j -d '%Y-%m-%d %H:%M:%S' -c '%+1.14g' $CurrentFile)
# echo $ExifData
lat=$(echo $ExifData | jq -r '.[0].GPSLatitude' | sed 's/+//')
lon=$(echo $ExifData | jq -r '.[0].GPSLongitude' | sed 's/+//')
# echo "lat=$lat&lon=$lon"
#Camera Model has be surprisingly useful to know who took the photo, particularly to know when you didn't, but were given a dump from someone else.
CameraModel=''
CameraModel=$(echo $ExifData | jq -r '.[0].Model')
if [ "$CameraModel" != '' ] && [ "$CameraModel" != 'null' ]; then
CameraModel="($(echo $ExifData | jq -r '.[0].Model'))"
else
CameraModel=''
echo $(date) " No Camera $(echo $ExifData | sed 's/\n/-/g')" >> "$GeoNamePhotoErrors"
fi
# Bad date...zero: '1970-01-01 16:23:53' There is a lot of variation on the hour/minutes/seconds part, I just check year-month-day
# FileModifyDate is sometimes is newer than all other dates including ModifyDate
# Do not use ProfileDateTime, it is the date a camera profile was set, not when the photo was taken.
# Maybe used MediaCreateDate and MediaModifyDate from mp4 files
CreateDateName='null'
PhotoTakeAt=''
PhotoTakeAt=$(date)
TempString=$(echo $ExifData | jq -r '.[0].FileModifyDate')
if [ "$TempString" != 'null' ] && [ "$TempString" != '' ] && [ "$TempString" != '0000:00:00 00:00:00' ] && [ "$(date -d $TempString +'%Y-%m-%d')" != '1970-01-01' ]; then
CreateDateName='FileModifyDate'
PhotoTakeAt=$(date -d $TempString +'%Y-%m-%d %H:%M:%S')
fi
TempString="$(echo $ExifData | jq -r '.[0].ModifyDate')"
if [ "$TempString" != 'null' ] && [ "$TempString" != '' ] && [ "$TempString" != '0000:00:00 00:00:00' ] && [ "$(date -d $TempString +'%Y-%m-%d')" != '1970-01-01' ]; then
CreateDateName='ModifyDate'
PhotoTakeAt=$(date -d $TempString +'%Y-%m-%d %H:%M:%S')
fi
TempString="$(echo $ExifData | jq -r '.[0].CreateDate')"
if [ "$TempString" != 'null' ] && [ "$TempString" != '' ] && [ "$TempString" != '0000:00:00 00:00:00' ] && [ "$(date -d $TempString +'%Y-%m-%d')" != '1970-01-01' ]; then
CreateDateName='CreateDate'
PhotoTakeAt=$(date -d $TempString +'%Y-%m-%d %H:%M:%S')
fi
TempString="$(echo $ExifData | jq -r '.[0].DateTimeOriginal')"
if [ "$TempString" != 'null' ] && [ "$TempString" != '' ] && [ "$TempString" != '0000:00:00 00:00:00' ] && [ "$(date -d $TempString +'%Y-%m-%d')" != '1970-01-01' ]; then
CreateDateName='DateTimeOriginal'
PhotoTakeAt=$(date -d $TempString +'%Y-%m-%d %H:%M:%S')
fi
if [ "$CreateDateName" == 'null' ]; then
echo "NO DATA $ExifData"
echo $(date) " No Date for image $(echo $ExifData | sed 's/\n/-/g')" >> "$GeoNamePhotoErrors"
exit 666
fi
if [ -z "$lat" -o "$lat" == '+0.00000000000000' -o "$lat" == 'null' ] ; then
city=''
Country=''
ImageDescription=$(date -d $PhotoTakeAt +'%A %d %B %Y at %I:%M:%S %p')
else
# Do not remove this. It would be evil to OpenStreetMaps and against their terms of use if you hit them more than once every 1.5 seconds.
# Consider making it much longer and running overnight if you have a ton of photo's.
echo Sleeping 3 seconds...
sleep 3
MapLoc=$(curl -s --retry 10 "https://nominatim.openstreetmap.org/reverse?format=json&zoom=10&email=$email&accept-language=en-US,en;q=0.5&lat=$lat&lon=$lon&zoom=19&addressdetails=1")
# echo "$MapLoc" | jq -r '.address' | fgrep -A 1 '{' | fgrep -A 1 '{' | fgrep -v '{' >> FirstAddressField.txt
# echo "$MapLoc"
# https://wiki.openstreetmap.org/wiki/User:Innesw/TagTree
# Find a good "City"...first ones take priority over later ones
city=$(echo $MapLoc | jq -r '.address.city')
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.town')
fi
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.village')
fi
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.hamlet')
fi
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.locality')
fi
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.municipality')
fi
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.state_district')
fi
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.county')
fi
if [ "$city" == 'null' ]; then
city=$(echo $MapLoc | jq -r '.address.state')
fi
# Find a good Landmark...first ones take priority over later ones
# Ignoring: aerialway, craft
Landmark=$(echo $MapLoc | jq -r '.address.tourism')
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.historic')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.isolated_dwelling')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.accommodation')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.cemetery')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.shop')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.club')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.natural')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.man_made')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.leisure')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.amenity')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.display_name')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.emergency')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.military')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.government')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.healthcare')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.office')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.building')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.place')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.waterway')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.railway')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.aeroway')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.neighbourhood')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.city_block')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.city_district')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.quarter')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.suburb')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.junction')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.road')
fi
if [ "$Landmark" == 'null' ]; then
Landmark=$(echo $MapLoc | jq -r '.address.highway')
fi
# Add the nearist landmark to the city name
if [ "$city" == 'null' ]; then
if [ "$Landmark" == 'null' ]; then
city=''
echo $(date) " No City or Landmark $CurrentFile lat=$lat&lon=$lon $MapLoc" >> "$GeoNamePhotoErrors"
else
city="$Landmark"
fi
else
if [ "$Landmark" == 'null' ]; then
city="$city"
echo $(date) " City but no Landmark $CurrentFile lat=$lat&lon=$lon $MapLoc" >> "$GeoNamePhotoErrors"
else
city="$Landmark-$city"
fi
fi
if [ $(echo $MapLoc | jq -r '.address.country')!='null' ]; then
Country="$(echo $MapLoc | jq -r '.address.country')"
ImageDescription="$(echo $city | sed 's/-/, /') ($(echo $MapLoc | jq -r '.address.country')) on $(date -d $PhotoTakeAt +'%A %d %B %Y at %I:%M:%S %p')"
else
Country=''
ImageDescription="$(echo $city | sed 's/-/, /') on $(date -d $PhotoTakeAt +'%A %d %B %Y at %I:%M:%S %p')"
echo $(date) " No Country lat=$lat&lon=$lon $MapLoc" >> "$GeoNamePhotoErrors"
fi
city="($city)"
fi
# replace bad filename characters: \ / : " * didn't ? < > | % # $ & { } [ ]
Country="$(echo $Country | sed 's/\//-/g;s/\\/-/g;s/\:/./g;s/\"//g;s/\*//g')/"
city=$(echo $city | sed 's/\//-/g;s/\\/-/g;s/\:/./g;s/\"//g;s/\*//g')
CameraModel=$(echo $CameraModel | sed 's/\//-/g;s/\\/-/g;s/\:/./g;s/\"//g;s/\*//g')
echo -e "-------------------------------${BOLD}$ImageDescription${NORM}-------------------------------"
# Add "-o ." to copy instead of move the files.
# Change -FileName to -TestName for a dry run with moving/copying files.
# exiftool -m -fixBase -q -P -ImageDescription=''"$ImageDescription"'' -d '%Y-%m/'"$Country%"'Y-%m-%d %H.%M.%S%%-c' '-FileName<'"$OutDir\${$CreateDateName}$city$CameraModel.%le"'' ''"$CurrentFile"''
exiftool -m -fixBase -q -P -d '%Y-%m/'"$Country%"'Y-%m-%d %H.%M.%S%%-c' '-FileName<'"$OutDir\${$CreateDateName}$city$CameraModel.%le"'' ''"$CurrentFile"''
# Someone can do a better job at this, I can't deal with float in bash to save my life
(( FileCount=$FileCount+1 ))
PercentDone=$(echo "scale=4; $FileCount*100/$FileTotal" | bc -l )
RunTime=$( echo "$(date +%s) - $StartTime" | bc -l )
SecondsLeft=$( echo "($RunTime/($PercentDone/100))-$RunTime;scale=0" | bc -l )
SecondsLeft=${SecondsLeft%.*}
(( SecondsLeft=$SecondsLeft+1 ))
echo
echo "$FileCount of $FileTotal. $(printf "%.2f" "$PercentDone")% complete."
printf "Running for %02d:%02d:%02d.\n" $(($RunTime/3600)) $(($RunTime%3600/60)) $(($RunTime%60))
printf "${BLUE}Only %02d:%02d:%02d left to go.${NC}\n" $(($SecondsLeft/3600)) $(($SecondsLeft%3600/60)) $(($SecondsLeft%60))
echo
done
# Delete now empty folders
find $InDir -type d -empty -delete -print
date