Skip to content

Instantly share code, notes, and snippets.

@joba-1
Last active August 7, 2024 21:42
Show Gist options
  • Save joba-1/bdff43371dee7e23f536e7da5e373116 to your computer and use it in GitHub Desktop.
Save joba-1/bdff43371dee7e23f536e7da5e373116 to your computer and use it in GitHub Desktop.
Fix wrong Gopro Hero7 2016 media timestamps from correct JPEG GPS metadata timestamps
"""
Read timestamps of jpegs from given or current directory.
If there are files with creation year 2016
and if the difference between creation date and gps date is the same for all those files
then fix metadata dates and file dates of all 2016 media files (not just the jpegs)
"""
TZ = 2*60 # desired difference in minutes between GPS (UTC) and TZ to use within media files
from subprocess import check_output
from datetime import datetime, timedelta
from sys import argv
from os import utime
from statistics import fmean
import json
def diff(cr, gd, gt):
created = datetime(int(cr[0:4]), int(cr[5:7]), int(cr[8:10]), int(cr[11:13]), int(cr[14:16]), int(cr[17:19]))
gps = datetime(int(gd[0:4]), int(gd[5:7]), int(gd[8:10]), int(gt[0:2]), int(gt[3:5]), int(gt[6:8]))
return timedelta.total_seconds(gps - created)
if len(argv) == 2:
dir = argv[1]
else:
dir = '.'
cmd = 'exiftool -j -CreateDate -GpsDateStamp -GpsTimeStamp -ext jpg'.split()
cmd.append(dir)
pics = json.loads(check_output(cmd))
pics = [{'file': pic['SourceFile'], 'diff': diff(pic['CreateDate'], pic['GPSDateStamp'], pic['GPSTimeStamp'])} for pic in pics if pic['CreateDate'].startswith('2016:') and 'GPSDateStamp' in pic]
if len(pics) == 0:
print(f'No 2016 jpegs found in {dir}')
exit
dmin = int(min([pic['diff'] for pic in pics]))
dmax = int(max([pic['diff'] for pic in pics]))
davg = round(fmean([pic['diff'] for pic in pics]))
if davg != dmax and davg != dmin:
print(f'multiple time differences found: min={dmin}, avg={davg}, max={dmax}')
exit
cmd = 'exiftool -j -CreateDate'.split()
cmd.append(dir)
meds = json.loads(check_output(cmd))
print(meds)
meds = [{'file': med['SourceFile'], 'date': med['CreateDate']} for med in meds if 'CreateDate' in med and med['CreateDate'].startswith('2016:')]
for med in meds:
cr = med['date']
created = datetime(int(cr[0:4]), int(cr[5:7]), int(cr[8:10]), int(cr[11:13]), int(cr[14:16]), int(cr[17:19]))
fixed = created + timedelta(seconds=davg + TZ*60)
fixed_str = fixed.strftime('%Y:%m:%d %H:%M:%S')
cmd = ['exiftool', f'-time:all="{fixed_str}"', '-wm', 'w']
cmd.append(med['file'])
result = check_output(cmd).decode()
fixed_ts = fixed.timestamp()
utime(med['file'], times=(fixed_ts, fixed_ts))
print(med['file'], result)
@joba-1
Copy link
Author

joba-1 commented Jul 19, 2024

fixes wrong timestamps in media files recorded after low battery situations

tested with python 3.11 and exiftool 12.89

limits:

  • all 2016 files in the directory are recorded after the same battery failure (i.e. all have the same time offset)
  • all files in the directory use the same timezone and DST offset (as given in TZ constant)
  • exiftool makes backups of the changed media files in the same directory. Plan for the extra space requirements.

if needed split the files up in different directories to match these requirements.

Times in media metadata should all be UTC, but Gopro seems to use local time. If you want UTC just set TZ=0

why doesn't Gopro use GPS time to set camera time after battery outage although they use it in JPEG metadata :/

@joba-1
Copy link
Author

joba-1 commented Jul 20, 2024

@joba-1
Copy link
Author

joba-1 commented Aug 7, 2024

not all JPEGs of the gopro seem to get GPS timestamps -> changed script to only use those that got it.
(used on caba1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment