Internal optimisations and additional flags for Photo/Video
This commit is contained in:
parent
93364ab53a
commit
1ffc2d994e
77
app/Console/Commands/CatalogAutoDelete.php
Normal file
77
app/Console/Commands/CatalogAutoDelete.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\PhotoMove;
|
||||
use App\Jobs\VideoMove;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
|
||||
use App\Jobs\PhotoDelete;
|
||||
use App\Models\Photo;
|
||||
use App\Traits\Type;
|
||||
|
||||
class CatalogAutoDelete extends Command
|
||||
{
|
||||
use DispatchesJobs,Type;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'catalog:autodelete {type : Photo | Video }';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Auto Delete Catalog Items';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$class = $this->getModelType($this->argument('type'));
|
||||
|
||||
$class::where('remove',1)->each(function($o) {
|
||||
foreach ($o->myduplicates() as $oo) {
|
||||
if (! $oo->signature OR ! $oo->file_signature)
|
||||
continue;
|
||||
|
||||
if ($oo->signature == $o->signature AND $oo->file_signature == $o->file_signature) {
|
||||
$this->info(sprintf('Removing: %s (%s)',$o->id,$o->filename));
|
||||
|
||||
continue;
|
||||
// Dispatch Job to move file.
|
||||
switch (strtolower($this->argument('type'))) {
|
||||
case 'photo':
|
||||
$this->dispatch((new PhotoDelete($o))->onQueue('delete'));
|
||||
break;
|
||||
|
||||
case 'video':
|
||||
$this->dispatch((new VideoDelete($o))->onQueue('delete'));
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->error('Dont know how to handle: ',$this->argument('type'));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -15,7 +15,10 @@ class CatalogScan extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'catalog:scan {type : Photo | Video } {id : Photo ID}';
|
||||
protected $signature = 'catalog:scan'.
|
||||
' {type : Photo | Video }'.
|
||||
' {id : Photo ID}'.
|
||||
' {--dirty : Show Dirty}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@ -49,17 +52,17 @@ class CatalogScan extends Command
|
||||
$o->setThumbnail();
|
||||
|
||||
// If this is a duplicate
|
||||
$x = $o->duplicates()->get();
|
||||
$x = $o->myduplicates()->get();
|
||||
if (count($x)) {
|
||||
foreach ($x as $oo) {
|
||||
// And that photo is not marked as a duplicate
|
||||
if (! $oo->duplicate) {
|
||||
$o->duplicate = '1';
|
||||
$this->warn(sprintf('Image [%s] marked as a duplicate',$o->file_path()));
|
||||
$this->warn(sprintf('Image [%s] marked as a duplicate',$o->filename));
|
||||
|
||||
// If the file signature also matches, we'll mark it for deletion
|
||||
if ($oo->file_signature AND $o->file_signature == $oo->file_signature) {
|
||||
$this->warn(sprintf('Image [%s] marked for deletion',$o->file_path()));
|
||||
$this->warn(sprintf('Image [%s] marked for deletion',$o->filename));
|
||||
$o->remove = '1';
|
||||
}
|
||||
|
||||
@ -72,9 +75,15 @@ class CatalogScan extends Command
|
||||
|
||||
if ($o->getDirty()) {
|
||||
$this->warn(sprintf('Image [%s] metadata changed',$o->filename));
|
||||
dump(['id'=>$o->id,'data'=>$o->getDirty()]);
|
||||
|
||||
if ($this->option('dirty'))
|
||||
dump(['id'=>$o->id,'data'=>$o->getDirty()]);
|
||||
}
|
||||
|
||||
// If the file signature changed, abort the update.
|
||||
if ($o->getOriginal('file_signature') AND $o->getDirty('file_signature'))
|
||||
abort(500,'File Signature Changed?');
|
||||
|
||||
$o->save();
|
||||
}
|
||||
}
|
@ -61,6 +61,7 @@ class CatalogScanAll extends Command
|
||||
}
|
||||
|
||||
$this->dispatch((new CatalogScan($item))->onQueue('scan'));
|
||||
$c++;
|
||||
});
|
||||
|
||||
Log::info(sprintf('Processed [%s]',$c));
|
||||
|
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
|
||||
use App\Jobs\PhotoDelete;
|
||||
use App\Models\Photo;
|
||||
|
||||
class PhotoAutoDelete extends Command
|
||||
{
|
||||
use DispatchesJobs;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'photo:autodelete';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Auto Delete Photos';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Photo::where('remove',1)->chunk(10,function($chunk) {
|
||||
foreach ($chunk as $o) {
|
||||
foreach ($o->list_duplicates(TRUE) as $oo) {
|
||||
if (! $oo->signature OR ! $oo->file_signature)
|
||||
continue;
|
||||
|
||||
if ($oo->signature == $o->signature AND $oo->file_signature == $o->file_signature) {
|
||||
if ($oo->remove) {
|
||||
$this->info(sprintf('Removing: %s (%s)',$oo->id,$oo->filename));
|
||||
|
||||
$this->dispatch((new PhotoDelete($o))->onQueue('delete'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -2,13 +2,13 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Models\Photo;
|
||||
use App\Jobs\PhotoDelete;
|
||||
use App\Traits\Multimedia;
|
||||
|
||||
class PhotoController extends Controller
|
||||
{
|
||||
use Multimedia;
|
||||
|
||||
protected $list_duplicates = 20;
|
||||
protected $list_deletes = 50;
|
||||
|
||||
@ -22,77 +22,41 @@ class PhotoController extends Controller
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
public function delete(Photo $o)
|
||||
{
|
||||
$o = Photo::notRemove()->findOrFail($id);
|
||||
$o->remove = TRUE;
|
||||
$o->save();
|
||||
|
||||
if ($o)
|
||||
{
|
||||
$o->remove = TRUE;
|
||||
$o->save();
|
||||
}
|
||||
|
||||
return redirect()->action('PhotoController@info',[$id]);
|
||||
return redirect()->action('PhotoController@info',[$o->id]);
|
||||
}
|
||||
|
||||
public function deletes($id=NULL)
|
||||
{
|
||||
return view('catalog.deletereview',[
|
||||
'catalog'=>is_null($id) ? Photo::where('remove',1)->with(['software.model.make'])->paginate($this->list_deletes) : Photo::where('id',$id)->paginate(1),
|
||||
'return'=>url('p/deletes'),
|
||||
'catalog'=>is_null($id) ? Photo::where('remove',1)->with(['software.model.make'])->paginate($this->list_deletes) : Photo::where('id',$id)->paginate(1)
|
||||
'type'=>'photo',
|
||||
]);
|
||||
}
|
||||
|
||||
public function deletesUpdate(Request $request)
|
||||
{
|
||||
foreach ($request->input('remove') as $id=>$k)
|
||||
{
|
||||
$o = Photo::findOrFail($id);
|
||||
|
||||
if ($o->remove AND $request->input('remove.'.$id))
|
||||
$this->dispatch((new PhotoDelete($o))->onQueue('delete'));
|
||||
}
|
||||
|
||||
return redirect()->action('PhotoController@deletes',$request->input('pagenext') ? '?page='.$request->input('pagenext') : NULL);
|
||||
}
|
||||
|
||||
public function duplicates($id=NULL)
|
||||
{
|
||||
return view('catalog.duplicatereview',[
|
||||
'catalog'=>is_null($id) ? Photo::duplicates()->with(['software.model.make'])->paginate($this->list_duplicates) : Photo::where('id',$id)->paginate(1),
|
||||
'return'=>url('p/duplicates'),
|
||||
'catalog'=>is_null($id) ? Photo::notRemove()->where('duplicate',1)->with(['software.model.make'])->paginate($this->list_duplicates) : Photo::where('id',$id)->paginate(1)
|
||||
'type'=>'photo',
|
||||
]);
|
||||
}
|
||||
|
||||
public function duplicatesUpdate(Request $request)
|
||||
{
|
||||
foreach ($request->input('items') as $id)
|
||||
{
|
||||
$o = Photo::findOrFail($id);
|
||||
|
||||
// Set if duplicate
|
||||
$o->duplicate = $request->input('duplicate.'.$id) ? 1 : NULL;
|
||||
|
||||
// Set if flag
|
||||
$o->flag = $request->input('flag.'.$id) ? 1 : NULL;
|
||||
|
||||
// Set if delete
|
||||
$o->remove = $request->input('remove.'.$id) ? 1 : NULL;
|
||||
|
||||
$o->save();
|
||||
}
|
||||
|
||||
return redirect()->action('PhotoController@duplicates','?page='.$request->input('page'));
|
||||
}
|
||||
|
||||
public function info(Photo $o)
|
||||
{
|
||||
return view('photo.view',['o'=>$o]);
|
||||
}
|
||||
|
||||
public function thumbnail($id)
|
||||
public function thumbnail(Photo $o)
|
||||
{
|
||||
return response(Photo::findOrFail($id)->thumbnail(TRUE))->header('Content-Type','image/jpeg');
|
||||
return response($o->thumbnail(TRUE))
|
||||
->header('Content-Type','image/jpeg');
|
||||
}
|
||||
|
||||
public function undelete(Photo $o)
|
||||
@ -103,8 +67,9 @@ class PhotoController extends Controller
|
||||
return redirect()->action('PhotoController@info',[$o->id]);
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
public function view(Photo $o)
|
||||
{
|
||||
return response(Photo::findOrFail($id)->image())->header('Content-Type','image/jpeg');
|
||||
return response($o->image())
|
||||
->header('Content-Type','image/jpeg');
|
||||
}
|
||||
}
|
@ -2,14 +2,17 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use App\Models\Video;
|
||||
use App\Jobs\VideoDelete;
|
||||
use App\Helpers\VideoStream;
|
||||
use App\Traits\Multimedia;
|
||||
|
||||
class VideoController extends Controller
|
||||
{
|
||||
use Multimedia;
|
||||
|
||||
protected $list_duplicates = 20;
|
||||
protected $list_deletes = 50;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
@ -20,71 +23,30 @@ class VideoController extends Controller
|
||||
$this->middleware('guest');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
public function delete(Video $o)
|
||||
{
|
||||
$o = Video::notRemove()->findOrFail($id);
|
||||
$o->remove = TRUE;
|
||||
$o->save();
|
||||
|
||||
if ($o)
|
||||
{
|
||||
$o->remove = TRUE;
|
||||
$o->save();
|
||||
}
|
||||
|
||||
return redirect()->action('VideoController@info',[$id]);
|
||||
return redirect()->action('VideoController@info',[$o->id]);
|
||||
}
|
||||
|
||||
public function deletes($id=NULL)
|
||||
{
|
||||
return view('catalog.deletereview',[
|
||||
'catalog'=>is_null($id) ? Video::where('remove',1)->with(['software.model.make'])->paginate($this->list_deletes) : Video::where('id',$id)->paginate(1),
|
||||
'return'=>url('v/deletes'),
|
||||
'catalog'=>is_null($id) ? Video::where('remove',1)->with(['software.model.make'])->paginate(50) : Video::where('id',$id)->paginate(1)
|
||||
'type'=>'photo',
|
||||
]);
|
||||
}
|
||||
|
||||
public function deletesUpdate(Request $request)
|
||||
{
|
||||
foreach ($request->input('remove') as $id=>$k)
|
||||
{
|
||||
$o = Video::findOrFail($id);
|
||||
|
||||
if ($o->remove AND $request->input('remove.'.$id))
|
||||
$this->dispatch((new VideoDelete($o))->onQueue('delete'));
|
||||
|
||||
elseif (! $o->remove) {
|
||||
$o->remove = TRUE;
|
||||
$o->save();
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->action('VideoController@deletes',$request->input('pagenext') ? '?page='.$request->input('pagenext') : NULL);
|
||||
}
|
||||
|
||||
public function duplicates($id=NULL)
|
||||
{
|
||||
return view('catalog.duplicatereview',[
|
||||
'catalog'=>is_null($id) ? Video::duplicates()->with(['software.model.make'])->paginate($this->list_duplicates) : Video::where('id',$id)->paginate(1),
|
||||
'return'=>url('v/duplicates'),
|
||||
'catalog'=>is_null($id) ? Video::notRemove()->where('duplicate',1)->with(['software.model.make'])->paginate(50) : Video::where('id',$id)->paginate(1)]);
|
||||
}
|
||||
|
||||
public function duplicatesUpdate(Request $request)
|
||||
{
|
||||
foreach ($request->input('items') as $id)
|
||||
{
|
||||
$o = Video::findOrFail($id);
|
||||
|
||||
// Set if duplicate
|
||||
$o->duplicate = $request->input('duplicate.'.$id) ? 1 : NULL;
|
||||
|
||||
// Set if flag
|
||||
$o->flag = $request->input('flag.'.$id) ? 1 : NULL;
|
||||
|
||||
// Set if delete
|
||||
$o->remove = $request->input('remove.'.$id) ? 1 : NULL;
|
||||
|
||||
$o->save();
|
||||
}
|
||||
|
||||
return redirect()->action('VideoController@duplicates','?page='.$request->input('page'));
|
||||
'type'=>'photo',
|
||||
]);
|
||||
}
|
||||
|
||||
public function info(Video $o)
|
||||
@ -100,8 +62,9 @@ class VideoController extends Controller
|
||||
return redirect()->action('VideoController@info',[$o->id]);
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
public function view(Video $o)
|
||||
{
|
||||
(new VideoStream(Video::findOrFail($id)->file_path()))->start();
|
||||
if ($o->isReadable())
|
||||
(new VideoStream($o->filename))->start();
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ namespace App\Models\Abstracted;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
@ -12,6 +13,7 @@ use App\Models\{Person,Software,Tag};
|
||||
abstract class Catalog extends Model
|
||||
{
|
||||
protected static $includeSubSecTime = FALSE;
|
||||
protected $dates = ['created'];
|
||||
|
||||
/**
|
||||
* People in Multimedia Object
|
||||
@ -44,18 +46,39 @@ abstract class Catalog extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Database for duplicates of this object
|
||||
* Find records marked as duplicate
|
||||
*
|
||||
* @param $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function scopeDuplicates($query) {
|
||||
$query->notRemove()
|
||||
->where('duplicate',TRUE)
|
||||
->where(function($q) {
|
||||
$q->Where('ignore_duplicate','<>',TRUE)
|
||||
->orWhereNull('ignore_duplicate');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Database for duplicates of this object
|
||||
*
|
||||
* @param $query
|
||||
* @return mixed
|
||||
*/
|
||||
public function scopeMyDuplicates($query) {
|
||||
if (! $this->exists)
|
||||
return $query;
|
||||
|
||||
// Exclude this record
|
||||
$query->where('id','<>',$this->attributes['id']);
|
||||
|
||||
// Skip ignore dups
|
||||
$query->where(function($q) {
|
||||
$q->whereNull('ignore_duplicate')
|
||||
->orWhere('ignore_duplicate','=',0);
|
||||
});
|
||||
|
||||
// Exclude those marked as remove
|
||||
$query->where(function ($q) {
|
||||
$q->where('remove','<>',TRUE)
|
||||
@ -69,11 +92,11 @@ abstract class Catalog extends Model
|
||||
// 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 ($this->attributes['created'] AND $this->software_id) {
|
||||
$q->where('created','=',$this->attributes['created'] ?: NULL);
|
||||
|
||||
if (static::$includeSubSecTime)
|
||||
$q->where('subsectime','=',$this->attributes['subsectime'] ?: NULL);
|
||||
$q->where('subsectime','=',Arr::get($this->attributes,'subsectime'));
|
||||
|
||||
$q->where('software_id','=',$this->attributes['software_id']);
|
||||
}
|
||||
@ -124,9 +147,7 @@ abstract class Catalog extends Model
|
||||
}
|
||||
|
||||
// Children objects must inherit this methods
|
||||
abstract public function setDateCreated();
|
||||
abstract public function setLocation();
|
||||
abstract public function setSignature();
|
||||
abstract public function setSubSecTime();
|
||||
abstract public function setThumbnail();
|
||||
abstract public function getHtmlImageURL();
|
||||
@ -136,9 +157,16 @@ abstract class Catalog extends Model
|
||||
*/
|
||||
public function date_taken(): string
|
||||
{
|
||||
return $this->date_created ? $this->date_created->format('Y-m-d H:i:s') : 'UNKNOWN';
|
||||
return $this->created
|
||||
? $this->created->format('Y-m-d H:i:s').(static::$includeSubSecTime ? sprintf('.%03d',$this->subsectime) : '')
|
||||
: 'UNKNOWN';
|
||||
}
|
||||
|
||||
/**
|
||||
* What device was the multimedia created on
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function device(): string
|
||||
{
|
||||
$result = '';
|
||||
@ -169,11 +197,13 @@ abstract class Catalog extends Model
|
||||
|
||||
switch ($type)
|
||||
{
|
||||
case 'a': $t = fileatime($this->file_path());
|
||||
case 'a': $t = fileatime($this->filename);
|
||||
break;
|
||||
case 'c': $t = filectime($this->file_path());
|
||||
|
||||
case 'c': $t = filectime($this->filename);
|
||||
break;
|
||||
case 'm': $t = filemtime($this->file_path());
|
||||
|
||||
case 'm': $t = filemtime($this->filename);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -188,12 +218,15 @@ abstract class Catalog extends Model
|
||||
public function file_name(bool $short=TRUE): string
|
||||
{
|
||||
// If the date created is not set, the file name will be based on the ID of the file.
|
||||
$file = sprintf('%s.%s',((is_null($this->date_created) OR ! $this->date_created)
|
||||
$file = sprintf('%s.%s',(is_null($this->created)
|
||||
? sprintf('UNKNOWN/%07s',$this->file_path_id())
|
||||
: sprintf('%s_%03s',$this->date_created->format('Y/m/d-His'),$this->subsectime).
|
||||
((! static::$includeSubSecTime OR $this->subsectime) ? '' : sprintf('-%05s',$this->id))),$this->type());
|
||||
: $this->created->format('Y/m/d-His').((! is_null($this->subsectime)) ? sprintf('.%03d',$this->subsectime) : '' ).
|
||||
((! static::$includeSubSecTime OR ! is_null($this->subsectime)) ? '' : sprintf('-%05s',$this->id)).
|
||||
($this->ignore_duplicate ? sprintf('-%06d',$this->id) : '')
|
||||
),$this->type()
|
||||
);
|
||||
|
||||
return (($short OR preg_match('/^\//',$file)) ? '' : config('photo.dir').DIRECTORY_SEPARATOR).$file;
|
||||
return (($short OR preg_match('/^\//',$file)) ? '' : config($this->type.'.dir').DIRECTORY_SEPARATOR).$file;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,11 +238,9 @@ abstract class Catalog extends Model
|
||||
$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());
|
||||
$file = $this->file_name(FALSE);
|
||||
|
||||
return (($short OR preg_match('/^\//',$file)) ? '' : config('video.dir').DIRECTORY_SEPARATOR).$file;
|
||||
return (($short OR preg_match('/^\//',$file)) ? '' : config($this->type.'.dir').DIRECTORY_SEPARATOR).$file;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -271,6 +302,14 @@ abstract class Catalog extends Model
|
||||
return $this->HTMLCheckbox('flag',$this->id,$this->flag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML Checkbox for ignore
|
||||
*/
|
||||
public function getIgnoreCheckboxAttribute()
|
||||
{
|
||||
return $this->HTMLCheckbox('ignore_duplicate',$this->id,$this->ignore_duplicate);
|
||||
}
|
||||
|
||||
public function getDateCreatedAttribute() {
|
||||
return $this->attributes['date_created'] ? Carbon::createFromTimestamp($this->attributes['date_created']) : NULL;
|
||||
}
|
||||
@ -328,7 +367,7 @@ abstract class Catalog extends Model
|
||||
*/
|
||||
public function isReadable(): bool
|
||||
{
|
||||
return is_readable($this->file_path());
|
||||
return is_readable($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,7 +383,7 @@ abstract class Catalog extends Model
|
||||
*/
|
||||
protected function HTMLLinkAttribute($id,$url)
|
||||
{
|
||||
return sprintf('<a href="%s" target="%s">%s</a>',url($url.$id),$id,$id);
|
||||
return sprintf('<a href="%s" target="%s">%s</a>',url($url,$id),$id,$id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -398,17 +437,6 @@ abstract class Catalog extends Model
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate if a file should be moved.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function mustMove(): bool
|
||||
{
|
||||
dump(['f'=>$this->filename,'fn'=>$this->file_name(),'test'=>($this->filename == $this->file_name())]);
|
||||
return $this->filename !== $this->file_name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the id of the previous record
|
||||
*/
|
||||
@ -441,6 +469,11 @@ abstract class Catalog extends Model
|
||||
->first();
|
||||
}
|
||||
|
||||
public function setDateCreated()
|
||||
{
|
||||
$this->created = $this->property('creationdate');
|
||||
}
|
||||
|
||||
public function setHeightWidth()
|
||||
{
|
||||
$this->height = $this->property('height');
|
||||
@ -448,6 +481,13 @@ abstract class Catalog extends Model
|
||||
$this->orientation = $this->property('orientation');
|
||||
}
|
||||
|
||||
public function setSignature()
|
||||
{
|
||||
$this->signature = $this->property('signature');
|
||||
|
||||
$this->file_signature = md5_file($this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the media signature
|
||||
*/
|
||||
@ -484,7 +524,7 @@ abstract class Catalog extends Model
|
||||
*/
|
||||
public function shouldMove(): bool
|
||||
{
|
||||
return ($this->filename != $this->file_path(TRUE,TRUE));
|
||||
return $this->filename !== $this->file_name();
|
||||
}
|
||||
|
||||
protected function TextTrueFalse($value): string
|
||||
@ -498,7 +538,7 @@ abstract class Catalog extends Model
|
||||
* @param bool $includeme
|
||||
* @return mixed
|
||||
*/
|
||||
public function list_duplicate($includeme=FALSE)
|
||||
private function list_duplicate($includeme=FALSE)
|
||||
{
|
||||
return $this->list_duplicates($includeme)->pluck('id');
|
||||
}
|
||||
@ -507,7 +547,7 @@ abstract class Catalog extends Model
|
||||
* Find duplicate images based on some attributes of the current image
|
||||
* @deprecate Use static::duplicates()
|
||||
*/
|
||||
public function list_duplicates($includeme=FALSE)
|
||||
private function list_duplicates($includeme=FALSE)
|
||||
{
|
||||
$o = static::select();
|
||||
|
||||
|
@ -22,33 +22,7 @@ class Photo extends Abstracted\Catalog
|
||||
|
||||
public function getIDLinkAttribute()
|
||||
{
|
||||
return $this->HTMLLinkAttribute($this->id,url('p/info').'/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Date the photo was taken
|
||||
*/
|
||||
public function date_taken(): string
|
||||
{
|
||||
return $this->date_created
|
||||
? ($this->date_created->format('Y-m-d H:i:s').($this->subsectime ? '.'.$this->subsectime : ''))
|
||||
: 'UNKNOWN';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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())
|
||||
: sprintf('%s_%03s',$this->date_created->format('Y/m/d-His'),$this->subsectime).
|
||||
($this->subsectime ? '' : sprintf('-%05s',$this->id))),$this->type());
|
||||
|
||||
return (($short OR preg_match('/^\//',$file)) ? '' : config('photo.dir').DIRECTORY_SEPARATOR).$file;
|
||||
return $this->HTMLLinkAttribute($this->id,'p/info');
|
||||
}
|
||||
|
||||
public function getHtmlImageURL(): string
|
||||
@ -169,10 +143,6 @@ class Photo extends Abstracted\Catalog
|
||||
return $this->o() ? $this->_o->getImageProperties() : [];
|
||||
}
|
||||
|
||||
public function setDateCreated()
|
||||
{
|
||||
$this->date_created = $this->property('creationdate');
|
||||
}
|
||||
|
||||
public function setLocation()
|
||||
{
|
||||
@ -202,15 +172,9 @@ class Photo extends Abstracted\Catalog
|
||||
$this->software_id = $so->id;
|
||||
}
|
||||
|
||||
public function setSignature()
|
||||
public function setSubSecTime(): int
|
||||
{
|
||||
$this->signature = $this->property('signature');
|
||||
$this->file_signature = md5_file($this->file_path());
|
||||
}
|
||||
|
||||
public function setSubSecTime()
|
||||
{
|
||||
$this->subsectime = $this->property('exif:SubSecTimeOriginal');
|
||||
$this->subsectime = (int)$this->property('exif:SubSecTimeOriginal');
|
||||
|
||||
// In case of an error.
|
||||
if ($this->subsectime > 32767)
|
||||
@ -240,7 +204,7 @@ class Photo extends Abstracted\Catalog
|
||||
public function thumbnail($rotate=TRUE)
|
||||
{
|
||||
if (! $this->thumbnail) {
|
||||
if ($this->o()->thumbnailimage(200,200,true,false)) {
|
||||
if ($this->isReadable() AND $this->o()->thumbnailimage(200,200,true,false)) {
|
||||
$this->_o->setImageFormat('jpg');
|
||||
return $this->_o->getImageBlob();
|
||||
|
||||
|
@ -11,7 +11,7 @@ class Video extends Abstracted\Catalog
|
||||
|
||||
public function getIDLinkAttribute()
|
||||
{
|
||||
return $this->HTMLLinkAttribute($this->id,url('v/info').'/');
|
||||
return $this->HTMLLinkAttribute($this->id,'v/info');
|
||||
}
|
||||
|
||||
public function getHtmlImageURL()
|
||||
@ -124,16 +124,6 @@ class Video extends Abstracted\Catalog
|
||||
return isset($data[$key]) ? $data[$key] : NULL;
|
||||
}
|
||||
|
||||
public function setDateCreated()
|
||||
{
|
||||
$this->date_created = $this->property('creationdate');
|
||||
}
|
||||
|
||||
public function setDateCreatedAttribute($value)
|
||||
{
|
||||
$this->attributes['date_created'] = strtotime($value);
|
||||
}
|
||||
|
||||
public function setLocation()
|
||||
{
|
||||
$this->gps_lat = $this->property('gps_lat');
|
||||
@ -172,8 +162,8 @@ class Video extends Abstracted\Catalog
|
||||
|
||||
public function setSignature()
|
||||
{
|
||||
$this->signature = $this->property('signature');
|
||||
$this->file_signature = md5_file($this->file_path());
|
||||
parent::setSignature();
|
||||
|
||||
$this->identifier = $this->property('identifier');
|
||||
}
|
||||
|
||||
|
75
app/Traits/Multimedia.php
Normal file
75
app/Traits/Multimedia.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Jobs\PhotoDelete;
|
||||
use App\Models\Photo;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Multimedia Controller Functions
|
||||
*
|
||||
* @package App\Traits
|
||||
*/
|
||||
trait Multimedia
|
||||
{
|
||||
use Type;
|
||||
|
||||
private function controller(string $type): string
|
||||
{
|
||||
switch (strtolower($type)) {
|
||||
case 'photo': return 'PhotoController';
|
||||
case 'video': return 'Videoontroller';
|
||||
default: abort(500,'Type not handled?');
|
||||
}
|
||||
}
|
||||
|
||||
public function deletesUpdate(Request $request)
|
||||
{
|
||||
$class = $this->getModelType($request->input('type'));
|
||||
$this->updatePostItems($request,$class,TRUE);
|
||||
|
||||
return redirect()->action(
|
||||
sprintf('%s@deletes',$this->controller($request->input('type'))),
|
||||
sprintf('?page=%s',$request->input('page'))
|
||||
);
|
||||
}
|
||||
|
||||
public function duplicatesUpdate(Request $request)
|
||||
{
|
||||
$class = $this->getModelType($request->input('type'));
|
||||
$this->updatePostItems($request,$class);
|
||||
|
||||
return redirect()->action(
|
||||
sprintf('%s@duplicates',$this->controller($request->input('type'))),
|
||||
sprintf('?page=%s',$request->input('page'))
|
||||
);
|
||||
}
|
||||
|
||||
private function updatePostItems(Request $request,string $class,bool $delete=FALSE)
|
||||
{
|
||||
foreach ($request->input('items') as $id) {
|
||||
$o = $class::findOrFail($id);
|
||||
|
||||
// Set if duplicate
|
||||
$o->duplicate = $request->input('duplicate.'.$id) ? 1 : NULL;
|
||||
|
||||
// Set if ignore duplicate
|
||||
$o->ignore_duplicate = $request->input('ignore_duplicate.'.$id) ? 1 : NULL;
|
||||
|
||||
// Set if flag
|
||||
$o->flag = $request->input('flag.'.$id) ? 1 : NULL;
|
||||
|
||||
// Set if delete
|
||||
if ($delete AND $o->remove AND ($request->input('remove.'.$id) ? 1 : NULL)) {
|
||||
Log::info(sprintf('Dispatching delete for [%s]',$o->id));
|
||||
|
||||
} else {
|
||||
$o->remove = $request->input('remove.'.$id) ? 1 : NULL;
|
||||
}
|
||||
|
||||
$o->save();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddOverrideKeepAttributes extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('photo', function (Blueprint $table) {
|
||||
$table->dateTime('created')->nullable();
|
||||
$table->dateTime('created_manual')->nullable();
|
||||
$table->boolean('ignore_duplicate')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dateTime('created')->nullable();
|
||||
$table->dateTime('created_manual')->nullable();
|
||||
$table->boolean('ignore_duplicate')->nullable();
|
||||
});
|
||||
|
||||
\App\Models\Photo::each(function($o) {
|
||||
$o->created = $o->date_created;
|
||||
$o->date_created = NULL;
|
||||
$o->save();
|
||||
});
|
||||
|
||||
\App\Models\Video::each(function($o) {
|
||||
$o->created = $o->date_created;
|
||||
$o->date_created = NULL;
|
||||
$o->save();
|
||||
});
|
||||
|
||||
Schema::table('photo', function (Blueprint $table) {
|
||||
$table->dropColumn('date_created');
|
||||
});
|
||||
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn('date_created');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('photos', function (Blueprint $table) {
|
||||
$table->integer('date_created')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->integer('date_created')->nullable();
|
||||
});
|
||||
|
||||
\App\Models\Photo::each(function($o) {
|
||||
$o->date_created = $o->created;
|
||||
$o->created = NULL;
|
||||
$o->save();
|
||||
});
|
||||
|
||||
\App\Models\Video::each(function($o) {
|
||||
$o->date_created = $o->created;
|
||||
$o->created = NULL;
|
||||
$o->save();
|
||||
});
|
||||
|
||||
Schema::table('videos', function (Blueprint $table) {
|
||||
$table->dropColumn(['created','created_manual','ignore_duplicate']);
|
||||
});
|
||||
|
||||
Schema::table('photo', function (Blueprint $table) {
|
||||
$table->dropColumn(['created','created_manual','ignore_duplicate']);
|
||||
});
|
||||
}
|
||||
}
|
@ -21,12 +21,13 @@
|
||||
|
||||
<form action="{{ $return }}" method="POST">
|
||||
{{ csrf_field() }}
|
||||
<input type="hidden" name="type" value="{{ $type }}">
|
||||
|
||||
@include('catalog.widgets.duplicates')
|
||||
|
||||
<div class="pb-2"><button class="btn btn-sm btn-danger">Confirm Delete</button></div>
|
||||
|
||||
<input type="hidden" name="pagenext" value="{{ $catalog->hasMorePages() ? $catalog->currentPage()+1 : NULL }}">
|
||||
<input type="hidden" name="page" value="{{ $catalog->hasMorePages() ? $catalog->currentPage()+1 : NULL }}">
|
||||
</form>
|
||||
@else
|
||||
NONE!
|
||||
|
@ -21,10 +21,13 @@
|
||||
|
||||
<form action="{{ $return }}" method="POST">
|
||||
{{ csrf_field() }}
|
||||
<input type="hidden" name="type" value="{{ $type }}">
|
||||
|
||||
@include('catalog.widgets.duplicates')
|
||||
|
||||
<div class="pb-2"><button class="btn btn-sm btn-primary">Update</button></div>
|
||||
|
||||
<input type="hidden" name="page" value="{{ $catalog->currentPage() }}">
|
||||
</form>
|
||||
@else
|
||||
NONE!
|
||||
|
@ -6,7 +6,15 @@
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@php
|
||||
// Remember what we have rendered
|
||||
$rendered = collect();
|
||||
@endphp
|
||||
|
||||
@foreach ($catalog as $o)
|
||||
@if($rendered->search($o->id)) @continue @endif
|
||||
@php($rendered->push($o->id))
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
@ -14,7 +22,7 @@
|
||||
@include($o->type.'.widgets.thumbnail',['o'=>$o])
|
||||
</td>
|
||||
|
||||
@if (! ($d=$o->duplicates()->get())->count())
|
||||
@if (! ($d=$o->myduplicates()->get())->count())
|
||||
<td>
|
||||
No other duplicates found?
|
||||
</td>
|
||||
@ -22,6 +30,8 @@
|
||||
|
||||
@else
|
||||
@foreach($d as $item)
|
||||
@if($rendered->search($item->id)) @continue @endif
|
||||
@php($rendered->push($item->id))
|
||||
<td>
|
||||
<input type="hidden" name="items[]" value="{{ $item->id }}">
|
||||
@include($item->type.'.widgets.thumbnail',['o'=>$item])
|
||||
|
@ -10,6 +10,7 @@
|
||||
@section('contentheader_description')
|
||||
@if(! $o->scanned)<button class="btn btn-sm btn-info">TO SCAN</button>@endif
|
||||
@if($o->duplicate)<button class="btn btn-sm btn-warning">DUPLICATE</button>@endif
|
||||
@if($o->ignore_duplicate)<button class="btn btn-sm btn-secondary">DUPLICATE IGNORE</button>@endif
|
||||
@if($o->remove)<button class="btn btn-sm btn-danger">PENDING DELETE</button>@endif
|
||||
@endsection
|
||||
@section('page_title')
|
||||
@ -41,10 +42,10 @@
|
||||
<div class="col-9">
|
||||
<div class="dl-horizontal">
|
||||
<dt>Signature</dt><dd>{{ $o->signature(TRUE) }}</dd>
|
||||
<dt>Filename</dt><dd>{{ $o->file_path(TRUE) }}<dd>
|
||||
<dt>Filename</dt><dd>{{ $o->filename }}<dd>
|
||||
|
||||
@if ($o->shouldMove())
|
||||
<dt>NEW Filename</dt><dd>{{ $o->file_path(TRUE,TRUE) }}<dd>
|
||||
<dt>NEW Filename</dt><dd>{{ $o->file_name() }}<dd>
|
||||
@endif
|
||||
|
||||
<dt>Size</dt><dd>{{ $o->file_size() }}<dd>
|
||||
@ -70,7 +71,7 @@
|
||||
</table>
|
||||
</dd>
|
||||
<hr>
|
||||
@if($x = $o->duplicates()->get())
|
||||
@if(($x=$o->myduplicates()->get())->count())
|
||||
<dt>Duplicates</dt>
|
||||
<dd>
|
||||
@foreach($x as $oo)
|
||||
|
@ -2,20 +2,21 @@
|
||||
'ID'=>['id','idlink'],
|
||||
'Signature'=>['signature','signature'],
|
||||
'File Signature'=>['file_signature','file_signature'],
|
||||
'Date Created'=>['date_created','date_created'],
|
||||
'Date Created'=>['created','created'],
|
||||
'Filename'=>['filename','filename'],
|
||||
'Filesize'=>['filesize','filesize'],
|
||||
'Dimensions'=>['height','dimensions'],
|
||||
'Duplicate'=>['duplicate','duplicatecheckbox'],
|
||||
'Flagged'=>['flag','flagcheckbox'],
|
||||
'Ignore Duplicate'=>['ignore','ignorecheckbox'],
|
||||
'Delete'=>['remove','removecheckbox'],
|
||||
];?>
|
||||
|
||||
<div class="card card-widget">
|
||||
<div class="card-header">
|
||||
<div class="user-block">
|
||||
<i class="float-left fa fa-2x fa-camera"></i><span class="username"><a href="#">#{{ $o->id }} - {{ \Illuminate\Support\Str::limit($o->filename,12).\Illuminate\Support\Str::substr($o->filename,-16) }}</a></span>
|
||||
<span class="description">{{ $o->date_created ? $o->date_created->toDateTimeString() : '-' }} [{{ $o->device() }}]</span>
|
||||
<i class="float-left fa fa-2x fa-camera"></i><span class="username"><a href="{{ url('p/info',$o->id) }}">#{{ $o->id }} - {{ \Illuminate\Support\Str::limit($o->filename,12).\Illuminate\Support\Str::substr($o->filename,-16) }}</a></span>
|
||||
<span class="description">{{ $o->created ? $o->created->toDateTimeString() : '-' }} [{{ $o->device() }}]</span>
|
||||
</div>
|
||||
<!-- /.user-block -->
|
||||
<div class="card-tools">
|
||||
|
@ -15,7 +15,7 @@
|
||||
<li class="nav-item">
|
||||
<a href="{{ url('p/duplicates') }}" class="nav-link @if(preg_match('#^p/duplicates$#',request()->path())) active @endif">
|
||||
<i class="fa fa-link nav-icon"></i> <p>Duplicate</p>
|
||||
<span class="badge badge-warning right">{{ \App\Models\Photo::where('duplicate',TRUE)->count() }}</span>
|
||||
<span class="badge badge-warning right">{{ \App\Models\Photo::duplicates()->count() }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
<li class="nav-item">
|
||||
<a href="{{ url('v/duplicates') }}" class="nav-link @if(preg_match('#^v/duplicates$#',request()->path())) active @endif">
|
||||
<i class="fa fa-link nav-icon"></i> <p>Duplicate</p>
|
||||
<span class="badge badge-warning right">{{ \App\Models\Video::where('duplicate',TRUE)->count() }}</span>
|
||||
<span class="badge badge-warning right">{{ \App\Models\Video::duplicates()->count() }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
@section('contentheader_description')
|
||||
@if(! $o->scanned)<button class="btn btn-sm btn-info">TO SCAN</button>@endif
|
||||
@if($o->duplicate)<button class="btn btn-sm btn-warning">DUPLICATE</button>@endif
|
||||
@if($o->ignore_duplicate)<button class="btn btn-sm btn-secondary">DUPLICATE IGNORE</button>@endif
|
||||
@if($o->remove)<button class="btn btn-sm btn-danger">PENDING DELETE</button>@endif
|
||||
@endsection
|
||||
@section('page_title')
|
||||
@ -41,10 +42,10 @@
|
||||
<div class="col-8">
|
||||
<div class="dl-horizontal">
|
||||
<dt>Signature</dt><dd>{{ $o->signature(TRUE) }}</dd>
|
||||
<dt>Filename</dt><dd>{{ $o->file_path(TRUE) }}<dd>
|
||||
<dt>Filename</dt><dd>{{ $o->filename }}<dd>
|
||||
|
||||
@if ($o->shouldMove())
|
||||
<dt>NEW Filename</dt><dd>{{ $o->file_path(TRUE,TRUE) }}<dd>
|
||||
<dt>NEW Filename</dt><dd>{{ $o->file_name() }}<dd>
|
||||
@endif
|
||||
|
||||
<dt>Size</dt><dd>{{ $o->file_size() }}<dd>
|
||||
@ -69,7 +70,7 @@
|
||||
@endif
|
||||
</dd>
|
||||
|
||||
@if($x = $o->duplicates()->get())
|
||||
@if(($x=$o->myduplicates()->get())->count())
|
||||
<dt>Duplicates</dt>
|
||||
<dd>
|
||||
@foreach($x as $oo)
|
||||
|
@ -2,21 +2,22 @@
|
||||
'ID'=>['id','idlink'],
|
||||
'Signature'=>['signature','signature'],
|
||||
'File Signature'=>['file_signature','file_signature'],
|
||||
'Date Created'=>['date_created','date_created'],
|
||||
'Date Created'=>['created','created'],
|
||||
'Filename'=>['filename','filename'],
|
||||
'Filesize'=>['filesize','filesize'],
|
||||
'Dimensions'=>['height','dimensions'],
|
||||
'Length'=>['length','length'],
|
||||
'Duplicate'=>['duplicate','duplicatecheckbox'],
|
||||
'Flagged'=>['flag','flagcheckbox'],
|
||||
'Ignore Duplicate'=>['ignore','ignorecheckbox'],
|
||||
'Delete'=>['remove','removecheckbox'],
|
||||
];?>
|
||||
|
||||
<div class="card card-widget">
|
||||
<div class="card-header">
|
||||
<div class="user-block">
|
||||
<i class="float-left fa fa-2x fa-camera"></i><span class="username"><a href="#">#{{ $o->id }} - {{ \Illuminate\Support\Str::limit($o->filename,12).\Illuminate\Support\Str::substr($o->filename,-16) }}</a></span>
|
||||
<span class="description">{{ $o->date_created ? $o->date_created->toDateTimeString() : '-' }} [{{ $o->device() }}]</span>
|
||||
<i class="float-left fa fa-2x fa-camera"></i><span class="username"><a href="{{ url('v/info',$o->id) }}">#{{ $o->id }} - {{ \Illuminate\Support\Str::limit($o->filename,12).\Illuminate\Support\Str::substr($o->filename,-16) }}</a></span>
|
||||
<span class="description">{{ $o->created ? $o->created->toDateTimeString() : '-' }} [{{ $o->device() }}]</span>
|
||||
</div>
|
||||
<!-- /.user-block -->
|
||||
<div class="card-tools">
|
||||
@ -28,7 +29,7 @@
|
||||
|
||||
<!-- /.card-body -->
|
||||
<div class="card-body">
|
||||
<a href="{{ url('p/view',$o->id) }}" target="{{ $o->id }}">{!! $o->getHtmlImageURL() !!}</a>
|
||||
<a href="{{ url('v/view',$o->id) }}" target="{{ $o->id }}">{!! $o->getHtmlImageURL() !!}</a>
|
||||
</div>
|
||||
|
||||
<!-- /.card-body -->
|
||||
|
Loading…
Reference in New Issue
Block a user