Thanks to visit codestin.com
Credit goes to github.com

Skip to content

A photo organizer for your file system without sticking to any application or vendor. Extract when and where (reverse geocoding) your photos are taken, archive or copy into a new organized folder with various folder & file naming strategies with an album support to categorize, list & view easily with a metadata stored on local SQLite & CSV support.

License

Notifications You must be signed in to change notification settings

photo-cli/photo-cli

A photo organizer for your file system without sticking to any application or vendor

Nuget release Nuget download count Docker Image Version Homebrew Coverage .github/workflows/CI.yml .github/workflows/stable.yml

Docs Quality Gate Status Reliability Rating Maintainability Rating Security Rating Bugs GitHub license Nuget pre-release .github/workflows/preview.yml

photo-cli is CLI tool (works on Linux & macOS & Windows) that extracts when and where (reverse geocode) your photographs are taken, archive or copy into a new organized folder (not modifying source folder) with various folder & file naming strategies with an album support to categorize, list & view easily. All photo metadata is stored on local SQLite for archive operation and CSV for others. On CSV file (can view on Microsoft Excel, Libre/OpenOffice Calc, Apple Numbers, Google Sheets), you can navigate your photo locations on Google Maps & Earth with your custom label and pin style.

Contents

Features Explained With Examples

There is five main feature that can be explained better with examples.

  1. Archive & index with albums into a specific folder with metadata stored locally on SQLite with photo-cli archive command
  2. Copy into a new organized folder example with photo-cli copy command
  3. List/Open Photos by their metadata on Archived Folder
  4. Export all extracted information into a CSV Report With photo-cli info Command
  5. Navigate Your Photo Locations on Google Maps & Earth

1. Archive & index with albums into a specific folder with metadata stored locally on SQLite with photo-cli archive command

Folder & File Hierarchy Before -> After

Original Folder Hierarchy After photo-cli
├── DSC_5727.jpg
├── GOPR6742.jpg
├── Italy album
│   ├── DJI_01732.jpg
│   ├── DJI_01733.jpg
│   ├── DSC00001.JPG
│   ├── DSC03467.jpg
│   ├── DSC_1769.JPG
│   ├── DSC_1770.JPG
│   ├── DSC_1770_(same).jpg
│   ├── DSC_1771.JPG
│   ├── GOPR7496.jpg
│   ├── GOPR7497.jpg
│   ├── IMG_0747.JPG
│   ├── IMG_1979.HEIC
│   ├── IMG_1979.mov
│   ├── IMG_1979.xmp
│   ├── IMG_2371.jpg
│   └── IMG_O1979.aae
└── Spain Journey
    ├── DSC_1807.jpg
    ├── DSC_1808.jpg
    └── IMG_5397.jpg

2 directories, 21 files

├── 2005
│   ├── 08
│   │   └── 13
│   │       └── 2005.08.13_09.47.23-5842c73cfdc5f347551bb6016e00c71bb1393169.jpg
│   └── 12
│       └── 14
│           └── 2005.12.14_14.39.47-03cb14d5c68beed97cbe73164de9771d537fcd96.jpg
├── 2008
│   ├── 07
│   │   └── 16
│   │       └── 2008.07.16_11.33.20-90d835861e1aa3c829e3ab28a7f01ec3a090f664.jpg
│   └── 10
│       └── 22
│           ├── 2008.10.22_16.28.39-5d66eec547469a1817bda4abe35c801359b2bb55.jpg
│           ├── 2008.10.22_16.29.49-629b0b141634d6c0906e49af448bec8d755ba32c.jpg
│           ├── 2008.10.22_16.38.20-620d23336a12ab54f9f0190fe93960a4dba2df59.jpg
│           ├── 2008.10.22_16.43.21-3b0a3215b4f66d7ff4804dd223f192c21aee71bc.jpg
│           ├── 2008.10.22_16.44.01-d470205a1d331a9d3765b3762b7c954bb8efc6ea.jpg
│           ├── 2008.10.22_16.46.53-f670f2bb6c54898894b06b083185b05086bd4e6e.jpg
│           ├── 2008.10.22_16.52.15-6b89a245809031ecc47789cdeaa332545330fc39.jpg
│           ├── 2008.10.22_16.55.37-dd42edcde2433a7df4a3d67bf61944a20884da89.jpg
│           └── 2008.10.22_17.00.07-a0ab699f5f99fce8ff49163e87c7590c2c9a66eb.jpg
├── 2012
│   └── 06
│       └── 22
│           └── 2012.06.22_19.52.31-bb649a18b3e7bb3df3701587a13f833749091817.jpg
├── 2015
│   └── 04
│       └── 10
│           ├── 2015.04.10_20.12.23-3907fc960f2873f40c8f35643dd444e0468be131.jpg
│           └── 2015.04.10_20.12.23-9f4e6d352ec172e1059571250655e376769080fe.jpg
├── 2025
│   └── 06
│       └── 03
│           ├── 2025.06.03_13.53.36-8a45af72730474e22582afbe72f53685d705a72c.heic
│           └── 2025.06.03_13.53.36-8a45af72730474e22582afbe72f53685d705a72c.mov
├── no-photo-taken-date
│   └── cf756397cc3ca81b2650c8801fd64e172504015a.jpg
└── photo-cli.sqlite3

20 directories, 19 files

What Happened? / How It Is Processed?

This archive process is done by running only the following single command;

photo-cli archive --input [relative|full folder path] --output [relative|full folder path] --album-type DateRange --album-name My-Album --auto-reverse-geocode-album --expected-day-range 7300 --delete-on-source --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city

Same command with shorter alias of all argument names & values

photo-cli archive -i [relative|full folder path] -o [relative|full folder path] -y 2 -a My-Album -s -w 7300 -f -e 2 -r country city
Console/terminal output (as progress may take time, for each operation completion status shown with progress)
Click to expand
[17:07:27] Searching photo main files: started
[17:07:27] Searching photo main files: finished. 18 photo(s) found.
[17:07:27] Searching photo companion files: started
[17:07:27] Searching photo companion files: finished. 1 companion file(s) found.
[17:07:27] No coordinate found on `Gps` directory. Path:</test-photographs/Spain Journey/IMG_5397.jpg>
[17:07:27] No coordinate found on `Gps` directory. Path:</test-photographs/Italy album/IMG_2371.jpg>
[17:07:28] Calculating file hashes: started
[17:07:28] Calculating file hashes: finished.
[17:07:28] This OpenStreetMapFoundation provider is using rate limit of 1 second(s) between each request
[17:07:28] Reverse Geocoding: started
[17:07:31] Requested address types: City on index #2, not found on OpenStreetMap's response. Available types found:
{"country_code":"gb","country":"United Kingdom","postcode":"SL4 2DR","suburb":"Sunninghill and Ascot","road":"Windsor Road"}
. Path:</test-photographs/GOPR6742.jpg>
[17:07:47] Reverse Geocoding: finished.
[17:07:47] Directory grouping: started
[17:07:47] Directory grouping: finished.
[17:07:47] Processing target folder: started
[17:07:47] Photo is skipped due to same photo has already been archived. Same photo paths: <test-photographs/Italy album/DSC_1770.JPG>, <
/test-photographs/Italy album/DSC_1770_(same).jpg>
[17:07:47] Processing target folder: finished.
[17:07:47] Verified all photo files copied successfully by comparing file hashes from original photo files.
[17:07:47] Archiving photos to SQLite: started
[17:07:47] Archiving photos to SQLite: finished.
[17:07:47] Saving new date range album: started
[17:07:47] Saving new date range album: finished.
[17:07:47] Saving reverse geocode albums: started
[17:07:47] Saving reverse geocode albums: finished.
[17:07:47] Deleting source files: started
[17:07:47] Deleting source files: finished.
[17:07:47] Deleting empty directories: started
[17:07:47] Deleting empty directories: finished.
                        Statistics
┌────────────────────────────────────────────────┬───────┐
│ Statistic                                      │ Count │
├────────────────────────────────────────────────┼───────┤
│ File System Error(s)                           │ 0     │
│ Photo(s) found                                 │ 18    │
│ Photo(s) copied                                │ 17    │
│ Photo(s) existed on the output                 │ 0     │
│ Photo(s) are skipped, they have the same photo │ 1     │
│ Directory/directories created                  │ 8     │
│                                                │       │
│ Companion file(s) found                        │ 1     │
│ Companion file(s) copied                       │ 1     │
│ Companion file(s) existed on the output        │ 0     │
│                                                │       │
│ Source photo file(s) deleted                   │ 17    │
│ Source companion file(s) deleted               │ 1     │
│ Source empty directory(ies) deleted            │ 1     │
│                                                │       │
│ User defined album created                     │ 1     │
│ User defined album updated                     │ 0     │
│ Auto address album created                     │ 15    │
│                                                │       │
│ Reverse geocode request sent                   │ 14    │
│ Reverse geocode evaluated from memory          │ 2     │
│ Reverse geocode evaluated from database        │ 0     │
│ Photo(s) has taken date and coordinate         │ 16    │
│ Photo(s) has taken date but no coordinate      │ 1     │
│ Photo(s) has coordinate but no taken date      │ 0     │
│ Photo(s) has no taken date and coordinate      │ 1     │
│                                                │       │
│ Photo(s) has unknown/invalid format            │ 0     │
│ Photo(s) caused unexpected error internally    │ 0     │
└────────────────────────────────────────────────┴───────┘
[17:07:47] Archive process completed successfully

Step By Step photo-cli archive Process

  1. Gather all photo paths in the source folder within subfolders.
  2. Gather all photo companion files (if there is any) is which used but not limited for storing metadata, edits, RAW format files stored with same file name. For example Live Photos on iPhone storing short video clip of photo with a mov extension.
  3. Extract EXIF data of each photograph's taken date and coordinate. As third-party reverse geocode is selected, we are building address with OpenStreetMap by using given administrative levels as city town for each photograph.
  4. As expected-day-range argument is given with, this is a validation between the photo taken date date range in days. If there is any photograph that has taken date outside the given day range, process won't start. This is optional argument to stop processing archive photos that are not within the expected given day range.
  5. Photos which doesn't have coordinate information or reverse geocode propery missing, would be listed on the output by their paths as warning.
  6. As verify argument is given, we are calculating file hashes of each photograph's file to verify that all photo files copied successfully by comparing file hashes from original photo files at the end of the process.
  7. On the output folder, photos will be placed on folder hierarchy by it's photo taken date's /[year]/[month]/[day]. For example: /2008/07/16/.
  8. Photo file names will be formatted as yyyy.MM.dd_HH.mm.ss-{sha1-hash-of-file}.{extension}. For example: 2008.07.16_11.33.20-90d835861e1aa3c829e3ab28a7f01ec3a090f664.jpg. Input file name is: IMG_2371.jpg. Companion files are copied with the same name with their original file extension.
  9. Input folder has duplicate photos with different names DSC_1770.JPG, DSC_1770_(same).JPG. We are only archiving one of them by comparing file hashes to output 2008/10/22/2008.10.22_17.00.07-a0ab699f5f99fce8ff49163e87c7590c2c9a66eb.jpg and logs warning to output with their paths.
  10. The photo that don't have any photo taken date Spain Journey/IMG_5397.jpg, copied into no-photo-taken-date folder with only a sha1-hash cf756397cc3ca81b2650c8801fd64e172504015a.jpg.
  11. After copying all photos, we are verifying that all photo files copied successfully by comparing file hashes. It guarantees that there won't be any corrupted photos that is caused by disk operation failures.
  12. All photo taken dates, address information is saved on local SQLite database on the output folder's top most folder photo-cli.sqlite3 to able to open photos by their all metadata information.
  13. As album-name argument is given with value of My-Album with a given album type of DateRange. We are creating an album on the database with the earliest and latest photo taken date able to match photos by date range with the album name to open photographs later.
  14. As auto-reverse-geocode-album argument is given, we are creating albums on the database by each reverse geocode location property level to be able to open photos by the reverse geocode location. These are some of albums created regarding to sample photographs. Firenze , Venezia, Italia, United Kingdom.
  15. As delete-on-source argument is given, we are deleting all source photo files, companion files & empty directories after archiving process is completed successfully..
  16. Showing all the statistics of the process on the output.
Using photo-cli list command to see the Albums metadata of the previous output folder
photo-cli list --input [relative|full folder path] --type Albums

Same command with shorter alias of all argument names & values

photo-cli list -i [relative|full folder path] -t 1
Console/terminal output, rendering as table.
Click to expand
┌────┬──────────────────┬────────────────┬─────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────┐
│ Id │ Name             │ Type           │ Created At          │ Configuration                                                                            │
├────┼──────────────────┼────────────────┼─────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────┤
│ 1  │ My-Album         │ UserDefined    │ 2025-07-27 17:07:47 │ {"PhotoIds":[3],"DateRange":{"Start":"2005-08-13T09:47:23","End":"2025-06-03T13:53:36"}} │
│ 2  │ United Kingdom   │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"ReverseGeocodeFormatted":"United Kingdom"}}                          │
│ 3  │ Kenya-Barut ward │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"ReverseGeocodeFormatted":"Kenya-Barut ward"}}                        │
│ 4  │ España-Madrid    │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"ReverseGeocodeFormatted":"España-Madrid"}}                           │
│ 5  │ Italia-Arezzo    │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"ReverseGeocodeFormatted":"Italia-Arezzo"}}                           │
│ 6  │ Italia-Venezia   │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"ReverseGeocodeFormatted":"Italia-Venezia"}}                          │
│ 7  │ Italia-Firenze   │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"ReverseGeocodeFormatted":"Italia-Firenze"}}                          │
│ 8  │ United Kingdom   │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address1":"United Kingdom"}}                                         │
│ 9  │ Kenya            │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address1":"Kenya"}}                                                  │
│ 10 │ España           │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address1":"España"}}                                                 │
│ 11 │ Italia           │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address1":"Italia"}}                                                 │
│ 12 │ Barut ward       │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address2":"Barut ward"}}                                             │
│ 13 │ Madrid           │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address2":"Madrid"}}                                                 │
│ 14 │ Arezzo           │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address2":"Arezzo"}}                                                 │
│ 15 │ Venezia          │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address2":"Venezia"}}                                                │
│ 16 │ Firenze          │ ReverseGeocode │ 2025-07-27 17:07:47 │ {"ReverseGeocode":{"Address2":"Firenze"}}                                                │
└────┴──────────────────┴────────────────┴─────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────┘
Contents of the Photos table in photo-cli.sqlite3 SQLite Database in Markdown Table (output of archive command)
Click to expand
Id Path CreatedAt DateTaken ReverseGeocodeFormatted Latitude Longitude Year Month Day Hour Minute Seconds Address1 Address2 Address3 Address4 Address5 Address6 Address7 Address8 Sha1Hash IsDeleted ModifiedAt
1 2012/06/22/2012.06.22_19.52.31-bb649a18b3e7bb3df3701587a13f833749091817.jpg 2025-07-27 17:07:47.378073 2012-06-22 19:52:31 United Kingdom 51.4248 -0.6736 2012 6 22 19 52 31 United Kingdom null null null null null null null bb649a18b3e7bb3df3701587a13f833749091817 0 null
2 2005/08/13/2005.08.13_09.47.23-5842c73cfdc5f347551bb6016e00c71bb1393169.jpg 2025-07-27 17:07:47.378273 2005-08-13 09:47:23 Kenya-Barut ward -0.3713 36.0564 2005 8 13 9 47 23 Kenya Barut ward null null null null null null 5842c73cfdc5f347551bb6016e00c71bb1393169 0 null
3 no-photo-taken-date/cf756397cc3ca81b2650c8801fd64e172504015a.jpg 2025-07-27 17:07:47.378275 null null null null null null null null null null null null null null null null null null cf756397cc3ca81b2650c8801fd64e172504015a 0 null
4 2015/04/10/2015.04.10_20.12.23-9f4e6d352ec172e1059571250655e376769080fe.jpg 2025-07-27 17:07:47.378276 2015-04-10 20:12:23 España-Madrid 40.447 -3.7248 2015 4 10 20 12 23 España Madrid null null null null null null 9f4e6d352ec172e1059571250655e376769080fe 0 null
5 2015/04/10/2015.04.10_20.12.23-3907fc960f2873f40c8f35643dd444e0468be131.jpg 2025-07-27 17:07:47.378277 2015-04-10 20:12:23 España-Madrid 40.447 -3.7248 2015 4 10 20 12 23 España Madrid null null null null null null 3907fc960f2873f40c8f35643dd444e0468be131 0 null
6 2008/07/16/2008.07.16_11.33.20-90d835861e1aa3c829e3ab28a7f01ec3a090f664.jpg 2025-07-27 17:07:47.378278 2008-07-16 11:33:20 null null null 2008 7 16 11 33 20 null null null null null null null null 90d835861e1aa3c829e3ab28a7f01ec3a090f664 0 null
7 2008/10/22/2008.10.22_16.44.01-d470205a1d331a9d3765b3762b7c954bb8efc6ea.jpg 2025-07-27 17:07:47.378278 2008-10-22 16:44:01 Italia-Arezzo 43.4684 11.8815 2008 10 22 16 44 1 Italia Arezzo null null null null null null d470205a1d331a9d3765b3762b7c954bb8efc6ea 0 null
8 2008/10/22/2008.10.22_17.00.07-a0ab699f5f99fce8ff49163e87c7590c2c9a66eb.jpg 2025-07-27 17:07:47.378279 2008-10-22 17:00:07 Italia-Arezzo 43.4645 11.8815 2008 10 22 17 0 7 Italia Arezzo null null null null null null a0ab699f5f99fce8ff49163e87c7590c2c9a66eb 0 null
9 2008/10/22/2008.10.22_16.52.15-6b89a245809031ecc47789cdeaa332545330fc39.jpg 2025-07-27 17:07:47.37828 2008-10-22 16:52:15 Italia-Arezzo 43.4673 11.8792 2008 10 22 16 52 15 Italia Arezzo null null null null null null 6b89a245809031ecc47789cdeaa332545330fc39 0 null
10 2008/10/22/2008.10.22_16.55.37-dd42edcde2433a7df4a3d67bf61944a20884da89.jpg 2025-07-27 17:07:47.378281 2008-10-22 16:55:37 Italia-Arezzo 43.466 11.8791 2008 10 22 16 55 37 Italia Arezzo null null null null null null dd42edcde2433a7df4a3d67bf61944a20884da89 0 null
11 2008/10/22/2008.10.22_16.43.21-3b0a3215b4f66d7ff4804dd223f192c21aee71bc.jpg 2025-07-27 17:07:47.378282 2008-10-22 16:43:21 Italia-Arezzo 43.4684 11.8816 2008 10 22 16 43 21 Italia Arezzo null null null null null null 3b0a3215b4f66d7ff4804dd223f192c21aee71bc 0 null
12 2008/10/22/2008.10.22_16.29.49-629b0b141634d6c0906e49af448bec8d755ba32c.jpg 2025-07-27 17:07:47.378282 2008-10-22 16:29:49 Italia-Arezzo 43.4672 11.8854 2008 10 22 16 29 49 Italia Arezzo null null null null null null 629b0b141634d6c0906e49af448bec8d755ba32c 0 null
13 2008/10/22/2008.10.22_16.38.20-620d23336a12ab54f9f0190fe93960a4dba2df59.jpg 2025-07-27 17:07:47.378283 2008-10-22 16:38:20 Italia-Arezzo 43.4671 11.8845 2008 10 22 16 38 20 Italia Arezzo null null null null null null 620d23336a12ab54f9f0190fe93960a4dba2df59 0 null
14 2008/10/22/2008.10.22_16.28.39-5d66eec547469a1817bda4abe35c801359b2bb55.jpg 2025-07-27 17:07:47.378284 2008-10-22 16:28:39 Italia-Arezzo 43.4674 11.8851 2008 10 22 16 28 39 Italia Arezzo null null null null null null 5d66eec547469a1817bda4abe35c801359b2bb55 0 null
15 2008/10/22/2008.10.22_16.46.53-f670f2bb6c54898894b06b083185b05086bd4e6e.jpg 2025-07-27 17:07:47.378284 2008-10-22 16:46:53 Italia-Arezzo 43.4682 11.8802 2008 10 22 16 46 53 Italia Arezzo null null null null null null f670f2bb6c54898894b06b083185b05086bd4e6e 0 null
16 2025/06/03/2025.06.03_13.53.36-8a45af72730474e22582afbe72f53685d705a72c.heic 2025-07-27 17:07:47.378285 2025-06-03 13:53:36 Italia-Venezia 45.4332 12.3246 2025 6 3 13 53 36 Italia Venezia null null null null null null 8a45af72730474e22582afbe72f53685d705a72c 0 null
17 2005/12/14/2005.12.14_14.39.47-03cb14d5c68beed97cbe73164de9771d537fcd96.jpg 2025-07-27 17:07:47.378286 2005-12-14 14:39:47 Italia-Firenze 43.7856 11.2346 2005 12 14 14 39 47 Italia Firenze null null null null null null 03cb14d5c68beed97cbe73164de9771d537fcd96 0 null

2. Copy Into a New Organized Folder Example With photo-cli copy Command

Folder & File Hierarchy Before -> After

Original Folder Hierarchy After photo-cli
├── DSC_5727.jpg
├── GOPR6742.jpg
├── Italy album
│   ├── DJI_01732.jpg
│   ├── DJI_01733.jpg
│   ├── DSC00001.JPG
│   ├── DSC03467.jpg
│   ├── DSC_1769.JPG
│   ├── DSC_1770.JPG
│   ├── DSC_1770_(same).jpg
│   ├── DSC_1771.JPG
│   ├── GOPR7496.jpg
│   ├── GOPR7497.jpg
│   ├── IMG_0747.JPG
│   ├── IMG_1979.HEIC
│   ├── IMG_1979.mov
│   ├── IMG_1979.xmp
│   ├── IMG_2371.jpg
│   └── IMG_O1979.aae
└── Spain Journey
    ├── DSC_1807.jpg
    ├── DSC_1808.jpg
    └── IMG_5397.jpg

2 directories, 21 files

.
├── 2005.08.13_09.47.23-Kenya-Barut ward.jpg
├── 2005.12.14-2025.06.03-Italy album
│   ├── 2005.12.14_14.39.47-Italia-Firenze.jpg
│   ├── 2008.10.22_16.28.39-Italia-Arezzo.jpg
│   ├── 2008.10.22_16.29.49-Italia-Arezzo.jpg
│   ├── 2008.10.22_16.38.20-Italia-Arezzo.jpg
│   ├── 2008.10.22_16.43.21-Italia-Arezzo.jpg
│   ├── 2008.10.22_16.44.01-Italia-Arezzo.jpg
│   ├── 2008.10.22_16.46.53-Italia-Arezzo.jpg
│   ├── 2008.10.22_16.52.15-Italia-Arezzo.jpg
│   ├── 2008.10.22_16.55.37-Italia-Arezzo.jpg
│   ├── 2008.10.22_17.00.07-Italia-Arezzo-1.jpg
│   ├── 2008.10.22_17.00.07-Italia-Arezzo-2.jpg
│   ├── 2025.06.03_13.53.36-Italia-Venezia.heic
│   └── 2025.06.03_13.53.36-Italia-Venezia.mov
├── 2012.06.22_19.52.31-United Kingdom.jpg
├── 2015.04.10-2015.04.10-Spain Journey
│   ├── 2015.04.10_20.12.23-España-Madrid-1.jpg
│   └── 2015.04.10_20.12.23-España-Madrid-2.jpg
├── Italy album
│   └── no-address
│       └── IMG_2371.jpg
├── Spain Journey
│   └── no-address-and-no-photo-taken-date
│       └── IMG_5397.jpg
├── photo-cli-report.csv
└── sha1.lst

6 directories, 21 files

What Happened? / How It Is Processed?

There are lots of transformation options and customization settings, this is just a one of them. This transformation is done by running only the following single command;

Command with explicit argument names & values

photo-cli copy --process-type SubFoldersPreserveFolderHierarchy --naming-style DateTimeWithSecondsAddress --number-style PaddingZeroCharacter --folder-append DayRange --folder-append-location Prefix --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city --output photo-cli-test --no-coordinate InSubFolder --no-taken-date InSubFolder --verify --expected-day-range 7300 --missing-reverse-geocode Continue

Same command with shorter alias of all argument names & values

photo-cli copy -f 2 -s 8 -n 2 -a 4 -p 1 -e 2 -r country city -o photo-cli-test -c 3 -t 3 -v -w 7300 -z 0
Console/terminal output (as progress may take time, for each operation completion status shown with progress)
Click to expand
[17:07:28] Searching photo main files: started
[17:07:28] Searching photo main files: finished. 18 photo(s) found.
[17:07:28] Searching photo companion files: started
[17:07:28] Searching photo companion files: finished. 1 companion file(s) found.
[17:07:28] No coordinate found on `Gps` directory. Path:<
/Users/ac/src/photo-cli/docs/test-photographs/Spain Journey/IMG_5397.jpg>
[17:07:28] No coordinate found on `Gps` directory. Path:<
/Users/ac/src/photo-cli/docs/test-photographs/Italy album/IMG_2371.jpg>
[17:07:28] This OpenStreetMapFoundation provider is using rate limit of 1
second(s) between each request
[17:07:28] Reverse Geocoding: started
[17:07:29] Requested address types: City on index #2, not found on OpenStreetMap's response. Available types found:
{"country_code":"gb","country":"United Kingdom","postcode":"SL4 2DR","suburb":"Sunninghill and Ascot","road":"Windsor Road"}
. Path:</Users/ac/src/photo-cli/docs/test-photographs/GOPR6742.jpg>
[17:07:44] Reverse Geocoding: finished.
[17:07:44] Directory grouping: started
[17:07:44] Directory grouping: finished.
[17:07:44] Processing target folder: started
[17:07:45] Processing target folder: finished.
[17:07:45] Verified all photo files copied successfully by comparing file hashes from original photo files.
[17:07:45] All files SHA1 hashes written into file: sha1.lst. You may verify yourself with `sha1sum --check sha1.lst` tool in Linux/macOS.
[17:07:45] Writing csv report: started
[17:07:45] Writing csv report: finished.
                        Statistics
┌────────────────────────────────────────────────┬───────┐ddress
│ Statistic                                      │ Count │
├────────────────────────────────────────────────┼───────┤
│ File System Error(s)                           │ 0     │
│ Photo(s) found                                 │ 18    │
│ Photo(s) copied                                │ 18    │
│ Photo(s) existed on the output                 │ 0     │
│ Photo(s) are skipped, they have the same photo │ 0     │
│ Directory/directories created                  │ 4     │
│                                                │       │
│ Companion file(s) found                        │ 1     │
│ Companion file(s) copied                       │ 1     │
│ Companion file(s) existed on the output        │ 0     │
│                                                │       │
│ Source photo file(s) deleted                   │ 0     │
│ Source companion file(s) deleted               │ 0     │
│ Source empty directory(ies) deleted            │ 0     │
│                                                │       │
│ User defined album created                     │ 0     │
│ User defined album updated                     │ 0     │
│ Auto address album created                     │ 0     │
│                                                │       │
│ Reverse geocode request sent                   │ 14    │
│ Reverse geocode evaluated from memory          │ 2     │
│ Reverse geocode evaluated from database        │ 0     │
│ Photo(s) has taken date and coordinate         │ 16    │
│ Photo(s) has taken date but no coordinate      │ 1     │
│ Photo(s) has coordinate but no taken date      │ 0     │
│ Photo(s) has no taken date and coordinate      │ 1     │
│                                                │       │
│ Photo(s) has unknown/invalid format            │ 0     │
│ Photo(s) caused unexpected error internally    │ 0     │
└────────────────────────────────────────────────┴───────┘
[17:07:45] Copy process completed successfully

Step By Step photo-cli copy Process

  1. Gather all photo paths in the source folder within subfolders.
  2. Extract EXIF data of each photograph's taken date and coordinate.
  3. As the file name strategy is selected as DateTimeWithSecondsAddress and it contains the address, by using third-party reverse geocode provider we are building the address with OpenStreetMap by using given administrative levels as city town suburb for each photograph.
  4. As the folder process type is selected as SubFoldersPreserveFolderHierarchy folder and file hierarchy at the new output folder will be the same.
  5. As the folder append type is selected as DayRange and folder append location is Prefix, folder names on output folder will be created with same name prefixed with a earliest and latest photograph taken date. For example: 2005.12.14-2008.10.22-Italy album (original folder name is Italy album)
  6. As the file name strategy is selected as DateTimeWithSecondsAddress each photograph file name would be copied as photo taken date unified with the address which is built from third party reverse geocode provider by photograph's coordinate. For example: 2012.06.22_19.52.31-United Kingdom-Ascot-Sunninghill and Ascot.jpg (original file name is GOPR6742.jpg)
  7. As no photograph taken date action is selected as InSubFolder and no coordinate action is selected as InSubFolder, photographs with no related EXIF data copied into a sub folder by obeying original folder hierarchy. For example: /Italy album/no-address/IMG_2371.jpg and /Spain Journey/no-address-and-no-photo-taken-date/IMG_5397.jpg
  8. As verify is added, it is verifying that all photo files copied successfully by comparing file hashes. By adding this, it guarantees that there won't be any corrupted photos that is caused by disk operation failures.
  9. To verify and see all information in one place, photo-cli-report.csv report will be created on the output file. Can be examined in Markdown table or CSV file.

