# bybenmorgan/google2amazon-photos architecture

> Architecture diagram of the GitHub repository bybenmorgan/google2amazon-photos, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-25). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/bybenmorgan/google2amazon-photos
- Repository: https://github.com/bybenmorgan/google2amazon-photos

## Overview

Photo Migration is a .NET CLI for preparing extracted Google Takeout media for Amazon Photos. Its user workflow is inventory or analyze an export, preview metadata decisions with `plan`, then use `prepare` to publish separate verified copies while preserving the source. The graph retains the distinct sidecar matching, embedded-metadata interpretation, planning, and publication stages, plus ExifTool integration. README documentation supports the major capabilities; excerpts are partial, so some internal call wiring is intentionally not shown.

Main entry points: [`src/PhotoMigration.Core/TakeoutPreparationService.cs`](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutPreparationService.cs) (Preparation service), [`src/PhotoMigration.Core/TakeoutMetadataPlanner.cs`](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutMetadataPlanner.cs) (Metadata planner), [`src/PhotoMigration.Cli/CliApplication.cs`](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Cli/CliApplication.cs) (Command dispatcher), [`src/PhotoMigration.Core/ExifToolProcessRunner.cs`](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/ExifToolProcessRunner.cs) (ExifTool runner)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_cli["CLI workflow"]
  node_cli["Command dispatcher<br/>[CliApplication.cs]"]
end

subgraph group_takeout["Takeout analysis"]
  node_inventory["File inventory<br/>[FileInventory.cs]"]
  node_analyzer["Takeout analyzer<br/>[TakeoutAnalyzer.cs]"]
  node_classifier["File classifier"]
  node_matcher["Sidecar matcher"]
  node_sidecar["Sidecar parser"]
end

subgraph group_metadata["Metadata planning"]
  node_planner["Metadata planner"]
  node_embedded["Embedded reader"]
  node_exifproc["ExifTool runner"]
  node_captureparse["Capture-time parser"]
  node_gpsparse["GPS parser"]
  node_decisions["Metadata decisions"]
end

subgraph group_publish["Verified publishing"]
  node_prep["Preparation service"]
  node_dest["Destination planner"]
  node_stager["Verified stager"]
  node_capturewrite["Capture-time writer"]
  node_captureverify["Capture-time verifier"]
  node_gpswrite["JPEG GPS writer"]
  node_gpsverify["JPEG GPS verifier"]
  node_unchanged["Unchanged publisher"]
  node_jpegpublish["Verified JPEG publisher"]
  node_capturepublish["Capture-time publisher"]
end

node_user(("CLI user"))
node_exiftool["ExifTool"]
node_source["Takeout files"]
node_output[("Output directory")]

node_user -->|"runs command"| node_cli
node_cli -->|"requests inventory"| node_inventory
node_cli -->|"requests analysis"| node_analyzer
node_cli -->|"requests plan"| node_planner
node_cli -->|"requests preparation"| node_prep
node_inventory -->|"reads files"| node_source
node_analyzer -->|"inventories files"| node_inventory
node_analyzer -->|"classifies files"| node_classifier
node_analyzer -->|"matches sidecars"| node_matcher
node_matcher -->|"parses candidates"| node_sidecar
node_planner -->|"inventories files"| node_inventory
node_planner -->|"matches sidecars"| node_matcher
node_planner -->|"reads metadata"| node_embedded
node_planner -->|"parses metadata"| node_sidecar
node_embedded -->|"runs ExifTool"| node_exifproc
node_exifproc -->|"invokes"| node_exiftool
node_planner -->|"builds plans"| node_decisions
node_decisions -->|"interprets dates"| node_captureparse
node_decisions -->|"interprets GPS"| node_gpsparse
node_prep -->|"plans metadata"| node_planner
node_prep -->|"plans destinations"| node_dest
node_prep -->|"stages copies"| node_stager
node_stager -->|"copies source"| node_source
node_prep -->|"writes capture time"| node_capturewrite
node_capturewrite -->|"runs ExifTool"| node_exifproc
node_prep -->|"verifies capture time"| node_captureverify
node_captureverify -->|"reads back metadata"| node_exifproc
node_prep -->|"writes JPEG GPS"| node_gpswrite
node_gpswrite -->|"runs ExifTool"| node_exifproc
node_prep -->|"verifies JPEG GPS"| node_gpsverify
node_gpsverify -->|"reads back metadata"| node_exiftool
node_prep -->|"publishes unchanged"| node_unchanged
node_prep -->|"publishes verified JPEG"| node_jpegpublish
node_prep -->|"publishes verified copy"| node_capturepublish
node_unchanged -->|"writes copy"| node_output
node_jpegpublish -->|"publishes copy"| node_output
node_capturepublish -->|"publishes copy"| node_output

