{"id":23,"date":"2019-10-02T22:17:10","date_gmt":"2019-10-03T02:17:10","guid":{"rendered":"http:\/\/iheinrich.com\/?p=23"},"modified":"2019-10-03T11:47:44","modified_gmt":"2019-10-03T15:47:44","slug":"drowning-in-a-sea-of-pictures","status":"publish","type":"post","link":"https:\/\/iheinrich.com\/index.php\/2019\/10\/02\/drowning-in-a-sea-of-pictures\/","title":{"rendered":"Drowning in a Sea of Pictures"},"content":{"rendered":"\n<p>If you are like me, you take a gazillion pictures and they are all over the place.<\/p>\n\n\n\n<p>Recently, I decided to the consolidate all my data from all my hard drives onto one massive NAS Drive (Synology 1515+).<\/p>\n\n\n\n<p>Synology DOES offer a tool that let\u2019s you sort all your images using \u201cSmart Folders\u201d or something, but that didn\u2019t appeal to me.<\/p>\n\n\n\n<p>I wanted a ruby script to that I could drop into a sea of pictures \nand I would get all the images sorted by month and year into named \nfolders.<\/p>\n\n\n\n<p>I am sure there are tools that do this, but I wanted to write my own.<\/p>\n\n\n\n<p>I\u2019m stubborn that way.<\/p>\n\n\n\n<p><strong>Step 1: Install <a href=\"https:\/\/web.archive.org\/web\/20190211074905\/http:\/\/exifr.rubyforge.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">exifr reader gem<\/a>.<\/strong><\/p>\n\n\n\n<p>One of the great things about ruby is that there usually is a \u201cgem for that\u201d.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nHeinrichs-iMac:~ hbeck$ sudo gem install exifr\n\nWARNING: Improper use of the sudo command could lead to data loss\nor the deletion of important system files. Please double-check your\ntyping when using sudo. Type \"man sudo\" for more information.\n\nTo proceed, enter your password, or type Ctrl-C to abort.\n\nPassword: StevePontEsMuyMacho\nSuccessfully installed exifr-1.1.3\n1 gem installed\nInstalling ri documentation for exifr-1.1.3...\nInstalling RDoc documentation for exifr-1.1.3...\nHeinrichs-iMac:~ hbeck$ \n<\/code><\/pre>\n\n\n\n<p><strong>Step 2: Write some code!<\/strong><\/p>\n\n\n\n<p>We are going to require<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>rubygems<\/strong> since we are using a gem.<\/li><li><strong>exifr<\/strong>, to read the exif data.<\/li><li><strong>date<\/strong>, to read the date.<\/li><li><strong>fileutils<\/strong>, to move the files.<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>#! \/usr\/bin\/env ruby\n\nrequire 'rubygems'\nrequire 'exifr'\nrequire 'date'\nrequire 'fileutils'\n\n# I want to just drop this in a folder of images. This gets the working directory\nwd = Dir.getwd\n\n# files is an array of file name strings in the working folder.\nfiles = Dir.entries(\"#{wd}\")\n\n#I'm going to go thru each file and read it's date.\nfiles.each do |file|  \n        # the meat of what I want to do will go here....\nend<\/code><\/pre>\n\n\n\n<p><strong>Step 3: There\u2019s trouble right here in Directory City<\/strong><\/p>\n\n\n\n<p>One of the pain in the keister issues in MacOS is that hidden files \ncalled \u201c.\u201d or \u201c..\u201d will be picked up in the directory scan. But since \nthey do not have exif data we need to sniff for them and NOT run the \nexifr method on them. Also, the ruby file will be in the directory so I \nneed to exclude that as well.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nif file.start_with?(\".\") || file.end_with?(\".rb\") \n    puts \"(hand wave) this is not the file you are looking for...\"\n  else\n       # the meat of what I want to do will go here.\n  end <\/code><\/pre>\n\n\n\n<p>Time to get some shooting data!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\ndateShot = EXIFR::JPEG.new(\"#{file}\").date_time<\/code><\/pre>\n\n\n\n<p>returns a date that looks like this: Sat Jun 22 22:09:22 -0400 2013<\/p>\n\n\n\n<p>I know it\u2019s a date class since I double checked thusly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nputs dateShot.class<\/code><\/pre>\n\n\n\n<p>and got \u2018Date\u2019.<br> If ever you are unsure what a variable actually is \u2018variable<strong>.class<\/strong>\u2018 is your best friend.<\/p>\n\n\n\n<p><strong>Step 4: There\u2019s MORE trouble right here in Directory City<\/strong><\/p>\n\n\n\n<p>The idea is to make directories with Month_Year, but what if there is\n ALREADY a directory in the folder? What if I dump a sea of images into a\n directory, run my magic ruby script, and then find MORE images after I \nhave sorted all the images into directories? I can\u2019t run the exifr \nmethod on a directory, so I need to sniff for that as well.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nif file.start_with?(\".\") || file.end_with?(\".rb\")\n    puts \"(hand wave) this is not the file you are looking for...\"\n  elsif file.include? \".\"\n     # I know that all my jpeg files will have a DOT in the name and my directories do NOT have dots in their name.\n     # So I will put the methods here....\n  else\n    puts \"it's a directory\"\n  end <\/code><\/pre>\n\n\n\n<p><strong>Step 4: How about a date, baby?<\/strong><\/p>\n\n\n\n<p>I want the date formatted like this: Mon_Year. The <a href=\"https:\/\/web.archive.org\/web\/20190211074905\/http:\/\/ruby-doc.org\/stdlib-2.0\/libdoc\/date\/rdoc\/Date.html#method-i-strftime\" target=\"_blank\" rel=\"noreferrer noopener\">Date class offers a method to format our date<\/a> pretty much any way we like.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n     dateShot = EXIFR::JPEG.new(\"#{file}\").date_time\n     folderName = dateShot.strftime(\"%b_%Y\")<\/code><\/pre>\n\n\n\n<p>This will return <strong>Jun_2013<\/strong>.<\/p>\n\n\n\n<p>I want to make the directory, but of course, if the directory ALREADY EXISTS, I want to leave it alone.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n if File.exists?(\"#{folderName}\") # I thought Dir.exists would work, but it doesn't. If you know WHY, please let me know.\n          puts \"the directory exists\"\n     else\n          Dir.mkdir(\"#{folderName}\")\n          puts \"make the directory\"\n     end<\/code><\/pre>\n\n\n\n<p>The directory has been created, let\u2019s move the files in there\u2026<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nFileUtils.move file, folderName<\/code><\/pre>\n\n\n\n<p>Yes, it really is that easy.<\/p>\n\n\n\n<p><strong>Step 5: It\u2019s ALWAYS SOMETHING ALICE!!&nbsp;<\/strong><\/p>\n\n\n\n<p>Ok, you are thrilled that everything is working fine and images are \ngetting sorted out but then exifr finds that SOME images have no \nshooting data.<\/p>\n\n\n\n<p>It happens. Maybe you downloaded the image from a website that stripped out the exif data.<\/p>\n\n\n\n<p>When this occurs, the exifr method returns nil.<\/p>\n\n\n\n<p>We need to catch that, so let\u2019s make a NO_Date directory and drop the undated images in there.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n   if dateShot.nil?\n         folderName = 'No_Date'\n         puts \"there is no date\"\n else\n         folderName = dateShot.strftime(\"%b_%Y\")\n end<\/code><\/pre>\n\n\n\n<p><strong>Step 6: Let\u2019s test it!&nbsp;<\/strong><\/p>\n\n\n\n<p>I suggest you start with a small folder of images, drop the ruby file\n in there, and see your results. I wrote this on my MacOS, but it should\n work on any OS. If you have Windows, it goes without saying that you \nneed to install Ruby to make this work (but I said it anyway).<\/p>\n\n\n\n<p><strong>Step 7: What happens if I have a NEF\/RAW image?&nbsp;<\/strong><\/p>\n\n\n\n<p>Good question! I\u2019ll investigate this another time. I have a lot of images to clean up!<\/p>\n\n\n\n<p>Here is the final script. Enjoy.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n\t#! \/usr\/bin\/env ruby\n\n\trequire 'rubygems'\n\trequire 'exifr'\n\trequire 'date'\n\trequire 'fileutils'\n\n\t# I want to just drop this in a folder of images. \n\twd = Dir.getwd\n\n\t# files is a list of a files in the working folder\n\tfiles = Dir.entries(\"#{wd}\")\n\n\t#I'm going to go thru each file and read it's date.\n\tfiles.each do |file|  \n\n\t  if file.start_with?(\".\") || file.end_with?(\".rb\") \n\t\t#ignore ruby scrip and hidden files.\n\t    puts \"(hand wave) this is not the file you are looking for...\" \n\t  elsif file.include? \".\"\n\t     dateShot = EXIFR::JPEG.new(\"#{file}\").date_time\n\n\t     if dateShot.nil?\n\t       folderName = 'No_Date'\n\t       puts \"there is no date\"\n\t     else\n\t       folderName = dateShot.strftime(\"%b_%Y\")\n\t     end\n\n\t     if File.exists?(\"#{folderName}\") \n\t     \tputs \"the directory exists\"\n\t     else\n\t     \tDir.mkdir(\"#{folderName}\")\n\t     end\n\n\t     FileUtils.move file, folderName\n\n\t  else\n\t    puts \"it's a directory\"\n\t  end<\/code><\/pre>\n\n\n\n<p><strong> Final thoughts <\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/web.archive.org\/web\/20190211074905\/http:\/\/www.iheinrich.com\/wordpress\/?p=77\">What if the file is a NEF and not a JPEG?<\/a><\/li><li><a href=\"https:\/\/web.archive.org\/web\/20190211074905\/http:\/\/www.iheinrich.com\/wordpress\/?p=91\">What if the file moved to a folder already has a file with SAME NAME?<\/a> What will happen? (<strong>Hint:<\/strong> <em>I already tested this. Your other file gets lost. Sorry about that, I need to fix that in a update<\/em>.)<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>If you are like me, you take a gazillion pictures and they are all over the place. Recently, I decided&#8230;<\/p>\n","protected":false},"author":1,"featured_media":24,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[5],"tags":[],"class_list":["post-23","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ruby","wpcat-5-id"],"_links":{"self":[{"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/posts\/23","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/comments?post=23"}],"version-history":[{"count":2,"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/posts\/23\/revisions"}],"predecessor-version":[{"id":26,"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/posts\/23\/revisions\/26"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/media\/24"}],"wp:attachment":[{"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/media?parent=23"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/categories?post=23"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iheinrich.com\/index.php\/wp-json\/wp\/v2\/tags?post=23"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}