プログラミング

Google Photosの写真をローカルに丸ごとバックアップするツールを作りました(gphoto_backup)

Google TakeoutのZIPを、EXIF整備済み・撮影年フォルダ分類・重複なしの写真ライブラリに変換するWindows向けツール gphoto_backup の紹介です。作った背景から導入手順、シナリオ別の使い方、コマンドとconfig.iniのリファレンス、トラブルシューティングまでまとめました。

目次

はじめに

Google Photosの有料プランをそろそろ解約したい、あるいはNASやiCloudに引っ越したい、と思ったことはありませんか。

私自身、Googleアカウントの料金見直しをきっかけに「Google Photosから完全に独立したオフラインバックアップ」を作ろうとしたのですが、これが想像以上に面倒でした。
Googleが用意している「Takeout」という公式の書き出し機能を使えば、確かに全データをZIPで一気にダウンロードできます。
ただ、出てきたデータをそのまま外付けHDDに置いても、他の写真管理アプリではまともに使えない状態だったのです。

そこで、Takeoutの落とし穴を一括で解決する gphoto_backup というWindows向けのツールを作りました。
run.bat をダブルクリックするだけで、

  • 分割された大量のZIPを順番に解凍し
  • JSONサイドカーから撮影日時・GPS・説明文を画像のEXIFに書き戻し
  • 撮影年フォルダに自動分類し
  • (任意で)完全重複を排除する

ところまでを全自動でやってくれます。
出力されるのは「他の写真管理アプリ(Windowsフォト、Mac写真、Lightroom、Synology Photosなど)でそのまま使える状態」の写真ライブラリです。

この記事では、なぜこのツールが必要なのかという背景から、ダウンロード・セットアップ・使い方・各機能のリファレンス・トラブルシューティングまで、ひと通り全部書いていきます。
少し長いですが、目次から気になるところに飛んで読んでもらえればと思います。

コードは「コード配布と注意事項」セクションでzipで配布しています。

なぜこのツールを作ったのか

ここでは、Google Takeoutを使ったことがない方向けに、「そのままでは使い物にならない」と私が感じた3つの落とし穴を紹介します。
すでにご存知の方は、次の「gphoto_backupでできること」まで読み飛ばしていただいて大丈夫です。

落とし穴1: 撮影日時とGPSが画像本体に入っていない

これが一番厄介でした。
Google Photosは、撮影日時・GPS座標・説明文といったメタデータを、画像とは別の .json ファイルとして書き出します。
たとえば IMG_1234.jpg という写真があると、その隣に IMG_1234.jpg.supplemental-metadata.json というファイルが置かれている、という構造です。

何が問題かというと、画像本体のEXIFには撮影日時すら入っていない場合が多いということです。
スマホで撮ったときには確かに記録されていたはずなのですが、Google Photosのサーバーに上がる過程で別出しにされてしまっています。

この状態のままWindowsフォトやMac写真、Lightroom、Synology Photosなどに取り込むと、全ての写真が「ダウンロードした日付」で表示されてしまいます。
「2024年に旅行した写真も2018年の子供の写真も、全部2025年5月撮影」みたいな悲しい状態になります。
せっかくのバックアップなのに、もったいないですよね。

落とし穴2: ZIPが大量に分割されて出てくる

数十GB〜数百GBのTakeoutを一気にダウンロードしようとすると、Google側の都合で 10個〜50個のZIPファイルに分割されて配信されます。
takeout-001.ziptakeout-002.zip ...という具合です。

これを手作業で1つずつ解凍して、中身を結合して、フォルダを整理して、というのは現実的ではありません。
私も最初は手作業でやろうとして、3つ目のZIPで諦めました。

落とし穴3: 同じ写真が複数のZIPにまたがって重複している

Takeoutの出力は、「アルバム」フォルダと「Photos from YYYY」フォルダの両方が含まれます。
アルバムに入れた写真は両方に入っているので、1枚の写真が複数の場所に重複して出力されることがあります。

しかもこれが分割されたZIPをまたいで存在することもあるので、解凍後にディレクトリを単純結合しても、結局重複ファイルだらけのライブラリになってしまいます。

この3つを一括で解決するツールが欲しかった

