2014-07-09 03:04:56 +00:00
|
|
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class supports Photos.
|
|
|
|
*
|
|
|
|
* @package Photo
|
|
|
|
* @category Models
|
|
|
|
* @author Deon George
|
|
|
|
* @copyright (c) 2014 Deon George
|
|
|
|
* @license http://dev.leenooks.net/license.html
|
|
|
|
*/
|
|
|
|
class Model_Photo extends ORM {
|
2015-05-15 04:12:10 +00:00
|
|
|
private $_path = '/mnt/net/qnap/Multimedia/Photos';
|
2014-07-09 03:04:56 +00:00
|
|
|
private $_io = NULL;
|
|
|
|
|
|
|
|
protected $_has_many = array(
|
|
|
|
'album'=>array('through'=>'album_photo'),
|
2015-07-12 11:23:57 +00:00
|
|
|
'people'=>array('through'=>'photo_people','far_key'=>'people_id'),
|
|
|
|
'tags'=>array('through'=>'photo_tag','far_key'=>'tag_id'),
|
|
|
|
'photo_tag'=>array('far_key'=>'id'),
|
|
|
|
'photo_people'=>array('far_key'=>'id'),
|
2014-07-09 03:04:56 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filters used to format the display of values into friendlier values
|
|
|
|
*/
|
|
|
|
protected $_display_filters = array(
|
|
|
|
'date_orig'=>array(
|
|
|
|
array('Site::Datetime',array(':value')),
|
|
|
|
),
|
|
|
|
'date_taken'=>array(
|
|
|
|
array('Site::Datetime',array(':value')),
|
|
|
|
),
|
|
|
|
'signature'=>array(
|
|
|
|
array('Model_Photo::Signaturetrim',array(':value')),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
public function date_file($type,$format=FALSE) {
|
|
|
|
switch ($type) {
|
|
|
|
case 'a': $t = fileatime($this->file_path());
|
|
|
|
break;
|
|
|
|
case 'c': $t = filectime($this->file_path());
|
|
|
|
break;
|
|
|
|
case 'm': $t = filemtime($this->file_path());
|
|
|
|
break;
|
2014-07-09 03:04:56 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
return $format ? Site::Datetime($t) : $t;
|
2014-07-09 03:04:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function date_taken() {
|
|
|
|
return $this->display('date_taken').($this->subsectime ? '.'.$this->subsectime : '');
|
|
|
|
}
|
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
public function file_path($short=FALSE,$new=FALSE) {
|
2015-06-04 04:25:58 +00:00
|
|
|
$file = $this->filename;
|
|
|
|
|
|
|
|
if ($new)
|
|
|
|
$file = sprintf('%s.%s',((is_null($this->date_taken) OR ! $this->date_taken)
|
|
|
|
? sprintf('UNKNOWN/%07s',$this->file_path_id())
|
|
|
|
: sprintf('%s_%03s',date('Y/m/d-His',$this->date_taken),$this->subsectime).($this->subsectime ? '' : sprintf('-%05s',$this->id))),$this->type());
|
2015-05-15 04:12:10 +00:00
|
|
|
|
|
|
|
return (($short OR preg_match('/^\//',$file)) ? '' : $this->_path.DIRECTORY_SEPARATOR).$file;
|
2014-07-09 03:04:56 +00:00
|
|
|
}
|
|
|
|
|
2015-06-04 04:25:58 +00:00
|
|
|
public function file_path_id($sep=3,$depth=9) {
|
|
|
|
return trim(chunk_split(sprintf("%0{$depth}s",$this->id),$sep,'/'),'/');
|
|
|
|
}
|
|
|
|
|
2015-07-12 11:23:57 +00:00
|
|
|
public function file_path_short($path) {
|
|
|
|
return preg_replace(":^{$this->_path}".DIRECTORY_SEPARATOR.":",'',$path);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function delete() {
|
|
|
|
if (unlink($this->file_path())) {
|
|
|
|
// Remove any tags
|
|
|
|
foreach ($this->photo_tag->find_all() as $o)
|
|
|
|
$o->delete();
|
|
|
|
|
|
|
|
// Remove any people
|
|
|
|
foreach ($this->photo_people->find_all() as $o)
|
|
|
|
$o->delete();
|
|
|
|
|
|
|
|
return parent::delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If unlink failed...
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2015-06-04 04:25:58 +00:00
|
|
|
public function file_size() {
|
|
|
|
return filesize($this->file_path());
|
|
|
|
}
|
|
|
|
|
2014-07-09 03:04:56 +00:00
|
|
|
public function gps(array $coordinate,$hemisphere) {
|
|
|
|
if (! $coordinate OR ! $hemisphere)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for ($i=0; $i<3; $i++) {
|
|
|
|
$part = explode('/', $coordinate[$i]);
|
|
|
|
|
|
|
|
if (count($part) == 1)
|
|
|
|
$coordinate[$i] = $part[0];
|
|
|
|
|
|
|
|
elseif (count($part) == 2)
|
|
|
|
$coordinate[$i] = floatval($part[0])/floatval($part[1]);
|
|
|
|
|
|
|
|
else
|
|
|
|
$coordinate[$i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
list($degrees, $minutes, $seconds) = $coordinate;
|
|
|
|
|
|
|
|
$sign = ($hemisphere == 'W' || $hemisphere == 'S') ? -1 : 1;
|
|
|
|
|
|
|
|
return round($sign*($degrees+$minutes/60+$seconds/3600),$degrees > 100 ? 3 : 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function image() {
|
|
|
|
$imo = $this->io();
|
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
if (array_key_exists('exif',$imo->getImageProfiles()))
|
|
|
|
$imo->removeImageProfile('exif');
|
|
|
|
|
2014-07-09 03:04:56 +00:00
|
|
|
$this->rotate($imo);
|
|
|
|
|
|
|
|
return $imo->getImageBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function info() {
|
|
|
|
$imo = $this->io();
|
|
|
|
|
|
|
|
return $imo->getImageProperties();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function io($attr=NULL) {
|
|
|
|
if (is_nulL($this->_io))
|
2015-06-04 04:25:58 +00:00
|
|
|
$this->_io = new Imagick($this->file_path());
|
2014-07-09 03:04:56 +00:00
|
|
|
|
|
|
|
return is_null($attr) ? $this->_io : $this->_io->getImageProperty($attr);
|
|
|
|
}
|
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
public function move($path='') {
|
|
|
|
if (! $path)
|
|
|
|
$path = $this->file_path(FALSE,TRUE);
|
2014-07-09 03:04:56 +00:00
|
|
|
|
2015-07-12 11:23:57 +00:00
|
|
|
// Check if there is already a phoot here - and if it is pending delete.
|
|
|
|
$po = ORM::factory('Photo',array('filename'=>$this->file_path_short($path)));
|
2014-07-09 03:04:56 +00:00
|
|
|
|
2015-07-12 11:23:57 +00:00
|
|
|
// @todo Move any tags if we are replacing an existing photo.
|
|
|
|
if ($po->loaded() AND (! $po->remove OR ($po->remove AND ! $po->delete())))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
unset($po);
|
|
|
|
|
|
|
|
// If the file already exists, or we cant create the dir structure, we'll ignore the move
|
|
|
|
if (file_exists($path) OR ! File::ParentDirExist(dirname($path),TRUE))
|
2015-05-15 04:12:10 +00:00
|
|
|
return FALSE;
|
2014-07-09 03:04:56 +00:00
|
|
|
|
2015-06-04 04:25:58 +00:00
|
|
|
if (rename($this->file_path(),$path)) {
|
|
|
|
// Convert the path to a relative one.
|
2015-07-12 11:23:57 +00:00
|
|
|
$this->filename = $this->file_path_short($path);
|
2014-07-09 03:04:56 +00:00
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
// If the DB update failed, move it back.
|
|
|
|
if (! $this->save() AND ! rename($path,$this->file_path()))
|
|
|
|
throw new Kohana_Exception('Error: Unable to move file, ID: :id, OLD: :oldname, NEW: :newname',
|
2015-06-04 04:25:58 +00:00
|
|
|
array(':id'=>$this->id,':oldname'=>$this->file_path(),':newname'=>$path));
|
|
|
|
|
|
|
|
chmod($this->file_path(),0444);
|
2015-05-15 04:12:10 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
2014-07-09 03:04:56 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
public function propertydiff($id) {
|
|
|
|
if ($id == $this->id)
|
|
|
|
return;
|
|
|
|
|
|
|
|
$po = ORM::factory($this->_object_name,$id);
|
|
|
|
if (! $po->loaded())
|
|
|
|
return;
|
|
|
|
|
|
|
|
$result = array_diff_assoc($this->info(),$po->info());
|
|
|
|
|
|
|
|
return join('|',array_keys($result));
|
|
|
|
}
|
2016-06-22 05:49:20 +00:00
|
|
|
|
|
|
|
/*
|
2014-07-09 03:04:56 +00:00
|
|
|
private function rotate(Imagick $imo) {
|
|
|
|
switch ($this->orientation) {
|
|
|
|
case 3: $imo->rotateImage(new ImagickPixel('none'),180);
|
|
|
|
break;
|
|
|
|
case 6: $imo->rotateImage(new ImagickPixel('none'),90);
|
|
|
|
break;
|
|
|
|
case 8: $imo->rotateImage(new ImagickPixel('none'),-90);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-06-22 05:49:20 +00:00
|
|
|
*/
|
2014-07-09 03:04:56 +00:00
|
|
|
|
2015-05-15 04:12:10 +00:00
|
|
|
public function rotation() {
|
|
|
|
switch ($this->orientation) {
|
|
|
|
case 1: return 'None!';
|
|
|
|
case 3: return 'Upside Down';
|
|
|
|
case 6: return 'Rotate Right';
|
|
|
|
case 8: return 'Rotate Left';
|
|
|
|
default:
|
|
|
|
return 'unknown?';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-12 11:23:57 +00:00
|
|
|
public static function SignatureTrim($signature,$chars=6) {
|
|
|
|
return sprintf('%s...%s',substr($signature,0,$chars),substr($signature,-1*$chars));
|
2014-07-09 03:04:56 +00:00
|
|
|
}
|
|
|
|
|
2016-06-22 05:49:20 +00:00
|
|
|
/*
|
2015-05-15 04:12:10 +00:00
|
|
|
public function thumbnail($rotate=TRUE) {
|
2015-07-12 11:23:57 +00:00
|
|
|
if (! $this->thumbnail)
|
|
|
|
return NULL;
|
|
|
|
|
2014-07-09 03:04:56 +00:00
|
|
|
$imo = new Imagick();
|
|
|
|
|
|
|
|
$imo->readImageBlob($this->thumbnail);
|
2015-05-15 04:12:10 +00:00
|
|
|
|
|
|
|
if ($rotate)
|
|
|
|
$this->rotate($imo);
|
2014-07-09 03:04:56 +00:00
|
|
|
|
|
|
|
return $imo->getImageBlob();
|
|
|
|
}
|
2016-06-22 05:49:20 +00:00
|
|
|
*/
|
2014-07-09 03:04:56 +00:00
|
|
|
|
2015-07-12 11:23:57 +00:00
|
|
|
public function thumbnail_sig() {
|
|
|
|
return md5($this->thumbnail()).':'.strlen($this->thumbnail());
|
|
|
|
}
|
|
|
|
|
2014-07-09 03:04:56 +00:00
|
|
|
public function type($mime=FALSE) {
|
|
|
|
return strtolower($mime ? File::mime_by_ext(pathinfo($this->filename,PATHINFO_EXTENSION)) : pathinfo($this->filename,PATHINFO_EXTENSION));
|
|
|
|
}
|
2015-05-15 04:12:10 +00:00
|
|
|
|
|
|
|
public function list_duplicate() {
|
|
|
|
$po = ORM::factory($this->_object_name);
|
|
|
|
|
|
|
|
if ($this->loaded())
|
|
|
|
$po->where('id','!=',$this->id);
|
|
|
|
|
|
|
|
// Ignore photo's pending removal.
|
|
|
|
$po->where_open();
|
|
|
|
$po->where('remove','!=',TRUE);
|
|
|
|
$po->or_where('remove','is',NULL);
|
|
|
|
$po->where_close();
|
|
|
|
|
|
|
|
// Where the signature is the same
|
|
|
|
$po->where_open();
|
|
|
|
$po->where('signature','=',$this->signature);
|
|
|
|
|
|
|
|
// Or they have the same time taken with the same camera
|
|
|
|
if ($this->date_taken AND ($this->model OR $this->make)) {
|
|
|
|
$po->or_where_open();
|
|
|
|
$po->where('date_taken','=',$this->date_taken);
|
|
|
|
$po->where('subsectime','=',$this->subsectime);
|
|
|
|
|
|
|
|
if (! is_null($this->model))
|
|
|
|
$po->and_where('model','=',$this->model);
|
|
|
|
|
|
|
|
if (! is_null($this->make))
|
|
|
|
$po->and_where('make','=',$this->make);
|
|
|
|
|
|
|
|
$po->where_close();
|
|
|
|
}
|
|
|
|
|
|
|
|
$po->where_close();
|
|
|
|
|
|
|
|
return $po;
|
|
|
|
}
|
2014-07-09 03:04:56 +00:00
|
|
|
}
|
|
|
|
?>
|