setup
brew install exiftool
exiftool -r -d %s -tagsfromfile "%d/%F.json" "-GPSAltitude<GeoDataAltitude" "-GPSLatitude<GeoDataLatitude" "-GPSLatitudeRef<GeoDataLatitude" "-GPSLongitude<GeoDataLongitude" "-GPSLongitudeRef<GeoDataLongitude" "-Keywords<Tags" "-Subject<Tags" "-Caption-Abstract<Description" "-ImageDescription<Description" "-DateTimeOriginal<PhotoTakenTimeTimestamp" -ext "*" -overwrite_original -progress --ext json .
exiftool -r -d %s -tagsfromfile "%d/%F.json" "-GPSAltitude<GeoDataAltitude" "-GPSLatitude<GeoDataLatitude" "-GPSLatitudeRef<GeoDataLatitude" "-GPSLongitude<GeoDataLongitude" "-GPSLongitudeRef<GeoDataLongitude" "-Keywords<Tags" "-Subject<Tags" "-Caption-Abstract<Description" "-ImageDescription<Description" "-DateTimeOriginal<PhotoTakenTimeTimestamp" -ext "*" -overwrite_original -progress --ext json . 2>&1 | tee log.log
prep: copy json file for -edited
files
find . -type f -name "*-edited.*" | sed 's/^\(.*\)-edited.\(.*\)/cp "\1.\2.json" "\1-edited.\2.json"/'
find too long filename
6. Compositie met rood, geel, blauw en zwart (1(1).jpg
??????????????????????????????????????????????????.jpg
?????????????????????????????????????????????????.json
# actual
./Archive/6. Compositie met rood, geel, blauw en zwart (1.jpg
./Archive/6. Compositie met rood, geel, blauw en zwart (.json
6. Compositie met rood, geel, blauw en zwart (1(1).jpg.json
??????????????????????????????????????????????????????.json
IMG_20141112_134804.jpg
???????????????????.jpg
"title":
12.b Bomen Aan Het Gein Bij Opkomende Maan (1907 - 1908).jpg
12.b Bomen Aan Het Gein Bij Opkomende Maan (190.jpg
12.b Bomen Aan Het Gein Bij Opkomende Maan (190(1).jpg
12.b Bomen Aan Het Gein Bij Opkomende Maan (19.json
explore filename length
➜ Google Photos ls -1R | awk 'length($0) == 54 { print $0 }'
12.b Bomen Aan Het Gein Bij Opkomende Maan (190(1).jpg
6. Compositie met rood, geel, blauw en zwart (1(1).jpg
Screenshot_2024-02-17-13-36-45-398_com.booking(1).json
ls -1R | awk '{ print length($0)" - "$0 }' | sort -n
[...]
51 - Screenshot_2024-08-14-13-29-41-439_com.google..json
51 - Screenshot_2024-08-14-13-29-41-439_com.google.a.jpg
51 - Screenshot_2024-08-27-15-43-37-077_com.kiwibro.json
51 - Screenshot_2024-08-27-15-43-37-077_com.kiwibrow.jpg
54 - 12.b Bomen Aan Het Gein Bij Opkomende Maan (190(1).jpg
54 - 6. Compositie met rood, geel, blauw en zwart (1(1).jpg
54 - Screenshot_2024-02-17-13-36-45-398_com.booking(1).json
➜ Google Photos ls -1R | awk '{ print length($0)" - "$0 }' | sed 's/^\([0-9]*\) - .*/\1/' | sort -n | uni
q -c
47 0
1 2
1 3
1 4
5 5
3 6
7 7
3 8
7 9
17 10
12 11
301 12
50 13
16 14
10 15
18 16
320 17
12 18
308 19
7 20
6 21
20 22
8991 23
279 24
56 25
91 26
44 27
9245 28
267 29
1014 30
220 31
48 32
22 33
43 34
204 35
129 36
10 37
23 38
43 39
10 40
3 41
10 42
7 43
5 45
3 47
1 48
8 49
27 50
1734 51
3 54
51 - Screenshot_2024-02-17-13-36-45-398_com.booking.json
Screenshot_2024-02-17-13-36-45-398_com.booking.json
???????????????????????????????????????????????????
find extensions
Google Photos find . -type f | sed 's/.*\.\([^.]*\)$/\1/' | sort | uniq -c
7 3gp
1 DS_Store
12 JPG
1 PNG
14 gif
227 jpeg
10798 jpg
11370 json
1 m4v
634 mp4
1 mpg
506 png
prepping -edited
copy json file from original.jpg.json
to original-edited.jpg.json
for each edited file (warning: overwrites!)
(!) handle normal cases
➜ Google Photos find . -type f -name "*-edited.*" | sed 's/^\(.*\)-edited.\(.*\)/cp "\1.\2.json" "\1-edited.\2.json"/' | zsh
cp: ./Archive/d5fe6737e888e7e.jpg.json: No such file or directory
cp: ./Archive/nog aanpassen van uc1.jpg.json: No such file or directory
(!) handle cases -edited(1).jpg
separately
find . -type f -name "*-edited(1).*" | sed 's/^\(.*\)-edited(1)\(.*\)/cp "\1(1)\2.json" "\1-edited(1)\2.json"/'
➜ Google Photos ls -w1 ./Archive/d5fe6737e888e7e*
./Archive/d5fe6737e888e7e-edited.jpg
./Archive/d5fe6737e888e7e.jpg
./Archive/d5fe6737e888e7e.json
➜ Google Photos ls -w1 ./Archive/nog\ aanpassen\ van\ uc1*
./Archive/nog aanpassen van uc1-edited.jpg
./Archive/nog aanpassen van uc1.jpg
./Archive/nog aanpassen van uc1.json
mv "./Archive/nog aanpassen van uc1.json" "./Archive/nog aanpassen van uc1.jpg.json"
mv ./Archive/d5fe6737e888e7e.json ./Archive/d5fe6737e888e7e.jpg.json
check if matching json for every file
find . -type f | sort
awk '{if ($1<prev) {offset++}; print ($1+(offset*10)); prev=$1}' input_file
https://stackoverflow.com/questions/13782016/comparing-consecutive-rows-in-awk
echo "foo\nfoo.json\nnope\nbar baz\nbar baz.json" | awk '{
if (prev) {
now=$0
prevjson=prev ".json"
if (prevjson == now) {
prev=""
}
else {
print prev
;
prev=$0
}
} else {
prev=$0
}
}'
# expected:
nope
final: find where filename of image != filename of jpg
find . -type f | grep -v "/metadata.json$" | sort | awk '{
if (prev) {
now=$0
prevjson=prev ".json"
if (prevjson == now) {
prev=""
}
else {
print prev
;
prev=$0
}
} else {
prev=$0
}
}'
(!) find if almost matching filenames
find . -type f | grep -v "/metadata.json$" | sort | awk '
function abs(v) {return v < 0 ? -v : v}
function min(a,b) {return a < b ? a : b}
function abs_diff(a,b) {return abs(a-b)}
function only_diff_last_char(a,b) {
false = 0
if(abs_diff(length(a)-length(b)) != 1) {
return false
}
minlen = min(length(a),length(b))
atrunc = substr(a, 1, minlen)
btrunc = substr(b, 1, minlen)
return atrunc == btrunc
}
function calc_match_count(minCount, prevs, nows) {
mc = 0
for (i = 1; i <= minCount; i++) {
a=prevs[i]
b=nows[i]
if(a == b) {
mc = i
continue
} else if(only_diff_last_char(a,b)) {
# count this one, but stop
mc = i
break
} else {
# stop
break
}
}
return mc
}
function get_json(a,b) {return index(a,".json") ? a : b}
function get_not_json(a,b) {return index(a,".json") ? b : a}
function quote(str) { return "\"" str "\""}
{
if (prev) {
now=$0
prevjson=prev ".json"
if (prevjson == now) {
prev=""
} else {
prevcount = split(prev, prevs, ".")
nowcount = split(now, nows, ".")
if ((prevs[prevcount] == "json" && nows[nowcount] != "json") || (prevs[prevcount] != "json" && nows[nowcount] == "json")) {
minCount = min(prevcount, nowcount)
minCount = minCount - 1 # do not care about extension
matchcount = calc_match_count(minCount, prevs, nows)
if (matchcount == minCount) {
print "mv " quote(get_json(now,prev)) " " quote(get_not_json(now,prev) ".json")
prev=""
} else {
# print "no match: " matchcount " / " minCount
print "#no_match " prev
prev=$0
}
} else {
print "#no_json " prev
prev=$0
}
}
} else {
prev=$0
}
}'
parse tags
exiftool -r -d %s -tagsfromfile "%d/%F.json" "-GPSAltitude<GeoDataAltitude" "-GPSLatitude<GeoDataLatitude" "-GPSLatitudeRef<GeoDataLatitude" "-GPSLongitude<GeoDataLongitude" "-GPSLongitudeRef<GeoDataLongitude" "-Keywords<Tags" "-Subject<Tags" "-Caption-Abstract<Description" "-ImageDescription<Description" "-DateTimeOriginal<PhotoTakenTimeTimestamp" -ext "*" -overwrite_original -progress --ext json .
(!) fix .jpg(1).json vs (1).jpg
find . -type f -name "*.jpg(1).json" | awk '
{
find=".jpg(1).json"
repl="(1).jpg.json"
i=index($0,find)
newfilename = substr($0,1,i-1) repl substr($0,i+length(find))
quote="\""
print "mv " quote $0 quote " " quote newfilename quote
}
'
speedrun
(!) handle -edited.jpg
normal cases
find . -type f -name "*-edited.*" | sed 's/^\(.*\)-edited.\(.*\)/cp "\1.\2.json" "\1-edited.\2.json"/' | zsh
(!) handle -edited(1).jpg
separately
find . -type f -name "*-edited(1).*" | sed 's/^\(.*\)-edited(1)\(.*\)/cp "\1(1)\2.json" "\1-edited(1)\2.json"/'
(!) fix .jpg(1).json vs (1).jpg
find . -type f -name "*.jpg(1).json" | awk '{
find=".jpg(1).json"
repl="(1).jpg.json"
i=index($0,find)
newfilename = substr($0,1,i-1) repl substr($0,i+length(find))
quote="\""
print "mv " quote $0 quote " " quote newfilename quote
}'
(!) fix almost matching files
find . -type f | grep -v "/metadata.json$" | sort | awk '
function abs(v) {return v < 0 ? -v : v}
function min(a,b) {return a < b ? a : b}
function abs_diff(a,b) {return abs(a-b)}
function only_diff_last_char(a,b) {
false = 0
if(abs_diff(length(a)-length(b)) != 1) {
return false
}
minlen = min(length(a),length(b))
atrunc = substr(a, 1, minlen)
btrunc = substr(b, 1, minlen)
return atrunc == btrunc
}
function calc_match_count(minCount, prevs, nows) {
mc = 0
for (i = 1; i <= minCount; i++) {
a=prevs[i]
b=nows[i]
if(a == b) {
mc = i
continue
} else if(only_diff_last_char(a,b)) {
# count this one, but stop
mc = i
break
} else {
# stop
break
}
}
return mc
}
function get_json(a,b) {return index(a,".json") ? a : b}
function get_not_json(a,b) {return index(a,".json") ? b : a}
function quote(str) { return "\"" str "\""}
{
if (prev) {
now=$0
prevjson=prev ".json"
if (prevjson == now) {
prev=""
} else {
prevcount = split(prev, prevs, ".")
nowcount = split(now, nows, ".")
if ((prevs[prevcount] == "json" && nows[nowcount] != "json") || (prevs[prevcount] != "json" && nows[nowcount] == "json")) {
minCount = min(prevcount, nowcount)
minCount = minCount - 1 # do not care about extension
matchcount = calc_match_count(minCount, prevs, nows)
if (matchcount == minCount) {
print "mv " quote(get_json(now,prev)) " " quote(get_not_json(now,prev) ".json")
prev=""
} else {
# print "no match: " matchcount " / " minCount
print "#no_match " prev
prev=$0
}
} else {
print "#no_json " prev
prev=$0
}
}
} else {
prev=$0
}
}' | grep "^mv" | zsh
fix image file metadata tags
exiftool -r -d %s -tagsfromfile "%d/%F.json" "-GPSAltitude<GeoDataAltitude" "-GPSLatitude<GeoDataLatitude" "-GPSLatitudeRef<GeoDataLatitude" "-GPSLongitude<GeoDataLongitude" "-GPSLongitudeRef<GeoDataLongitude" "-Keywords<Tags" "-Subject<Tags" "-Caption-Abstract<Description" "-ImageDescription<Description" "-DateTimeOriginal<PhotoTakenTimeTimestamp" -ext "*" -overwrite_original -progress --ext json .