これらをひとつずつ手作業で解決するのは現実的ではないので、思い切ってPythonでツールを作ることにしました。
それが gphoto_backup です。

gphoto_backupでできること

このツールが何をやってくれるのかを、まず一覧で紹介します。

gphoto_backupでできること(解凍・EXIF適用・整理・重複削除の4ステップ)

操作としては run.bat をダブルクリックして待つだけで、最終的に以下のような構成のフォルダができあがります。

organized_dir/
└── 2026-05-04/        ← 実行日のフォルダ
    ├── 2018/
    │   ├── IMG_0001.jpg
    │   └── ...
    ├── 2019/
    └── 2024/

実行日のフォルダの下に「年」フォルダで分類されているので、Windowsフォトなどで開けば撮影年順にきれいに並びます。
.json サイドカーはコピーされず、画像本体にメタデータが書き込まれた状態でコピーされるので、別の写真管理アプリにそのまま取り込めます。

出力先のフォルダ構成。実行日 → 撮影年の二段階で分類される。

想定している使い方

私自身は、主に2つの用途を想定して作りました。

用途1: Google Photos解約前のオフラインバックアップ

Google Oneの料金見直しや、サービス縮小・アカウント停止リスクに備えて、「Googleアカウントが消えても手元に残るバックアップ」を作りたいケースです。
organized_dir を外付けHDDやNASに指定しておけば、ローカルとクラウドの両方を保持できます。

用途2: 別の写真管理サービスへの移行素材として

iCloud、Synology Photos、Amazon Photos、Lightroom、ImmichなどのセルフホストPhotos代替サービスへ引っ越したいケースです。
EXIFに撮影日時とGPSが書き込まれた状態の写真ライブラリを作っておけば、移行先のアプリに取り込んだとき、撮影日順に正しく並びます。
これがTakeoutの出力をそのまま渡したときとの最大の違いです。

全体の流れ

ここまでで紹介した4ステップを、もう少し詳しく図解するとこんな感じです。

[Takeoutの分割ZIP]
       ↓
   ① 解凍 (1個ずつ順番に)
       ↓
[一時フォルダ extract_dir]
       ↓
   ② JSON → EXIF 書き込み
       ↓
   ③ 年フォルダへコピー
       ↓
[整理先 organized_dir/<実行日>/<年>/]
       ↓
   ④ 一時フォルダを削除 (次のZIPへ)
       ↓
   (全ZIP終了後) ⑤ 任意で重複削除

ZIPは1個ずつ処理して、解凍 → EXIF書き込み → 整理 → 一時フォルダ削除 のサイクルを回します。
これによって、ZIPの合計サイズ分の空き容量がディスクに無くても処理できます(ZIP1個分の空きがあれば動きます)。
数百GB級のTakeoutでも、外付けHDDの空きが十分にあれば問題ありません。

ここまでが概要のお話です。
ここから先は、実際にダウンロードして動かすための詳細マニュアルになります。
インストール方法、各コマンドのリファレンス、トラブルシューティングを順番に紹介していくので、実際に使ってみたい方は読み進めてみてください。

gphoto_backupとは何か

ここからはマニュアルパートです。
改めて整理すると、gphoto_backup は Google Photos の Takeout で書き出したZIPを、ワンクリックで「EXIF整備済み・年フォルダ分類・重複なし」の状態にローカル保管するためのWindows向けツールです。

ライセンスはMIT、Pythonで書かれていて、必要なライブラリは piexif(EXIF書き込み用)と Pillow(画像読み込み用)の2つだけです。
動作確認はPython 3.11 / Windows 11で行っていますが、Python 3.8以降であれば動くはずです。

既存の選択肢との違い

同じ用途のツールはいくつか存在します。
たとえば google-photos-takeout-helper や、ImmichのCLI、その他GitHub上の小さなスクリプトなどです。
私が自分用に作り直した理由は以下の2点です。

  • Windowsの非エンジニアでも run.bat ダブルクリックで完結する ことを最優先した。仮想環境のセットアップから設定ファイルの準備までバッチで自動化しています。
  • Google Takeoutが2024年後半に変えたJSON命名仕様(46文字切り詰め含む)に対応 した。古いツールでは新形式を拾えなくて、結局メタデータが反映されない、というケースに当たったので、自分でメンテできるものを作りました。