Contents of photo-cli-report.csv File in Markdown Table (report of copy command)

Click to expand
PhotoPath PhotoNewPath PhotoDateTaken ReverseGeocodeFormatted Latitude Longitude PhotoTakenYear PhotoTakenMonth PhotoTakenDay PhotoTakenHour PhotoTakenMinute PhotoTakenSeconds Address1 Address2 Address3 Address4 Address5 Address6 Address7 Address8
/TestImages/DSC_5727.jpg photo-cli-test/2005.08.13_09.47.23-Kenya.jpg 08/13/2005 09:47:23 Kenya -0.37129999999999996 36.056416666666664 2005 8 13 9 47 23 Kenya
/TestImages/GOPR6742.jpg photo-cli-test/2012.06.22_19.52.31-United Kingdom-Ascot-Sunninghill and Ascot.jpg 06/22/2012 19:52:31 United Kingdom-Ascot-Sunninghill and Ascot 51.424838333333334 -0.6735616666666666 2012 6 22 19 52 31 United Kingdom Ascot Sunninghill and Ascot
/TestImages/Italy album/DSC03467.jpg photo-cli-test/2005.12.14-2008.10.22-Italy album/2005.12.14_14.39.47-Italia-Firenze-Quartiere 1.jpg 12/14/2005 14:39:47 Italia-Firenze-Quartiere 1 43.78559443333333 11.234619433333334 2005 12 14 14 39 47 Italia Firenze Quartiere 1
/TestImages/Italy album/GOPR7497.jpg photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.28.39-Italia-Arezzo.jpg 10/22/2008 16:28:39 Italia-Arezzo 43.46744833333334 11.885126666663888 2008 10 22 16 28 39 Italia Arezzo
/TestImages/Italy album/DJI_01732.jpg photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.29.49-Italia-Arezzo.jpg 10/22/2008 16:29:49 Italia-Arezzo 43.46715666666389 11.885394999997223 2008 10 22 16 29 49 Italia Arezzo
/TestImages/Italy album/GOPR7496.jpg photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.38.20-Italia-Arezzo.jpg 10/22/2008 16:38:20 Italia-Arezzo 43.467081666663894 11.884538333330555 2008 10 22 16 38 20 Italia Arezzo
/TestImages/Italy album/DJI_01733.jpg photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.43.21-Italia-Arezzo.jpg 10/22/2008 16:43:21 Italia-Arezzo 43.468365 11.881634999972222 2008 10 22 16 43 21 Italia Arezzo
/TestImages/Italy album/DSC00001.JPG photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.44.01-Italia-Arezzo.jpg 10/22/2008 16:44:01 Italia-Arezzo 43.46844166666667 11.881515 2008 10 22 16 44 1 Italia Arezzo
/TestImages/Italy album/IMG_0747.JPG photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.46.53-Italia-Arezzo.jpg 10/22/2008 16:46:53 Italia-Arezzo 43.468243333330555 11.880171666638889 2008 10 22 16 46 53 Italia Arezzo
/TestImages/Italy album/DSC_1771.JPG photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.52.15-Italia-Arezzo.jpg 10/22/2008 16:52:15 Italia-Arezzo 43.46725499999722 11.879213333333334 2008 10 22 16 52 15 Italia Arezzo
/TestImages/Italy album/DSC_1769.JPG photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.55.37-Italia-Arezzo.jpg 10/22/2008 16:55:37 Italia-Arezzo 43.46601166663889 11.87911166663889 2008 10 22 16 55 37 Italia Arezzo
/TestImages/Italy album/DSC_1770.JPG photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_17.00.07-Italia-Arezzo-1.jpg 10/22/2008 17:00:07 Italia-Arezzo 43.464455 11.881478333333334 2008 10 22 17 0 7 Italia Arezzo
/TestImages/Italy album/DSC_1770_(same).jpg photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_17.00.07-Italia-Arezzo-2.jpg 10/22/2008 17:00:07 Italia-Arezzo 43.464455 11.881478333333334 2008 10 22 17 0 7 Italia Arezzo
/TestImages/Italy album/IMG_2371.jpg photo-cli-test/Italy album/no-address/IMG_2371.jpg 07/16/2008 11:33:20 2008 7 16 11 33 20
/TestImages/Spain Journey/DSC_1807.jpg photo-cli-test/2015.04.10-2015.04.10-Spain Journey/2015.04.10_20.12.23-España-Madrid-1.jpg 04/10/2015 20:12:23 España-Madrid 40.44697222222222 -3.724752777777778 2015 4 10 20 12 23 España Madrid
/TestImages/Spain Journey/DSC_1808.jpg photo-cli-test/2015.04.10-2015.04.10-Spain Journey/2015.04.10_20.12.23-España-Madrid-2.jpg 04/10/2015 20:12:23 España-Madrid 40.44697222222222 -3.724752777777778 2015 4 10 20 12 23 España Madrid
/TestImages/Spain Journey/IMG_5397.jpg photo-cli-test/Spain Journey/no-address-and-no-photo-taken-date/IMG_5397.jpg

Contents of photo-cli-report.csv File in Raw Text Format (report of copy command)

Click to expand
PhotoPath,PhotoNewPath,PhotoDateTaken,ReverseGeocodeFormatted,Latitude,Longitude,PhotoTakenYear,PhotoTakenMonth,PhotoTakenDay,PhotoTakenHour,PhotoTakenMinute,PhotoTakenSeconds,Address1,Address2,Address3,Address4,Address5,Address6,Address7,Address8
/TestImages/DSC_5727.jpg,photo-cli-test/2005.08.13_09.47.23-Kenya.jpg,08/13/2005 09:47:23,Kenya,-0.37129999999999996,36.056416666666664,2005,8,13,9,47,23,Kenya,,,,,,,
/TestImages/GOPR6742.jpg,photo-cli-test/2012.06.22_19.52.31-United Kingdom-Ascot-Sunninghill and Ascot.jpg,06/22/2012 19:52:31,United Kingdom-Ascot-Sunninghill and Ascot,51.424838333333334,-0.6735616666666666,2012,6,22,19,52,31,United Kingdom,Ascot,Sunninghill and Ascot,,,,,
/TestImages/Italy album/DSC03467.jpg,photo-cli-test/2005.12.14-2008.10.22-Italy album/2005.12.14_14.39.47-Italia-Firenze-Quartiere 1.jpg,12/14/2005 14:39:47,Italia-Firenze-Quartiere 1,43.78559443333333,11.234619433333334,2005,12,14,14,39,47,Italia,Firenze,Quartiere 1,,,,,
/TestImages/Italy album/GOPR7497.jpg,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.28.39-Italia-Arezzo.jpg,10/22/2008 16:28:39,Italia-Arezzo,43.46744833333334,11.885126666663888,2008,10,22,16,28,39,Italia,Arezzo,,,,,,
/TestImages/Italy album/DJI_01732.jpg,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.29.49-Italia-Arezzo.jpg,10/22/2008 16:29:49,Italia-Arezzo,43.46715666666389,11.885394999997223,2008,10,22,16,29,49,Italia,Arezzo,,,,,,
/TestImages/Italy album/GOPR7496.jpg,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.38.20-Italia-Arezzo.jpg,10/22/2008 16:38:20,Italia-Arezzo,43.467081666663894,11.884538333330555,2008,10,22,16,38,20,Italia,Arezzo,,,,,,
/TestImages/Italy album/DJI_01733.jpg,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.43.21-Italia-Arezzo.jpg,10/22/2008 16:43:21,Italia-Arezzo,43.468365,11.881634999972222,2008,10,22,16,43,21,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC00001.JPG,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.44.01-Italia-Arezzo.jpg,10/22/2008 16:44:01,Italia-Arezzo,43.46844166666667,11.881515,2008,10,22,16,44,1,Italia,Arezzo,,,,,,
/TestImages/Italy album/IMG_0747.JPG,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.46.53-Italia-Arezzo.jpg,10/22/2008 16:46:53,Italia-Arezzo,43.468243333330555,11.880171666638889,2008,10,22,16,46,53,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC_1771.JPG,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.52.15-Italia-Arezzo.jpg,10/22/2008 16:52:15,Italia-Arezzo,43.46725499999722,11.879213333333334,2008,10,22,16,52,15,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC_1769.JPG,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_16.55.37-Italia-Arezzo.jpg,10/22/2008 16:55:37,Italia-Arezzo,43.46601166663889,11.87911166663889,2008,10,22,16,55,37,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC_1770.JPG,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_17.00.07-Italia-Arezzo-1.jpg,10/22/2008 17:00:07,Italia-Arezzo,43.464455,11.881478333333334,2008,10,22,17,0,7,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC_1770_(same).jpg,photo-cli-test/2005.12.14-2008.10.22-Italy album/2008.10.22_17.00.07-Italia-Arezzo-2.jpg,10/22/2008 17:00:07,Italia-Arezzo,43.464455,11.881478333333334,2008,10,22,17,0,7,Italia,Arezzo,,,,,,
/TestImages/Italy album/IMG_2371.jpg,photo-cli-test/Italy album/no-address/IMG_2371.jpg,07/16/2008 11:33:20,,,,2008,7,16,11,33,20,,,,,,,,
/TestImages/Spain Journey/DSC_1807.jpg,photo-cli-test/2015.04.10-2015.04.10-Spain Journey/2015.04.10_20.12.23-España-Madrid-1.jpg,04/10/2015 20:12:23,España-Madrid,40.44697222222222,-3.724752777777778,2015,4,10,20,12,23,España,Madrid,,,,,,
/TestImages/Spain Journey/DSC_1808.jpg,photo-cli-test/2015.04.10-2015.04.10-Spain Journey/2015.04.10_20.12.23-España-Madrid-2.jpg,04/10/2015 20:12:23,España-Madrid,40.44697222222222,-3.724752777777778,2015,4,10,20,12,23,España,Madrid,,,,,,
/TestImages/Spain Journey/IMG_5397.jpg,photo-cli-test/Spain Journey/no-address-and-no-photo-taken-date/IMG_5397.jpg,,,,,,,,,,,,,,,,,,

3. List/Open Photos by their metadata on Archived Folder

After archiving our photos like the first example, we can list all the photos metadata and open the photos (currently only macOS Preview App is supported) by some filters.

Viewing Photos Taken on Year 2008 October in macOS Preview App

viewing-in-preview-app

This viewing process is done by running the following single command;

photo-cli list --input [relative|full existing archive path] --type PhotosByDate --year 2008 --month 10

Same command with shorter alias of all argument names & values

photo-cli list -i [relative|full existing archive path] -t 3 -y 2008 -m 10

Note: You can also open the photos by geolocation name (if you have used the argument auto-reverse-geocode-album while archiving) or by album ID.

Important note: If using Windows or Linux, you will get the list of full photo paths as output currently (like below) as there is no default photo viewer apps on these operating systems. If you want to open the photos on your photo viewer app of your, you can pipe this photo paths to your prefered photo viewer app.

Console Output of list command on Windows & Linux
Click to expand
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.29.49-629b0b141634d6c0906e49af448bec8d755ba32c.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.55.37-dd42edcde2433a7df4a3d67bf61944a20884da89.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_17.00.07-a0ab699f5f99fce8ff49163e87c7590c2c9a66eb.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.46.53-f670f2bb6c54898894b06b083185b05086bd4e6e.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.44.01-d470205a1d331a9d3765b3762b7c954bb8efc6ea.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.52.15-6b89a245809031ecc47789cdeaa332545330fc39.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.28.39-5d66eec547469a1817bda4abe35c801359b2bb55.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.38.20-620d23336a12ab54f9f0190fe93960a4dba2df59.jpg
/[archive-full-folder-path]/2008/10/22/2008.10.22_16.43.21-3b0a3215b4f66d7ff4804dd223f192c21aee71bc.jpg

4. Export all extracted information into a CSV Report With photo-cli info Command

Contents of photo-info.csv File in Markdown Table (output of info command)

Click to expand
PhotoPath PhotoNewPath PhotoDateTaken ReverseGeocodeFormatted Latitude Longitude PhotoTakenYear PhotoTakenMonth PhotoTakenDay PhotoTakenHour PhotoTakenMinute PhotoTakenSeconds Address1 Address2 Address3 Address4 Address5 Address6 Address7 Address8
/TestImages/DSC_5727.jpg 08/13/2005 09:47:23 Kenya -0.37129999999999996 36.056416666666664 2005 8 13 9 47 23 Kenya
/TestImages/GOPR6742.jpg 06/22/2012 19:52:31 United Kingdom-Ascot-Sunninghill and Ascot 51.424838333333334 -0.6735616666666666 2012 6 22 19 52 31 United Kingdom Ascot Sunninghill and Ascot
/TestImages/Italy album/DSC_1770.JPG 10/22/2008 17:00:07 Italia-Arezzo 43.464455 11.881478333333334 2008 10 22 17 0 7 Italia Arezzo
/TestImages/Italy album/DSC_1771.JPG 10/22/2008 16:52:15 Italia-Arezzo 43.46725499999722 11.879213333333334 2008 10 22 16 52 15 Italia Arezzo
/TestImages/Italy album/IMG_0747.JPG 10/22/2008 16:46:53 Italia-Arezzo 43.468243333330555 11.880171666638889 2008 10 22 16 46 53 Italia Arezzo
/TestImages/Italy album/IMG_2371.jpg 07/16/2008 11:33:20 2008 7 16 11 33 20
/TestImages/Italy album/DSC_1770_(same).jpg 10/22/2008 17:00:07 Italia-Arezzo 43.464455 11.881478333333334 2008 10 22 17 0 7 Italia Arezzo
/TestImages/Italy album/DJI_01733.jpg 10/22/2008 16:43:21 Italia-Arezzo 43.468365 11.881634999972222 2008 10 22 16 43 21 Italia Arezzo
/TestImages/Italy album/DSC00001.JPG 10/22/2008 16:44:01 Italia-Arezzo 43.46844166666667 11.881515 2008 10 22 16 44 1 Italia Arezzo
/TestImages/Italy album/DSC_1769.JPG 10/22/2008 16:55:37 Italia-Arezzo 43.46601166663889 11.87911166663889 2008 10 22 16 55 37 Italia Arezzo
/TestImages/Italy album/GOPR7497.jpg 10/22/2008 16:28:39 Italia-Arezzo 43.46744833333334 11.885126666663888 2008 10 22 16 28 39 Italia Arezzo
/TestImages/Italy album/DSC03467.jpg 12/14/2005 14:39:47 Italia-Firenze-Quartiere 1 43.78559443333333 11.234619433333334 2005 12 14 14 39 47 Italia Firenze Quartiere 1
/TestImages/Italy album/GOPR7496.jpg 10/22/2008 16:38:20 Italia-Arezzo 43.467081666663894 11.884538333330555 2008 10 22 16 38 20 Italia Arezzo
/TestImages/Italy album/DJI_01732.jpg 10/22/2008 16:29:49 Italia-Arezzo 43.46715666666389 11.885394999997223 2008 10 22 16 29 49 Italia Arezzo
/TestImages/Spain Journey/DSC_1807.jpg 04/10/2015 20:12:23 España-Madrid 40.44697222222222 -3.724752777777778 2015 4 10 20 12 23 España Madrid
/TestImages/Spain Journey/DSC_1808.jpg 04/10/2015 20:12:23 España-Madrid 40.44697222222222 -3.724752777777778 2015 4 10 20 12 23 España Madrid
/TestImages/Spain Journey/IMG_5397.jpg

