belongsToMany(Person::class); } public function tags() { return $this->belongsToMany(Tag::class); } public function scopeDuplicates($query) { if (! $this->exists) return $query; // Exclude this record $query->where('id','<>',$this->attributes['id']); // Exclude those marked as remove $query->where(function ($q) { $q->where('remove','<>',TRUE) ->orWhere('remove','=',NULL); }); $query->where(function ($q) { $q->where('signature','=',$this->attributes['signature']) ->orWhere('file_signature','=',$this->attributes['file_signature']) // Where the signature is the same ->orWhere(function($q) { // Or they have the same time taken with the same camera if ($this->attributes['date_created'] AND $this->software_id) { $q->where('date_created','=',$this->attributes['date_created'] ?: NULL); if (static::$includeSubSecTime) $q->where('subsectime','=',$this->attributes['subsectime'] ?: NULL); $q->where('software_id','=',$this->attributes['software_id']); } }); }); } /** * Multimedia NOT duplicate. * * @return \Illuminate\Database\Eloquent\Builder */ public function scopeNotDuplicate($query) { return $query->where(function($query) { $query->where('duplicate','<>',TRUE) ->orWhere('duplicate','=',NULL); }); } /** * Multimedia NOT pending removal. * * @return \Illuminate\Database\Eloquent\Builder */ public function scopeNotRemove($query) { return $query->where(function($query) { $query->where('remove','<>',TRUE) ->orWhere('remove','=',NULL); }); } /** * Multimedia NOT scanned. * * @return \Illuminate\Database\Eloquent\Builder */ public function scopeNotScanned($query) { return $query->where(function($query) { $query->where('scanned','<>',TRUE) ->orWhere('scanned','=',NULL); }); } abstract public function setDateCreated(); abstract public function setLocation(); abstract public function setSignature(); abstract public function setSubSecTime(); abstract public function setThumbnail(); abstract public function getHtmlImageURL(); /** * Date the multimedia was created */ public function date_taken(): string { return $this->date_created ? $this->date_created->format('Y-m-d H:i:s') : 'UNKNOWN'; } public function device(): string { $result = ''; if ($this->software_id) { if ($this->software->model_id) { if ($this->software->model->make_id) { $result .= $this->software->model->make->name; } $result .= ($result ? ' ' : '').$this->software->model->name; } $result .= ($result ? ' ' : '').$this->software->name; } return $result; } /** * Return the date of the file * @todo return Carbon date or NULL */ 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 * @todo Make Generic for Photo and Video */ 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()) : $this->date_created->format('Y/m/d-His')),$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): string { return trim(chunk_split(sprintf("%0{$depth}s",$this->id),$sep,'/'),'/'); } /** * Display the file signature */ public function file_signature($short=FALSE): string { return $short ? static::stringtrim($this->file_signature) : $this->file_signature; } /** * Return the file size * @deprecated */ public function file_size() { return (! is_readable($this->file_path())) ? NULL : filesize($this->file_path()); } /** * Return item dimensions */ public function getDimensionsAttribute(): string { return $this->width.'x'.$this->height; } /** * Return HTML Checkbox for duplicate */ public function getDuplicateCheckboxAttribute() { return $this->HTMLCheckbox('duplicate',$this->id,$this->duplicate); } /** * Return the file size * * @return false|int|null */ public function getFileSizeAttribute() { return (! is_readable($this->file_path())) ? NULL : filesize($this->file_path()); } /** * Return HTML Checkbox for flagged */ public function getFlagCheckboxAttribute() { return $this->HTMLCheckbox('flag',$this->id,$this->flag); } public function getDateCreatedAttribute() { return Carbon::createFromTimestamp($this->attributes['date_created']); } /** * @deprecated */ public function getDuplicateTextAttribute() { return $this->TextTrueFalse($this->duplicate); } /** * @deprecated */ public function getFlagTextAttribute() { return $this->TextTrueFalse($this->flag); } /** * Return HTML Checkbox for remove */ public function getRemoveCheckboxAttribute() { return $this->HTMLCheckbox('remove',$this->id,$this->remove); } /** * Display the GPS coordinates */ public function gps(): string { return ($this->gps_lat AND $this->gps_lon) ? sprintf('%s/%s',$this->gps_lat,$this->gps_lon) : 'UNKNOWN'; } /** * Return an HTML checkbox */ protected function HTMLCheckbox($name,$id,$value) { return sprintf('',$name,$id,$value ? ' checked="checked"' : ''); } /** * Get ID Info link */ protected function HTMLLinkAttribute($id,$url) { return sprintf('%s',url($url.$id),$id,$id); } /** * Determine if the parent dir is writable * * @param $dir * @return bool */ public function isParentWritable($dir): bool { 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 * @todo Change to boolean and rename isMoveable() Log any FALSE reason. */ 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 record */ public function next() { return DB::table($this->getTable()) ->where('id','>',$this->id) ->orderby('id','ASC') ->first(); } abstract public function property(string $property); /** * Return my class shortname */ public function objectType(): string { return (new \ReflectionClass($this))->getShortName(); } /** * Get the id of the previous record */ public function previous() { return DB::table($this->getTable()) ->where('id','<',$this->id) ->orderby('id','DESC') ->first(); } public function setHeightWidth() { $this->height = $this->property('height'); $this->width = $this->property('width'); $this->orientation = $this->property('orientation'); } /** * Display the media signature */ public function signature($short=FALSE) { return ($short AND $this->signature) ? static::stringtrim($this->signature) : $this->signature; } /** * Trim a string * * @param string $string * @param int $chrs * @return string */ public static function stringtrim(string $string,int $chrs=6) { return sprintf('%s...%s',substr($string,0,$chrs),substr($string,-1*$chrs)); } /** * @deprecated * @param string $string * @param int $chrs * @return string */ public static function signaturetrim(string $string,int $chrs=6) { return static::stringtrim($string,$chrs); } /** * Determine if the image should be moved */ public function shouldMove(): bool { return ($this->filename != $this->file_path(TRUE,TRUE)); } protected function TextTrueFalse($value): string { return $value ? 'TRUE' : 'FALSE'; } /** * @todo Check if this is redundant * * @param bool $includeme * @return mixed */ public function list_duplicate($includeme=FALSE) { return $this->list_duplicates($includeme)->pluck('id'); } /** * Find duplicate images based on some attributes of the current image * @deprecate Use static::duplicates() */ public function list_duplicates($includeme=FALSE) { $o = static::select(); if ($this->id AND ! $includeme) $o->where('id','!=',$this->id); // Ignore photo's pending removal. if (! $includeme) $o->where(function($query) { $query->where('remove','<>',TRUE) ->orWhere('remove','=',NULL); }); // Where the signature is the same $o->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 (Schema::hasColumn($this->getTable(),'subsectime')) $query->where('subsectime','=',$this->subsectime ? $this->subsectime : NULL); if (! is_null($this->model)) $query->where('model','=',$this->model); if (! is_null($this->make)) $query->where('make','=',$this->make); }); } }); return $o->get(); } }