機能面でいうと、EXIF書き込みはJPEG/TIFFのみ対応(HEIC や PNG は piexif の制約で書き込めない)という割り切りはしていますが、整理ステップではJSONやファイル名から日付を補って分類するので、HEIC・PNGも年フォルダ分けはちゃんと動きます。
実用上はあまり困りません。

動作環境と必要なもの

必要なもの

  • Windows 10 または 11
  • Python 3.8 以降

Pythonは python.org からインストーラーをダウンロードしてインストールしてください。
インストール時に「Add Python to PATH」のチェックボックスを必ず有効にしてください。
これを忘れると、後の setup.bat がPythonを見つけられずエラーになります。

ディスク容量について

extract_dir(一時解凍先)には、処理中のZIP1個分以上の空き容量が必要です。
たとえば1個4GBのZIPが10個ある場合、合計40GBではなく、4GBの空きがあれば動きます(各ZIPの処理が終わったら一時フォルダを削除して次へ進むため)。

organized_dir(整理先)には、画像の総容量分の空きが必要です。
こちらは削除されないので、たとえば150GBの写真ライブラリなら最低150GBの空きを用意してください。
重複削除を有効にすれば、最終的にはこれより小さくなります。

スコープ外(対応していないこと)

正直に書いておくと、以下のケースは想定していません。

  • EXIF書き込みのHEIC・PNG対応: piexifの仕様上できません。整理(年フォルダ分け)は動きます。
  • 編集版(*-edited.jpg)・Live Photos(.HEIC + .MOV ペア)の特別扱い: 個別のメディアファイルとして処理します。
  • 同時並行実行: run.bat を2つ同時に起動した場合の挙動は保証していません。
  • 処理中断時のリカバリ: 電源喪失や強制終了が起きた場合は、extract_dir を手動で削除してから再実行してください。
  • ネットワークドライブ・読み取り専用フォルダ: ローカルの書き込み可能なディスクで使ってください。

ご自身の用途と合わなさそうな場合は、別ツールを検討していただいて大丈夫です。

ダウンロードと導入

1. zipのダウンロードと展開

コード一式は記事末尾の「コード配布と注意事項」セクションでzipとして配布しています。
下のリンクからダウンロードして、好きな場所に展開してください。

google_photo_backup-clean.zip(35.2KB)

展開すると google_photo_backup フォルダができます。以降はこのフォルダを「展開したフォルダ」と呼びます。

2. setup.batをダブルクリック

展開したフォルダのルートにある setup.bat をダブルクリックします。
これだけで以下が自動的に行われます。

  • Pythonの存在チェック
  • 仮想環境(.venv)の作成
  • 依存パッケージ(piexif、Pillow)のインストール
  • config.example.ini から config.ini のコピー作成

PATHにPythonが通っていれば、コマンドプロンプトを開く必要すらありません。
完了すると「セットアップ完了」と表示されます。

3. config.iniを編集する

展開したフォルダの直下に作られた config.ini をテキストエディタ(メモ帳でもVSCodeでも何でも)で開いて、3つのパスを自分の環境に合わせて書き換えてください。

[paths]
zip_dir       = C:\Users\you\Downloads\Takeout
extract_dir   = C:\Users\you\Downloads\Takeout\_extracted
organized_dir = C:\Users\you\Pictures\Organized

[options]
run_dedup      = false
keep_extracted = false

ポイントは以下の通りです。

  • zip_dir: TakeoutのZIPファイルを置いたフォルダのパス。takeout-001.zip などが直接入っているフォルダを指定します。
  • extract_dir: 一時的な解凍先。処理後に自動削除されますので、空のフォルダか、存在しないパスを指定して大丈夫です。
  • organized_dir: 整理済み写真の出力先。実行日のサブフォルダの下に「年」フォルダで分類されます。
  • パスはバックスラッシュ \ で記述してください。%USERPROFILE% などの環境変数は展開されないので、絶対パスで書く必要があります。

4. run.batをダブルクリック

設定が終わったら run.bat をダブルクリックするだけです。
あとは終わるまで待ってください。
150GB級の処理だと数時間かかることもありますので、寝る前や外出前に走らせるのがおすすめです。

