Skip to content

Instantly share code, notes, and snippets.

@JamesSwift
Last active July 5, 2020 18:05
Show Gist options
  • Save JamesSwift/400035125985797db5ffbc8cf530fc87 to your computer and use it in GitHub Desktop.
Save JamesSwift/400035125985797db5ffbc8cf530fc87 to your computer and use it in GitHub Desktop.
Scan a directory and move or copy all files to a date-structured directory
<?php
/*
This script will scan the directory you specify and move or copy
all files within it and its subdirectories. The files will be moved
to a subdirectory of the location where the script is, based on
either it's creation date or if the file contains exif data, the
date the photo was taken.
Call this script like so:
php import.php --dry-run --remove /home/me/Pictures
Flags:
--dry-run
Run the script but don't actually remove or copy any files
--quiet
Suppress all output expect errors
--remove
Remove the source files and folders after copying
Note: With this flag, any subdirectories in the source won't be
removed on the first run, but will be if you rin it again. This
is to prevent errors where files are in the process of being
copied or synchronized while the script is being run.
*/
//Hide output
$quiet = !!array_search("--quiet", $argv);
//Dry-run
$dry_run = !!array_search("--dry-run", $argv);
//remove
$remove = !!array_search("--remove", $argv);
//Find dir
if (sizeof($argv)===1+intval($quiet)+intval($dry_run)+intval($remove)) die("Please specify a directory to import as the final argument.\n");
$search = array_pop($argv);
//Suppress exif notices
error_reporting(E_ERROR);
//UNSAFE OPTION
//Instead of checking that images are identical before de-duplicating,
//If the filenames match, and the image dimensions match, assume the
//images are identical
$similar_image_deduplicate=false;
importPhotos($search);
function importPhotos($dir, $deleteDir=false){
global $quiet, $dry_run, $remove, $similar_image_deduplicate;
if (!$quiet) print "\nImporting \e[0;30;42m$dir\e[0m\n";
$list = scandir($dir);
//Delete empty directories (intentionally left over from previous run)
if (sizeof($list)===2 && $deleteDir===true){
if (!$quiet) print "Deleted empty dir $dir\n";
if (!$dry_run && $remove) rmdir($dir);
}
foreach ($list as $item){
if ($item==="." || $item==="..") continue;
if (is_dir($dir."/".$item)){
importPhotos($dir."/".$item, true);
continue;
}
//try to get exif data
$exifData = exif_read_data( $dir."/".$item, 'IFD0');
if( $exifData !== FALSE && array_key_exists('DateTime', $exifData ) ) {
$date = strtotime( $exifData['DateTime'] );
} else {
$date = filemtime($dir."/".$item);
}
$new_dir = date("Y/m F", $date);
//if (!$quiet) print $item." > ".$new_dir."\n";
if (!is_dir($new_dir)){
if (!$dry_run) mkdir($new_dir, 0770, true);
}
//Check for duplicates
if (file_exists($new_dir."/".$item)){
//Check if dimensions are the same;
if ($similar_image_deduplicate && in_array(strtolower(pathinfo($dir."/".$item, PATHINFO_EXTENSION)), ["png","bmp","jpeg","jpg","gif"])){
$imgsize = getimagesize($dir."/".$item);
$new_imgsize = getimagesize($new_dir."/".$item);
if ($imgsize['height'] === $new_imgsize['height'] && $imgsize['width'] === $new_imgsize['width']){
if (!$quiet) print "duplicate filename skipped: ".red($dir."/".$item." = ".$new_dir."/".$item)."\n";
if (!$dry_run && $remove) unlink($dir."/".$item);
continue;
}
print "\n\n---\n\n";
}
//Compare hash of the files
if (hash("sha256", file_get_contents($dir."/".$item)) === hash("sha256", file_get_contents($new_dir."/".$item))){
if (!$quiet) print "duplicate skipped: ".purple($dir."/".$item." === ".$new_dir."/".$item)."\n";
if (!$dry_run && $remove) unlink($dir."/".$item);
continue;
}
//Files have same name, but aren't identical, rename the new file
$OriginalFilename = $dir."/".$item;
$filename = pathinfo($OriginalFilename, PATHINFO_FILENAME);
$extension = pathinfo($OriginalFilename, PATHINFO_EXTENSION);
$FinalFilename = $item;
$FileCounter = 1;
while (file_exists( $new_dir.'/'.$FinalFilename )){
$FinalFilename = $filename . '_' . $FileCounter++ . '.' . $extension;
}
if (!$quiet) print "renaming due to conflict: ".$dir."/".$item." > ".cyan($new_dir."/".$FinalFilename)."\n";
if (!$dry_run){
if ($remove){
rename($dir."/".$item, $new_dir."/".$FinalFilename);
} else {
copy($dir."/".$item, $new_dir."/".$FinalFilename);
}
}
} else {
if (!$quiet) print $dir."/".$item." > ".green($new_dir."/".$item)."\n";
if (!$dry_run){
if ($remove){
rename($dir."/".$item, $new_dir."/".$item);
} else {
copy($dir."/".$item, $new_dir."/".$item);
}
}
}
}
}
//Helper colour functions
function green($txt){ return "\e[32m".$txt."\e[0m"; }
function cyan($txt){ return "\e[36m".$txt."\e[0m"; }
function purple($txt){ return "\e[35m".$txt."\e[0m"; }
function red($txt){ return "\e[31m".$txt."\e[0m"; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment