belongsToMany('App\Model\Person'); } public function Tags() { return $this->belongsToMany('App\Model\Tag'); } /** * Photo's NOT pending removal. * * @return \Illuminate\Database\Eloquent\Builder */ public function scopeNotRemove($query) { return $query->where(function($query) { $query->where('remove','!=',TRUE) ->orWhere('remove','=',NULL); }); } /** * Photo's NOT duplicate. * * @return \Illuminate\Database\Eloquent\Builder */ public function scopeNotDuplicate($query) { return $query->where(function($query) { $query->where('duplicate','!=',TRUE) ->orWhere('duplicate','=',NULL); }); } public function date_taken() { return $this->date_created ? date('Y-m-d H:i:s',$this->date_created) : 'UNKNOWN'; } public function dump() { return $this->_o->info; } /** * Return the date of the file */ public function file_date($type,$format=FALSE) { if (! is_readable($this->file_path())) return NULL; 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; } return $format ? date('d-m-Y H:i:s',$t) : $t; } /** * Determine the new name for the image */ public function file_path($short=FALSE,$new=FALSE) { $file = $this->filename; if ($new) $file = sprintf('%s.%s',((is_null($this->date_created) OR ! $this->date_created) ? sprintf('UNKNOWN/%07s',$this->file_path_id()) : date('Y/m/d-His',$this->date_created)),$this->type()); return (($short OR preg_match('/^\//',$file)) ? '' : config('video.dir').DIRECTORY_SEPARATOR).$file; } /** * Calculate a file path ID based on the id of the file */ public function file_path_id($sep=3,$depth=9) { return trim(chunk_split(sprintf("%0{$depth}s",$this->id),$sep,'/'),'/'); } /** * Return the photo size */ public function file_size() { return (! is_readable($this->file_path())) ? NULL : filesize($this->file_path()); } /** * Display the GPS coordinates */ public function gps() { return ($this->gps_lat AND $this->gps_lon) ? sprintf('%s/%s',$this->gps_lat,$this->gps_lon) : 'UNKNOWN'; } public function isParentWritable($dir) { if (file_exists($dir) AND is_writable($dir) AND is_dir($dir)) return TRUE; elseif ($dir == dirname($dir) OR file_exists($dir)) return FALSE; else return ($this->isParentWritable(dirname($dir))); } /** * Determine if a file is moveable * * useID boolean Determine if the path is based on the the ID or date */ public function moveable() { // If the source and target are the same, we dont need to move it if ($this->file_path() == $this->file_path(FALSE,TRUE)) return FALSE; // If there is already a file in the target. // @todo If the target file is to be deleted, we could move this file if (file_exists($this->file_path(FALSE,TRUE))) return 1; // Test if the source is readable if (! is_readable($this->file_path())) return 2; // Test if the dir is writable (so we can remove the file) if (! $this->isParentWritable(dirname($this->file_path()))) return 3; // Test if the target dir is writable // @todo The target dir may not exist yet, so we should check that a parent exists and is writable. if (! $this->isParentWritable($this->file_path(FALSE,TRUE))) return 4; return TRUE; } /** * Get the id of the previous photo */ public function next() { $po = DB::table('videos'); $po->where('id','>',$this->id); $po->orderby('id','ASC'); return $po->first(); } /** * Return an Imagick object or attribute * */ protected function o($attr=NULL) { if (! $this->filename OR ! file_exists($this->file_path()) OR ! is_readable($this->file_path())) return FALSE; if (is_null($this->_o)) { $this->_o = new \getID3; $this->_o->analyze($this->file_path()); $this->_o->getHashdata('sha1'); } return is_null($attr) ? $this->_o : (array_key_exists($attr,$this->_o->info) ? $this->_o->info[$attr] : NULL); } /** * Get the id of the previous photo */ public function previous() { $po = DB::table('videos'); $po->where('id','<',$this->id); $po->orderby('id','DEC'); return $po->first(); } public function property($property) { if (! $this->o()) return NULL; switch ($property) { case 'creationdate': // Try creation_Data $x = array_get($this->_o->info,'quicktime.comments.creation_date.0'); // Try creation_Data if (is_null($x)) $x = array_get($this->_o->info,'quicktime.comments.creationdate.0'); return $x; case 'make': return array_get($this->_o->info,'quicktime.comments.make.0'); case 'model': return array_get($this->_o->info,'quicktime.comments.model.0'); case 'signature': return array_get($this->_o->info,'sha1_data'); #case 'height': return $this->subatomsearch('quicktime.moov.subatoms',['trak','tkhd'],'height'); break; #case 'width': return $this->subatomsearch('quicktime.moov.subatoms',['trak','tkhd'],'width'); break; case 'height': return array_get($this->_o->info,'video.resolution_y'); case 'width': return array_get($this->_o->info,'video.resolution_x'); case 'length': return array_get($this->_o->info,'playtime_seconds'); case 'type': return array_get($this->_o->info,'video.dataformat'); case 'codec': return array_get($this->_o->info,'audio.codec'); case 'audiochannels': return array_get($this->_o->info,'audio.channels'); case 'samplerate': return array_get($this->_o->info,'audio.sample_rate'); case 'channelmode': return array_get($this->_o->info,'audio.channelmode'); case 'gps_lat': return array_get($this->_o->info,'quicktime.comments.gps_latitude.0'); case 'gps_lon': return array_get($this->_o->info,'quicktime.comments.gps_longitude.0'); case 'gps_altitude': return array_get($this->_o->info,'quicktime.comments.gps_altitude.0'); default: return NULL; } } /** Navigate through ID3 data to find the value. */ private function subatomsearch($atom,array $paths,$key,array $data=[]) { if (! $data AND is_null($data = array_get($this->_o->info,$atom))) { // Didnt find it. return NULL; } foreach ($paths as $path) { $found = FALSE; foreach ($data as $array) { if ($array['name'] === $path) { $found = TRUE; if ($path != last($paths)) $data = $array['subatoms']; else $data = $array; break; } } if (! $found) break; } return isset($data[$key]) ? $data[$key] : NULL; } public function properties() { return $this->o() ? $this->_io->info : []; } /** * Display the photo signature */ public function signature($short=FALSE) { return $short ? static::signaturetrim($this->signature) : $this->signature; } public static function signaturetrim($signature,$chars=6) { return sprintf('%s...%s',substr($signature,0,$chars),substr($signature,-1*$chars)); } /** * Determine if the image should be moved */ public function shouldMove() { return ($this->filename != $this->file_path(TRUE,TRUE)); } /** * Return the extension of the image */ public function type($mime=FALSE) { return strtolower($mime ? File::mime_by_ext(pathinfo($this->filename,PATHINFO_EXTENSION)) : pathinfo($this->filename,PATHINFO_EXTENSION)); } /** * Find duplicate images based on some attributes of the current image */ public function list_duplicate($includeme=FALSE) { $po = DB::table('videos'); if ($this->id AND ! $includeme) $po->where('id','!=',$this->id); // Ignore photo's pending removal. if (! $includeme) $po->where(function($query) { $query->where('remove','!=',TRUE) ->orWhere('remove','=',NULL); }); // Where the signature is the same $po->where(function($query) { $query->where('signature','=',$this->signature); // Or they have the same time taken with the same camera if ($this->date_created AND ($this->model OR $this->make)) { $query->orWhere(function($query) { $query->where('date_created','=',$this->date_created ? $this->date_created : NULL); if (! is_null($this->model)) $query->where('model','=',$this->model); if (! is_null($this->make)) $query->where('make','=',$this->make); }); } }); return $po->pluck('id'); } }