処理が終わると「全処理完了!」と表示され、organized_dir\<実行日>\<年> の下に整理された写真ライブラリができあがっています。

サンプルデータで動作確認したい場合

実際のTakeout(数十GB級)を流し込む前に、ダミーデータで動作確認したい場合は、zipに同梱されている tools/generate_sample.bat が使えます。

tools\generate_sample.bat

これを実行すると、./sample_takeout/ に以下の2つのZIPが生成されます。

  • sample_takeout/takeout-001.zip (2020/2021/2022 年の写真 + JSON)
  • sample_takeout/takeout-002.zip (2023/2024 年 + JSONなしの1枚)

その後 config.ini を以下のように設定して run.bat を実行すれば、sample_organized/ 以下に年フォルダで分類されます。

[paths]
zip_dir       = C:\path\to\google_photo_backup\sample_takeout
extract_dir   = C:\path\to\google_photo_backup\sample_takeout\_extracted
organized_dir = C:\path\to\google_photo_backup\sample_organized

sample_takeout/sample_organized/.gitignore に入っているので、展開したフォルダを汚す心配はありません。

使い方チュートリアル(シナリオ別)

ここでは、想定される3つのシナリオに沿って、実際の操作手順を紹介します。

シナリオA: 一括処理(普通のユースケース)

最も典型的な使い方です。
「TakeoutのZIPが10個あるので全部処理したい」場合は、これだけでOKです。

  • Takeoutのダウンロード: Google Takeout で「Google フォト」だけを選択し、ZIPで書き出します。書き出しサイズは2GBか10GBから選べますが、10GBのほうが分割数が減って楽です。
  • ZIPを1つのフォルダに集める: ダウンロードしたZIPを全て、zip_dir で指定したフォルダに置きます。解凍は不要です。
  • run.bat をダブルクリック: あとは終わるまで待つだけです。

進捗はコマンドプロンプトに表示されます。
各ZIPごとに「解凍中 → EXIF適用中 → 整理中 → 一時ファイル削除」の4ステップが進んでいきます。

シナリオB: EXIF適用だけやりたい

「すでにZIPは解凍済みで、JSONサイドカーから日時だけ画像に書き戻したい」場合は、個別のサブコマンドが使えます。

:: ドライランで何が変わるかを確認
run.bat apply-exif "C:\path\to\extracted" --dry-run

:: 確認できたら本番実行
run.bat apply-exif "C:\path\to\extracted"

ドライラン(--dry-run)を付けるとファイルを変更せず、何が書き込まれる予定なのかをログで確認できます。
最初は必ずドライランで試すことをおすすめします。

シナリオC: 整理だけ・重複削除だけやりたい

:: 撮影日ごとに年フォルダへ整理
run.bat organize "C:\path\to\extracted" "C:\path\to\Organized"

:: ハッシュベースで重複ファイル削除(まずドライラン)
run.bat dedup "C:\path\to\Organized" --dry-run
run.bat dedup "C:\path\to\Organized"

organize は撮影日(EXIF → JSON → ファイル名 → mtime の順で判定)を元に年フォルダへコピーします。
元ファイルは削除しないので、後から extract_dir を消す運用です。

dedup は同サイズ → MD5ハッシュ一致でグループ化し、最も短い名前のファイルを残します(オリジナル相当の可能性が高いため)。
これも本番前に必ずドライランで確認してください。

各機能の詳細リファレンス

ここからはコマンドや設定ファイルの細かい仕様を紹介します。
読み飛ばして後から戻ってきても問題ありません。

コマンド一覧

run.bat から引数を渡すか、.venv を有効化して python main.py を直接呼べます。

:: config.ini に従って一括処理
run.bat
run.bat run

:: JSON サイドカー → EXIF (画像のみ書き換え)
run.bat apply-exif  [--dry-run]

:: 撮影日ごとに年フォルダへ整理
run.bat organize   [--dry-run]

:: ハッシュベースで重複ファイル削除
run.bat dedup  [--dry-run]

:: ヘルプ
run.bat --help

引数なしで run.bat を起動した場合は、run サブコマンド(=一括処理)と同じ動作になります。

