jpgDate

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

jpgDate

Post by s.dot »

for those of you who have unorganized digital pictures

jpgdate.class.php

Code: Select all

<?php

/**
 * This is a class to scan a directory for jpg images, scan the meta data for 
 * each jpg found, convert the date it was taken into a string, and rename or 
 * copy each image into a file with the date in it.
 *
 * This would be useful for those of you who don't organize your digital images.
 * (like me) 
 *
 * @date November 14th, 2007
 * @author Scott Martin <smp_info[at]yahoo[dot]com>
 */
 
class jpgDate
{
	/**
	 * The directory with jpg images in it that you want to scan and date
	 */
	private $_directory;
	
	/**
	 * The syntax of the date you want to add into the filename of images
	 */
	private $_dateSyntax = 'n-d-Y';
	
	/**
	 * Whether or not to overwrite images, or rename them.  Value may be true
	 * or false boolean
	 */
	private $_overwrite = false;
	
	/**
	 * The position to add the date onto the file name.  Values can be prepend 
	 * (before) or append (after)
	 */
	private $_pos = 'prepend';
	
	/**
	 * Contain array for jpg images found
	 */
	private $_jpgs = array();
	
	/**
	 * Sets the directory to scan for jpg images
	 * @param string $directory
	 * @access public
	 */
	public function setDirectory($directory)
	{
		if (is_dir($directory))
		{
			if ((substr($directory, -1) == '/') || (substr($directory, -1) == '\\'))
			{
				$directory = substr($directory, 0, strlen($directory)-1);
			}
			
			$this->_directory = $directory;
		} else
		{
			trigger_error('jpgDate: Cannot find directory ' . $directory, E_USER_ERROR);
		}
	}
	
	/**
	 * Sets the syntax to use for dating the images
	 * @param strying $syntax - syntax values from http://www.php.net/date
	 * @access public
	 */
	public function setDateSyntax($syntax)
	{
		$this->_dateSyntax = $syntax;
	}
	
	/**
	 * Sets the position to add the date string in the file name
	 * @param string $pos - 'prepend' or 'append'
	 * @access public
	 */
	public function setPos($pos)
	{
		if (in_array($pos, array('prepend', 'append')))
		{
			$this->_pos = $pos;
		} else
		{
			trigger_error('jpgDate: setPos() value must be prepend or append, ' . $pos . ' given', E_USER_ERROR);
		}
	}
	
	/**
	 * Sets whether this script should overwrite existing image files with 
	 * the new name, or create a copy of the file with the new name.
	 * @param boolean $bool - true = overwrite, false = don't overwrite
	 * @access public
	 */
	public function setOverwrite($bool)
	{
		if (is_bool($bool))
		{
			$this->_overwrite = $bool;
		} else
		{
			trigger_error('jpgDate: setOverwrite() expecting boolean true or false, ' . $bool . ' given', E_USER_ERROR);
		}
	}
	
	/**
	 * Runs through the methods
	 * @access public
	 */
	public function run()
	{
		if ($this->_hasJPGs())
		{
			$this->_getJPGDates();
			$this->_dateJPGs();
			$this->_writeJPGs();
		} else
		{
			trigger_error('jpgDate: Jpg images were not found in the given directory', E_USER_ERROR);
		}
	}
	
	/**
	 * Generates a list of valid jpg extensions, then checks the directory to 
	 * see if any jpg's exist in that directory
	 * @return boolean
	 * @access private
	 */
	private function _hasJPGs()
	{
		foreach (glob($this->_directory . DIRECTORY_SEPARATOR . '*.*') AS $file)
		{
			if (preg_match("/.+?\.(jpg|jpeg)/i", $file))
			{
				$this->_jpgs[array_pop(explode(DIRECTORY_SEPARATOR, $file))] = '';
			}
		}
		return !empty($this->_jpgs);
	}
	
	/**
	 * Scans each found jpg for meta data, then checks if it has a FileDateTime attribute
	 * @access private
	 */
	private function _getJPGDates()
	{
		foreach ($this->_jpgs AS $jpg => $time)
		{
			//var_dump($this->_jpgs);
			if ($exif = exif_read_data($this->_directory . DIRECTORY_SEPARATOR . $jpg, 'COMPUTED'))
			{
				if (array_key_exists('FileDateTime', $exif))
				{
					$this->_jpgs[$jpg] = $exif['FileDateTime'];
				} else
				{
					$this->_jpgs[$jpg] = filemtime($jpg);
				}
			} else
			{
				$this->_jpgs[$jpg] = filemtime($jpg);
			}
		}
	}
	
