photo-cli
is CLI tool (works on Linux & macOS & Windows) that extracts when and where (reverse geocode) your photographs are taken, copy into a new organized folder (not modifying source folder) with various folder & file naming strategies or export all data into CSV (can view on Microsoft Excel, Libre/OpenOffice Calc, Apple Numbers, Google Sheets) files. With this exported CSV file you can navigate your photo locations on Google Maps & Earth with your custom label and pin style.
- Features Explained With An Example
- Installation
- How It's Done?
- Supported Photo Types
- Processing Companion Files
- Address Building & Reverse Geocoding
- Usages
- Commands
- Command Line Options/Arguments
- Settings
- Exit Codes
- Roadmap
- Contributing
- Code of Conduct
- Changelog - Release History
- Attribution
- License
- Uninstallation
- Credits
There is three main feature that can be explained better with examples.
- Copy into a new organized folder example with
photo-cli copy
command - Archive & index into local SQLite incrementally in a specific folder with
photo-cli archive
command - Export all extracted information into a CSV Report With
photo-cli info
Command - Navigate Your Photo Locations on Google Maps & Earth
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 |
├── 2005.08.13_09.47.23-Kenya.jpg ├── 2005.12.14-2008.10.22-Italy album │ ├── 2005.12.14_14.39.47-Italia-Firenze-Quartiere 1.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 ├── 2012.06.22_19.52.31-United Kingdom-Ascot-Sunninghill and Ascot.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 ├── photo-cli-report.csv └── Spain Journey └── no-address-and-no-photo-taken-date └── IMG_5397.jpg |
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 town suburb --output photo-cli-test --no-coordinate InSubFolder --no-taken-date InSubFolder --verify
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 town suburb -o photo-cli-test -c 3 -t 3 -v
Console/terminal output (as progress may take time, for each operation completion status shown with progress)
Searching photos: finished. 17 photos found.
Parsing photo exif information: finished.
This OpenStreetMapFoundation provider is using rate limit of 1 seconds between each request
Reverse Geocoding: finished.
Directory grouping: finished.
Processing target folder: finished.
Verified all photo files copied successfully by comparing file hashes from original photo files.
All files SHA1 hashes written into file: sha1.lst. You may verify yourself with `sha1sum --check sha1.lst` tool in Linux/macOS.
Writing csv report: finished.
- 17 photos copied.
- 4 directories created.
- 15 photos has taken date and coordinate.
- 1 photos has taken date but no coordinate.
- 1 photos has no taken date and coordinate.
- Gather all photo paths in the source folder within subfolders.
- Extract EXIF data of each photograph's taken date and coordinate.
- 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 withOpenStreetMap
by using given administrative levels ascity town suburb
for each photograph. - As the folder process type is selected as
SubFoldersPreserveFolderHierarchy
folder and file hierarchy at the new output folder will be the same. - As the folder append type is selected as
DayRange
and folder append location isPrefix
, 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 isItaly album
) - 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 isGOPR6742.jpg
) - As no photograph taken date action is selected as
InSubFolder
and no coordinate action is selected asInSubFolder
, 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
- 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.
- 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.
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 |
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,,,,,,,,,,,,,,,,,,
2. Archive & index into local SQLite incrementally in a specific folder with photo-cli archive
command
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 |
├── 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 ├── no-photo-taken-date │ └── cf756397cc3ca81b2650c8801fd64e172504015a.jpg └── photo-cli.sqlite3 |
This archive process is done by running only the following single command;
photo-cli archive --output archive-folder --reverse-geocode OpenStreetMapFoundation --openstreetmap-properties country city town suburb
Same command with shorter alias of all argument names & values
photo-cli archive -o archive-folder -e 2 -r country city town suburb
Console/terminal output (as progress may take time, for each operation completion status shown with progress)
Searching photos: finished. 17 photo(s) found.
warn: PhotoCli.Services.Implementations.ExifParserService[0]
No coordinate found on `Gps` directory for /Users/ac/dev/src/photo-cli/photo-cli/docs/test-photographs/Spain Journey/IMG_5397.jpg
warn: PhotoCli.Services.Implementations.ExifParserService[0]
No coordinate found on `Gps` directory for /Users/ac/dev/src/photo-cli/photo-cli/docs/test-photographs/Italy album/IMG_2371.jpg
Parsing photo exif information: finished.
This OpenStreetMapFoundation provider is using rate limit of 1 second(s) between each request
Reverse Geocoding: finished.
Directory grouping: started.
Directory grouping: finished.
warn: PhotoCli.Services.Implementations.DuplicatePhotoRemoveService[0]
Photo is skipped due to same photo has already been archived. Same photo paths: /Users/ac/dev/src/photo-cli/photo-cli/docs/test-photographs/Italy album/DSC_1770.JPG, /Users/ac/dev/src/photo-cli/photo-cli/docs/test-photographs/Italy album/DSC_1770_(same).jpg
Processing target folder: finished.
Verified all photo files copied successfully by comparing file hashes from original photo files.
- 16 photo(s) existed on the output.
- 1 photo(s) are skipped, they have the same photo.
- 15 photo(s) has taken date and coordinate.
- 1 photo(s) has taken date but no coordinate.
- 1 photo(s) has no taken date and coordinate.
- Gather all photo paths in the source folder within subfolders.
- 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 ascity town suburb
for each photograph. - 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/
. - 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
. - 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 output2008/10/22/2008.10.22_17.00.07-a0ab699f5f99fce8ff49163e87c7590c2c9a66eb.jpg
and logs warning on the console with a messagePhoto is skipped due to same photo has already been archived. Same photo paths: /Users/ac/dev/src/photo-cli/photo-cli/docs/test-photographs/Italy album/DSC_1770.JPG, /Users/ac/dev/src/photo-cli/photo-cli/docs/test-photographs/Italy album/DSC_1770_(same).jpg
. - The photo that don't have any photo taken date
Spain Journey/IMG_5397.jpg
, copied intono-photo-taken-date
folder with only a sha1-hashcf756397cc3ca81b2650c8801fd64e172504015a.jpg
. - 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.
- All photo taken dates, address information is saved on local SQLite database on the output folder's top most folder
photo-cli.sqlite3
. It's an extra feature for technical persons who has technical knowledge about databases who wants to query for personal purposes.
Id | Path | CreatedAt | DateTaken | ReverseGeocodeFormatted | Latitude | Longitude | Year | Month | Day | Hour | Minute | Seconds | Address1 | Address2 | Address3 | Address4 | Address5 | Address6 | Address7 | Address8 | Sha1Hash |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2012/06/22/2012.06.22_19.52.31-bb649a18b3e7bb3df3701587a13f833749091817.jpg | 2023-11-17 00:44:18.942182 | 2012-06-22 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 | null | null | null | null | null | bb649a18b3e7bb3df3701587a13f833749091817 |
2 | 2005/08/13/2005.08.13_09.47.23-5842c73cfdc5f347551bb6016e00c71bb1393169.jpg | 2023-11-17 00:44:18.942391 | 2005-08-13 09:47:23 | Kenya-Barut ward | -0.37129999999999996 | 36.056416666666664 | 2005 | 8 | 13 | 9 | 47 | 23 | Kenya | Barut ward | null | null | null | null | null | null | 5842c73cfdc5f347551bb6016e00c71bb1393169 |
3 | no-photo-taken-date/cf756397cc3ca81b2650c8801fd64e172504015a.jpg | 2023-11-17 00:44:18.942393 | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | null | cf756397cc3ca81b2650c8801fd64e172504015a |
4 | 2015/04/10/2015.04.10_20.12.23-9f4e6d352ec172e1059571250655e376769080fe.jpg | 2023-11-17 00:44:18.942394 | 2015-04-10 20:12:23 | España-Madrid | 40.44697222222222 | -3.724752777777778 | 2015 | 4 | 10 | 20 | 12 | 23 | España | Madrid | null | null | null | null | null | null | 9f4e6d352ec172e1059571250655e376769080fe |
5 | 2015/04/10/2015.04.10_20.12.23-3907fc960f2873f40c8f35643dd444e0468be131.jpg | 2023-11-17 00:44:18.942395 | 2015-04-10 20:12:23 | España-Madrid | 40.44697222222222 | -3.724752777777778 | 2015 | 4 | 10 | 20 | 12 | 23 | España | Madrid | null | null | null | null | null | null | 3907fc960f2873f40c8f35643dd444e0468be131 |
6 | 2008/07/16/2008.07.16_11.33.20-90d835861e1aa3c829e3ab28a7f01ec3a090f664.jpg | 2023-11-17 00:44:18.942396 | 2008-07-16 11:33:20 | null | null | null | 2008 | 7 | 16 | 11 | 33 | 20 | null | null | null | null | null | null | null | null | 90d835861e1aa3c829e3ab28a7f01ec3a090f664 |
7 | 2008/10/22/2008.10.22_16.44.01-d470205a1d331a9d3765b3762b7c954bb8efc6ea.jpg | 2023-11-17 00:44:18.942398 | 2008-10-22 16:44:01 | Italia-Arezzo | 43.46844166666667 | 11.881515 | 2008 | 10 | 22 | 16 | 44 | 1 | Italia | Arezzo | null | null | null | null | null | null | d470205a1d331a9d3765b3762b7c954bb8efc6ea |
8 | 2008/10/22/2008.10.22_17.00.07-a0ab699f5f99fce8ff49163e87c7590c2c9a66eb.jpg | 2023-11-17 00:44:18.942401 | 2008-10-22 17:00:07 | Italia-Arezzo | 43.464455 | 11.881478333333334 | 2008 | 10 | 22 | 17 | 0 | 7 | Italia | Arezzo | null | null | null | null | null | null | a0ab699f5f99fce8ff49163e87c7590c2c9a66eb |
9 | 2008/10/22/2008.10.22_16.52.15-6b89a245809031ecc47789cdeaa332545330fc39.jpg | 2023-11-17 00:44:18.942401 | 2008-10-22 16:52:15 | Italia-Arezzo | 43.46725499999722 | 11.879213333333334 | 2008 | 10 | 22 | 16 | 52 | 15 | Italia | Arezzo | null | null | null | null | null | null | 6b89a245809031ecc47789cdeaa332545330fc39 |
10 | 2008/10/22/2008.10.22_16.55.37-dd42edcde2433a7df4a3d67bf61944a20884da89.jpg | 2023-11-17 00:44:18.942402 | 2008-10-22 16:55:37 | Italia-Arezzo | 43.46601166663889 | 11.87911166663889 | 2008 | 10 | 22 | 16 | 55 | 37 | Italia | Arezzo | null | null | null | null | null | null | dd42edcde2433a7df4a3d67bf61944a20884da89 |
11 | 2008/10/22/2008.10.22_16.43.21-3b0a3215b4f66d7ff4804dd223f192c21aee71bc.jpg | 2023-11-17 00:44:18.942403 | 2008-10-22 16:43:21 | Italia-Arezzo | 43.468365 | 11.881634999972222 | 2008 | 10 | 22 | 16 | 43 | 21 | Italia | Arezzo | null | null | null | null | null | null | 3b0a3215b4f66d7ff4804dd223f192c21aee71bc |
12 | 2008/10/22/2008.10.22_16.29.49-629b0b141634d6c0906e49af448bec8d755ba32c.jpg | 2023-11-17 00:44:18.942403 | 2008-10-22 16:29:49 | Italia-Arezzo | 43.46715666666389 | 11.885394999997223 | 2008 | 10 | 22 | 16 | 29 | 49 | Italia | Arezzo | null | null | null | null | null | null | 629b0b141634d6c0906e49af448bec8d755ba32c |
13 | 2008/10/22/2008.10.22_16.38.20-620d23336a12ab54f9f0190fe93960a4dba2df59.jpg | 2023-11-17 00:44:18.942404 | 2008-10-22 16:38:20 | Italia-Arezzo | 43.467081666663894 | 11.884538333330555 | 2008 | 10 | 22 | 16 | 38 | 20 | Italia | Arezzo | null | null | null | null | null | null | 620d23336a12ab54f9f0190fe93960a4dba2df59 |
14 | 2008/10/22/2008.10.22_16.28.39-5d66eec547469a1817bda4abe35c801359b2bb55.jpg | 2023-11-17 00:44:18.942405 | 2008-10-22 16:28:39 | Italia-Arezzo | 43.46744833333334 | 11.885126666663888 | 2008 | 10 | 22 | 16 | 28 | 39 | Italia | Arezzo | null | null | null | null | null | null | 5d66eec547469a1817bda4abe35c801359b2bb55 |
15 | 2008/10/22/2008.10.22_16.46.53-f670f2bb6c54898894b06b083185b05086bd4e6e.jpg | 2023-11-17 00:44:18.942406 | 2008-10-22 16:46:53 | Italia-Arezzo | 43.468243333330555 | 11.880171666638889 | 2008 | 10 | 22 | 16 | 46 | 53 | Italia | Arezzo | null | null | null | null | null | null | f670f2bb6c54898894b06b083185b05086bd4e6e |
16 | 2005/12/14/2005.12.14_14.39.47-03cb14d5c68beed97cbe73164de9771d537fcd96.jpg | 2023-11-17 00:44:18.942407 | 2005-12-14 14:39:47 | Italia-Firenze-Quartiere 1 | 43.78559443333333 | 11.234619433333334 | 2005 | 12 | 14 | 14 | 39 | 47 | Italia | Firenze | Quartiere 1 | null | null | null | null | null | 03cb14d5c68beed97cbe73164de9771d537fcd96 |
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 |
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,,,,,,,,,,,,,,,,,,,
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 town suburb --no-taken-date Continue --no-coordinate Continue
Same command with shorter alias of all argument names & values
photo-cli info -a -o photo-info.csv -e 2 -r country city town suburb -t Continue -c Continue
Console/terminal output (as progress may take time, for each operation completion status shown as percentage)
Searching photos: finished. 17 photos found.
Parsing photo exif information: finished.
Reverse Geocoding: finished.
Writing csv report: finished.
- 15 photos has taken date and coordinate.
- 1 photos has taken date but no coordinate.
- 1 photos has no taken date and coordinate.
- As all folders is selected. We are gathering all photo paths in the source folder within subfolders.
- 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 ascity town suburb
for each photograph. - As no photograph taken date action is selected as
Continue
and no coordinate action is selected asContinue
, they are listing in report with empty data.
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.
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).
After installing Google Earth Desktop, on File
menu, you can import your CSV file via Import
menu item.
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.
Important note: This application is a command line tool which don't have any user interface. To use this application, basic knowledge of how to run and send arguments to CLI applications is a must.
-
Installing .NET 8 runtime for your platform (Linux, macOS, Windows).
-
Install or update with these commands both on Linux, macOS, Windows;
Install: | To update newer version: |
---|---|
dotnet tool install photo-cli -g |
dotnet tool update photo-cli -g |
- Installing the tool globally provides access to the
photo-cli
command in your terminal.
photo-cli [command]
photo-cli help [command]
Note: You may test commands on test photographs which has coordinates and photograph taken dates in it.
- (For macOS and Linux) You should add your
.dotnet/tools
(path may change for your installation choices) to your PATH environment variable.
For macOS - Z Shell add the following line to your ~/.zshenv
file.
export PATH="$PATH:/Users/[your-account-name]/.dotnet/tools"
For Linux Bash add the following line to your ~/.profile
file.
export PATH="$PATH:/home/ac/.dotnet/tools"
The following command used in all samples with test photographs
photo-cli copy -i photos -f 2 -s 8 -n 2 -a 4 -p 1 -e 2 -r country city town suburb -o organized-albums -c 3 -t 3
By extracting Exchangeable image file format stored on each of your photographs.
Photograph's taken date used to determine when photograph's date. Most of the camera/cell phones save this data without any setting.
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.
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.
settings -k SupportedExtensions -v jpg,ext1,ext2,ext3
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.
settings -k CompanionExtensions -v mov,ext1,ext2,ext3
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.
To build addresses we need a reserve geocode provider. Currently, there is four reverse geocode provider is supported.
- BigDataCloud
- Open Street Map Foundation - Nominatim
- Google Maps
- LocationIq
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 | - | - | |
LocationIq | Yes | Yes | 5.000 req/day | 1req/sec | Open Street Map Foundation |
After selecting reverse geocode provider, you need to provide an API key. There are three ways to provide this API key;
- Send as an argument every time
- Use persist a setting to save it as configuration, so you don't need to submit everytime.
- 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 |
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.
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.
photo-cli address -i DSC_7082.jpg -e 1
AdminLevel2: Turkey
AdminLevel3: Central Anatolia Region
AdminLevel4: Ankara Province
AdminLevel6: Çankaya
AdminLevel8: Mebusevleri Mahallesi
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
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
photo-cli address -i DSC_7082.jpg -e 4
CountryCode: tr
Country: Türkiye
City: Ankara
Postcode: 06580
Suburb: Mebusevleri Mahallesi
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
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.
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 |
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 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 |
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 |
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
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
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
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
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
.
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.
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 |
├── 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 |
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 |
├── 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 |
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 |
├── 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 |
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 |
├── 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 |
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 |
├── 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 |
Subcommand | description |
---|---|
info |
Creates a report (CSV file) listing all photo taken date and address (reverse geocode). |
copy |
Copies photos into new folder hierarchy with given arguments using photograph's taken date and coordinate address (reverse geocode). |
archive |
Archives photos into specific folder and indexing photo taken date, address (reverse geocode) information into SQLite database. |
address |
Get address (reverse geocode) of single photo. |
settings |
Lists, saves and get settings. |
Creates a report (CSV file) listing all photo taken date and address (reverse geocode).
photo-cli help info
-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 input path )
-a, --all-folders (Optional) Read & list all photos in all sub folders (no extra parameter needed)
-t, --no-taken-date (Optional) Action to do when a photo with a no taken date. ( Continue: 0, PreventProcess: 1 )
-c, --no-coordinate (Optional) Action to do when a photo with a no coordinate. ( Continue: 0, 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. )
-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:
- 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
Copies photos into new folder hierarchy with given arguments using photograph's taken date and coordinate address (reverse geocode).
photo-cli help copy
-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 exists)
-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 )
-t, --no-taken-date (MUST) Action to do when a photo with a no taken date. ( Continue: 0, PreventProcess: 1, DontCopyToOutput: 2, InSubFolder: 3, AppendToEndOrderByFileName: 4, InsertToBeginningOrderByFileName: 5 )
-c, --no-coordinate (MUST) Action to do when a photo with a no coordinate. ( Continue: 0, 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 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, Address: 4 )
-a, --folder-append (Optional) Appending name strategy to folder names cloned from source folder hierarchy. [Can be with `FolderProcessType` is `SubFoldersPreserveFolderHierarchy`] ( FirstYearMonthDay: 1, FirstYearMonth: 2, FirstYear: 3, DayRange: 4,
MatchingMinimumAddress: 5 )
-p, --folder-append-location (Optional) Append location for `FolderAppendType`. [Can be use with `FolderProcessType` is `SubFoldersPreserveFolderHierarchy`] ( Prefix: 1, Suffix: 2 )
-v, --verify (Optional) Verify that all photo files copied successfully by comparing file hashes. (no extra parameter needed)
-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. )
-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:
- By preserving existing folder hierarchy, photos copied with a file name as photo taken date and time. Photos taken on same date and time, will have a number suffix. Photos that don't have any coordinate or photo taken date will be copied in a relative sub folder.
Example with long argument names;
photo-cli copy --no-coordinate InSubFolder --process-type SubFoldersPreserveFolderHierarchy --input [input-folder] --number-style PaddingZeroCharacter --output [output-folder] --naming-style DateTimeWithMinutes --no-taken-date InSubFolder
Example with short argument names;
photo-cli copy -c InSubFolder -f SubFoldersPreserveFolderHierarchy -i [input-folder] -n PaddingZeroCharacter -o [output-folder] -s DateTimeWithMinutes -t InSubFolder
- Preserving folder hierarchy, photos copied with a sequential file name. Photos that don't have any coordinate or photo taken date won't be copied to output.
Example with long argument names;
photo-cli copy --no-coordinate DontCopyToOutput --process-type SubFoldersPreserveFolderHierarchy --input [input-folder] --number-style AllNamesAreSameLength --output [output-folder] --naming-style Numeric --no-taken-date DontCopyToOutput
Example with short argument names;
photo-cli copy -c DontCopyToOutput -f SubFoldersPreserveFolderHierarchy -i [input-folder] -n AllNamesAreSameLength -o [output-folder] -s Numeric -t DontCopyToOutput
- Grouping all photos in folders with a name photo taken year and month. Photos will be copied with a file name date, time, and address received from OpenStreetMap reverse geocode provider.
Example with long argument names;
photo-cli copy --reverse-geocode OpenStreetMapFoundation --process-type FlattenAllSubFolders --group-by YearMonth --input [input-folder] --number-style OnlySequentialNumbers --output [output-folder] --openstreetmap-properties country city town suburb --naming-style DateTimeWithSecondsAddress --no-taken-date AppendToEndOrderByFileName
Example with short argument names;
photo-cli copy -e OpenStreetMapFoundation -f FlattenAllSubFolders -g YearMonth -i [input-folder] -n OnlySequentialNumbers -o [output-folder] -r country city town suburb -s DateTimeWithSecondsAddress -t AppendToEndOrderByFileName
- Process single folder and copy photos with file names as their address which will get from BigDataCloud
Example with long argument names;
photo-cli copy --no-coordinate PreventProcess --reverse-geocode BigDataCloud --process-type Single --input [input-folder] --number-style AllNamesAreSameLength --output [output-folder] --naming-style Address --no-taken-date DontCopyToOutput --bigdatacloud-levels 2 4 6 8
Example with short argument names;
photo-cli copy -c PreventProcess -e BigDataCloud -f Single -i [input-folder] -n AllNamesAreSameLength -o [output-folder] -s Address -t DontCopyToOutput -u 2 4 6 8
- Adding year month day as a prefix to existing folder names and photos copied with a file name as day and address. The address will be built from Google Maps (need API key).
Example with long argument names;
photo-cli copy --folder-append FirstYearMonthDay --no-coordinate PreventProcess --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 DayAddress --no-taken-date PreventProcess
Example with short argument names;
photo-cli copy -a FirstYearMonthDay -c PreventProcess -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 DayAddress -t PreventProcess
Archives photos into specific folder and indexing photo taken date, address (reverse geocode) information into SQLite database.
photo-cli help archive
-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 )
-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/documentation/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-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:
- 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 input folder (and it's subfolders recursively), fetches all photo's reverse geocode information, copies 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 and it's address (reverse geocode) into 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
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
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
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
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.
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.
Must be used on copy
verb. 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 |
Must be used on copy
verb. 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 |
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 |
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
Option | Value |
---|---|
Prefix | 1 |
Suffix | 2 |
Optional use for copy
verb. 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 |
Must be used on copy
verb. Number naming strategy when using Naming Style as Numeric
or using to numbering the possible same names.
Option | Value |
---|---|
AllNamesAreSameLength | 1 |
PaddingZeroCharacter | 2 |
OnlySequentialNumbers | 3 |
Optional use for copy
verb. Verify that all photo files copied successfully by comparing file hashes. (no extra parameter needed)
Optional use for copy
, archive
verb. Simulate the same process without writing to output folder. No extra parameter needed.
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 |
Sets Big Data Cloud reverse geocode API key. Alternatively, you may use the environment variable PHOTO_CLI_BIG_DATA_CLOUD_API_KEY
.
Sets Google Maps reverse geocode API key. Alternatively, you may use the environment variable PHOTO_CLI_GOOGLE_MAPS_API_KEY
.
Sets Location Iq reverse geocode API key. Alternatively, you may use the environment variable PHOTO_CLI_LOCATIONIQ_API_KEY
.
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. )
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. )
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. )
Bypass the free rate limit if you have paid license. (For LocationIq
reverse geocode providers)
Optional use for copy
verb. 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 |
Optional use for copy
verb. Action to do when a photo with a no coordinate.
Option | Value |
---|---|
Continue | 0 |
PreventProcess | 1 |
DontCopyToOutput | 2 |
InSubFolder | 3 |
Optional use for info
verb. Read & list all photos in all subfolders.
Optional use for info
verb. Action to do when a photograph with a no taken date. Default is Continue
.
Option | Value |
---|---|
Continue (default) | 0 (default) |
PreventProcess | 1 |
Optional use for info
verb. Action to do when a photo with a no coordinate.
Option | Value |
---|---|
Continue | 0 |
PreventProcess | 1 |
Optional use for archive
verb. Action to do when a photograph with a no taken date. Default is Continue
.
Option | Value |
---|---|
Continue (default) | 0 (default) |
PreventProcess | 1 |
Optional use for archive
verb. Action to do when a photo with a no coordinate.
Option | Value |
---|---|
Continue | 0 |
PreventProcess | 1 |
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.
photo-cli settings
LogLevel=Warning
CompanionExtensions=mov
SupportedExtensions=jpg,jpeg,heic,png
AddressSeparator=-
ArchivePhotoTakenDateHashSeparator=-
BigDataCloudApiKey=
ConnectionLimit=4
CoordinatePrecision=4
CsvReportFileName=photo-cli-report.csv
DateFormatWithDay=yyyy.MM.dd
DateFormatWithMonth=yyyy.MM
DateTimeFormatWithMinutes=yyyy.MM.dd_HH.mm
DateTimeFormatWithSeconds=yyyy.MM.dd_HH.mm.ss
DayFormat=dd
DayRangeSeparator=-
DryRunCsvReportFileName=photo-cli-dry-run.csv
FolderAppendSeparator=-
GoogleMapsApiKey=
LocationIqApiKey=
MonthFormat=MM
NoAddressAndPhotoTakenDateFolderName=no-address-and-no-photo-taken-date
NoAddressFolderName=no-address
NoPhotoTakenDateFolderName=no-photo-taken-date
PhotoFormatInvalidFolderName=invalid-photo-format
SameNameNumberSeparator=-
YearFormat=yyyy
photo-cli settings --key YearFormat
YearFormat=yyyy
photo-cli settings --key YearFormat --value y
No output when successful.
photo-cli settings --reset
No output when successful
Process exit codes listed below;
Option | Value |
---|---|
Success | 0 |
ParseArgsFailed | 1 |
AppSettingsInvalidFile | 2 |
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 |
PhotosWithNoDatePreventedProcess | 30 |
PhotosWithNoCoordinatePreventedProcess | 31 |
PhotosWithNoCoordinateAndNoDatePreventedProcess | 32 |
PropertyNotFound | 40 |
InvalidSettingsValue | 41 |
See the roadmap.
See the contributing.
See the code of conduct.
See the changelog.
Many thanks to these open source libraries. This work can not be done without these beautiful libraries and their contributors.
- CommandLineParser
- coverlet
- CsvHelper
- FluentAssertions
- FluentValidation
- MetadataExtractor
- Moq
- Polly
- System.IO.Abstractions
- xunit
Also thanks exif-samples for sample images, to make project test various EXIF data variations.
Everything inside this repository is Apache 2.0 licensed.
dotnet tool uninstall -g photo-cli
This tool is currently developed by Alp Coker and is open for contributors.