config.iniの全項目

[paths]
zip_dir       = ZIP の置き場所
extract_dir   = 一時解凍先 (処理後に自動削除)
organized_dir = 整理済み写真の出力先

[options]
run_dedup      = false   ; 整理後にハッシュ重複削除するか
keep_extracted = false   ; 一時ファイルを残すか (デバッグ用)

[options] セクションは省略可能です(省略時は全て false)。

config.iniの[options]セクション(run_dedup / keep_extracted)の説明表

撮影日判定の優先順位

organize コマンドは、各画像の撮影日を以下の順序で判定します。

  • EXIF DateTimeOriginal: 画像本体のEXIFに書かれた撮影日時(apply-exif 後はここに入っている)
  • JSONサイドカー: 隣にある .supplemental-metadata.json の photoTakenTime
  • ファイル名: ファイル名に YYYYMMDD 形式の8桁数字が含まれていれば、それを撮影日とみなす(例: IMG_20210101_120000.jpg)
  • ファイルのmtime(更新日時): 上記すべて当たらない場合のフォールバック

EXIFが書き込まれていない画像(HEIC、PNG など)でも、JSONサイドカーやファイル名から日付を補えるので、実用上はほとんどの画像が正しい年に分類されます。

EXIF書き込みの対応形式

EXIF書き込みの対応形式(拡張子ごとのEXIF書き込み可否と整理の可否)

EXIFが書き込めない形式でも、整理ステップで年フォルダに正しく分類されるので、結果として写真管理アプリでは正しい撮影年順に並びます。
HEICが多いiPhoneユーザーでも実用上は困らないはずです。

重複判定のロジック

dedup コマンドは2段階でグループ化します。

  • ファイルサイズで一次グループ化: 同サイズのファイルだけを候補に絞ります(全ファイルのMD5を取らないので高速)
  • MD5ハッシュで二次グループ化: 一次グループ内でMD5を計算し、ハッシュが一致するファイル群を「重複」と判定します
  • 保持するファイル: 重複グループの中で最も短いファイル名のものを残し、残りを削除します(オリジナルの可能性が高いため)

メディア拡張子(.jpg .jpeg .png .heic .mp4 等)以外は対象外なので、.txt.json といったファイルは触りません。

Google Takeout の JSON 命名バリエーション対応

ここはこのツールで一番気合を入れた部分です。
Googleは2024年後半にJSONサイドカーの命名を *.json から *.supplemental-metadata.json に変更しました。
さらに46文字でファイル名を切り詰める仕様があるため、現実のTakeoutには複数の命名形式が混在します。
本ツールは以下のパターンをすべて自動検出します。

Google TakeoutのJSON命名バリエーション対応(新形式・旧形式・切り詰め・重複サフィックス・拡張子なし)

これらのパターンは tools/test_takeout_naming.py に14ケースのテストとして網羅してあります。
新しいパターンが見つかったら、テストケースを追加して対応するという運用にしています。

書き込まれるEXIFタグ

apply-exif コマンドが画像に書き込むEXIFタグは以下の通りです。
既存のEXIFタグ(Make、Model など)は保持され、新しい情報が追記されます(完全に上書きされるわけではない、という挙動です)。

書き込まれるEXIFタグ(DateTimeOriginal・GPS・ImageDescriptionと元データの対応)

GPSが0,0(=データ欠如)の場合は、GPSタグは書き込まれません(座標0,0という間違った位置情報になるのを防ぐため)。

内部のディレクトリ構造

google_photo_backup/         ← zipを展開してできるフォルダ
├── main.py                  ← CLIエントリポイント
├── gphoto_backup/           ← コアパッケージ
│   ├── apply_exif.py        ← JSON → EXIF
│   ├── organize.py          ← 年フォルダ振り分け
│   ├── dedup.py             ← 重複削除
│   ├── pipeline.py          ← 一括処理
│   └── config.py            ← config.ini ローダ
├── config.example.ini       ← 設定ひな形
├── requirements.txt
├── setup.bat / run.bat      ← Windows用ワンクリック
├── tools/
│   ├── generate_sample.py   ← 動作確認用ダミーTakeout生成
│   ├── test_edge_cases.py   ← エッジケーステスト(17ケース)
│   ├── test_more_cases.py   ← 追加エッジケーステスト(25ケース)
│   └── test_takeout_naming.py ← JSON命名バリエーション(14ケース)
└── README.md / LICENSE

