困っている状況
- Amazon Photosでファイル同期すると、ローカルファイルの時刻のうちmtime(更新日時)とatime(アクセス日時)が更新されてしまう
- Exif情報のあるファイルは良いが、Exif情報のないファイルの場合はAmazon Photoで同期した日に撮影したかのように見えて困る
- Google Photosに移行したいが、上書きされてしまったファイル情報は戻らず困っている
Google Photosの挙動を確認
- Exif情報があれば、Exif情報のDateTimeOriginalを日時として採用するらしい
- Exif情報がなければ、mtime(更新日時)を日時として採用するらしい
課題
上記を踏まえると、Amazon Photosのアプリに上書きされたmtimeを参照してしまうExif情報を含まない画像が、Amazon Photosで同期した日時の写真として認識されてしまうのが問題であり、何らかの方法で撮影日時を認識させてやれば良い。
対策
- 全ファイルをなめて、Exif情報を含む画像ファイルは処理をパスする
- Exif情報を含まない画像の場合、
- バックアップファイルのExif情報がないか探す
- ローカルファイル、バックアップファイルのctime,mtime,atimeを全て読み込み、最も古い日時をもってmtimeを上書きする
- 実際にはLinux上にあるバックアップファイルのctimeはmtimeと同じようなタイミングで更新されるため、Windows上にあるctimeを参照することが専らとなる
コード
Exif情報の読み込み
細かいことは、python3のPILでexifが読み込めない時 - Qiitaを参照。
def get_exiftime(path):
'''
Parameters
----------
path : string
Returns
-------
exiftime : datetime
'''
try:
with Image.open(path) as im:
exifdict = piexif.load(im.info['exif'])["Exif"]
return datetime.datetime.strptime(
exifdict[36867].decode(),
'%Y:%m:%d %H:%M:%S'
)
except:
# 今回はExif情報を読み込めなくても処理継続する
return None
ctime、mtimeの読み込み
わざわざリストで返却しているのは、後で連結するため
def get_cmtime(path):
'''
Parameters
----------
path : string
Returns
-------
ctime : datetime
mtime : datetime
'''
fstat = os.stat(path)
return [datetime.datetime.fromtimestamp(fstat.st_ctime), datetime.datetime.fromtimestamp(fstat.st_mtime), ]
ループ
フォルダ構造からファイル名から完全にそのままバックアップされていることを想定した処理なので注意。
srv_dpath = 'サーバのディレクトリへのパス'
loc_dpath = 'ローカルディレクトリへのパス'
# ディレクトリとか非画像ファイルとかの除外は環境に合わせて実装してちょ
flist = ['ファイル名のリストだと思いネェ']
for filename in flist:
loc_fpath = f'{loc_dpath}/{filename}'
srv_fpath = f'{srv_dpath}/{filename}'
loc_exiftime = get_exiftime(loc_fpath)
if loc_exiftime is not None:
continue
else:
srv_exiftime = get_exiftime(srv_fpath)
if srv_exiftime is not None:
# バックアップにのみExif情報を含む場合。もし自動処理するならsrv_fpathをloc_fpathにコピーすればいい。
raise Exception('local file was changed!')
else:
oldesttime = sorted(get_cmtime(srv_fpath) + get_cmtime(loc_fpath))[0]
newtime = time.mktime(oldesttime.timetuple())
#os.utime(srv_fpath, (newtime, newtime)) # サーバも修正するのは勇気がいるので、ご利用は計画的に
os.utime(loc_fpath, (newtime, newtime))