click node_cli "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Cli/CliApplication.cs"
click node_inventory "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/FileInventory.cs"
click node_analyzer "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutAnalyzer.cs"
click node_classifier "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutFileClassifier.cs"
click node_matcher "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutSidecarMatcher.cs"
click node_sidecar "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutSidecarParser.cs"
click node_prep "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutPreparationService.cs"
click node_planner "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutMetadataPlanner.cs"
click node_embedded "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/ExifToolMetadataReader.cs"
click node_exifproc "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/ExifToolProcessRunner.cs"
click node_captureparse "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/EmbeddedCaptureTimeParser.cs"
click node_gpsparse "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/EmbeddedGpsParser.cs"
click node_decisions "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/MediaMetadataPlanBuilder.cs"
click node_dest "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/DestinationPathPlanner.cs"
click node_stager "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedMediaFileStager.cs"
click node_capturewrite "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/AmazonCaptureTimeMetadataWriter.cs"
click node_captureverify "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/AmazonCaptureTimeMetadataWriteVerifier.cs"
click node_gpswrite "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/JpegGpsMetadataWriter.cs"
click node_gpsverify "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/JpegGpsMetadataWriteVerifier.cs"
click node_unchanged "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedUnchangedMediaPublisher.cs"
click node_jpegpublish "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedJpegPublisher.cs"
click node_capturepublish "https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedAmazonCaptureTimePublisher.cs"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_cli,node_user toneBlue
class node_inventory,node_analyzer,node_classifier,node_matcher,node_sidecar,node_output toneAmber
class node_planner,node_embedded,node_exifproc,node_captureparse,node_gpsparse,node_decisions toneMint
class node_prep,node_dest,node_stager,node_capturewrite,node_captureverify,node_gpswrite,node_gpsverify,node_unchanged,node_jpegpublish,node_capturepublish toneRose
class node_exiftool,node_source toneIndigo
```

## Components

### CLI workflow

- [Command dispatcher](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Cli/CliApplication.cs): `src/PhotoMigration.Cli/CliApplication.cs`

### Takeout analysis

- [File inventory](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/FileInventory.cs): `src/PhotoMigration.Core/FileInventory.cs`
- [Takeout analyzer](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutAnalyzer.cs): `src/PhotoMigration.Core/TakeoutAnalyzer.cs`
- [File classifier](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutFileClassifier.cs): `src/PhotoMigration.Core/TakeoutFileClassifier.cs`
- [Sidecar matcher](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutSidecarMatcher.cs): `src/PhotoMigration.Core/TakeoutSidecarMatcher.cs`
- [Sidecar parser](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutSidecarParser.cs): `src/PhotoMigration.Core/TakeoutSidecarParser.cs`

### Metadata planning

- [Metadata planner](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutMetadataPlanner.cs): `src/PhotoMigration.Core/TakeoutMetadataPlanner.cs`
- [Embedded reader](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/ExifToolMetadataReader.cs): `src/PhotoMigration.Core/ExifToolMetadataReader.cs`
- [ExifTool runner](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/ExifToolProcessRunner.cs): `src/PhotoMigration.Core/ExifToolProcessRunner.cs`
- [Capture-time parser](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/EmbeddedCaptureTimeParser.cs): `src/PhotoMigration.Core/EmbeddedCaptureTimeParser.cs`
- [GPS parser](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/EmbeddedGpsParser.cs): `src/PhotoMigration.Core/EmbeddedGpsParser.cs`
- [Metadata decisions](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/MediaMetadataPlanBuilder.cs): `src/PhotoMigration.Core/MediaMetadataPlanBuilder.cs`

### Verified publishing

- [Preparation service](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/TakeoutPreparationService.cs): `src/PhotoMigration.Core/TakeoutPreparationService.cs`
- [Destination planner](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/DestinationPathPlanner.cs): `src/PhotoMigration.Core/DestinationPathPlanner.cs`
- [Verified stager](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedMediaFileStager.cs): `src/PhotoMigration.Core/VerifiedMediaFileStager.cs`
- [Capture-time writer](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/AmazonCaptureTimeMetadataWriter.cs): `src/PhotoMigration.Core/AmazonCaptureTimeMetadataWriter.cs`
- [Capture-time verifier](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/AmazonCaptureTimeMetadataWriteVerifier.cs): `src/PhotoMigration.Core/AmazonCaptureTimeMetadataWriteVerifier.cs`
- [JPEG GPS writer](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/JpegGpsMetadataWriter.cs): `src/PhotoMigration.Core/JpegGpsMetadataWriter.cs`
- [JPEG GPS verifier](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/JpegGpsMetadataWriteVerifier.cs): `src/PhotoMigration.Core/JpegGpsMetadataWriteVerifier.cs`
- [Unchanged publisher](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedUnchangedMediaPublisher.cs): `src/PhotoMigration.Core/VerifiedUnchangedMediaPublisher.cs`
- [Verified JPEG publisher](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedJpegPublisher.cs): `src/PhotoMigration.Core/VerifiedJpegPublisher.cs`
- [Capture-time publisher](https://github.com/bybenmorgan/google2amazon-photos/blob/main/src/PhotoMigration.Core/VerifiedAmazonCaptureTimePublisher.cs): `src/PhotoMigration.Core/VerifiedAmazonCaptureTimePublisher.cs`

### Other components

- **CLI user**
- **ExifTool**
- **Takeout files**
- **Output directory**

## Connections

- CLI user → Command dispatcher: runs command
- Command dispatcher → File inventory: requests inventory
- Command dispatcher → Takeout analyzer: requests analysis
- Command dispatcher → Metadata planner: requests plan
- Command dispatcher → Preparation service: requests preparation
- File inventory → Takeout files: reads files
- Takeout analyzer → File inventory: inventories files
- Takeout analyzer → File classifier: classifies files
- Takeout analyzer → Sidecar matcher: matches sidecars
- Sidecar matcher → Sidecar parser: parses candidates
- Metadata planner → File inventory: inventories files
- Metadata planner → Sidecar matcher: matches sidecars
- Metadata planner → Embedded reader: reads metadata
- Metadata planner → Sidecar parser: parses metadata
- Embedded reader → ExifTool runner: runs ExifTool
- ExifTool runner → ExifTool: invokes
- Metadata planner → Metadata decisions: builds plans
- Metadata decisions → Capture-time parser: interprets dates
- Metadata decisions → GPS parser: interprets GPS
- Preparation service → Metadata planner: plans metadata
- Preparation service → Destination planner: plans destinations
- Preparation service → Verified stager: stages copies
- Verified stager → Takeout files: copies source
- Preparation service → Capture-time writer: writes capture time
- Capture-time writer → ExifTool runner: runs ExifTool
- Preparation service → Capture-time verifier: verifies capture time
- Capture-time verifier → ExifTool runner: reads back metadata
- Preparation service → JPEG GPS writer: writes JPEG GPS
- JPEG GPS writer → ExifTool runner: runs ExifTool
- Preparation service → JPEG GPS verifier: verifies JPEG GPS
- JPEG GPS verifier → ExifTool: reads back metadata
- Preparation service → Unchanged publisher: publishes unchanged
- Preparation service → Verified JPEG publisher: publishes verified JPEG
- Preparation service → Capture-time publisher: publishes verified copy
- Unchanged publisher → Output directory: writes copy
- Verified JPEG publisher → Output directory: publishes copy
- Capture-time publisher → Output directory: publishes copy