各モジュールは独立したコマンドとしても呼び出せます。
apply_exif.py だけ自分のスクリプトに組み込みたい、といった使い方も可能です。

よくあるトラブルとQ&A

セットアップ関連

Q: setup.bat を実行すると「Python が見つかりません」と出る

Pythonがインストールされていないか、PATHが通っていません。
python.org からPython 3.8以降をダウンロードし、インストール時に「Add Python to PATH」のチェックを必ず有効にしてから再実行してください。

Q: pip install がネットワークエラーで失敗する

社内プロキシ環境などでよくある現象です。
プロキシ設定を環境変数に通すか、以下のコマンドで手動再実行してください。

.venv\Scripts\python -m pip install -r requirements.txt

Q: 仮想環境を作り直したい

展開したフォルダ直下の .venv フォルダを丸ごと削除して、もう一度 setup.bat をダブルクリックすれば作り直せます。

実行時のトラブル

Q: config.ini が見つかりません というエラーが出る

setup.bat を一度実行すれば自動的に作成されます。
手動で作る場合は、config.example.iniconfig.ini という名前でコピーしてください。

Q: [paths] に未設定の項目があります と言われる

config.ini[paths] セクションで、zip_dir / extract_dir / organized_dir のいずれかが空欄になっています。
3つとも絶対パスで設定してください。

Q: 実行中に文字化けする

コマンドプロンプト(cmd.exe)よりも、Windows TerminalまたはPowerShellでの使用をおすすめします。
日本語ファイル名や絵文字を含むファイル名でも崩れにくくなります。

Q: ZIPが見つからないと言われる

zip_dir で指定したフォルダ直下に、*.zip ファイルが存在するか確認してください。
サブフォルダの中に入れてしまっていると認識されません。

Q: 処理が途中で止まった/PCが落ちた

extract_dir を手動で削除してから、run.bat をもう一度実行してください。
整理済みのファイルは organized_dir に既にコピーされているので、重複として [DUPLICATE] 表示でスキップされます(再度コピーされることはありません)。

EXIF・整理関連

Q: HEICのEXIFが書き換わらない

これはpiexifライブラリの仕様で、HEIC形式へのEXIF書き込みは現状サポートされていません。
ただし整理ステップではJSON経由で日付を判定するので、年フォルダ分類は正しく行われます。
実用上は問題にならないはずです。

Q: 整理後にWindowsフォトで開くと、まだ「ダウンロード日」で表示される

Windowsフォトはファイルのmtime(更新日時)を「撮影日」として表示することがあります。
EXIFのDateTimeOriginalを優先するアプリ(Lightroom、Synology Photos、Immich など)では正しい撮影日で表示されるので、写真管理アプリ側を変えるのが解決策です。

どうしてもWindowsフォトで使いたい場合は、exiftool などで全ファイルのmtimeをDateTimeOriginalに合わせる後処理を入れる必要があります(本ツールには含まれていません)。

Q: ファイル名がGoogle Takeoutから変わってしまう

ファイル名は変えていません。
organize はファイル名をそのまま維持してコピーします。
元の IMG_1234.jpgIMG_1234.jpg のままです。

Q: 同じ写真が複数の年に分類された

撮影日判定の優先順位(EXIF → JSON → ファイル名 → mtime)が原因の可能性があります。
たとえば、EXIFに2020年が書かれているのにJSONに2021年と書かれていると、EXIFが優先されます(本ツールは「EXIFが正」というスタンスです)。

意図通りでない場合は、該当画像のEXIFをexiftoolなどで確認してみてください。

重複削除関連

Q: dedupが「同じ写真」と判定する基準は?

バイト単位で完全一致(MD5ハッシュ一致)した場合のみです。
ファイル名・撮影日・サイズが同じでも、内容が1バイトでも違えば別ファイルと判定します。

Q: 似た写真(連写やバースト撮影)を重複として削除できる?

できません。
あくまで完全一致のみです。
連写の中から良い1枚を選ぶような処理は、別の専用ツール(visipics、AntiDupl など)を使ってください。

Q: 重複削除で間違ってオリジナルが消えないか心配

最初は必ず --dry-run で確認してください。
本番実行する前に、削除されるファイル一覧がログに表示されるので、それを見て問題ないか確認できます。

Windows特有の問題

Q: パスが260文字を超えるファイルでエラーが出る

Windowsの LongPathsEnabled レジストリ設定に依存します。
gpedit.msc または regeditHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled を 1 にすれば回避できます。

Q: ネットワークドライブ(\\nas\photos\ など)で動く?

想定外です。
ローカルの書き込み可能なディスクで使用してください。
organized_dir をローカルに作って、後からNASに転送する運用をおすすめします。

コード配布と注意事項

コード一式はzipで配布しています。

google_photo_backup-clean.zip(35.2KB)

ライセンス

本ツールは MIT License で公開しています。
商用・非商用を問わず自由に使用・改変・再配布できますが、無保証です。

使用上の注意

  • 本ツールは個人利用を想定しています。Google Takeoutで取得できるのは自分のGoogleアカウントのデータのみです。他人のアカウントのデータを許可なく扱うことはしないでください。
  • 重要なデータを扱う前に、必ず別の場所にバックアップを取ることをおすすめします。本ツールはコピーベースで動作するので元データは保護されますが、それでも念のため。
  • 本ツールが使用しているライブラリ(piexif、Pillow)はそれぞれのライセンスに従います。requirements.txt を確認してください。

動作確認とテスト

開発時のテストとして、zipには以下の3種類のテストスクリプトを同梱しています。
手元で動作を確認したい方は、setup.bat を済ませた後に以下を実行してください。

:: 17件の基本エッジケース
.venv\Scripts\python.exe tools\test_edge_cases.py

:: 25件の追加エッジケース
.venv\Scripts\python.exe tools\test_more_cases.py

:: 14件のJSON命名バリエーション
.venv\Scripts\python.exe tools\test_takeout_naming.py

合計56件のテストが実行されます。
これらは私が開発中に「これ大丈夫かな」と思った具体的なケースを、ひとつずつテストとして残したものです。
代表的なテストケースを抜粋すると以下のような内容です。

  • 既存EXIFタグ(Make/Model)を保持したまま日時を追記できるか
  • 緯度経度の極値(±90、±180、赤道近傍)で正しいRefが書き込まれるか
  • 高度負値(海面下)でAltitudeRef=1になるか
  • Unicode(日本語+絵文字)ファイル名で動くか
  • 壊れたJSON、空のJSON、不正なtimestampでクラッシュしないか
  • 破損ZIPをスキップして他のZIPは処理されるか
  • 読み取り専用ファイルでクラッシュしないか
  • BOM付きJSONでも読めるか
  • 大文字拡張子(.JPG.PNG)も対象か

実装の詳細が気になる方は、tools/test_*.py を読むのが一番早いと思います。

更新履歴

  • 1.0.0: 初版リリース。一括処理パイプライン、EXIF適用、年フォルダ整理、ハッシュベース重複削除、Google Takeoutの命名バリエーション対応(新形式・旧形式・切り詰め・(N)サフィックス・拡張子なし)を実装。

まとめ

いかがでしたか。
Google Photos の Takeout が抱える「撮影日時が画像本体に入っていない」「ZIPが分割されている」「写真が重複している」という3つの問題を、run.bat のダブルクリックひとつで解決するツールを紹介しました。

ポイントを振り返ると以下の通りです。

  • setup.bat で仮想環境とパッケージを自動セットアップ
  • config.ini の3つのパスだけ書き換える
  • run.bat をダブルクリックして待つ

その結果として、撮影年フォルダで整理され、画像本体にEXIF(撮影日時・GPS・説明文)が書き込まれた、他の写真管理アプリでそのまま使える写真ライブラリが手元に残ります。

Google Photosからの卒業や、別サービスへの移行素材として使っていただけたらうれしいです。
私自身も家族の写真の長期バックアップ用途で使っていて、これが無いとTakeoutのZIPはずっと放置されたままだったと思います。

この記事が誰かの役に立てばうれしいです。