setup

brew install exiftool

https://exiftool.org/

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 .

https://stackoverflow.com/questions/65210140/how-to-merge-json-and-google-takeout-photos-to-get-the-right-dates-back

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 .