	/**
	 * Stores appropriate filenames with dates in the class member
	 * @access private
	 */
	private function _dateJPGs()
	{
		foreach ($this->_jpgs AS $jpg => $time)
		{
			if ($this->_pos == 'prepend')
			{
				$filename = date($this->_dateSyntax, $time) . '-' . $jpg;
			} else
			{
				$ext = strstr($jpg, ".");
				$filename = str_replace($ext, '', $jpg);
				$filename = $filename . '-' . date($this->_dateSyntax, $time) . $ext;
			}
			
			$this->_jpgs[$jpg] = $filename;
		}
	}
	
	/**
	 * Writes the new files names, be it a new file or renaming old files
	 * @access private
	 */
	private function _writeJPGs()
	{
		if ($this->_overwrite == false)
		{
			foreach ($this->_jpgs AS $original => $new)
			{
				if (!empty($new))
				{
					if (!copy($this->_directory . DIRECTORY_SEPARATOR . $original, $this->_directory . DIRECTORY_SEPARATOR . $new))
					{
						trigger_error('jpgDate: Could not save image (' . $original . ') with new image name, check permissions on the directory', E_USER_NOTICE);
					}
				}
			}
		} else
		{
			foreach ($this->_jpgs AS $original => $new)
			{
				if (!empty($new))
				{
					if (!rename($this->_directory . DIRECTORY_SEPARATOR . $original, $this->_directory . DIRECTORY_SEPARATOR . $new))
					{
						trigger_error('jpgDate: Could not rename image (' . $original . '), check permissions on the image', E_USER_NOTICE);
					}
				}
			}
		}
	}
}
sample usage

Code: Select all

require_once 'jpgdate.class.php';
$jpgDate = new jpgDate();

$jpgDate->setDirectory('C:\Users\scott\Pictures\pictures');
$jpgDate->setDateSyntax('n-d-y-gi-A');  //this defaults to 'n-d-Y'
$jpgDate->run();
My image folder before:

Code: Select all

picturea.JPG
pictureb.JPG
picturec.JPG
pictured.JPG
picturee.JPG
picturef.JPG
My image folder after:

Code: Select all

picturea.JPG
pictureb.JPG
picturec.JPG
pictured.JPG
picturee.JPG
picturef.JPG
1-11-2007-658-PM-picturea.JPG
1-11-2007-701-PM-pictureb.JPG
1-11-2007-701-PM-picturec.JPG
3-10-2007-858-AM-pictured.JPG
4-25-2007-902-AM-picturee.JPG
10-31-2007-811-PM-picturef.JPG
Last edited by s.dot on Wed Nov 14, 2007 10:25 am, edited 2 times in total.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I'd likely not use glob() for _hasJPGs() unless it was to simply grab the entire file list and filter it via a regex pattern.

Code: Select all

(?:\.jpe?g)$
(or similar) by the way.

preg_quote() isn't useful for the haystack, only the pattern.

What happens if they are already dated?

I'm not sure throwing errors when no exif data can be found or the FileDateTime field cannot be found is a great idea, but it isn't a horrible one. ;) Instead, I would suggest returning the file's timestamp data (via filemtime() or a sibling.)
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

feyd wrote:I'd likely not use glob() for _hasJPGs() unless it was to simply grab the entire file list and filter it via a regex pattern.

Code: Select all

(?:\.jpe?g)$
(or similar) by the way.
Nice. Doing this enabled me to get rid of the _gatherJPGs() method. I just gather them in the _hasJPGs() method.
feyd wrote:preg_quote() isn't useful for the haystack, only the pattern.
Yeah, don't know what i was thinking.
feyd wrote:I'm not sure throwing errors when no exif data can be found or the FileDateTime field cannot be found is a great idea, but it isn't a horrible one. ;) Instead, I would suggest returning the file's timestamp data (via filemtime() or a sibling.)
Genius. Is there a function for the file creation time? Instead of the modification or access time.

I've edited the code to reflect your input.
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

scottayy wrote:Genius. Is there a function for the file creation time? Instead of the modification or access time.
Unfortunately, the variance between file systems does not afford PHP to supply such a function as some store that information, some do not.

The three provided by php are filemtime(), fileatime() and filectime(). filemtime() and filectime() are similar in concept, but can vary in response due to what is considered modification versus change depending on the file system in question.
User avatar
s.dot
Tranquility In Moderation
Posts: 5001
Joined: Sun Feb 06, 2005 7:18 pm
Location: Indiana

Post by s.dot »

That's ok.

I suppose filemtime() will be acceptable for most people. I didn't need it because all of my meta data was intact.

I just gathered all directories on my external hdd, looped through them, and dated all of my images :-D It's sweet. Of course I could right click on the image and go to properties, and then the last tab, and see the date.. but I like it in the filename.

While doing it, I would've liked to have saved to a different directory, so maybe I'll add that in as a feature if someone requests it (if someone even uses this).
Set Search Time - A google chrome extension. When you search only results from the past year (or set time period) are displayed. Helps tremendously when using new technologies to avoid outdated results.
Post Reply