Contents of photo-info.csv File in Raw Text Format (report of info command)

Click to expand
PhotoPath,PhotoNewPath,PhotoDateTaken,ReverseGeocodeFormatted,Latitude,Longitude,PhotoTakenYear,PhotoTakenMonth,PhotoTakenDay,PhotoTakenHour,PhotoTakenMinute,PhotoTakenSeconds,Address1,Address2,Address3,Address4,Address5,Address6,Address7,Address8
/TestImages/DSC_5727.jpg,,08/13/2005 09:47:23,Kenya,-0.37129999999999996,36.056416666666664,2005,8,13,9,47,23,Kenya,,,,,,,
/TestImages/GOPR6742.jpg,,06/22/2012 19:52:31,United Kingdom-Ascot-Sunninghill and Ascot,51.424838333333334,-0.6735616666666666,2012,6,22,19,52,31,United Kingdom,Ascot,Sunninghill and Ascot,,,,,
/TestImages/Italy album/DSC_1770.JPG,,10/22/2008 17:00:07,Italia-Arezzo,43.464455,11.881478333333334,2008,10,22,17,0,7,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC_1771.JPG,,10/22/2008 16:52:15,Italia-Arezzo,43.46725499999722,11.879213333333334,2008,10,22,16,52,15,Italia,Arezzo,,,,,,
/TestImages/Italy album/IMG_0747.JPG,,10/22/2008 16:46:53,Italia-Arezzo,43.468243333330555,11.880171666638889,2008,10,22,16,46,53,Italia,Arezzo,,,,,,
/TestImages/Italy album/IMG_2371.jpg,,07/16/2008 11:33:20,,,,2008,7,16,11,33,20,,,,,,,,
/TestImages/Italy album/DSC_1770_(same).jpg,,10/22/2008 17:00:07,Italia-Arezzo,43.464455,11.881478333333334,2008,10,22,17,0,7,Italia,Arezzo,,,,,,
/TestImages/Italy album/DJI_01733.jpg,,10/22/2008 16:43:21,Italia-Arezzo,43.468365,11.881634999972222,2008,10,22,16,43,21,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC00001.JPG,,10/22/2008 16:44:01,Italia-Arezzo,43.46844166666667,11.881515,2008,10,22,16,44,1,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC_1769.JPG,,10/22/2008 16:55:37,Italia-Arezzo,43.46601166663889,11.87911166663889,2008,10,22,16,55,37,Italia,Arezzo,,,,,,
/TestImages/Italy album/GOPR7497.jpg,,10/22/2008 16:28:39,Italia-Arezzo,43.46744833333334,11.885126666663888,2008,10,22,16,28,39,Italia,Arezzo,,,,,,
/TestImages/Italy album/DSC03467.jpg,,12/14/2005 14:39:47,Italia-Firenze-Quartiere 1,43.78559443333333,11.234619433333334,2005,12,14,14,39,47,Italia,Firenze,Quartiere 1,,,,,
/TestImages/Italy album/GOPR7496.jpg,,10/22/2008 16:38:20,Italia-Arezzo,43.467081666663894,11.884538333330555,2008,10,22,16,38,20,Italia,Arezzo,,,,,,
/TestImages/Italy album/DJI_01732.jpg,,10/22/2008 16:29:49,Italia-Arezzo,43.46715666666389,11.885394999997223,2008,10,22,16,29,49,Italia,Arezzo,,,,,,
/TestImages/Spain Journey/DSC_1807.jpg,,04/10/2015 20:12:23,España-Madrid,40.44697222222222,-3.724752777777778,2015,4,10,20,12,23,España,Madrid,,,,,,
/TestImages/Spain Journey/DSC_1808.jpg,,04/10/2015 20:12:23,España-Madrid,40.44697222222222,-3.724752777777778,2015,4,10,20,12,23,España,Madrid,,,,,,
/TestImages/Spain Journey/IMG_5397.jpg,,,,,,,,,,,,,,,,,,,

What Happened? / How It Is Processed?

There is some options and lots of customization settings, this is just a one of them. This information extracted is done by running only the following single command;

Command with explicit argument names & values

photo-cli info --all-folders --output photo-info.csv --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city --no-taken-date Continue --no-coordinate Continue --missing-reverse-geocode Continue

Same command with shorter alias of all argument names & values

photo-cli info -a -o photo-info.csv -e 2 -r country city -t 0 -c 0 -z 0
Console/terminal output (as progress may take time, for each operation completion status shown as percentage)
Click to expand
[17:07:33] Searching photo main files: started
[17:07:33] Searching photo main files: finished. 18 photo(s) found.
[17:07:33] No coordinate found on `Gps` directory. Path:</Users/ac/src/photo-cli/docs/test-photographs/Spain Journey/IMG_5397.jpg>
[17:07:33] No coordinate found on `Gps` directory. Path:</Users/ac/src/photo-cli/docs/test-photographs/Italy album/IMG_2371.jpg>
[17:07:33] Reverse Geocoding: started
[17:07:33] Requested address types: City on index #2, not found on OpenStreetMap's response. Available types found:
{"country_code":"gb","country":"United Kingdom","postcode":"SL4 2DR","suburb":"Sunninghill and Ascot","road":"Windsor Road"}
. Path:</Users/ac/src/photo-cli/docs/test-photographs/GOPR6742.jpg>
[17:07:49] Reverse Geocoding: finished.
[17:07:49] Writing csv report: started
[17:07:49] Writing csv report: finished.
                        Statistics
┌────────────────────────────────────────────────┬───────┐
│ Statistic                                      │ Count │
├────────────────────────────────────────────────┼───────┤
│ File System Error(s)                           │ 0     │
│ Photo(s) found                                 │ 18    │
│ Photo(s) copied                                │ 0     │
│ Photo(s) existed on the output                 │ 0     │
│ Photo(s) are skipped, they have the same photo │ 0     │
│ Directory/directories created                  │ 0     │
│                                                │       │
│ Companion file(s) found                        │ 0     │
│ Companion file(s) copied                       │ 0     │
│ Companion file(s) existed on the output        │ 0     │
│                                                │       │
│ Source photo file(s) deleted                   │ 0     │
│ Source companion file(s) deleted               │ 0     │
│ Source empty directory(ies) deleted            │ 0     │
│                                                │       │
│ User defined album created                     │ 0     │
│ User defined album updated                     │ 0     │
│ Auto address album created                     │ 0     │
│                                                │       │
│ Reverse geocode request sent                   │ 14    │
│ Reverse geocode evaluated from memory          │ 2     │
│ Reverse geocode evaluated from database        │ 0     │
│ Photo(s) has taken date and coordinate         │ 16    │
│ Photo(s) has taken date but no coordinate      │ 1     │
│ Photo(s) has coordinate but no taken date      │ 0     │
│ Photo(s) has no taken date and coordinate      │ 1     │
│                                                │       │
│ Photo(s) has unknown/invalid format            │ 0     │
│ Photo(s) caused unexpected error internally    │ 0     │
└────────────────────────────────────────────────┴───────┘

Step By Step photo-cli info Process

  1. As all folders is selected. We are gathering all photo paths in the source folder within subfolders.
  2. Extract EXIF data of each photograph's taken date and coordinate.
  3. As third-party reverse geocode is selected, we are building address with OpenStreetMap by using given administrative levels as city town suburb for each photograph.
  4. As no photograph taken date action is selected as Continue and no coordinate action is selected as Continue, they are listing in report with empty data.

5. Navigate Your Photo Locations on Google Maps & Earth

If you want to discover your photographs interactively in the world, you may do it by importing your CSV output (whether photo-cli copy or photo-cli info command) to Google Maps and Google Earth, you can interactively navigate through your photographs.

Google Maps

Open Google My Maps and after clicking Create a New Map, you can import your CSV file on a layer(you may add many layers).

google-maps

Google Earth Desktop

After installing Google Earth Desktop, on File menu, you can import your CSV file via Import menu item.

google-earth-desktop

Google Earth Web

To navigate your photographs on Google Earth Web, first you should import your CSV on Google Earth Desktop and save it as KMZ or KML. Then you can create a project and add this KML file.

google-earth-web

Installation

This application can be installed by Homebrew (macOS & Linux), container (Docker, Podman), standalone executable (without dependency and SDK) or as .NET tool.

See the installation for details.

Note: You may test commands on test photographs which has coordinates and photograph taken dates in it.

Sample Usage Screenshots

The following command used in all samples with test photographs

photo-cli archive --input [relative|full folder path] --output [relative|full folder path] --album-type DateRange --album-name My-Album --auto-reverse-geocode-album --expected-day-range 7300 --delete-on-source --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city

macOS

Executing

mac-os-execute

Opening Photos By Some Metadata Filter on Preview App

photo-cli list --input [relative|full existing archive path] --type PhotosByDate --year 2008 --month 10
Click to expand

mac-os-execute

Finder
Click to expand

mac-os-finder

Tree Command
Click to expand

mac-os-tree-command

Windows

Executing

windows-execute

File Explorer

Click to expand

windows-file-explorer

Tree Command

Click to expand

windows-tree-command

Linux

Executing

linux-execute

File Manager

Click to expand

linux-file-manager

Tree Command

Click to expand

linux-tree-command

Container - Docker / Podman

docker run --rm --volume ./test-photographs:/photos/input --volume ./archive:/photos/output photocli/photocli archive --input /photos/input --output /photos/output --album-type DateRange --album-name My-Album --auto-reverse-geocode-album --expected-day-range 7300 --delete-on-source --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city

Executing

container-execute

File Manager

Click to expand

container-file-system

Tree Command

Click to expand

conatiner-tree-command

How It's Done?

By extracting Exchangeable image file format stored on each of your photographs.

When

Photograph's taken date used to determine when photograph's date. Most of the camera/cell phones save this data without any setting.

Where

Photograph's coordinate data is sent to selected third-party reverse geocode provider to build a address.

Most of the cameras and cellphones have GPS receiver on them. You need to be sure that on settings something like Save GPS location should be enabled.

Supported Photo Types

With the default settings, we can process jpg, jpeg, heic and png photo files. As this tool internally using MetadataExtractor package to extract image EXIF data, you can extend the supported photos with the MetadataExtractor supported files by using settings verb's set action.

Setting Supported Extensions Example

settings -k SupportedExtensions -v jpg,ext1,ext2,ext3

Processing Companion Files

We are also processing the companion files with the same name of your main photo files. The default settings is currently only mov which is popular among iPhone Live Photos output is also exporting with a video file next to your photo.

You can extend this companion extension by using settings verb's set action.

Setting Companion Extensions Example

settings -k CompanionExtensions -v mov,ext1,ext2,ext3

Address Building & Reverse Geocoding

If you use only photo taken date and not interested in building address from reverse geocode, you can skip reading this section. But if you want to use address (reverse geocode) in file and/or folder naming, you should read the following sections and must learn the details.

1. Selecting Third-Party Reverse Geocode Provider

To build addresses we need a reserve geocode provider. Currently, there is four reverse geocode provider is supported.

  1. BigDataCloud
  2. Open Street Map Foundation - Nominatim
  3. Google Maps
  4. LocationIq

Comparison of Supported Third-Party Reverse Geocode Providers

Reverse Geocode Provider API Key Required Free Tier Free Count Limit Free Rate Limit (free) Map Data Owner
BigDataCloud Yes Yes - 50.000 req/month Big Data Cloud
Open Street Map Foundation - Nominatim No - - 1req/sec Open Street Map Foundation
GoogleMaps Yes No - - Google
LocationIq Yes Yes 5.000 req/day 1req/sec Open Street Map Foundation

2. Setting API Key

After selecting reverse geocode provider, you need to provide an API key. There are three ways to provide this API key;

  1. Send as an argument every time
  2. Use persist a setting to save it as configuration, so you don't need to submit everytime.
  3. Use environment variable, so you don't need to submit everytime.
Reverse Geocode Provider Settings Key Environment Variable Argument
BigDataCloud BigDataCloudApiKey PHOTO_CLI_BIG_DATA_CLOUD_API_KEY -b or --bigdatacloud-key
Open Street Map Foundation - Nominatim - - -
GoogleMaps GoogleMapsApiKey PHOTO_CLI_GOOGLE_MAPS_API_KEY -k or --googlemaps-key
LocationIq LocationIqApiKey PHOTO_CLI_LOCATIONIQ_API_KEY -q or --locationiq-key

3. Understanding Reverse Geocode Response

Every reverse geocode provider has its data and they also represent it very differently. The information returned from reverse geocode provider is different or may differ in the level of detail. As there is no way to generalize every reverse geocode provider's response into the same address administrative level, users must understand the response returned from their selected reverse geocode provider.

There is two way to understand the reverse geocoding response.

  1. Easy Way To Inspect Reserve Geocode Response
  2. Power User Way To Inspect Reserve Geocode Response

Easy Way To Inspect Reserve Geocode Response

photo-cli has a feature to extract and list the response of each reverse geocode provider. If you are using a reverse geocode provider that needs an API key, first you need to get it from the provider and set API key.

Listing Reverse Geocode Response. Ref: reverse geocode provider command line arguments

photo-cli address --input [input-file.jpg] --reverse-geocode [selected-reverse-geocode-provider]`

For example, a photo was taken on Anıtkabir(place), Çankaya(town), Ankara(city), Turkey(country) with coordinate as 39.925054 and longitude as 32.8347552 (Coordinate in Google Maps) responses should be like the following.

Big Data Cloud
photo-cli address -i DSC_7082.jpg -e 1
AdminLevel2: Turkey
AdminLevel3: Central Anatolia Region
AdminLevel4: Ankara Province
AdminLevel6: Çankaya
AdminLevel8: Mebusevleri Mahallesi
Open Street Map - Nominatim
photo-cli address -i DSC_7082.jpg -e 2
CountryCode: tr
Country: Türkiye
Region: İç Anadolu Bölgesi
Province: Ankara
City: Ankara
Town: Çankaya
Postcode: 06430
Suburb: Yücetepe Mahallesi
Road: İlk Sokak
Military: Anıtkabir
Google Maps
photo-cli address -i DSC_7082.jpg -e 3
plus_code: WRGM+2W
administrative_area_level_2: Çankaya
administrative_area_level_1: Ankara
country: Turkey
route: Anıtkabir
administrative_area_level_4: Mebusevleri
postal_code: 06570
street_number: 108
Map Quest
photo-cli address -i DSC_7082.jpg -e 4
CountryCode: tr
Country: Türkiye
City: Ankara
Postcode: 06580
Suburb: Mebusevleri Mahallesi
Location Iq
photo-cli address -i DSC_7082.jpg -e 5
CountryCode: tr
Country: Turkey
Region: Central Anatolia Region
Province: Ankara
City: Ankara
Town: Çankaya
Postcode: 06570
Suburb: Yücetepe Mahallesi
Road: Ata Sokak
Barracks: Anıtkabir

Power User Way To Inspect Reserve Geocode Response

You should inspect the reverse geocode provider's response with the locations you occasionally take photographs. After getting the response, you can send the properties as an argument of your choice of the administrative level you want to build an address for each photograph.

Using HTTP Files

To trigger HTTP files you may use Visual Studio Code with the extension REST Client or any IntelliJ IDE's.

You may replace {{ApiKey}} in the address with your API key or you can use the IntelliJ environment variable file.

Reverse Geocode Provider HTTP File
BigDataCloud big-data-cloud.http
Open Street Map Foundation - Nominatim open-street-map.http
GoogleMaps google-maps.http
LocationIq location-iq.http
Using Postman

You may import photo-cli | Reverse Geocode collection into Postman.

You should prepare the following environment variables on Postman.

Reverse Geocode Provider Postman Environment Variable
BigDataCloud BigDataCloud-ApiKey
Open Street Map Foundation - Nominatim -
GoogleMaps GoogleMaps-ApiKey
LocationIq LocationIq-ApiKey
Sample Responses

Sample responses in JSON format are listed below from each reverse geocode for the coordinate with latitude as 39.925054 and longitude as 32.8347552 (Coordinate in Google Maps).

Reverse Geocode Provider Sample Response
BigDataCloud big-data-cloud.json
Open Street Map Foundation - Nominatim open-street-map.json
GoogleMaps google-maps.json
LocationIq location-iq.json

4. Building Your Own Address With Selected Properties

Every reverse geocode provider has its address building parameters. With address command you can inspect any photograph's reverse geocode response. These different levels of selected address properties will be used in exported into CSV file for info command or used as file and/or folder names depending on your naming strategies for copy command.

Reverse Geocode Provider Address Building Parameters
BigDataCloud BigDataCloud Properties
Open Street Map Foundation - Nominatim OpenStreet Properties
GoogleMaps GoogleMaps Properties
LocationIq OpenStreet Properties

Big Data Cloud Parameters

Getting a sample reverse geocoding response with all properties listed.

photo-cli address -i DSC_7082.jpg -e 1
AdminLevel2: Turkey
AdminLevel3: Central Anatolia Region
AdminLevel4: Ankara Province
AdminLevel6: Çankaya
AdminLevel8: Mebusevleri Mahallesi

If we want to build an address like with levels only contains Turkey, Ankara Province, and Çankaya, we should use levels 2,4,6. To verify our address is building correctly, you may use type parameter as SelectedProperties and bigdatacloud-levels arguments separated with space like the following example.

photo-cli address --input DSC_7082.jpg --reverse-geocode BigDataCloud --type SelectedProperties --bigdatacloud-levels 2 4 6
Turkey
Ankara Province
Çankaya

Open Street Map Parameters

Getting a sample reverse geocoding response with all properties listed.

photo-cli address -i DSC_7082.jpg -e 2
CountryCode: tr
Country: Türkiye
Region: İç Anadolu Bölgesi
Province: Ankara
City: Ankara
Town: Çankaya
Postcode: 06430
Suburb: Yücetepe Mahallesi
Road: İlk Sokak
Military: Anıtkabir

If we want to build an address like with levels only contains tr, 06430, and Yücetepe Mahallesi, we should use properties CountryCode, Postcode, Suburb. To verify our address is building correctly, you may use type as SelectedProperties and openstreetmap-properties arguments separated with space like the following example.

photo-cli address --input DSC_7082.jpg --reverse-geocode OpenStreetMapFoundation --type SelectedProperties --openstreetmap-properties CountryCode Postcode Suburb
tr
06430
Yücetepe Mahallesi

Google Maps Properties

Getting a sample reverse geocoding response with all properties listed.

photo-cli address -i DSC_7082.jpg -e GoogleMaps
plus_code: WRGM+2W
administrative_area_level_2: Çankaya
administrative_area_level_1: Ankara
country: Turkey
route: Anıtkabir
administrative_area_level_4: Mebusevleri
postal_code: 06570
street_number: 108

If we want to build an address like with levels only contains Mebusevleri, 108, and Anıtkabir, we should use properties administrative_area_level_4, street_number, route. To verify our address is building correctly, you may use type as SelectedProperties and googlemaps-types arguments separated with space like the following example.

photo-cli address --input DSC_7082.jpg --reverse-geocode GoogleMaps --type SelectedProperties --googlemaps-types administrative_area_level_4 street_number route
Mebusevleri
108
Anıtkabir

5. Merging Selected Address Level Properties Into Single Address

After selecting our properties specialized by our selected third-party reverse geocode provider, we can use our address in file and folder names. To merge address levels, - character is used as default.

Example merged address may used in file/folder names: Turkey-Ankara-Çankaya-Mebusevleri-Anıtkabir

You may change default separator (-) via settings command with a setting key AddressSeparator

6. Caching Reverse Geocode Responses

Since the responses in close coordinate's requests in pretty close results, we implemented a caching mechanism for optimization. This is done by rounding the fractional digits of coordinates from the end. We are currently only use 4 fraction digits.

For example the original coordinate for 39.92501234567890, 32.83471234567890 will interpreted as 39.9250, 32.8347 internally before sending the request.

If you need more precise results in your reverse geocode responses, you can increase this value on settings with a key of CoordinatePrecision.

Usages

We can't cover all possible options, because there are so many option combination. Some important copy command examples with comparing of original photos directory structure and output directory of photo-cli listed below.

Names as Sequential Numbering in Same Folder Hierarchy

Preserve same folder hierarchy, copy photos with sequential number ordering by photo taken date.

photo-cli copy --process-type SubFoldersPreserveFolderHierarchy --naming-style Numeric --number-style PaddingZeroCharacter --input photos --output organized-albums
Click to expand
Original Folder Hierarchy After photo-cli
├── DSC_5727.jpg
├── GOPR6742.jpg
├── Italy album
│   ├── DJI_01732.jpg
│   ├── DJI_01733.jpg
│   ├── DSC00001.JPG
│   ├── DSC03467.jpg
│   ├── DSC_1769.JPG
│   ├── DSC_1770.JPG
│   ├── DSC_1770_(same).jpg
│   ├── DSC_1771.JPG
│   ├── GOPR7496.jpg
│   ├── GOPR7497.jpg
│   ├── IMG_0747.JPG
│   └── IMG_2371.jpg
└── Spain Journey
    ├── DSC_1807.jpg
    ├── DSC_1808.jpg
    └── IMG_5397.jpg

2 directories, 17 files

├── 1.jpg
├── 2.jpg
├── Italy album
│   ├── 01.jpg
│   ├── 02.jpg
│   ├── 03.jpg
│   ├── 04.jpg
│   ├── 05.jpg
│   ├── 06.jpg
│   ├── 07.jpg
│   ├── 08.jpg
│   ├── 09.jpg
│   ├── 10.jpg
│   ├── 11.jpg
│   └── 12.jpg
├── photo-cli-report.csv
└── Spain Journey
    ├── 1.jpg
    ├── 2.jpg
    └── 3.jpg

2 directories, 18 files

Group Into Taken Year/Month/Day Folders, Name as Date & Time

Groups photos by photo taken year, month, day than copy on [year]/[month]/[day] directory with a file name as photo taken date.

photo-cli copy --process-type FlattenAllSubFolders --group-by YearMonthDay --naming-style DateTimeWithSeconds --number-style OnlySequentialNumbers --input photos --output organized-albums
Click to expand
Original Folder Hierarchy After photo-cli
├── DSC_5727.jpg
├── GOPR6742.jpg
├── Italy album
│   ├── DJI_01732.jpg
│   ├── DJI_01733.jpg
│   ├── DSC00001.JPG
│   ├── DSC03467.jpg
│   ├── DSC_1769.JPG
│   ├── DSC_1770.JPG
│   ├── DSC_1770_(same).jpg
│   ├── DSC_1771.JPG
│   ├── GOPR7496.jpg
│   ├── GOPR7497.jpg
│   ├── IMG_0747.JPG
│   └── IMG_2371.jpg
└── Spain Journey
    ├── DSC_1807.jpg
    ├── DSC_1808.jpg
    └── IMG_5397.jpg

2 directories, 17 files

├── 2005
│   ├── 08
│   │   └── 13
│   │       └── 2005.08.13_09.47.23.jpg
│   └── 12
│       └── 14
│           └── 2005.12.14_14.39.47.jpg
├── 2008
│   ├── 07
│   │   └── 16
│   │       └── 2008.07.16_11.33.20.jpg
│   └── 10
│       └── 22
│           ├── 2008.10.22_16.28.39.jpg
│           ├── 2008.10.22_16.29.49.jpg
│           ├── 2008.10.22_16.38.20.jpg
│           ├── 2008.10.22_16.43.21.jpg
│           ├── 2008.10.22_16.44.01.jpg
│           ├── 2008.10.22_16.46.53.jpg
│           ├── 2008.10.22_16.52.15.jpg
│           ├── 2008.10.22_16.55.37.jpg
│           ├── 2008.10.22_17.00.07-1.jpg
│           └── 2008.10.22_17.00.07-2.jpg
├── 2012
│   └── 06
│       └── 22
│           └── 2012.06.22_19.52.31.jpg
├── 2015
│   └── 04
│       └── 10
│           ├── 2015.04.10_20.12.23-1.jpg
│           └── 2015.04.10_20.12.23-2.jpg
├── IMG_5397.jpg
└── photo-cli-report.csv

16 directories, 18 files

Folders Prefixed With Date Range, Names as Address & Date

Adding day range as a prefix to existing folder names and photos copied with a file name as address and day.

photo-cli copy --process-type SubFoldersPreserveFolderHierarchy --folder-append DayRange --folder-append-location Prefix --naming-style AddressDay --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city town suburb  --number-style AllNamesAreSameLength --input photos --output organized-albums
Click to expand
Original Folder Hierarchy After photo-cli
├── DSC_5727.jpg
├── GOPR6742.jpg
├── Italy album
│   ├── DJI_01732.jpg
│   ├── DJI_01733.jpg
│   ├── DSC00001.JPG
│   ├── DSC03467.jpg
│   ├── DSC_1769.JPG
│   ├── DSC_1770.JPG
│   ├── DSC_1770_(same).jpg
│   ├── DSC_1771.JPG
│   ├── GOPR7496.jpg
│   ├── GOPR7497.jpg
│   ├── IMG_0747.JPG
│   └── IMG_2371.jpg
└── Spain Journey
    ├── DSC_1807.jpg
    ├── DSC_1808.jpg
    └── IMG_5397.jpg

2 directories, 17 files

├── 2005.12.14-2008.10.22-Italy album
│   ├── IMG_2371.jpg
│   ├── Italia-Arezzo-2008.10.22-10.jpg
│   ├── Italia-Arezzo-2008.10.22-11.jpg
│   ├── Italia-Arezzo-2008.10.22-12.jpg
│   ├── Italia-Arezzo-2008.10.22-13.jpg
│   ├── Italia-Arezzo-2008.10.22-14.jpg
│   ├── Italia-Arezzo-2008.10.22-15.jpg
│   ├── Italia-Arezzo-2008.10.22-16.jpg
│   ├── Italia-Arezzo-2008.10.22-17.jpg
│   ├── Italia-Arezzo-2008.10.22-18.jpg
│   ├── Italia-Arezzo-2008.10.22-19.jpg
│   └── Italia-Firenze-Quartiere 1-2005.12.14.jpg
├── 2015.04.10-2015.04.10-Spain Journey
│   ├── España-Madrid-2015.04.10-1.jpg
│   ├── España-Madrid-2015.04.10-2.jpg
│   └── IMG_5397.jpg
├── Kenya-2005.08.13.jpg
├── photo-cli-report.csv
└── United Kingdom-Ascot-Sunninghill and Ascot-2012.06.22.jpg

2 directories, 18 files

Naming With Address, Date in Same Folder Hierarchy

Preserve same folder hierarchy, copy photos with a file name as photo taken date, time and address. Possible file name will have number suffix. Photos that don't have any coordinate or photo taken date will be copied in a relative subfolder.

photo-cli copy --process-type SubFoldersPreserveFolderHierarchy --naming-style AddressDateTimeWithSeconds  --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city town suburb --number-style AllNamesAreSameLength --no-taken-date InSubFolder --no-coordinate InSubFolder --input photos --output organized-albums
Click to expand
Original Folder Hierarchy After photo-cli
├── DSC_5727.jpg
├── GOPR6742.jpg
├── Italy album
│   ├── DJI_01732.jpg
│   ├── DJI_01733.jpg
│   ├── DSC00001.JPG
│   ├── DSC03467.jpg
│   ├── DSC_1769.JPG
│   ├── DSC_1770.JPG
│   ├── DSC_1770_(same).jpg
│   ├── DSC_1771.JPG
│   ├── GOPR7496.jpg
│   ├── GOPR7497.jpg
│   ├── IMG_0747.JPG
│   └── IMG_2371.jpg
└── Spain Journey
    ├── DSC_1807.jpg
    ├── DSC_1808.jpg
    └── IMG_5397.jpg

2 directories, 17 files

├── Italy album
│   ├── Italia-Arezzo-2008.10.22_16.28.39.jpg
│   ├── Italia-Arezzo-2008.10.22_16.29.49.jpg
│   ├── Italia-Arezzo-2008.10.22_16.38.20.jpg
│   ├── Italia-Arezzo-2008.10.22_16.43.21.jpg
│   ├── Italia-Arezzo-2008.10.22_16.44.01.jpg
│   ├── Italia-Arezzo-2008.10.22_16.46.53.jpg
│   ├── Italia-Arezzo-2008.10.22_16.52.15.jpg
│   ├── Italia-Arezzo-2008.10.22_16.55.37.jpg
│   ├── Italia-Arezzo-2008.10.22_17.00.07-1.jpg
│   ├── Italia-Arezzo-2008.10.22_17.00.07-2.jpg
│   ├── Italia-Firenze-Quartiere 1-2005.12.14_14.39.47.jpg
│   └── no-address
│       └── IMG_2371.jpg
├── Kenya-2005.08.13_09.47.23.jpg
├── photo-cli-report.csv
├── Spain Journey
│   ├── España-Madrid-2015.04.10_20.12.23-1.jpg
│   ├── España-Madrid-2015.04.10_20.12.23-2.jpg
│   └── no-address-and-no-photo-taken-date
│       └── IMG_5397.jpg
└── United Kingdom-Ascot-Sunninghill and Ascot-2012.06.22_19.52.31.jpg

4 directories, 18 files

Grouped Into Country/City/Town Folders, Names as Taken Date and Address

Groups photos by address hierarchy than copy on [country]/[city]/[town] directory with a file name as photo taken date. Photos that don't have any coordinate will be copied in a relative subfolder.

photo-cli copy --process-type FlattenAllSubFolders --group-by AddressHierarchy --naming-style DayAddress --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city town suburb --number-style OnlySequentialNumbers --no-taken-date AppendToEndOrderByFileName --no-coordinate InSubFolder --input photos --output organized-albums
Click to expand
Original Folder Hierarchy After photo-cli
├── DSC_5727.jpg
├── GOPR6742.jpg
├── Italy album
│   ├── DJI_01732.jpg
│   ├── DJI_01733.jpg
│   ├── DSC00001.JPG
│   ├── DSC03467.jpg
│   ├── DSC_1769.JPG
│   ├── DSC_1770.JPG
│   ├── DSC_1770_(same).jpg
│   ├── DSC_1771.JPG
│   ├── GOPR7496.jpg
│   ├── GOPR7497.jpg
│   ├── IMG_0747.JPG
│   └── IMG_2371.jpg
└── Spain Journey
    ├── DSC_1807.jpg
    ├── DSC_1808.jpg
    └── IMG_5397.jpg

2 directories, 17 files

├── España
│   └── Madrid
│       ├── 2015.04.10-España-Madrid-1.jpg
│       └── 2015.04.10-España-Madrid-2.jpg
├── Italia
│   ├── Arezzo
│   │   ├── 2008.10.22-Italia-Arezzo-10.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-1.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-2.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-3.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-4.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-5.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-6.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-7.jpg
│   │   ├── 2008.10.22-Italia-Arezzo-8.jpg
│   │   └── 2008.10.22-Italia-Arezzo-9.jpg
│   └── Firenze
│       └── Quartiere 1
│           └── 2005.12.14-Italia-Firenze-Quartiere 1.jpg
├── Kenya
│   └── 2005.08.13-Kenya.jpg
├── no-address
│   ├── IMG_2371.jpg
│   └── IMG_5397.jpg
├── photo-cli-report.csv
└── United Kingdom
    └── Ascot
        └── Sunninghill and Ascot
            └── 2012.06.22-United Kingdom-Ascot-Sunninghill and Ascot.jpg

11 directories, 18 files

Commands / Verbs

Subcommand description
archive Archives photos into same specific folder, optionally groups them by albums (date range, reverse geocode or individual), and indexes photo taken date, address (reverse geocode) information into SQLite database.
copy Copies photos into new folder hierarchy with given arguments using photograph's taken date and coordinate address (reverse geocode).
list List & open photos from archive folders.
info Creates a report (CSV file) listing all photo taken date and address (reverse geocode).
address Get address (reverse geocode) of single photo.
settings Lists, saves and get settings.

Archive

Archives photos into same specific folder, optionally groups them by albums (date range, reverse geocode or individual), and indexes photo taken date, address (reverse geocode) information into SQLite database.

photo-cli help archive
Click to expand
  -o, --output                        (MUST) File system path to create new
                                      organized folder. A new folder hierarchy
                                      will be created on that location with new
                                      file names. (will create folder if not
                                      exist)

  -i, --input                         (Default current executing folder) File
                                      system path to read & copy photos from. (
                                      there will be no modification on the input
                                      path )

  -d, --dry-run                       (Optional) Simulate the same process
                                      without writing to the output folder. (no
                                      extra parameter needed)

  -x, --invalid-format                (Optional) Action to do when a photo
                                      format is invalid. ( Continue: 0
                                      [default], PreventProcess: 1 )

  -t, --no-taken-date                 (Optional) Action to do when a photo with
                                      a no taken date. ( Continue: 0 [default],
                                      PreventProcess: 1 )

  -c, --no-coordinate                 (Optional) Action to do when a photo with
                                      a no coordinate. ( Continue: 0 [default],
                                      PreventProcess: 1 )

  -w, --expected-day-range            (Optional) Provide a maximum expected day
                                      difference as number for your photos to
                                      prevent processing if it's exceeding the
                                      range

  -y, --album-type                    (Optional) Whether you want to link photos
                                      as album by picking the album type( Unset:
                                      0 [default - no album linking], Individual
                                      = 1, DateRange = 2 )

  -a, --album-name                    (Optional) Album name to create a new one
                                      for currently archiving photos[Can use
                                      with `AlbumType` as `Individual` or
                                      `DateRange`]

  -p, --update-album                  (Optional) Existing Album ID number value
                                      to link currently archiving photos[Can use
                                      with `AlbumType` as `Individual` or
                                      `DateRange`][Album IDs can be listed by
                                      `photo-cli list --type Albums`]

  -s, --auto-reverse-geocode-album    (Optional) Automatically linking photos to
                                      an album (creating or using existing) for
                                      each reverse geocode property
                                      individuallyFor example if you use reverse
                                      geocode properties are country, city each
                                      archive operation, you could have albums
                                      for each country and city variants

  -f, --delete-on-source              (Optional) [Dangerous parameter] Deleting
                                      the source folder on successful archive
                                      operation

  -e, --reverse-geocode               (Optional) Third-party provider to resolve
                                      photo taken address by photo's
                                      coordinates. ( Disabled: 0 [default],
                                      BigDataCloud: 1, OpenStreetMapFoundation:
                                      2, GoogleMaps: 3, LocationIq: 5 )

  -b, --bigdatacloud-key              (Optional) API key needed to use
                                      BigDataCloud.
                                      https://www.bigdatacloud.com/geocoding-api
                                      s/reverse-geocode-to-city-api/ (Instead of
                                      using this option, environment name:
                                      PHOTO_CLI_BIG_DATA_CLOUD_API_KEY can be
                                      used or `BigDataCloudApiKey` key can be
                                      set via settings command. )

  -u, --bigdatacloud-levels           (Optional) Admin levels separated with
                                      space. ( To see which level correspond to
                                      which address level, you may use
                                      `photo-cli address` to see the full
                                      response returned from BigDataCloud. )

  -m, --googlemaps-types              (Optional) GoogleMaps address types
                                      separated with space. ( To see which level
                                      correspond to which address level, you may
                                      use `photo-cli address` to see the full
                                      response returned from GoogleMaps. )

  -k, --googlemaps-key                (Optional) API key needed to use
                                      GoogleMaps.
                                      https://developers.google.com/maps/documen
                                      tation/geocoding/overview/ (Instead of
                                      using this option, environment name:
                                      PHOTO_CLI_GOOGLE_MAPS_API_KEY can be used
                                      or `GoogleMapsApiKey` key can be set via
                                      settings command. )

  -r, --openstreetmap-properties      (Optional) OpenStreetMap properties
                                      separated with space. ( To see which level
                                      correspond to which address level, you may
                                      use `photo-cli address` to see the full
                                      response returned from OpenStreetMap
                                      provider. )

  -q, --locationiq-key                (Optional) API key needed to use
                                      LocationIq. https://locationiq.com/docs/
                                      (Instead of using this option, environment
                                      name: PHOTO_CLI_LOCATIONIQ_API_KEY can be
                                      used or `LocationIqApiKey` key can be set
                                      via settings command. )

  -h, --has-paid-license              (Optional) Bypass the free rate limit if
                                      you have paid license. ( For LocationIq. )

  -l, --language                      (Optional) Language/culture value to get
                                      localized address result for BigDataCloud
                                      (
                                      https://www.bigdatacloud.com/supported-lan
                                      guages/ ) and GoogleMaps
                                      (https://developers.google.com/maps/faq#la
                                      nguagesupport ).

  -z, --missing-reverse-geocode       (Optional) Action to do when any of the
                                      photo has missing reserver geocode
                                      information( Continue: 0 [default],
                                      PreventProcess: 1 )

  --help                              Display this help screen.

  --version                           Display version information.

NOTES:
- Instead of option names (for ex: DateTimeWithMinutes), you may use options
values too. (for ex: 3)
- You can use relative folder paths. If you use the input folder as the working
directory, you don't need to use the input argument.

EXAMPLE USAGES:
- Archive all photos in current folder (and it's subfolders recursively) into
output folder by (year)/(month)/(day) hierarchy with a file name photo taken
date with seconds prefixed by file hash. Saves all photo taken information into
local SQLite database.

Example with long argument names;
photo-cli archive --output (output-folder)

Example with short argument names;
photo-cli archive -o (output-folder)

- Archive all photos in the input folder and its subfolders recursively by
fetching each photo's reverse geocode information, copying them into the output
folder organized by year/month/day hierarchy with filenames consisting of the
photo-taken date with seconds prefixed by file hash, and saving all photo
metadata and addresses into a local SQLite database.

Example with long argument names;
photo-cli archive --no-coordinate PreventProcess --reverse-geocode
OpenStreetMapFoundation --input (input-folder) --output (output-folder)
--openstreetmap-properties country city town suburb --no-taken-date
PreventProcess --invalid-format PreventProcess --album-type Unset

Example with short argument names;
photo-cli archive -c PreventProcess -e OpenStreetMapFoundation -i (input-folder)
-o (output-folder) -r country city town suburb -t PreventProcess -x
PreventProcess -y Unset

- Archive all photos in the input folder (and its subfolders recursively) by
creating a date range album named 'album 1', fetching each photo's reverse
geocode information using Google Maps with properties administrative area level
1 and 2 to create location-based albums, and copying them into the output folder
with a (year)/(month)/(day) hierarchy using filenames formatted as
photo-taken-date-with-seconds prefixed by file hash but the process won't start
if any photo has an invalid format, missing photo-taken date or coordinates, or
if the photo-taken date span exceeds 30 days and upon successful operation, it
would delete the input source folder.

Example with long argument names;
photo-cli archive --album-name "album 1" --no-coordinate PreventProcess
--reverse-geocode GoogleMaps --delete-on-source --input (input-folder)
--googlemaps-types administrative_area_level_1 administrative_area_level_2
--output (output-folder) --auto-reverse-geocode-album --no-taken-date
PreventProcess --expected-day-range 30 --invalid-format PreventProcess
--album-type DateRange --missing-reverse-geocode PreventProcess

Example with short argument names;
photo-cli archive -a "album 1" -c PreventProcess -e GoogleMaps -f -i
(input-folder) -m administrative_area_level_1 administrative_area_level_2 -o
(output-folder) -s -t PreventProcess -w 30 -x PreventProcess -y DateRange -z
PreventProcess

Copy

Copies photos into new folder hierarchy with given arguments using photograph's taken date and coordinate address (reverse geocode).

photo-cli help copy
Click to expand
  -o, --output                      (MUST) File system path to create new
                                    organized folder. A new folder hierarchy
                                    will be created on that location with new
                                    file names. (will create folder if not
                                    exist)

  -s, --naming-style                (MUST) Naming strategy of newly copied file
                                    name. ( Numeric: 1, Day: 2,
                                    DateTimeWithMinutes: 3, DateTimeWithSeconds:
                                    4, Address: 5, DayAddress: 6,
                                    DateTimeWithMinutesAddress: 7,
                                    DateTimeWithSecondsAddress: 8, AddressDay:
                                    9, AddressDateTimeWithMinutes: 10,
                                    AddressDateTimeWithSeconds: 11 )

  -f, --process-type                (MUST) Reading photos strategy from input
                                    folder. ( Single: 1,
                                    SubFoldersPreserveFolderHierarchy: 2,
                                    FlattenAllSubFolders: 3 )

  -n, --number-style                (MUST) Number naming strategy when using
                                    `NamingStyle` as `Numeric` or using to
                                    numbering the possible same names. (
                                    AllNamesAreSameLength: 1,
                                    PaddingZeroCharacter: 2,
                                    OnlySequentialNumbers: 3 )

  -x, --invalid-format              (Optional) Action to do when a photo format
                                    is invalid. ( Continue: 0 [default],
                                    PreventProcess: 1, DontCopyToOutput: 2,
                                    InSubFolder: 3 )

  -t, --no-taken-date               (Optional) Action to do when a photo with a
                                    no taken date. ( Continue: 0 [default],
                                    PreventProcess: 1, DontCopyToOutput: 2,
                                    InSubFolder: 3, AppendToEndOrderByFileName:
                                    4, InsertToBeginningOrderByFileName: 5 )

  -c, --no-coordinate               (Optional) Action to do when a photo with a
                                    no coordinate. ( Continue: 0 [default],
                                    PreventProcess: 1, DontCopyToOutput: 2,
                                    InSubFolder: 3 )

  -i, --input                       (Default current executing folder) File
                                    system path to read & copy photos from. (
                                    there will be no modification on the input
                                    path )

  -d, --dry-run                     (Optional) Simulate the same process without
                                    writing to the output folder. (no extra
                                    parameter needed)

  -g, --group-by                    (Optional) Strategy to group photos into
                                    folders. [Can't use with `FolderProcessType`
                                    is `SubFoldersPreserveFolderHierarchy`] (
                                    YearMonthDay: 1, YearMonth: 2, Year: 3,
                                    AddressFlat: 4, AddressHierarchy: 5 )

  -a, --folder-append               (Optional) Appending name strategy to folder
                                    names cloned from source folder hierarchy.
                                    [Can use with `FolderProcessType` as
                                    `SubFoldersPreserveFolderHierarchy`] (
                                    FirstYearMonthDay: 1, FirstYearMonth: 2,
                                    FirstYear: 3, DayRange: 4,
                                    MatchingMinimumAddress: 5 )

  -p, --folder-append-location      (Optional) Append location for
                                    `FolderAppendType`. [Can use with
                                    `FolderProcessType` as
                                    `SubFoldersPreserveFolderHierarchy`] (
                                    Prefix: 1, Suffix: 2 )

  -v, --verify                      (Optional) Verify that all photo files
                                    copied successfully by comparing file
                                    hashes. (no extra parameter needed)

  -w, --expected-day-range          (Optional) Provide a maximum expected day
                                    difference as number for your photos to
                                    prevent processing if it's exceeding the
                                    range

  -e, --reverse-geocode             (Optional) Third-party provider to resolve
                                    photo taken address by photo's coordinates.
                                    ( Disabled: 0 [default], BigDataCloud: 1,
                                    OpenStreetMapFoundation: 2, GoogleMaps: 3,
                                    LocationIq: 5 )

  -b, --bigdatacloud-key            (Optional) API key needed to use
                                    BigDataCloud.
                                    https://www.bigdatacloud.com/geocoding-apis/
                                    reverse-geocode-to-city-api/ (Instead of
                                    using this option, environment name:
                                    PHOTO_CLI_BIG_DATA_CLOUD_API_KEY can be used
                                    or `BigDataCloudApiKey` key can be set via
                                    settings command. )

  -u, --bigdatacloud-levels         (Optional) Admin levels separated with
                                    space. ( To see which level correspond to
                                    which address level, you may use `photo-cli
                                    address` to see the full response returned
                                    from BigDataCloud. )

  -m, --googlemaps-types            (Optional) GoogleMaps address types
                                    separated with space. ( To see which level
                                    correspond to which address level, you may
                                    use `photo-cli address` to see the full
                                    response returned from GoogleMaps. )

  -k, --googlemaps-key              (Optional) API key needed to use GoogleMaps.
                                    https://developers.google.com/maps/documenta
                                    tion/geocoding/overview/ (Instead of using
                                    this option, environment name:
                                    PHOTO_CLI_GOOGLE_MAPS_API_KEY can be used or
                                    `GoogleMapsApiKey` key can be set via
                                    settings command. )

  -r, --openstreetmap-properties    (Optional) OpenStreetMap properties
                                    separated with space. ( To see which level
                                    correspond to which address level, you may
                                    use `photo-cli address` to see the full
                                    response returned from OpenStreetMap
                                    provider. )

  -q, --locationiq-key              (Optional) API key needed to use LocationIq.
                                    https://locationiq.com/docs/ (Instead of
                                    using this option, environment name:
                                    PHOTO_CLI_LOCATIONIQ_API_KEY can be used or
                                    `LocationIqApiKey` key can be set via
                                    settings command. )

  -h, --has-paid-license            (Optional) Bypass the free rate limit if you
                                    have paid license. ( For LocationIq. )

  -l, --language                    (Optional) Language/culture value to get
                                    localized address result for BigDataCloud (
                                    https://www.bigdatacloud.com/supported-langu
                                    ages/ ) and GoogleMaps
                                    (https://developers.google.com/maps/faq#lang
                                    uagesupport ).

  -z, --missing-reverse-geocode     (Optional) Action to do when any of the
                                    photo has missing reserver geocode
                                    information( Continue: 0 [default],
                                    PreventProcess: 1 )

  --help                            Display this help screen.

  --version                         Display version information.

NOTES:
- Instead of option names (for ex: DateTimeWithMinutes), you may use options
values too. (for ex: 3)
- You can use relative folder paths. If you use the input folder as the working
directory, you don't need to use the input argument.

EXAMPLE USAGES:
- Preserve same folder hierarchy, copy photos with sequential number ordering by
photo taken date.

Example with long argument names;
photo-cli copy --process-type SubFoldersPreserveFolderHierarchy --input
(input-folder) --number-style PaddingZeroCharacter --output (output-folder)
--naming-style Numeric

Example with short argument names;
photo-cli copy -f SubFoldersPreserveFolderHierarchy -i (input-folder) -n
PaddingZeroCharacter -o (output-folder) -s Numeric

- Groups photos by photo taken year, month, day than copy on
(year)/(month)/(day) directory with a file name as photo taken date.

Example with long argument names;
photo-cli copy --process-type FlattenAllSubFolders --group-by YearMonthDay
--input (input-folder) --number-style OnlySequentialNumbers --output
(output-folder) --naming-style DateTimeWithSeconds

Example with short argument names;
photo-cli copy -f FlattenAllSubFolders -g YearMonthDay -i (input-folder) -n
OnlySequentialNumbers -o (output-folder) -s DateTimeWithSeconds

- Adding day range as a prefix to existing folder names and photos copied with a
file name as address and day.

Example with long argument names;
photo-cli copy --folder-append DayRange --no-coordinate InSubFolder
--reverse-geocode GoogleMaps --process-type SubFoldersPreserveFolderHierarchy
--input (input-folder) --googlemaps-key google-api-key --googlemaps-types
administrative_area_level_1 administrative_area_level_2
administrative_area_level_3 --number-style AllNamesAreSameLength --output
(output-folder) --folder-append-location Prefix --naming-style AddressDay
--no-taken-date InSubFolder --invalid-format PreventProcess

Example with short argument names;
photo-cli copy -a DayRange -c InSubFolder -e GoogleMaps -f
SubFoldersPreserveFolderHierarchy -i (input-folder) -k google-api-key -m
administrative_area_level_1 administrative_area_level_2
administrative_area_level_3 -n AllNamesAreSameLength -o (output-folder) -p
Prefix -s AddressDay -t InSubFolder -x PreventProcess

- Preserve the same folder hierarchy while copying photos with filenames
consisting of the photo-taken date, time, and address (with possible number
suffixes), and copy photos without coordinates or photo-taken dates into a
relative subfolder.

Example with long argument names;
photo-cli copy --no-coordinate InSubFolder --reverse-geocode
OpenStreetMapFoundation --process-type SubFoldersPreserveFolderHierarchy --input
(input-folder) --number-style AllNamesAreSameLength --output (output-folder)
--openstreetmap-properties country city town suburb --naming-style
AddressDateTimeWithSeconds --no-taken-date InSubFolder --invalid-format
PreventProcess

Example with short argument names;
photo-cli copy -c InSubFolder -e OpenStreetMapFoundation -f
SubFoldersPreserveFolderHierarchy -i (input-folder) -n AllNamesAreSameLength -o
(output-folder) -r country city town suburb -s AddressDateTimeWithSeconds -t
InSubFolder -x PreventProcess

- Groups photos by photo-taken year, month, and day, then copies them into a
year/month/day directory structure with filenames as the photo-taken date, while
photos without coordinates are copied into a relative subfolder.

Example with long argument names;
photo-cli copy --no-coordinate InSubFolder --reverse-geocode BigDataCloud
--process-type FlattenAllSubFolders --group-by AddressHierarchy --input
(input-folder) --number-style OnlySequentialNumbers --output (output-folder)
--naming-style DayAddress --bigdatacloud-levels 2 4 6 8 --invalid-format
PreventProcess

Example with short argument names;
photo-cli copy -c InSubFolder -e BigDataCloud -f FlattenAllSubFolders -g
AddressHierarchy -i (input-folder) -n OnlySequentialNumbers -o (output-folder)
-s DayAddress -u 2 4 6 8 -x PreventProcess

List

List & open photos from archive folders.

photo-cli help list
Click to expand
  -t, --type     (Optional) Listing type for archive folder( Summary: 0
                 [default], Albums: 1, PhotosByAlbum: 2, PhotosByDate: 3 )

  -i, --input    (Default current executing folder) Archive path to list & open
                 photos from

  -n, --id       (Optional) Album ID to be used while using the type of
                 PhotosByAlbum

  -y, --year     (Optional) Year as number to be used while using the type of
                 PhotosByDate

  -m, --month    (Optional) Month as number to be used while using the type of
                 PhotosByDate

  -d, --day      (Optional) Day as number to be used while using the type of
                 PhotosByDate

  -r, --raw      (Optional) Listing photo paths each on new line instead of
                 trying to open the default OS app while using the type of
                 PhotosByAlbum or PhotosByDate

  --help         Display this help screen.

  --version      Display version information.

NOTES:
- Instead of option names (for ex: DateTimeWithMinutes), you may use options
values too. (for ex: 3)
- You can use relative folder paths. If you use the input folder as the working
directory, you don't need to use the input argument.

EXAMPLE USAGES:
- List statistics of the archive folder

Example with long argument names;
photo-cli list --input (input-folder)

Example with short argument names;
photo-cli list -i (input-folder)

- List all the album information of the archive folder

Example with long argument names;
photo-cli list --input (input-folder) --type Albums

Example with short argument names;
photo-cli list -i (input-folder) -t Albums

- List paths (to be send as process arguments to photo viewers) or open (only
supporting in macOS , Preview app for now) for the given album id

Example with long argument names;
photo-cli list --input (input-folder) --id 1 --type PhotosByAlbum

Example with short argument names;
photo-cli list -i (input-folder) -n 1 -t PhotosByAlbum

- List paths (to be send as process arguments to photo viewers) or open (only
supporting in macOS , Preview app for now) for the given year

Example with long argument names;
photo-cli list --input (input-folder) --type PhotosByDate --year 2007

Example with short argument names;
photo-cli list -i (input-folder) -t PhotosByDate -y 2007

- List paths (to be send as process arguments to photo viewers) or open (only
supporting in macOS , Preview app for now) for the given year & month

Example with long argument names;
photo-cli list --input (input-folder) --month 8 --type PhotosByDate --year 2007

Example with short argument names;
photo-cli list -i (input-folder) -m 8 -t PhotosByDate -y 2007

- List paths (to be send as process arguments to photo viewers) or open (only
supporting in macOS , Preview app for now) for the given year, month & day

Example with long argument names;
photo-cli list --day 19 --input (input-folder) --month 8 --type PhotosByDate
--year 2007

Example with short argument names;
photo-cli list -d 19 -i (input-folder) -m 8 -t PhotosByDate -y 2007

Info

Creates a report (CSV file) listing all photo taken date and address (reverse geocode).

photo-cli help info
Click to expand
  -o, --output                      (MUST) File system path to write report
                                    file.

  -i, --input                       (Default current executing folder) File
                                    system path to read & copy photos from. (
                                    there will be no modification on the input
                                    path )

  -a, --all-folders                 (Optional) Read & list all photos in all
                                    subfolders (no extra parameter needed)

  -x, --invalid-format              (Optional) Action to do when a photo format
                                    is invalid. ( Continue: 0 [default],
                                    PreventProcess: 1 )

  -t, --no-taken-date               (Optional) Action to do when a photo with a
                                    no taken date. ( Continue: 0 [default],
                                    PreventProcess: 1 )

  -c, --no-coordinate               (Optional) Action to do when a photo with a
                                    no coordinate. ( Continue: 0 [default],
                                    PreventProcess: 1 )

  -e, --reverse-geocode             (Optional) Third-party provider to resolve
                                    photo taken address by photo's coordinates.
                                    ( Disabled: 0 [default], BigDataCloud: 1,
                                    OpenStreetMapFoundation: 2, GoogleMaps: 3,
                                    LocationIq: 5 )

  -b, --bigdatacloud-key            (Optional) API key needed to use
                                    BigDataCloud.
                                    https://www.bigdatacloud.com/geocoding-apis/
                                    reverse-geocode-to-city-api/ (Instead of
                                    using this option, environment name:
                                    PHOTO_CLI_BIG_DATA_CLOUD_API_KEY can be used
                                    or `BigDataCloudApiKey` key can be set via
                                    settings command. )

  -u, --bigdatacloud-levels         (Optional) Admin levels separated with
                                    space. ( To see which level correspond to
                                    which address level, you may use `photo-cli
                                    address` to see the full response returned
                                    from BigDataCloud. )

  -m, --googlemaps-types            (Optional) GoogleMaps address types
                                    separated with space. ( To see which level
                                    correspond to which address level, you may
                                    use `photo-cli address` to see the full
                                    response returned from GoogleMaps. )

  -k, --googlemaps-key              (Optional) API key needed to use GoogleMaps.
                                    https://developers.google.com/maps/documenta
                                    tion/geocoding/overview/ (Instead of using
                                    this option, environment name:
                                    PHOTO_CLI_GOOGLE_MAPS_API_KEY can be used or
                                    `GoogleMapsApiKey` key can be set via
                                    settings command. )

  -r, --openstreetmap-properties    (Optional) OpenStreetMap properties
                                    separated with space. ( To see which level
                                    correspond to which address level, you may
                                    use `photo-cli address` to see the full
                                    response returned from OpenStreetMap
                                    provider. )

  -q, --locationiq-key              (Optional) API key needed to use LocationIq.
                                    https://locationiq.com/docs/ (Instead of
                                    using this option, environment name:
                                    PHOTO_CLI_LOCATIONIQ_API_KEY can be used or
                                    `LocationIqApiKey` key can be set via
                                    settings command. )

  -h, --has-paid-license            (Optional) Bypass the free rate limit if you
                                    have paid license. ( For LocationIq. )

  -l, --language                    (Optional) Language/culture value to get
                                    localized address result for BigDataCloud (
                                    https://www.bigdatacloud.com/supported-langu
                                    ages/ ) and GoogleMaps
                                    (https://developers.google.com/maps/faq#lang
                                    uagesupport ).

  -z, --missing-reverse-geocode     (Optional) Action to do when any of the
                                    photo has missing reserver geocode
                                    information( Continue: 0 [default],
                                    PreventProcess: 1 )

  --help                            Display this help screen.

  --version                         Display version information.

NOTES:
- Instead of option names (for ex: DateTimeWithMinutes), you may use options
values too. (for ex: 3)
- You can use relative folder paths. If you use the input folder as the working
directory, you don't need to use the input argument.

EXAMPLE USAGES:
- Photos located on all subfolders will be processed and their photograph's
taken date and address information will be saved on CSV file using BigDataCloud
reverse geocode provider.

Example with long argument names;
photo-cli info --all-folders --reverse-geocode OpenStreetMapFoundation --input
(input-folder) --output (output-file).csv --openstreetmap-properties country
city town suburb

Example with short argument names;
photo-cli info -a -e OpenStreetMapFoundation -i (input-folder) -o
(output-file).csv -r country city town suburb

- Using Google Maps reverse geocode provider (need api key) with an option to
prevent processing if there is no coordinate or no photo taken date found on any
photo.

Example with long argument names;
photo-cli info --no-coordinate PreventProcess --reverse-geocode GoogleMaps
--input (input-folder) --googlemaps-key google-api-key --googlemaps-types
administrative_area_level_1 administrative_area_level_2 --output
(output-file).csv --no-taken-date PreventProcess

Example with short argument names;
photo-cli info -c PreventProcess -e GoogleMaps -i (input-folder) -k
google-api-key -m administrative_area_level_1 administrative_area_level_2 -o
(output-file).csv -t PreventProcess

Address

Get address (reverse geocode) of single photo.

photo-cli help address
  -i, --input                       (Default current executing folder) File system path to read & copy photos from. ( there will be no modification on the input path )

  -e, --reverse-geocode             (Optional) Third-party provider to resolve photo taken address by photo's coordinates. ( Disabled: 0 [default], BigDataCloud: 1, OpenStreetMapFoundation: 2, GoogleMaps: 3, LocationIq: 5 )

  -t, --type                        (MUST) Response list detail level. ( AllAvailableProperties: 0, SelectedProperties: 1, FullResponse: 2 )

  -b, --bigdatacloud-key            (Optional) API key needed to use BigDataCloud. https://www.bigdatacloud.com/geocoding-apis/reverse-geocode-to-city-api/ (Instead of using this option, environment name: PHOTO_CLI_BIG_DATA_CLOUD_API_KEY can be used. )

  -u, --bigdatacloud-levels         (Optional) Admin levels separated with space. ( To see which level correspond to which address level, you may use `photo-cli address` to see full response returned from BigDataCloud. )

  -m, --googlemaps-types            (Optional) GoogleMaps address types separated with space. ( To see which level correspond to which address level, you may use `photo-cli address` to see full response returned from GoogleMaps. )

  -k, --googlemaps-key              (Optional) API key needed to use GoogleMaps. https://developers.google.com/maps/documentation/geocoding/overview/ (Instead of using this option, environment name: PHOTO_CLI_GOOGLE_MAPS_API_KEY can be used. )

  -r, --openstreetmap-properties    (Optional) OpenStreetMap properties separated with space. ( To see which level correspond to which address level, you may use `photo-cli address` to see full response returned from OpenStreetMap provider. )

  -q, --locationiq-key              (Optional) API key needed to use LocationIq. https://locationiq.com/docs/ (Instead of using this option, environment name: PHOTO_CLI_LOCATIONIQ_API_KEY can be used. )

  -h, --has-paid-license            (Optional) Bypass rate limit if you have paid license. ( For LocationIq. )

  -l, --language                    (Optional) Language/culture value to get localized address result for BigDataCloud ( https://www.bigdatacloud.com/supported-languages/ ) and GoogleMaps (https://developers.google.com/maps/faq#languagesupport ).

  --help                            Display this help screen.

  --version                         Display version information.

NOTES:
- Instead of option names (for ex: DateTimeWithMinutes), you may use options values too. (for ex: 3)
- You can use relative folder paths. If you use the input folder as the working directory, you don't need to use the input argument.

EXAMPLE USAGES:
- All properties

Example with long argument names;
photo-cli address --reverse-geocode OpenStreetMapFoundation --input [photo-path].jpg

Example with short argument names;
photo-cli address -e OpenStreetMapFoundation -i [photo-path].jpg

- Selected properties

Example with long argument names;
photo-cli address --reverse-geocode OpenStreetMapFoundation --input [photo-path].jpg --openstreetmap-properties country city town suburb --type SelectedProperties

Example with short argument names;
photo-cli address -e OpenStreetMapFoundation -i [photo-path].jpg -r country city town suburb -t SelectedProperties

- Show full response

Example with long argument names;
photo-cli address --reverse-geocode OpenStreetMapFoundation --input [photo-path].jpg --type FullResponse

Example with short argument names;
photo-cli address -e OpenStreetMapFoundation -i [photo-path].jpg -t FullResponse

Settings

List, save and get settings.

photo-cli help settings
  -k, --key      (Optional) Setting property name to change.

  -v, --value    (Optional) Setting value to set.

  -r, --reset    (Optional) Reset all settings value to default ones. (no extra parameter needed)

  --help         Display this help screen.

  --version      Display version information.

NOTES:
- Instead of option names (for ex: DateTimeWithMinutes), you may use options values too. (for ex: 3)
- You can use relative folder paths. If you use input folder as working directory, you don't need to use input argument.

EXAMPLE USAGES:
- List all settings

Example with long argument names;
photo-cli settings

Example with short argument names;
photo-cli settings

- Get a setting

Example with long argument names;
photo-cli settings --key YearFormat

Example with short argument names;
photo-cli settings -k YearFormat

- Save a setting

Example with long argument names;
photo-cli settings --key YearFormat --value yyyy

Example with short argument names;
photo-cli settings -k YearFormat -v yyyy

- Reset all settings

Example with long argument names;
photo-cli settings --reset

Example with short argument names;
photo-cli settings -r

Command Line Options / Arguments

Common Arguments Used Across Verbs (Command Type) in Same Purpose

Input Path ( -i, --input )

Optional use for copy, archive, info verb. Must be used on address verb. File system path to read & copy photos from. If not given, the current executing folder will be used. There will be no modification on input path.

Output Path ( -o, --output )

Must be used on copy, archive, info verbs. File system path to write output. For copy, new folder hierarchy created on that location with new file names. It will create folder if not exists. For info, report csv file path to write.

Is Dry Run ( -d, --dry-run )

Optional use for copy, archive verb. Simulate the same process without writing to output folder. No extra parameter needed.

Reverse Geocode Provider ( -e, --reverse-geocode )

Optional use for copy, archive, info verb. Must be used on address verb. Third-party provider to resolve photo taken address by photo's coordinates.

Option Value
BigDataCloud 1
OpenStreetMapFoundation 2
GoogleMaps 3
LocationIq 5

( -z, --missing-reverse-geocode )

Optional use for copy, archive, info verb.

( -w, --expected-day-range )

Big Data Cloud API Key ( -b, --bigdatacloud-key )

Sets Big Data Cloud reverse geocode API key. Alternatively, you may use the environment variable PHOTO_CLI_BIG_DATA_CLOUD_API_KEY.

Google Maps API Key ( -k, --googlemaps-key )

Sets Google Maps reverse geocode API key. Alternatively, you may use the environment variable PHOTO_CLI_GOOGLE_MAPS_API_KEY.

Location Iq API Key ( -q, --locationiq-key )

Sets Location Iq reverse geocode API key. Alternatively, you may use the environment variable PHOTO_CLI_LOCATIONIQ_API_KEY.

BigDataCloud Admin Levels ( -u, --bigdatacloud-levels )

Must be used when BigDataCloud is selected as reverse geocode provider. Big Data Cloud admin levels are separated with space. ( To see which level correspond to which address level, you may use photo-cli address to see the full response returned from BigDataCloud. )

OpenStreetMap Properties ( -r, --openstreetmap-properties )

Must be used when any of OpenStreetMapFoundation, LocationIq is selected as reverse geocode provider. OpenStreetMap properties separated with space. ( To see which level correspond to which address level, you may use photo-cli address to see the full response returned from OpenStreetMap provider. )

Google Maps Address Types ( -m, --googlemaps-types )

Must be used when GoogleMaps selected as reverse geocode provider. Google Maps address types separated with space. ( To see which level correspond to which address level, you may use photo-cli address to see full the response returned from GoogleMaps. )

Has Paid License ( -h, --has-paid-license )

Bypass the free rate limit if you have paid license. (For LocationIq reverse geocode provider)

Archive Verb Arguments

( -y, --album-type )

Whether you want to link photos as album by picking the album type.

Option Value
NoAlbumLinking (default) 0
Individual 1
DateRange 2

( -a, --album-name )

( -p, --update-album )

( -s, --auto-reverse-geocode-album )

( -f, --delete-on-source )

No Photograph Taken Date Action [for archive command ] ( -t, --no-taken-date )

Optional action to do when a photograph with a no taken date. Default is Continue.

Option Value
Continue (default) 0 (default)
PreventProcess 1

No Coordinate Action [for archive command ] ( -c, --no-coordinate )

Optional action to do when a photo with a no coordinate.

Option Value
Continue 0
PreventProcess 1

Copy Verb Arguments

Folder Process Type ( -f, --process-type )

You must select folder process behavior to whether use original folder hierarchy or flatten into single folder/grouped folder by Group By Folder.

Option Name
Single 1
SubFoldersPreserveFolderHierarchy 2
FlattenAllSubFolders 3

Naming Style ( -s, --naming-style )

While copying to a new organized folder, you must select one of these file naming strategies for a newly copied photo file name.

Option Value
Numeric 1
Day 2
DateTimeWithMinutes 3
DateTimeWithSeconds 4
Address 5
DayAddress 6
DateTimeWithMinutesAddress 7
DateTimeWithSecondsAddress 8
AddressDay 9
AddressDateTimeWithMinutes 10
AddressDateTimeWithSeconds 11

Folder Append Type ( -a, --folder-append )

Optional use for copy verb. While copying to a new organized folder (you should select Folder Process Type as SubFoldersPreserveFolderHierarchy ), you may select one of these file folder naming strategies. Must used with Folder Append Location Type

Option Value
FirstYearMonthDay 1
FirstYearMonth 2
FirstYear 3
DayRange 4
MatchingMinimumAddress 5

Folder Append Location Type ( -p, --folder-append-location )

While copying to a new organized folder (you should select Folder Process Type as SubFoldersPreserveFolderHierarchy ), you may select one of these file folder naming strategies. Must used with Folder Append Location

Option Value
Prefix 1
Suffix 2

Group By Folder ( -g, --group-by )

If you want to group photos by EXIF data, you may select one of these strategies.

Option Value
YearMonthDay 1
YearMonth 2
Year 3
Address 4

Number Naming Text Style ( -n, --number-style )

Number naming strategy must be selected when using Naming Style as Numeric or using to numbering the possible same names.

Option Value
AllNamesAreSameLength 1
PaddingZeroCharacter 2
OnlySequentialNumbers 3

Verify ( -v, --verify)

Verify that all photo files copied successfully by comparing file hashes. (no extra parameter needed)

No Photograph Taken Date Action [for copy command ] ( -t, --no-taken-date )

Optional action to do when a photograph with a no taken date. Default is Continue.

Option Value
Continue (default) 0 (default)
PreventProcess 1
DontCopyToOutput 2
InSubFolder 3
AppendToEndOrderByFileName 4
InsertToBeginningOrderByFileName 5

No Coordinate Action [for copy command ] ( -c, --no-coordinate )

Optional action to do when a photo with a no coordinate.

Option Value
Continue 0
PreventProcess 1
DontCopyToOutput 2
InSubFolder 3

Info Verb Arguments

All Folders ( -a, --all-folders )

Optional behavior to read & list all photos in all subfolders. Default behavior is to read & list only photos in current working folder. (no extra parameter needed)

No Photograph Taken Date Action [for info command ] ( -t, --no-taken-date )

Optional action to do when a photograph with a no taken date. Default is Continue.

Option Value
Continue (default) 0 (default)
PreventProcess 1

No Coordinate Action [for info command ] ( -c, --no-coordinate )

Optional action to do when a photo with a no coordinate.

Option Value
Continue 0
PreventProcess 1

Settings

User can customize & set these options via settings command.

  • All date & time formats. Reference values: MSDN Date Time Format Strings
  • File & folder naming separators
  • Report file names
  • No Photo Taken Date Action folder name.
  • No Address Action folder name.
  • Third-party reverse geocode API keys.
  • Change maximum concurrent connection limit to connect third-party reverse geocode provider.

Listing All Settings

photo-cli settings
LogLevel.Default=Error
LogLevel.Microsoft=Warning
LogLevel.PhotoCli=Warning
LogLevel.PhotoCli.Services.Implementations.ReverseGeocodes=Warning
LogLevel.Polly=Warning
LogLevel.System.Net.Http.HttpClient=Warning
YearFormat=yyyy
MonthFormat=MM
DayFormat=dd
DateFormatWithMonth=yyyy.MM
DateFormatWithDay=yyyy.MM.dd
DateTimeFormatWithMinutes=yyyy.MM.dd_HH.mm
DateTimeFormatWithSeconds=yyyy.MM.dd_HH.mm.ss
AddressSeparator=-
FolderAppendSeparator=-
DayRangeSeparator=-
SameNameNumberSeparator=-
PhotoFormatInvalidFolderName=invalid-photo-format
NoPhotoTakenDateFolderName=no-photo-taken-date
NoAddressFolderName=no-address
NoAddressAndPhotoTakenDateFolderName=no-address-and-no-photo-taken-date
CsvReportFileName=photo-cli-report.csv
DryRunCsvReportFileName=photo-cli-dry-run.csv
ConnectionLimit=4
BigDataCloudApiKey=
GoogleMapsApiKey=
LocationIqApiKey=
CoordinatePrecision=4
ArchivePhotoTakenDateHashSeparator=-
SupportedExtensions=jpg,jpeg,heic,png,hif
CompanionExtensions=mov
LogCategoryNameOutput=False
MacOsCommand=open
MacOsArgumentPrefix=-a Preview

Getting a Single Value

photo-cli settings --key YearFormat
YearFormat=yyyy

Setting a Single Value

photo-cli settings --key YearFormat --value y
No output when successful.

Resetting All Values To Defaults

photo-cli settings --reset
No output when successful

Exit Codes

Process exit codes listed below;

Option Value
Success 0
ParseArgsFailed 1
AppSettingsInvalidFile 2
UnexpectedError 3
ApiKeyStoreValidationFailed 10
AddressOptionsValidationFailed 11
InfoOptionsValidationFailed 12
CopyOptionsValidationFailed 13
SettingsOptionsValidationFailed 14
ArchiveOptionsValidationFailed 15
InputFolderNotExists 20
NoPhotoFoundOnDirectory 21
OutputFolderIsNotEmpty 22
OutputPathIsExists 23
OutputPathDontHaveWriteFilePermission 24
OutputPathDontHaveCreateDirectoryPermission 25
InputFileNotExists 26
FileVerifyErrors 27
PhotosWithNoDatePreventedProcess 30
PhotosWithNoCoordinatePreventedProcess 31
PhotosWithNoCoordinateAndNoDatePreventedProcess 32
PhotosWithInvalidFileFormatPreventedProcess 33
PhotosWithMissingReverseGeocodeInfoAsRequested 34
PhotosWithUnexpectedDateRangePreventedProcess 35
PropertyNotFound 40
InvalidSettingsValue 41
InvalidSettingsLogLevelChange 42
AlbumExist 50
InvalidSettingsLogLevelChange 50
InconsistencyOnSavingPhotosToDatabase 51
InconsistencyOnSavingUserDefinedAlbumToDatabase 52
AlbumNameMustBeUniqueWhileAddingOrUseUpdate 53
AlbumNotFoundById 54
NoPhotosToAddInAlbum 55
NoDataRangeFoundOnPhotos 56
ExistingAlbumConfigurationNotValid 57
NoArchiveDatabaseFound 60
NoPhotoFoundToList 61

Contributing

See the contributing.

Code of Conduct

See the code of conduct.

Changelog - Release History

See the changelog.

Attribution

Many thanks to these open source libraries. This work can not be done without these beautiful libraries and their contributors.

Also thanks exif-samples for sample images, to make project test various EXIF data variations.

License

Everything inside this repository is Apache 2.0 licensed.

Uninstallation

dotnet tool uninstall -g photo-cli

Credits

This tool is currently developed by Alp Coker and is open for contributors.

About

A photo organizer for your file system without sticking to any application or vendor. Extract when and where (reverse geocoding) your photos are taken, archive or copy into a new organized folder with various folder & file naming strategies with an album support to categorize, list & view easily with a metadata stored on local SQLite & CSV support.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •